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 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @param {string} parentPath
34 * @param {string} name
35 * @param {string} originURL
37 * @param {!WebInspector.ResourceType} contentType
38 * @param {boolean} isEditable
39 * @param {boolean=} isContentScript
41 WebInspector.FileDescriptor = function(parentPath, name, originURL, url, contentType, isEditable, isContentScript)
43 this.parentPath = parentPath;
45 this.originURL = originURL;
47 this.contentType = contentType;
48 this.isEditable = isEditable;
49 this.isContentScript = isContentScript || false;
54 * @extends {WebInspector.EventTarget}
56 WebInspector.ProjectDelegate = function() { }
58 WebInspector.ProjectDelegate.Events = {
59 FileAdded: "FileAdded",
60 FileRemoved: "FileRemoved",
64 WebInspector.ProjectDelegate.prototype = {
78 displayName: function() { },
81 * @param {string} path
82 * @param {function(?Date, ?number)} callback
84 requestMetadata: function(path, callback) { },
87 * @param {string} path
88 * @param {function(?string)} callback
90 requestFileContent: function(path, callback) { },
95 canSetFileContent: function() { },
98 * @param {string} path
99 * @param {string} newContent
100 * @param {function(?string)} callback
102 setFileContent: function(path, newContent, callback) { },
107 canRename: function() { },
110 * @param {string} path
111 * @param {string} newName
112 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
114 rename: function(path, newName, callback) { },
117 * @param {string} path
119 refresh: function(path) { },
122 * @param {string} path
124 excludeFolder: function(path) { },
127 * @param {string} path
128 * @param {?string} name
129 * @param {string} content
130 * @param {function(?string)} callback
132 createFile: function(path, name, content, callback) { },
135 * @param {string} path
137 deleteFile: function(path) { },
139 remove: function() { },
142 * @param {string} path
143 * @param {string} query
144 * @param {boolean} caseSensitive
145 * @param {boolean} isRegex
146 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
148 searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },
151 * @param {!Array.<string>} queries
152 * @param {!Array.<string>} fileQueries
153 * @param {boolean} caseSensitive
154 * @param {boolean} isRegex
155 * @param {!WebInspector.Progress} progress
156 * @param {function(!Array.<string>)} callback
158 findFilesMatchingSearchRequest: function(queries, fileQueries, caseSensitive, isRegex, progress, callback) { },
161 * @param {!WebInspector.Progress} progress
162 * @param {function()} callback
164 indexContent: function(progress, callback) { }
168 * @param {!WebInspector.Workspace} workspace
169 * @param {!WebInspector.ProjectDelegate} projectDelegate
172 WebInspector.Project = function(workspace, projectDelegate)
174 /** @type {!Object.<string, !{uiSourceCode: !WebInspector.UISourceCode, index: number}>} */
175 this._uiSourceCodesMap = {};
176 /** @type {!Array.<!WebInspector.UISourceCode>} */
177 this._uiSourceCodesList = [];
178 this._workspace = workspace;
179 this._projectDelegate = projectDelegate;
180 this._displayName = this._projectDelegate.displayName();
181 this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
182 this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
183 this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.Reset, this._reset, this);
186 WebInspector.Project.prototype = {
192 return this._projectDelegate.id();
200 return this._projectDelegate.type();
206 displayName: function()
208 return this._displayName;
214 isServiceProject: function()
216 return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.LiveEdit;
219 _fileAdded: function(event)
221 var fileDescriptor = /** @type {!WebInspector.FileDescriptor} */ (event.data);
222 var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
223 var uiSourceCode = this.uiSourceCode(path);
227 uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType, fileDescriptor.isEditable);
228 uiSourceCode.isContentScript = fileDescriptor.isContentScript;
230 this._uiSourceCodesMap[path] = {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length};
231 this._uiSourceCodesList.push(uiSourceCode);
232 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
235 _fileRemoved: function(event)
237 var path = /** @type {string} */ (event.data);
238 this._removeFile(path);
242 * @param {string} path
244 _removeFile: function(path)
246 var uiSourceCode = this.uiSourceCode(path);
250 var entry = this._uiSourceCodesMap[path];
251 var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
252 this._uiSourceCodesList[entry.index] = movedUISourceCode;
253 var movedEntry = this._uiSourceCodesMap[movedUISourceCode.path()];
254 movedEntry.index = entry.index;
255 this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
256 delete this._uiSourceCodesMap[path];
257 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
262 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectWillReset, this);
263 this._uiSourceCodesMap = {};
264 this._uiSourceCodesList = [];
268 * @return {!WebInspector.Workspace}
270 workspace: function()
272 return this._workspace;
276 * @param {string} path
277 * @return {?WebInspector.UISourceCode}
279 uiSourceCode: function(path)
281 var entry = this._uiSourceCodesMap[path];
282 return entry ? entry.uiSourceCode : null;
286 * @param {string} originURL
287 * @return {?WebInspector.UISourceCode}
289 uiSourceCodeForOriginURL: function(originURL)
291 for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
292 var uiSourceCode = this._uiSourceCodesList[i];
293 if (uiSourceCode.originURL() === originURL)
300 * @return {!Array.<!WebInspector.UISourceCode>}
302 uiSourceCodes: function()
304 return this._uiSourceCodesList;
308 * @param {!WebInspector.UISourceCode} uiSourceCode
309 * @param {function(?Date, ?number)} callback
311 requestMetadata: function(uiSourceCode, callback)
313 this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
317 * @param {!WebInspector.UISourceCode} uiSourceCode
318 * @param {function(?string)} callback
320 requestFileContent: function(uiSourceCode, callback)
322 this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
328 canSetFileContent: function()
330 return this._projectDelegate.canSetFileContent();
334 * @param {!WebInspector.UISourceCode} uiSourceCode
335 * @param {string} newContent
336 * @param {function(?string)} callback
338 setFileContent: function(uiSourceCode, newContent, callback)
340 this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));
343 * @param {?string} content
344 * @this {WebInspector.Project}
346 function onSetContent(content)
348 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
356 canRename: function()
358 return this._projectDelegate.canRename();
362 * @param {!WebInspector.UISourceCode} uiSourceCode
363 * @param {string} newName
364 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
366 rename: function(uiSourceCode, newName, callback)
368 if (newName === uiSourceCode.name()) {
369 callback(true, uiSourceCode.name(), uiSourceCode.url, uiSourceCode.originURL(), uiSourceCode.contentType());
373 this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));
376 * @param {boolean} success
377 * @param {string=} newName
378 * @param {string=} newURL
379 * @param {string=} newOriginURL
380 * @param {!WebInspector.ResourceType=} newContentType
381 * @this {WebInspector.Project}
383 function innerCallback(success, newName, newURL, newOriginURL, newContentType)
385 if (!success || !newName) {
389 var oldPath = uiSourceCode.path();
390 var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
391 this._uiSourceCodesMap[newPath] = this._uiSourceCodesMap[oldPath];
392 delete this._uiSourceCodesMap[oldPath];
393 callback(true, newName, newURL, newOriginURL, newContentType);
398 * @param {string} path
400 refresh: function(path)
402 this._projectDelegate.refresh(path);
406 * @param {string} path
408 excludeFolder: function(path)
410 this._projectDelegate.excludeFolder(path);
411 var uiSourceCodes = this._uiSourceCodesList.slice();
412 for (var i = 0; i < uiSourceCodes.length; ++i) {
413 var uiSourceCode = uiSourceCodes[i];
414 if (uiSourceCode.path().startsWith(path.substr(1)))
415 this._removeFile(uiSourceCode.path());
420 * @param {string} path
421 * @param {?string} name
422 * @param {string} content
423 * @param {function(?string)} callback
425 createFile: function(path, name, content, callback)
427 this._projectDelegate.createFile(path, name, content, innerCallback);
429 function innerCallback(filePath)
436 * @param {string} path
438 deleteFile: function(path)
440 this._projectDelegate.deleteFile(path);
445 this._projectDelegate.remove();
449 * @param {!WebInspector.UISourceCode} uiSourceCode
450 * @param {string} query
451 * @param {boolean} caseSensitive
452 * @param {boolean} isRegex
453 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
455 searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
457 this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
461 * @param {!Array.<string>} queries
462 * @param {!Array.<string>} fileQueries
463 * @param {boolean} caseSensitive
464 * @param {boolean} isRegex
465 * @param {!WebInspector.Progress} progress
466 * @param {function(!Array.<string>)} callback
468 findFilesMatchingSearchRequest: function(queries, fileQueries, caseSensitive, isRegex, progress, callback)
470 this._projectDelegate.findFilesMatchingSearchRequest(queries, fileQueries, caseSensitive, isRegex, progress, callback);
474 * @param {!WebInspector.Progress} progress
475 * @param {function()} callback
477 indexContent: function(progress, callback)
479 this._projectDelegate.indexContent(progress, callback);
484 this._projectDelegate.reset();
488 WebInspector.projectTypes = {
489 Debugger: "debugger",
490 LiveEdit: "liveedit",
492 Snippets: "snippets",
493 FileSystem: "filesystem"
498 * @extends {WebInspector.Object}
499 * @param {!WebInspector.FileSystemMapping} fileSystemMapping
501 WebInspector.Workspace = function(fileSystemMapping)
503 this._fileSystemMapping = fileSystemMapping;
504 /** @type {!Object.<string, !WebInspector.Project>} */
506 this._hasResourceContentTrackingExtensions = false;
509 WebInspector.Workspace.Events = {
510 UISourceCodeAdded: "UISourceCodeAdded",
511 UISourceCodeRemoved: "UISourceCodeRemoved",
512 UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
513 ProjectWillReset: "ProjectWillReset"
516 WebInspector.Workspace.prototype = {
518 * @return {!Array.<!WebInspector.UISourceCode>}
520 unsavedSourceCodes: function()
522 function filterUnsaved(sourceCode)
524 return sourceCode.isDirty();
526 return this.uiSourceCodes().filter(filterUnsaved);
530 * @param {string} projectId
531 * @param {string} path
532 * @return {?WebInspector.UISourceCode}
534 uiSourceCode: function(projectId, path)
536 var project = this._projects[projectId];
537 return project ? project.uiSourceCode(path) : null;
541 * @param {string} originURL
542 * @return {?WebInspector.UISourceCode}
544 uiSourceCodeForOriginURL: function(originURL)
546 var networkProjects = this.projectsForType(WebInspector.projectTypes.Network)
547 for (var i = 0; i < networkProjects.length; ++i) {
548 var project = networkProjects[i];
549 var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
557 * @param {string} type
558 * @return {!Array.<!WebInspector.UISourceCode>}
560 uiSourceCodesForProjectType: function(type)
563 for (var projectName in this._projects) {
564 var project = this._projects[projectName];
565 if (project.type() === type)
566 result = result.concat(project.uiSourceCodes());
572 * @param {!WebInspector.ProjectDelegate} projectDelegate
573 * @return {!WebInspector.Project}
575 addProject: function(projectDelegate)
577 var projectId = projectDelegate.id();
578 this._projects[projectId] = new WebInspector.Project(this, projectDelegate);
579 return this._projects[projectId];
583 * @param {string} projectId
585 removeProject: function(projectId)
587 var project = this._projects[projectId];
591 delete this._projects[projectId];
595 * @param {string} projectId
596 * @return {!WebInspector.Project}
598 project: function(projectId)
600 return this._projects[projectId];
604 * @return {!Array.<!WebInspector.Project>}
608 return Object.values(this._projects);
612 * @param {string} type
613 * @return {!Array.<!WebInspector.Project>}
615 projectsForType: function(type)
617 function filterByType(project)
619 return project.type() === type;
621 return this.projects().filter(filterByType);
625 * @return {!Array.<!WebInspector.UISourceCode>}
627 uiSourceCodes: function()
630 for (var projectId in this._projects) {
631 var project = this._projects[projectId];
632 result = result.concat(project.uiSourceCodes());
638 * @param {string} url
641 hasMappingForURL: function(url)
643 return this._fileSystemMapping.hasMappingForURL(url);
647 * @param {string} url
648 * @return {?WebInspector.UISourceCode}
650 _networkUISourceCodeForURL: function(url)
652 var splitURL = WebInspector.ParsedURL.splitURL(url);
653 var projectId = WebInspector.SimpleProjectDelegate.projectId(splitURL[0], WebInspector.projectTypes.Network);
654 var project = this.project(projectId);
655 return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
659 * @param {string} url
660 * @return {?WebInspector.UISourceCode}
662 uiSourceCodeForURL: function(url)
664 var file = this._fileSystemMapping.fileForURL(url);
666 return this._networkUISourceCodeForURL(url);
668 var projectId = WebInspector.FileSystemProjectDelegate.projectId(file.fileSystemPath);
669 var project = this.project(projectId);
670 return project ? project.uiSourceCode(file.filePath) : null;
674 * @param {string} fileSystemPath
675 * @param {string} filePath
678 urlForPath: function(fileSystemPath, filePath)
680 return this._fileSystemMapping.urlForPath(fileSystemPath, filePath);
684 * @param {!WebInspector.UISourceCode} networkUISourceCode
685 * @param {!WebInspector.UISourceCode} uiSourceCode
686 * @param {!WebInspector.FileSystemWorkspaceProvider} fileSystemWorkspaceProvider
688 addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceProvider)
690 var url = networkUISourceCode.url;
691 var path = uiSourceCode.path();
692 var fileSystemPath = fileSystemWorkspaceProvider.fileSystemPath(uiSourceCode);
693 this._fileSystemMapping.addMappingForResource(url, fileSystemPath, path);
694 WebInspector.suggestReload();
698 * @param {!WebInspector.UISourceCode} uiSourceCode
700 removeMapping: function(uiSourceCode)
702 this._fileSystemMapping.removeMappingForURL(uiSourceCode.url);
703 WebInspector.suggestReload();
707 * @param {boolean} hasExtensions
709 setHasResourceContentTrackingExtensions: function(hasExtensions)
711 this._hasResourceContentTrackingExtensions = hasExtensions;
717 hasResourceContentTrackingExtensions: function()
719 return this._hasResourceContentTrackingExtensions;
722 __proto__: WebInspector.Object.prototype
726 * @type {!WebInspector.Workspace}
728 WebInspector.workspace;