Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / sources / NavigatorView.js
blob67ab24da6bc9e0df78b58c6e9e19f06af436a121
1 /*
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
6 * met:
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
14 * distribution.
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.
29 /**
30 * @constructor
31 * @extends {WebInspector.VBox}
33 WebInspector.NavigatorView = function()
35 WebInspector.VBox.call(this);
36 this.registerRequiredCSS("sources/navigatorView.css");
38 this.element.classList.add("navigator-container");
39 var scriptsOutlineElement = this.element.createChild("div", "navigator");
40 this._scriptsTree = new TreeOutline();
41 this._scriptsTree.setComparator(WebInspector.NavigatorView._treeElementsCompare);
42 this._scriptsTree.element.classList.add("outline-disclosure");
43 scriptsOutlineElement.appendChild(this._scriptsTree.element);
45 this.setDefaultFocusedElement(this._scriptsTree.element);
47 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.NavigatorUISourceCodeTreeNode>} */
48 this._uiSourceCodeNodes = new Map();
49 /** @type {!Map.<!WebInspector.NavigatorTreeNode, !Map.<string, !WebInspector.NavigatorFolderTreeNode>>} */
50 this._subfolderNodes = new Map();
52 this._rootNode = new WebInspector.NavigatorRootTreeNode(this);
53 this._rootNode.populate();
55 this.element.addEventListener("contextmenu", this.handleContextMenu.bind(this), false);
58 WebInspector.NavigatorView.Events = {
59 ItemSelected: "ItemSelected",
60 ItemRenamed: "ItemRenamed",
63 WebInspector.NavigatorView.Types = {
64 Root: "Root",
65 Domain: "Domain",
66 Folder: "Folder",
67 UISourceCode: "UISourceCode",
68 FileSystem: "FileSystem"
71 /**
72 * @param {string} type
73 * @return {string}
75 WebInspector.NavigatorView.iconClassForType = function(type)
77 if (type === WebInspector.NavigatorView.Types.Domain)
78 return "navigator-domain-tree-item";
79 if (type === WebInspector.NavigatorView.Types.FileSystem)
80 return "navigator-folder-tree-item";
81 return "navigator-folder-tree-item";
84 /**
85 * @param {!WebInspector.ContextMenu} contextMenu
87 WebInspector.NavigatorView.appendAddFolderItem = function(contextMenu)
89 function addFolder()
91 WebInspector.isolatedFileSystemManager.addFileSystem();
94 var addFolderLabel = WebInspector.UIString.capitalize("Add ^folder to ^workspace");
95 contextMenu.appendItem(addFolderLabel, addFolder);
98 WebInspector.NavigatorView.prototype = {
99 setWorkspace: function(workspace)
101 this._workspace = workspace;
102 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
103 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
104 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved.bind(this), this);
107 wasShown: function()
109 if (this._loaded)
110 return;
111 this._loaded = true;
112 this._workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this));
116 * @param {!WebInspector.UISourceCode} uiSourceCode
117 * @return {boolean}
119 accept: function(uiSourceCode)
121 return !uiSourceCode.project().isServiceProject();
125 * @param {!WebInspector.UISourceCode} uiSourceCode
127 _addUISourceCode: function(uiSourceCode)
129 if (!this.accept(uiSourceCode))
130 return;
131 var projectNode = this._projectNode(uiSourceCode.project());
132 var folderNode = this._folderNode(projectNode, uiSourceCode.parentPath());
133 var uiSourceCodeNode = new WebInspector.NavigatorUISourceCodeTreeNode(this, uiSourceCode);
134 this._uiSourceCodeNodes.set(uiSourceCode, uiSourceCodeNode);
135 folderNode.appendChild(uiSourceCodeNode);
139 * @param {!WebInspector.Event} event
141 _uiSourceCodeAdded: function(event)
143 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
144 this._addUISourceCode(uiSourceCode);
148 * @param {!WebInspector.Event} event
150 _uiSourceCodeRemoved: function(event)
152 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
153 this._removeUISourceCode(uiSourceCode);
157 * @param {!WebInspector.Event} event
159 _projectRemoved: function(event)
161 var project = /** @type {!WebInspector.Project} */ (event.data);
162 project.removeEventListener(WebInspector.Project.Events.DisplayNameUpdated, this._updateProjectNodeTitle, this);
163 var uiSourceCodes = project.uiSourceCodes();
164 for (var i = 0; i < uiSourceCodes.length; ++i)
165 this._removeUISourceCode(uiSourceCodes[i]);
169 * @param {!WebInspector.Project} project
170 * @return {!WebInspector.NavigatorTreeNode}
172 _projectNode: function(project)
174 if (!project.displayName())
175 return this._rootNode;
177 var projectNode = this._rootNode.child(project.id());
178 if (!projectNode) {
179 projectNode = this._createProjectNode(project);
180 this._rootNode.appendChild(projectNode);
182 return projectNode;
186 * @param {!WebInspector.Project} project
187 * @return {!WebInspector.NavigatorTreeNode}
189 _createProjectNode: function(project)
191 var type = project.type() === WebInspector.projectTypes.FileSystem ? WebInspector.NavigatorView.Types.FileSystem : WebInspector.NavigatorView.Types.Domain;
192 var projectNode = new WebInspector.NavigatorFolderTreeNode(this, project, project.id(), type, "", project.displayName());
193 project.addEventListener(WebInspector.Project.Events.DisplayNameUpdated, this._updateProjectNodeTitle, this);
194 return projectNode;
198 * @param {!WebInspector.Event} event
200 _updateProjectNodeTitle: function(event)
202 var project = /** @type {!WebInspector.Project} */(event.target);
203 var projectNode = this._rootNode.child(project.id());
204 if (!projectNode)
205 return;
206 projectNode.treeNode().titleText = project.displayName();
210 * @param {!WebInspector.NavigatorTreeNode} projectNode
211 * @param {string} folderPath
212 * @return {!WebInspector.NavigatorTreeNode}
214 _folderNode: function(projectNode, folderPath)
216 if (!folderPath)
217 return projectNode;
219 var subfolderNodes = this._subfolderNodes.get(projectNode);
220 if (!subfolderNodes) {
221 subfolderNodes = /** @type {!Map.<string, !WebInspector.NavigatorFolderTreeNode>} */ (new Map());
222 this._subfolderNodes.set(projectNode, subfolderNodes);
225 var folderNode = subfolderNodes.get(folderPath);
226 if (folderNode)
227 return folderNode;
229 var parentNode = projectNode;
230 var index = folderPath.lastIndexOf("/");
231 if (index !== -1)
232 parentNode = this._folderNode(projectNode, folderPath.substring(0, index));
234 var name = folderPath.substring(index + 1);
235 folderNode = new WebInspector.NavigatorFolderTreeNode(this, null, name, WebInspector.NavigatorView.Types.Folder, folderPath, name);
236 subfolderNodes.set(folderPath, folderNode);
237 parentNode.appendChild(folderNode);
238 return folderNode;
242 * @param {!WebInspector.UISourceCode} uiSourceCode
243 * @param {boolean=} select
245 revealUISourceCode: function(uiSourceCode, select)
247 var node = this._uiSourceCodeNodes.get(uiSourceCode);
248 if (!node)
249 return;
250 if (this._scriptsTree.selectedTreeElement)
251 this._scriptsTree.selectedTreeElement.deselect();
252 this._lastSelectedUISourceCode = uiSourceCode;
253 node.reveal(select);
257 * @param {!WebInspector.UISourceCode} uiSourceCode
258 * @param {boolean} focusSource
260 _sourceSelected: function(uiSourceCode, focusSource)
262 this._lastSelectedUISourceCode = uiSourceCode;
263 var data = { uiSourceCode: uiSourceCode, focusSource: focusSource};
264 this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemSelected, data);
268 * @param {!WebInspector.UISourceCode} uiSourceCode
270 sourceDeleted: function(uiSourceCode)
275 * @param {!WebInspector.UISourceCode} uiSourceCode
277 _removeUISourceCode: function(uiSourceCode)
279 var node = this._uiSourceCodeNodes.get(uiSourceCode);
280 if (!node)
281 return;
283 var projectNode = this._projectNode(uiSourceCode.project());
284 var subfolderNodes = this._subfolderNodes.get(projectNode);
285 var parentNode = node.parent;
286 this._uiSourceCodeNodes.remove(uiSourceCode);
287 parentNode.removeChild(node);
288 node = parentNode;
290 while (node) {
291 parentNode = node.parent;
292 if (!parentNode || !node.isEmpty())
293 break;
294 if (subfolderNodes)
295 subfolderNodes.remove(node._folderPath);
296 parentNode.removeChild(node);
297 node = parentNode;
302 * @param {!WebInspector.UISourceCode} uiSourceCode
304 _updateIcon: function(uiSourceCode)
306 var node = this._uiSourceCodeNodes.get(uiSourceCode);
307 node.updateIcon();
310 reset: function()
312 var nodes = this._uiSourceCodeNodes.valuesArray();
313 for (var i = 0; i < nodes.length; ++i)
314 nodes[i].dispose();
316 this._scriptsTree.removeChildren();
317 this._uiSourceCodeNodes.clear();
318 this._subfolderNodes.clear();
319 this._rootNode.reset();
323 * @param {!Event} event
325 handleContextMenu: function(event)
327 var contextMenu = new WebInspector.ContextMenu(event);
328 WebInspector.NavigatorView.appendAddFolderItem(contextMenu);
329 contextMenu.show();
333 * @param {!WebInspector.Project} project
334 * @param {string} path
336 _handleContextMenuRefresh: function(project, path)
338 project.refresh(path);
342 * @param {!WebInspector.Project} project
343 * @param {string} path
344 * @param {!WebInspector.UISourceCode=} uiSourceCode
346 _handleContextMenuCreate: function(project, path, uiSourceCode)
348 this.create(project, path, uiSourceCode);
352 * @param {!WebInspector.UISourceCode} uiSourceCode
354 _handleContextMenuRename: function(uiSourceCode)
356 this.rename(uiSourceCode, false);
360 * @param {!WebInspector.Project} project
361 * @param {string} path
363 _handleContextMenuExclude: function(project, path)
365 var shouldExclude = window.confirm(WebInspector.UIString("Are you sure you want to exclude this folder?"));
366 if (shouldExclude) {
367 WebInspector.startBatchUpdate();
368 project.excludeFolder(path);
369 WebInspector.endBatchUpdate();
374 * @param {!WebInspector.UISourceCode} uiSourceCode
376 _handleContextMenuDelete: function(uiSourceCode)
378 var shouldDelete = window.confirm(WebInspector.UIString("Are you sure you want to delete this file?"));
379 if (shouldDelete)
380 uiSourceCode.project().deleteFile(uiSourceCode.path());
384 * @param {!Event} event
385 * @param {!WebInspector.UISourceCode} uiSourceCode
387 handleFileContextMenu: function(event, uiSourceCode)
389 var contextMenu = new WebInspector.ContextMenu(event);
390 contextMenu.appendApplicableItems(uiSourceCode);
391 contextMenu.appendSeparator();
393 var project = uiSourceCode.project();
394 if (project.type() === WebInspector.projectTypes.FileSystem) {
395 var path = uiSourceCode.parentPath();
396 contextMenu.appendItem(WebInspector.UIString.capitalize("Rename\u2026"), this._handleContextMenuRename.bind(this, uiSourceCode));
397 contextMenu.appendItem(WebInspector.UIString.capitalize("Make a ^copy\u2026"), this._handleContextMenuCreate.bind(this, project, path, uiSourceCode));
398 contextMenu.appendItem(WebInspector.UIString.capitalize("Delete"), this._handleContextMenuDelete.bind(this, uiSourceCode));
399 contextMenu.appendSeparator();
402 contextMenu.show();
406 * @param {!Event} event
407 * @param {!WebInspector.NavigatorFolderTreeNode} node
409 handleFolderContextMenu: function(event, node)
411 var contextMenu = new WebInspector.ContextMenu(event);
412 var path = "/";
413 var projectNode = node;
414 while (projectNode.parent !== this._rootNode) {
415 path = "/" + projectNode.id + path;
416 projectNode = projectNode.parent;
419 var project = projectNode._project;
421 if (project.type() === WebInspector.projectTypes.FileSystem) {
422 contextMenu.appendItem(WebInspector.UIString.capitalize("Refresh"), this._handleContextMenuRefresh.bind(this, project, path));
423 contextMenu.appendItem(WebInspector.UIString.capitalize("New ^file"), this._handleContextMenuCreate.bind(this, project, path));
424 contextMenu.appendItem(WebInspector.UIString.capitalize("Exclude ^folder"), this._handleContextMenuExclude.bind(this, project, path));
426 contextMenu.appendSeparator();
427 WebInspector.NavigatorView.appendAddFolderItem(contextMenu);
429 function removeFolder()
431 var shouldRemove = window.confirm(WebInspector.UIString("Are you sure you want to remove this folder?"));
432 if (shouldRemove)
433 project.remove();
436 if (project.type() === WebInspector.projectTypes.FileSystem && node === projectNode) {
437 var removeFolderLabel = WebInspector.UIString.capitalize("Remove ^folder from ^workspace");
438 contextMenu.appendItem(removeFolderLabel, removeFolder);
441 contextMenu.show();
445 * @param {!WebInspector.UISourceCode} uiSourceCode
446 * @param {boolean} deleteIfCanceled
448 rename: function(uiSourceCode, deleteIfCanceled)
450 var node = this._uiSourceCodeNodes.get(uiSourceCode);
451 console.assert(node);
452 node.rename(callback.bind(this));
455 * @this {WebInspector.NavigatorView}
456 * @param {boolean} committed
458 function callback(committed)
460 if (!committed) {
461 if (deleteIfCanceled)
462 uiSourceCode.remove();
463 return;
466 this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemRenamed, uiSourceCode);
467 this._updateIcon(uiSourceCode);
468 this._sourceSelected(uiSourceCode, true);
473 * @param {!WebInspector.Project} project
474 * @param {string} path
475 * @param {!WebInspector.UISourceCode=} uiSourceCodeToCopy
477 create: function(project, path, uiSourceCodeToCopy)
479 var filePath;
480 var uiSourceCode;
483 * @this {WebInspector.NavigatorView}
484 * @param {?string} content
486 function contentLoaded(content)
488 createFile.call(this, content || "");
491 if (uiSourceCodeToCopy)
492 uiSourceCodeToCopy.requestContent(contentLoaded.bind(this));
493 else
494 createFile.call(this);
497 * @this {WebInspector.NavigatorView}
498 * @param {string=} content
500 function createFile(content)
502 project.createFile(path, null, content || "", fileCreated.bind(this));
506 * @this {WebInspector.NavigatorView}
507 * @param {?string} path
509 function fileCreated(path)
511 if (!path)
512 return;
513 filePath = path;
514 uiSourceCode = project.uiSourceCode(filePath);
515 if (!uiSourceCode) {
516 console.assert(uiSourceCode);
517 return;
519 this._sourceSelected(uiSourceCode, false);
520 this.revealUISourceCode(uiSourceCode, true);
521 this.rename(uiSourceCode, true);
525 __proto__: WebInspector.VBox.prototype
529 * @constructor
530 * @extends {WebInspector.NavigatorView}
532 WebInspector.SourcesNavigatorView = function()
534 WebInspector.NavigatorView.call(this);
535 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this);
538 WebInspector.SourcesNavigatorView.prototype = {
540 * @override
541 * @param {!WebInspector.UISourceCode} uiSourceCode
542 * @return {boolean}
544 accept: function(uiSourceCode)
546 if (!WebInspector.NavigatorView.prototype.accept(uiSourceCode))
547 return false;
548 return uiSourceCode.project().type() !== WebInspector.projectTypes.ContentScripts && uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets;
553 * @param {!WebInspector.Event} event
555 _inspectedURLChanged: function(event)
557 var nodes = this._uiSourceCodeNodes.valuesArray();
558 for (var i = 0; i < nodes.length; ++i) {
559 var uiSourceCode = nodes[i].uiSourceCode();
560 var inspectedPageURL = WebInspector.targetManager.inspectedPageURL();
561 if (inspectedPageURL && WebInspector.networkMapping.networkURL(uiSourceCode) === inspectedPageURL)
562 this.revealUISourceCode(uiSourceCode, true);
567 * @override
568 * @param {!WebInspector.UISourceCode} uiSourceCode
570 _addUISourceCode: function(uiSourceCode)
572 WebInspector.NavigatorView.prototype._addUISourceCode.call(this, uiSourceCode);
573 var inspectedPageURL = WebInspector.targetManager.inspectedPageURL();
574 if (inspectedPageURL && WebInspector.networkMapping.networkURL(uiSourceCode) === inspectedPageURL)
575 this.revealUISourceCode(uiSourceCode, true);
578 __proto__: WebInspector.NavigatorView.prototype
582 * @constructor
583 * @extends {WebInspector.NavigatorView}
585 WebInspector.ContentScriptsNavigatorView = function()
587 WebInspector.NavigatorView.call(this);
590 WebInspector.ContentScriptsNavigatorView.prototype = {
592 * @override
593 * @param {!WebInspector.UISourceCode} uiSourceCode
594 * @return {boolean}
596 accept: function(uiSourceCode)
598 if (!WebInspector.NavigatorView.prototype.accept(uiSourceCode))
599 return false;
600 return uiSourceCode.project().type() === WebInspector.projectTypes.ContentScripts;
603 __proto__: WebInspector.NavigatorView.prototype
607 * @param {!TreeElement} treeElement1
608 * @param {!TreeElement} treeElement2
609 * @return {number}
611 WebInspector.NavigatorView._treeElementsCompare = function compare(treeElement1, treeElement2)
613 // Insert in the alphabetical order, first domains, then folders, then scripts.
614 function typeWeight(treeElement)
616 var type = treeElement.type();
617 if (type === WebInspector.NavigatorView.Types.Domain) {
618 if (treeElement.titleText === WebInspector.targetManager.inspectedPageDomain())
619 return 1;
620 return 2;
622 if (type === WebInspector.NavigatorView.Types.FileSystem)
623 return 3;
624 if (type === WebInspector.NavigatorView.Types.Folder)
625 return 4;
626 return 5;
629 var typeWeight1 = typeWeight(treeElement1);
630 var typeWeight2 = typeWeight(treeElement2);
632 var result;
633 if (typeWeight1 > typeWeight2)
634 result = 1;
635 else if (typeWeight1 < typeWeight2)
636 result = -1;
637 else {
638 var title1 = treeElement1.titleText;
639 var title2 = treeElement2.titleText;
640 result = title1.compareTo(title2);
642 return result;
646 * @constructor
647 * @extends {TreeElement}
648 * @param {string} type
649 * @param {string} title
650 * @param {!Array.<string>} iconClasses
651 * @param {boolean} expandable
652 * @param {boolean=} noIcon
654 WebInspector.BaseNavigatorTreeElement = function(type, title, iconClasses, expandable, noIcon)
656 this._type = type;
657 TreeElement.call(this, "", expandable);
658 this._titleText = title;
659 this._iconClasses = iconClasses;
660 this._noIcon = noIcon;
663 WebInspector.BaseNavigatorTreeElement.prototype = {
664 onattach: function()
666 this.listItemElement.removeChildren();
667 if (this._iconClasses) {
668 for (var i = 0; i < this._iconClasses.length; ++i)
669 this.listItemElement.classList.add(this._iconClasses[i]);
672 this.listItemElement.createChild("div", "selection");
674 if (!this._noIcon)
675 this.imageElement = this.listItemElement.createChild("img", "icon");
677 this.titleElement = this.listItemElement.createChild("div", "base-navigator-tree-element-title");
678 this.titleElement.textContent = this._titleText;
682 * @param {!Array.<string>} iconClasses
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]);
693 onreveal: function()
695 if (this.listItemElement)
696 this.listItemElement.scrollIntoViewIfNeeded(true);
700 * @return {string}
702 get titleText()
704 return this._titleText;
707 set titleText(titleText)
709 if (this._titleText === titleText)
710 return;
711 this._titleText = titleText || "";
712 if (this.titleElement) {
713 this.titleElement.textContent = this._titleText;
714 this.titleElement.title = this._titleText;
719 * @return {string}
721 type: function()
723 return this._type;
726 __proto__: TreeElement.prototype
730 * @constructor
731 * @extends {WebInspector.BaseNavigatorTreeElement}
732 * @param {!WebInspector.NavigatorView} navigatorView
733 * @param {string} type
734 * @param {string} title
736 WebInspector.NavigatorFolderTreeElement = function(navigatorView, type, title)
738 var iconClass = WebInspector.NavigatorView.iconClassForType(type);
739 WebInspector.BaseNavigatorTreeElement.call(this, type, title, [iconClass], true);
740 this._navigatorView = navigatorView;
743 WebInspector.NavigatorFolderTreeElement.prototype = {
744 onpopulate: function()
746 this._node.populate();
749 onattach: function()
751 WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
752 this.collapse();
753 this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
757 * @param {!WebInspector.NavigatorFolderTreeNode} node
759 setNode: function(node)
761 this._node = node;
762 var paths = [];
763 while (node && !node.isRoot()) {
764 paths.push(node._title);
765 node = node.parent;
767 paths.reverse();
768 this.tooltip = paths.join("/");
772 * @param {!Event} event
774 _handleContextMenuEvent: function(event)
776 if (!this._node)
777 return;
778 this.select();
779 this._navigatorView.handleFolderContextMenu(event, this._node);
782 __proto__: WebInspector.BaseNavigatorTreeElement.prototype
786 * @constructor
787 * @extends {WebInspector.BaseNavigatorTreeElement}
788 * @param {!WebInspector.NavigatorView} navigatorView
789 * @param {!WebInspector.UISourceCode} uiSourceCode
790 * @param {string} title
792 WebInspector.NavigatorSourceTreeElement = function(navigatorView, uiSourceCode, title)
794 this._navigatorView = navigatorView;
795 this._uiSourceCode = uiSourceCode;
796 WebInspector.BaseNavigatorTreeElement.call(this, WebInspector.NavigatorView.Types.UISourceCode, title, this._calculateIconClasses(), false);
797 this.tooltip = uiSourceCode.originURL();
800 WebInspector.NavigatorSourceTreeElement.prototype = {
802 * @return {!WebInspector.UISourceCode}
804 get uiSourceCode()
806 return this._uiSourceCode;
810 * @return {!Array.<string>}
812 _calculateIconClasses: function()
814 return ["navigator-" + this._uiSourceCode.contentType().name() + "-tree-item"];
817 updateIcon: function()
819 this.updateIconClasses(this._calculateIconClasses());
822 onattach: function()
824 WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
825 this.listItemElement.draggable = true;
826 this.listItemElement.addEventListener("click", this._onclick.bind(this), false);
827 this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
828 this.listItemElement.addEventListener("mousedown", this._onmousedown.bind(this), false);
829 this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
832 _onmousedown: function(event)
834 if (event.which === 1) // Warm-up data for drag'n'drop
835 this._uiSourceCode.requestContent(callback.bind(this));
837 * @param {?string} content
838 * @this {WebInspector.NavigatorSourceTreeElement}
840 function callback(content)
842 this._warmedUpContent = content;
846 _shouldRenameOnMouseDown: function()
848 if (!this._uiSourceCode.canRename())
849 return false;
850 var isSelected = this === this.treeOutline.selectedTreeElement;
851 var document = this.treeOutline.element.ownerDocument;
852 var isFocused = this.treeOutline.element.isSelfOrAncestor(document.activeElement);
853 return isSelected && isFocused && !WebInspector.isBeingEdited(this.treeOutline.element);
856 selectOnMouseDown: function(event)
858 if (event.which !== 1 || !this._shouldRenameOnMouseDown()) {
859 TreeElement.prototype.selectOnMouseDown.call(this, event);
860 return;
862 setTimeout(rename.bind(this), 300);
865 * @this {WebInspector.NavigatorSourceTreeElement}
867 function rename()
869 if (this._shouldRenameOnMouseDown())
870 this._navigatorView.rename(this.uiSourceCode, false);
874 _ondragstart: function(event)
876 event.dataTransfer.setData("text/plain", this._warmedUpContent);
877 event.dataTransfer.effectAllowed = "copy";
878 return true;
882 * @override
883 * @return {boolean}
885 onspace: function()
887 this._navigatorView._sourceSelected(this.uiSourceCode, true);
888 return true;
892 * @param {!Event} event
894 _onclick: function(event)
896 this._navigatorView._sourceSelected(this.uiSourceCode, false);
900 * @override
901 * @return {boolean}
903 ondblclick: function(event)
905 var middleClick = event.button === 1;
906 this._navigatorView._sourceSelected(this.uiSourceCode, !middleClick);
907 return false;
911 * @override
912 * @return {boolean}
914 onenter: function()
916 this._navigatorView._sourceSelected(this.uiSourceCode, true);
917 return true;
921 * @override
922 * @return {boolean}
924 ondelete: function()
926 this._navigatorView.sourceDeleted(this.uiSourceCode);
927 return true;
931 * @param {!Event} event
933 _handleContextMenuEvent: function(event)
935 this.select();
936 this._navigatorView.handleFileContextMenu(event, this._uiSourceCode);
939 __proto__: WebInspector.BaseNavigatorTreeElement.prototype
943 * @constructor
944 * @param {string} id
946 WebInspector.NavigatorTreeNode = function(id)
948 this.id = id;
949 /** @type {!Map.<string, !WebInspector.NavigatorTreeNode>} */
950 this._children = new Map();
953 WebInspector.NavigatorTreeNode.prototype = {
955 * @return {!TreeElement}
957 treeNode: function() { throw "Not implemented"; },
959 dispose: function() { },
962 * @return {boolean}
964 isRoot: function()
966 return false;
970 * @return {boolean}
972 hasChildren: function()
974 return true;
977 populate: function()
979 if (this.isPopulated())
980 return;
981 if (this.parent)
982 this.parent.populate();
983 this._populated = true;
984 this.wasPopulated();
987 wasPopulated: function()
989 var children = this.children();
990 for (var i = 0; i < children.length; ++i)
991 this.treeNode().appendChild(/** @type {!TreeElement} */ (children[i].treeNode()));
995 * @param {!WebInspector.NavigatorTreeNode} node
997 didAddChild: function(node)
999 if (this.isPopulated())
1000 this.treeNode().appendChild(/** @type {!TreeElement} */ (node.treeNode()));
1004 * @param {!WebInspector.NavigatorTreeNode} node
1006 willRemoveChild: function(node)
1008 if (this.isPopulated())
1009 this.treeNode().removeChild(/** @type {!TreeElement} */ (node.treeNode()));
1013 * @return {boolean}
1015 isPopulated: function()
1017 return this._populated;
1021 * @return {boolean}
1023 isEmpty: function()
1025 return !this._children.size;
1029 * @param {string} id
1030 * @return {?WebInspector.NavigatorTreeNode}
1032 child: function(id)
1034 return this._children.get(id) || null;
1038 * @return {!Array.<!WebInspector.NavigatorTreeNode>}
1040 children: function()
1042 return this._children.valuesArray();
1046 * @param {!WebInspector.NavigatorTreeNode} node
1048 appendChild: function(node)
1050 this._children.set(node.id, node);
1051 node.parent = this;
1052 this.didAddChild(node);
1056 * @param {!WebInspector.NavigatorTreeNode} node
1058 removeChild: function(node)
1060 this.willRemoveChild(node);
1061 this._children.remove(node.id);
1062 delete node.parent;
1063 node.dispose();
1066 reset: function()
1068 this._children.clear();
1073 * @constructor
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 = {
1085 * @override
1086 * @return {boolean}
1088 isRoot: function()
1090 return true;
1094 * @override
1095 * @return {!TreeElement}
1097 treeNode: function()
1099 return this._navigatorView._scriptsTree.rootElement();
1102 __proto__: WebInspector.NavigatorTreeNode.prototype
1106 * @constructor
1107 * @extends {WebInspector.NavigatorTreeNode}
1108 * @param {!WebInspector.NavigatorView} navigatorView
1109 * @param {!WebInspector.UISourceCode} uiSourceCode
1111 WebInspector.NavigatorUISourceCodeTreeNode = function(navigatorView, uiSourceCode)
1113 WebInspector.NavigatorTreeNode.call(this, uiSourceCode.name());
1114 this._navigatorView = navigatorView;
1115 this._uiSourceCode = uiSourceCode;
1116 this._treeElement = null;
1119 WebInspector.NavigatorUISourceCodeTreeNode.prototype = {
1121 * @return {!WebInspector.UISourceCode}
1123 uiSourceCode: function()
1125 return this._uiSourceCode;
1128 updateIcon: function()
1130 if (this._treeElement)
1131 this._treeElement.updateIcon();
1135 * @override
1136 * @return {!TreeElement}
1138 treeNode: function()
1140 if (this._treeElement)
1141 return this._treeElement;
1143 this._treeElement = new WebInspector.NavigatorSourceTreeElement(this._navigatorView, this._uiSourceCode, "");
1144 this.updateTitle();
1146 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
1147 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
1148 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
1150 return this._treeElement;
1154 * @param {boolean=} ignoreIsDirty
1156 updateTitle: function(ignoreIsDirty)
1158 if (!this._treeElement)
1159 return;
1161 var titleText = this._uiSourceCode.displayName();
1162 if (!ignoreIsDirty && (this._uiSourceCode.isDirty() || this._uiSourceCode.hasUnsavedCommittedChanges()))
1163 titleText = "*" + titleText;
1164 this._treeElement.titleText = titleText;
1168 * @override
1169 * @return {boolean}
1171 hasChildren: function()
1173 return false;
1176 dispose: function()
1178 if (!this._treeElement)
1179 return;
1180 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
1181 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
1182 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
1185 _titleChanged: function(event)
1187 this.updateTitle();
1190 _workingCopyChanged: function(event)
1192 this.updateTitle();
1195 _workingCopyCommitted: function(event)
1197 this.updateTitle();
1201 * @param {boolean=} select
1203 reveal: function(select)
1205 this.parent.populate();
1206 this.parent.treeNode().expand();
1207 this._treeElement.reveal();
1208 if (select)
1209 this._treeElement.select(true);
1213 * @param {function(boolean)=} callback
1215 rename: function(callback)
1217 if (!this._treeElement)
1218 return;
1220 // Tree outline should be marked as edited as well as the tree element to prevent search from starting.
1221 var treeOutlineElement = this._treeElement.treeOutline.element;
1222 WebInspector.markBeingEdited(treeOutlineElement, true);
1225 * @param {!Element} element
1226 * @param {string} newTitle
1227 * @param {string} oldTitle
1228 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1230 function commitHandler(element, newTitle, oldTitle)
1232 if (newTitle !== oldTitle) {
1233 this._treeElement.titleText = newTitle;
1234 this._uiSourceCode.rename(newTitle, renameCallback.bind(this));
1235 return;
1237 afterEditing.call(this, true);
1241 * @param {boolean} success
1242 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1244 function renameCallback(success)
1246 if (!success) {
1247 WebInspector.markBeingEdited(treeOutlineElement, false);
1248 this.updateTitle();
1249 this.rename(callback);
1250 return;
1252 afterEditing.call(this, true);
1256 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1258 function cancelHandler()
1260 afterEditing.call(this, false);
1264 * @param {boolean} committed
1265 * @this {WebInspector.NavigatorUISourceCodeTreeNode}
1267 function afterEditing(committed)
1269 WebInspector.markBeingEdited(treeOutlineElement, false);
1270 this.updateTitle();
1271 this._treeElement.treeOutline.focus();
1272 if (callback)
1273 callback(committed);
1276 var editingConfig = new WebInspector.InplaceEditor.Config(commitHandler.bind(this), cancelHandler.bind(this));
1277 this.updateTitle(true);
1278 WebInspector.InplaceEditor.startEditing(this._treeElement.titleElement, editingConfig);
1279 treeOutlineElement.getComponentSelection().setBaseAndExtent(this._treeElement.titleElement, 0, this._treeElement.titleElement, 1);
1282 __proto__: WebInspector.NavigatorTreeNode.prototype
1286 * @constructor
1287 * @extends {WebInspector.NavigatorTreeNode}
1288 * @param {!WebInspector.NavigatorView} navigatorView
1289 * @param {?WebInspector.Project} project
1290 * @param {string} id
1291 * @param {string} type
1292 * @param {string} folderPath
1293 * @param {string} title
1295 WebInspector.NavigatorFolderTreeNode = function(navigatorView, project, id, type, folderPath, title)
1297 WebInspector.NavigatorTreeNode.call(this, id);
1298 this._navigatorView = navigatorView;
1299 this._project = project;
1300 this._type = type;
1301 this._folderPath = folderPath;
1302 this._title = title;
1305 WebInspector.NavigatorFolderTreeNode.prototype = {
1307 * @override
1308 * @return {!TreeElement}
1310 treeNode: function()
1312 if (this._treeElement)
1313 return this._treeElement;
1314 this._treeElement = this._createTreeElement(this._title, this);
1315 return this._treeElement;
1319 * @return {!TreeElement}
1321 _createTreeElement: function(title, node)
1323 var treeElement = new WebInspector.NavigatorFolderTreeElement(this._navigatorView, this._type, title);
1324 treeElement.setNode(node);
1325 return treeElement;
1328 wasPopulated: function()
1330 if (!this._treeElement || this._treeElement._node !== this)
1331 return;
1332 this._addChildrenRecursive();
1335 _addChildrenRecursive: function()
1337 var children = this.children();
1338 for (var i = 0; i < children.length; ++i) {
1339 var child = children[i];
1340 this.didAddChild(child);
1341 if (child instanceof WebInspector.NavigatorFolderTreeNode)
1342 child._addChildrenRecursive();
1346 _shouldMerge: function(node)
1348 return this._type !== WebInspector.NavigatorView.Types.Domain && node instanceof WebInspector.NavigatorFolderTreeNode;
1351 didAddChild: function(node)
1353 function titleForNode(node)
1355 return node._title;
1358 if (!this._treeElement)
1359 return;
1361 var children = this.children();
1363 if (children.length === 1 && this._shouldMerge(node)) {
1364 node._isMerged = true;
1365 this._treeElement.titleText = this._treeElement.titleText + "/" + node._title;
1366 node._treeElement = this._treeElement;
1367 this._treeElement.setNode(node);
1368 return;
1371 var oldNode;
1372 if (children.length === 2)
1373 oldNode = children[0] !== node ? children[0] : children[1];
1374 if (oldNode && oldNode._isMerged) {
1375 delete oldNode._isMerged;
1376 var mergedToNodes = [];
1377 mergedToNodes.push(this);
1378 var treeNode = this;
1379 while (treeNode._isMerged) {
1380 treeNode = treeNode.parent;
1381 mergedToNodes.push(treeNode);
1383 mergedToNodes.reverse();
1384 var titleText = mergedToNodes.map(titleForNode).join("/");
1386 var nodes = [];
1387 treeNode = oldNode;
1388 do {
1389 nodes.push(treeNode);
1390 children = treeNode.children();
1391 treeNode = children.length === 1 ? children[0] : null;
1392 } while (treeNode && treeNode._isMerged);
1394 if (!this.isPopulated()) {
1395 this._treeElement.titleText = titleText;
1396 this._treeElement.setNode(this);
1397 for (var i = 0; i < nodes.length; ++i) {
1398 delete nodes[i]._treeElement;
1399 delete nodes[i]._isMerged;
1401 return;
1403 var oldTreeElement = this._treeElement;
1404 var treeElement = this._createTreeElement(titleText, this);
1405 for (var i = 0; i < mergedToNodes.length; ++i)
1406 mergedToNodes[i]._treeElement = treeElement;
1407 oldTreeElement.parent.appendChild(treeElement);
1409 oldTreeElement.setNode(nodes[nodes.length - 1]);
1410 oldTreeElement.titleText = nodes.map(titleForNode).join("/");
1411 oldTreeElement.parent.removeChild(oldTreeElement);
1412 this._treeElement.appendChild(oldTreeElement);
1413 if (oldTreeElement.expanded)
1414 treeElement.expand();
1416 if (this.isPopulated())
1417 this._treeElement.appendChild(node.treeNode());
1420 willRemoveChild: function(node)
1422 if (node._isMerged || !this.isPopulated())
1423 return;
1424 this._treeElement.removeChild(node._treeElement);
1427 __proto__: WebInspector.NavigatorTreeNode.prototype