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.
30
* @extends {WebInspector.View}
33
WebInspector.NavigatorView = function()
35
WebInspector.View.call(this);
36
this.registerRequiredCSS("navigatorView.css");
38
this._treeSearchBoxElement = document.createElement("div");
39
this._treeSearchBoxElement.className = "navigator-tree-search-box";
40
this.element.appendChild(this._treeSearchBoxElement);
42
var scriptsTreeElement = document.createElement("ol");
43
this._scriptsTree = new WebInspector.NavigatorTreeOutline(this._treeSearchBoxElement, scriptsTreeElement);
45
var scriptsOutlineElement = document.createElement("div");
46
scriptsOutlineElement.addStyleClass("outline-disclosure");
47
scriptsOutlineElement.addStyleClass("navigator");
48
scriptsOutlineElement.appendChild(scriptsTreeElement);
50
this.element.addStyleClass("fill");
51
this.element.addStyleClass("navigator-container");
52
this.element.appendChild(scriptsOutlineElement);
53
this.setDefaultFocusedElement(this._scriptsTree.element);
55
this._folderTreeElements = {};
56
this._scriptTreeElementsByUISourceCode = new Map();
58
WebInspector.settings.showScriptFolders.addChangeListener(this._showScriptFoldersSettingChanged.bind(this));
62
WebInspector.NavigatorView.Events = {
63
ItemSelected: "ItemSelected",
64
FileRenamed: "FileRenamed"
67
WebInspector.NavigatorView.prototype = {
69
* @param {WebInspector.UISourceCode} uiSourceCode
71
addUISourceCode: function(uiSourceCode)
73
if (this._scriptTreeElementsByUISourceCode.get(uiSourceCode))
76
var scriptTreeElement = new WebInspector.NavigatorSourceTreeElement(this, uiSourceCode, "");
77
this._scriptTreeElementsByUISourceCode.put(uiSourceCode, scriptTreeElement);
78
this._updateScriptTitle(uiSourceCode);
79
this._addUISourceCodeListeners(uiSourceCode);
81
var folderTreeElement = this.getOrCreateFolderTreeElement(uiSourceCode);
82
folderTreeElement.appendChild(scriptTreeElement);
85
_uiSourceCodeTitleChanged: function(event)
87
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
88
this._updateScriptTitle(uiSourceCode)
91
_uiSourceCodeWorkingCopyChanged: function(event)
93
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
94
this._updateScriptTitle(uiSourceCode)
97
_uiSourceCodeWorkingCopyCommitted: function(event)
99
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
100
this._updateScriptTitle(uiSourceCode)
103
_uiSourceCodeFormattedChanged: function(event)
105
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
106
this._updateScriptTitle(uiSourceCode);
110
* @param {WebInspector.UISourceCode} uiSourceCode
111
* @param {boolean=} ignoreIsDirty
113
_updateScriptTitle: function(uiSourceCode, ignoreIsDirty)
115
var scriptTreeElement = this._scriptTreeElementsByUISourceCode.get(uiSourceCode);
116
if (!scriptTreeElement)
120
if (uiSourceCode.parsedURL.isValid) {
121
titleText = uiSourceCode.parsedURL.lastPathComponent;
122
if (uiSourceCode.parsedURL.queryParams)
123
titleText += "?" + uiSourceCode.parsedURL.queryParams;
124
} else if (uiSourceCode.parsedURL)
125
titleText = uiSourceCode.parsedURL.url;
127
titleText = WebInspector.UIString("(program)");
128
if (!ignoreIsDirty && uiSourceCode.isDirty())
129
titleText = "*" + titleText;
130
scriptTreeElement.titleText = titleText;
134
* @param {WebInspector.UISourceCode} uiSourceCode
137
isScriptSourceAdded: function(uiSourceCode)
139
var scriptTreeElement = this._scriptTreeElementsByUISourceCode.get(uiSourceCode);
140
return !!scriptTreeElement;
144
* @param {WebInspector.UISourceCode} uiSourceCode
146
revealUISourceCode: function(uiSourceCode)
148
if (this._scriptsTree.selectedTreeElement)
149
this._scriptsTree.selectedTreeElement.deselect();
151
this._lastSelectedUISourceCode = uiSourceCode;
153
var scriptTreeElement = this._scriptTreeElementsByUISourceCode.get(uiSourceCode);
154
scriptTreeElement.revealAndSelect(true);
158
* @param {WebInspector.UISourceCode} uiSourceCode
159
* @param {boolean} focusSource
161
_scriptSelected: function(uiSourceCode, focusSource)
163
this._lastSelectedUISourceCode = uiSourceCode;
164
var data = { uiSourceCode: uiSourceCode, focusSource: focusSource};
165
this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemSelected, data);
169
* @param {WebInspector.UISourceCode} uiSourceCode
171
removeUISourceCode: function(uiSourceCode)
173
var treeElement = this._scriptTreeElementsByUISourceCode.get(uiSourceCode);
174
while (treeElement) {
175
var parent = treeElement.parent;
177
if (treeElement instanceof WebInspector.NavigatorFolderTreeElement)
178
delete this._folderTreeElements[treeElement.folderIdentifier];
179
parent.removeChild(treeElement);
180
if (parent.children.length)
183
treeElement = parent;
185
this._scriptTreeElementsByUISourceCode.remove(uiSourceCode);
186
this._removeUISourceCodeListeners(uiSourceCode);
190
* @param {WebInspector.UISourceCode} uiSourceCode
192
_addUISourceCodeListeners: function(uiSourceCode)
194
uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
195
uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
196
uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
197
uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this);
201
* @param {WebInspector.UISourceCode} uiSourceCode
203
_removeUISourceCodeListeners: function(uiSourceCode)
205
uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
206
uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
207
uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
208
uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this);
211
_showScriptFoldersSettingChanged: function()
213
var uiSourceCodes = this._scriptsTree.scriptTreeElements();
216
for (var i = 0; i < uiSourceCodes.length; ++i)
217
this.addUISourceCode(uiSourceCodes[i]);
219
if (this._lastSelectedUISourceCode)
220
this.revealUISourceCode(this._lastSelectedUISourceCode);
223
_fileRenamed: function(uiSourceCode, newTitle)
225
var data = { uiSourceCode: uiSourceCode, name: newTitle };
226
this.dispatchEventToListeners(WebInspector.NavigatorView.Events.FileRenamed, data);
230
* @param {WebInspector.UISourceCode} uiSourceCode
231
* @param {function(boolean)=} callback
233
rename: function(uiSourceCode, callback)
235
var scriptTreeElement = this._scriptTreeElementsByUISourceCode.get(uiSourceCode);
236
if (!scriptTreeElement)
239
// Tree outline should be marked as edited as well as the tree element to prevent search from starting.
240
var treeOutlineElement = scriptTreeElement.treeOutline.element;
241
WebInspector.markBeingEdited(treeOutlineElement, true);
243
function commitHandler(element, newTitle, oldTitle)
245
if (newTitle && newTitle !== oldTitle)
246
this._fileRenamed(uiSourceCode, newTitle);
247
afterEditing.call(this, true);
250
function cancelHandler()
252
afterEditing.call(this, false);
256
* @param {boolean} committed
258
function afterEditing(committed)
260
WebInspector.markBeingEdited(treeOutlineElement, false);
261
this._updateScriptTitle(uiSourceCode);
266
var editingConfig = new WebInspector.EditingConfig(commitHandler.bind(this), cancelHandler.bind(this));
267
this._updateScriptTitle(uiSourceCode, true);
268
WebInspector.startEditing(scriptTreeElement.titleElement, editingConfig);
269
window.getSelection().setBaseAndExtent(scriptTreeElement.titleElement, 0, scriptTreeElement.titleElement, 1);
274
var uiSourceCodes = this._scriptsTree.scriptTreeElements;
275
for (var i = 0; i < uiSourceCodes.length; ++i)
276
this._removeUISourceCodeListeners(uiSourceCodes[i]);
278
this._scriptsTree.stopSearch();
279
this._scriptsTree.removeChildren();
280
this._folderTreeElements = {};
281
this._scriptTreeElementsByUISourceCode.clear();
285
* @param {string} folderIdentifier
286
* @param {string} domain
287
* @param {string} folderName
289
createFolderTreeElement: function(parentFolderElement, folderIdentifier, domain, folderName)
291
var folderTreeElement = new WebInspector.NavigatorFolderTreeElement(folderIdentifier, domain, folderName);
292
parentFolderElement.appendChild(folderTreeElement);
293
this._folderTreeElements[folderIdentifier] = folderTreeElement;
294
return folderTreeElement;
298
* @param {WebInspector.UISourceCode} uiSourceCode
300
getOrCreateFolderTreeElement: function(uiSourceCode)
302
return this._getOrCreateFolderTreeElement(uiSourceCode.parsedURL.host, uiSourceCode.parsedURL.folderPathComponents);
306
* @param {string} domain
307
* @param {string} folderName
309
_getOrCreateFolderTreeElement: function(domain, folderName)
311
var folderIdentifier = domain + "/" + folderName;
313
if (this._folderTreeElements[folderIdentifier])
314
return this._folderTreeElements[folderIdentifier];
316
var showScriptFolders = WebInspector.settings.showScriptFolders.get();
318
if ((!domain && !folderName) || !showScriptFolders)
319
return this._scriptsTree;
321
var parentFolderElement;
323
parentFolderElement = this._scriptsTree;
325
parentFolderElement = this._getOrCreateFolderTreeElement(domain, "");
327
return this.createFolderTreeElement(parentFolderElement, folderIdentifier, domain, folderName);
330
handleContextMenu: function(event, uiSourceCode)
332
var contextMenu = new WebInspector.ContextMenu(event);
333
contextMenu.appendApplicableItems(uiSourceCode);
337
__proto__: WebInspector.View.prototype
342
* @extends {TreeOutline}
343
* @param {Element} treeSearchBoxElement
344
* @param {Element} element
346
WebInspector.NavigatorTreeOutline = function(treeSearchBoxElement, element)
348
TreeOutline.call(this, element);
349
this.element = element;
351
this._treeSearchBoxElement = treeSearchBoxElement;
353
this.comparator = WebInspector.NavigatorTreeOutline._treeElementsCompare;
355
this.searchable = true;
356
this.searchInputElement = document.createElement("input");
359
WebInspector.NavigatorTreeOutline._treeElementsCompare = function compare(treeElement1, treeElement2)
361
// Insert in the alphabetical order, first domains, then folders, then scripts.
362
function typeWeight(treeElement)
364
if (treeElement instanceof WebInspector.NavigatorFolderTreeElement) {
365
if (treeElement.isDomain) {
366
if (treeElement.titleText === WebInspector.inspectedPageDomain)
375
var typeWeight1 = typeWeight(treeElement1);
376
var typeWeight2 = typeWeight(treeElement2);
379
if (typeWeight1 > typeWeight2)
381
else if (typeWeight1 < typeWeight2)
384
var title1 = treeElement1.titleText;
385
var title2 = treeElement2.titleText;
386
result = title1.localeCompare(title2);
391
WebInspector.NavigatorTreeOutline.prototype = {
393
* @return {Array.<WebInspector.UISourceCode>}
395
scriptTreeElements: function()
398
if (this.children.length) {
399
for (var treeElement = this.children[0]; treeElement; treeElement = treeElement.traverseNextTreeElement(false, this, true)) {
400
if (treeElement instanceof WebInspector.NavigatorSourceTreeElement)
401
result.push(treeElement.uiSourceCode);
407
searchStarted: function()
409
this._treeSearchBoxElement.appendChild(this.searchInputElement);
410
this._treeSearchBoxElement.addStyleClass("visible");
413
searchFinished: function()
415
this._treeSearchBoxElement.removeChild(this.searchInputElement);
416
this._treeSearchBoxElement.removeStyleClass("visible");
419
__proto__: TreeOutline.prototype
424
* @extends {TreeElement}
425
* @param {string} title
426
* @param {Array.<string>} iconClasses
427
* @param {boolean} hasChildren
428
* @param {boolean=} noIcon
430
WebInspector.BaseNavigatorTreeElement = function(title, iconClasses, hasChildren, noIcon)
432
TreeElement.call(this, "", null, hasChildren);
433
this._titleText = title;
434
this._iconClasses = iconClasses;
435
this._noIcon = noIcon;
438
WebInspector.BaseNavigatorTreeElement.prototype = {
441
this.listItemElement.removeChildren();
442
if (this._iconClasses) {
443
for (var i = 0; i < this._iconClasses.length; ++i)
444
this.listItemElement.addStyleClass(this._iconClasses[i]);
447
var selectionElement = document.createElement("div");
448
selectionElement.className = "selection";
449
this.listItemElement.appendChild(selectionElement);
452
this.imageElement = document.createElement("img");
453
this.imageElement.className = "icon";
454
this.listItemElement.appendChild(this.imageElement);
457
this.titleElement = document.createElement("div");
458
this.titleElement.className = "base-navigator-tree-element-title";
459
this._titleTextNode = document.createTextNode("");
460
this._titleTextNode.textContent = this._titleText;
461
this.titleElement.appendChild(this._titleTextNode);
462
this.listItemElement.appendChild(this.titleElement);
468
if (this.listItemElement)
469
this.listItemElement.scrollIntoViewIfNeeded(true);
477
return this._titleText;
480
set titleText(titleText)
482
if (this._titleText === titleText)
484
this._titleText = titleText || "";
485
if (this.titleElement)
486
this.titleElement.textContent = this._titleText;
490
* @param {string} searchText
492
matchesSearchText: function(searchText)
494
return this.titleText.match(new RegExp("^" + searchText.escapeForRegExp(), "i"));
497
__proto__: TreeElement.prototype
502
* @extends {WebInspector.BaseNavigatorTreeElement}
503
* @param {string} folderIdentifier
504
* @param {string} domain
505
* @param {string} folderName
507
WebInspector.NavigatorFolderTreeElement = function(folderIdentifier, domain, folderName)
509
this._folderIdentifier = folderIdentifier;
510
this._folderName = folderName;
512
var iconClass = this.isDomain ? "navigator-domain-tree-item" : "navigator-folder-tree-item";
513
var title = this.isDomain ? domain : folderName.substring(1);
515
WebInspector.BaseNavigatorTreeElement.call(this, title, [iconClass], true);
516
this.tooltip = folderName;
519
WebInspector.NavigatorFolderTreeElement.prototype = {
523
get folderIdentifier()
525
return this._folderIdentifier;
533
return this._folderName === "";
538
WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
539
if (this.isDomain && this.titleText != WebInspector.inspectedPageDomain)
545
__proto__: WebInspector.BaseNavigatorTreeElement.prototype
550
* @extends {WebInspector.BaseNavigatorTreeElement}
551
* @param {WebInspector.NavigatorView} navigatorView
552
* @param {WebInspector.UISourceCode} uiSourceCode
553
* @param {string} title
555
WebInspector.NavigatorSourceTreeElement = function(navigatorView, uiSourceCode, title)
557
WebInspector.BaseNavigatorTreeElement.call(this, title, ["navigator-" + uiSourceCode.contentType().name() + "-tree-item"], false);
558
this._navigatorView = navigatorView;
559
this._uiSourceCode = uiSourceCode;
560
this.tooltip = uiSourceCode.url;
563
WebInspector.NavigatorSourceTreeElement.prototype = {
565
* @return {WebInspector.UISourceCode}
569
return this._uiSourceCode;
574
WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
575
this.listItemElement.draggable = true;
576
this.listItemElement.addEventListener("click", this._onclick.bind(this), false);
577
this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
578
this.listItemElement.addEventListener("mousedown", this._onmousedown.bind(this), false);
579
this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
582
_onmousedown: function(event)
584
if (event.which === 1) // Warm-up data for drag'n'drop
585
this._uiSourceCode.requestContent(callback.bind(this));
587
* @param {?string} content
588
* @param {boolean} contentEncoded
589
* @param {string} mimeType
591
function callback(content, contentEncoded, mimeType)
593
this._warmedUpContent = content;
597
_ondragstart: function(event)
599
event.dataTransfer.setData("text/plain", this._warmedUpContent);
600
event.dataTransfer.effectAllowed = "copy";
606
this._navigatorView._scriptSelected(this.uiSourceCode, true);
611
* @param {Event} event
613
_onclick: function(event)
615
this._navigatorView._scriptSelected(this.uiSourceCode, false);
619
* @param {Event} event
621
ondblclick: function(event)
623
var middleClick = event.button === 1;
624
this._navigatorView._scriptSelected(this.uiSourceCode, !middleClick);
629
this._navigatorView._scriptSelected(this.uiSourceCode, true);
634
* @param {Event} event
636
_handleContextMenuEvent: function(event)
638
this._navigatorView.handleContextMenu(event, this._uiSourceCode);
641
__proto__: WebInspector.BaseNavigatorTreeElement.prototype