blob: 035d3897a19eb77073ab5a6ae3892514ee695ae4 [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @constructor
* @extends {WebInspector.Object}
* @implements {WebInspector.ContentProvider}
* @param {!WebInspector.Project} project
* @param {string} parentPath
* @param {string} name
* @param {string} originURL
* @param {string} url
* @param {!WebInspector.ResourceType} contentType
*/
WebInspector.UISourceCode = function(project, parentPath, name, originURL, url, contentType)
{
this._project = project;
this._parentPath = parentPath;
this._name = name;
this._originURL = originURL;
this._url = url;
this._contentType = contentType;
/** @type {!Array.<function(?string)>} */
this._requestContentCallbacks = [];
/** @type {!Array.<!WebInspector.PresentationMessage>} */
this._consoleMessages = [];
/** @type {!Array.<!WebInspector.Revision>} */
this.history = [];
}
WebInspector.UISourceCode.Events = {
WorkingCopyChanged: "WorkingCopyChanged",
WorkingCopyCommitted: "WorkingCopyCommitted",
TitleChanged: "TitleChanged",
SavedStateUpdated: "SavedStateUpdated",
ConsoleMessageAdded: "ConsoleMessageAdded",
ConsoleMessageRemoved: "ConsoleMessageRemoved",
ConsoleMessagesCleared: "ConsoleMessagesCleared",
SourceMappingChanged: "SourceMappingChanged",
}
WebInspector.UISourceCode.prototype = {
/**
* @return {string}
*/
get url()
{
return this._url;
},
/**
* @return {string}
*/
name: function()
{
return this._name;
},
/**
* @return {string}
*/
parentPath: function()
{
return this._parentPath;
},
/**
* @return {string}
*/
path: function()
{
return this._parentPath ? this._parentPath + "/" + this._name : this._name;
},
/**
* @return {string}
*/
fullDisplayName: function()
{
return this._project.displayName() + "/" + (this._parentPath ? this._parentPath + "/" : "") + this.displayName(true);
},
/**
* @param {boolean=} skipTrim
* @return {string}
*/
displayName: function(skipTrim)
{
var displayName = this.name() || WebInspector.UIString("(index)");
return skipTrim ? displayName : displayName.trimEnd(100);
},
/**
* @return {string}
*/
uri: function()
{
var path = this.path();
if (!this._project.id())
return path;
if (!path)
return this._project.id();
return this._project.id() + "/" + path;
},
/**
* @return {string}
*/
originURL: function()
{
return this._originURL;
},
/**
* @return {boolean}
*/
canRename: function()
{
return this._project.canRename();
},
/**
* @param {string} newName
* @param {function(boolean)} callback
*/
rename: function(newName, callback)
{
this._project.rename(this, newName, innerCallback.bind(this));
/**
* @param {boolean} success
* @param {string=} newName
* @param {string=} newURL
* @param {string=} newOriginURL
* @param {!WebInspector.ResourceType=} newContentType
* @this {WebInspector.UISourceCode}
*/
function innerCallback(success, newName, newURL, newOriginURL, newContentType)
{
if (success)
this._updateName(/** @type {string} */ (newName), /** @type {string} */ (newURL), /** @type {string} */ (newOriginURL), /** @type {!WebInspector.ResourceType} */ (newContentType));
callback(success);
}
},
remove: function()
{
this._project.deleteFile(this.path());
},
/**
* @param {string} name
* @param {string} url
* @param {string} originURL
* @param {!WebInspector.ResourceType=} contentType
*/
_updateName: function(name, url, originURL, contentType)
{
var oldURI = this.uri();
this._name = name;
if (url)
this._url = url;
if (originURL)
this._originURL = originURL;
if (contentType)
this._contentType = contentType;
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.TitleChanged, oldURI);
},
/**
* @return {string}
*/
contentURL: function()
{
return this.originURL();
},
/**
* @return {!WebInspector.ResourceType}
*/
contentType: function()
{
return this._contentType;
},
/**
* @return {!WebInspector.Project}
*/
project: function()
{
return this._project;
},
/**
* @param {function(?Date, ?number)} callback
*/
requestMetadata: function(callback)
{
this._project.requestMetadata(this, callback);
},
/**
* @param {function(?string)} callback
*/
requestContent: function(callback)
{
if (this._content || this._contentLoaded) {
callback(this._content);
return;
}
this._requestContentCallbacks.push(callback);
if (this._requestContentCallbacks.length === 1)
this._project.requestFileContent(this, this._fireContentAvailable.bind(this));
},
/**
* @param {function()} callback
*/
_pushCheckContentUpdatedCallback: function(callback)
{
if (!this._checkContentUpdatedCallbacks)
this._checkContentUpdatedCallbacks = [];
this._checkContentUpdatedCallbacks.push(callback);
},
_terminateContentCheck: function()
{
delete this._checkingContent;
if (this._checkContentUpdatedCallbacks) {
this._checkContentUpdatedCallbacks.forEach(function(callback) { callback(); });
delete this._checkContentUpdatedCallbacks;
}
},
/**
* @param {function()=} callback
*/
checkContentUpdated: function(callback)
{
callback = callback || function() {};
if (!this._project.canSetFileContent()) {
callback();
return;
}
this._pushCheckContentUpdatedCallback(callback);
if (this._checkingContent) {
return;
}
this._checkingContent = true;
this._project.requestFileContent(this, contentLoaded.bind(this));
/**
* @param {?string} updatedContent
* @this {WebInspector.UISourceCode}
*/
function contentLoaded(updatedContent)
{
if (updatedContent === null) {
var workingCopy = this.workingCopy();
this._commitContent("", false);
this.setWorkingCopy(workingCopy);
this._terminateContentCheck();
return;
}
if (typeof this._lastAcceptedContent === "string" && this._lastAcceptedContent === updatedContent) {
this._terminateContentCheck();
return;
}
if (this._content === updatedContent) {
delete this._lastAcceptedContent;
this._terminateContentCheck();
return;
}
if (!this.isDirty()) {
this._commitContent(updatedContent, false);
this._terminateContentCheck();
return;
}
var shouldUpdate = window.confirm(WebInspector.UIString("This file was changed externally. Would you like to reload it?"));
if (shouldUpdate)
this._commitContent(updatedContent, false);
else
this._lastAcceptedContent = updatedContent;
this._terminateContentCheck();
}
},
/**
* @param {function(?string)} callback
*/
requestOriginalContent: function(callback)
{
this._project.requestFileContent(this, callback);
},
/**
* @param {string} content
* @param {boolean} shouldSetContentInProject
*/
_commitContent: function(content, shouldSetContentInProject)
{
delete this._lastAcceptedContent;
this._content = content;
this._contentLoaded = true;
var lastRevision = this.history.length ? this.history[this.history.length - 1] : null;
if (!lastRevision || lastRevision._content !== this._content) {
var revision = new WebInspector.Revision(this, this._content, new Date());
this.history.push(revision);
}
this._innerResetWorkingCopy();
this._hasCommittedChanges = true;
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyCommitted);
if (this._url && WebInspector.fileManager.isURLSaved(this._url))
this._saveURLWithFileManager(false, this._content);
if (shouldSetContentInProject)
this._project.setFileContent(this, this._content, function() { });
},
/**
* @param {boolean} forceSaveAs
* @param {?string} content
*/
_saveURLWithFileManager: function(forceSaveAs, content)
{
WebInspector.fileManager.save(this._url, /** @type {string} */ (content), forceSaveAs, callback.bind(this));
WebInspector.fileManager.close(this._url);
/**
* @param {boolean} accepted
* @this {WebInspector.UISourceCode}
*/
function callback(accepted)
{
if (!accepted)
return;
this._savedWithFileManager = true;
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.SavedStateUpdated);
}
},
/**
* @param {boolean} forceSaveAs
*/
save: function(forceSaveAs)
{
if (this.project().type() === WebInspector.projectTypes.FileSystem || this.project().type() === WebInspector.projectTypes.Snippets) {
this.commitWorkingCopy();
return;
}
if (this.isDirty()) {
this._saveURLWithFileManager(forceSaveAs, this.workingCopy());
this.commitWorkingCopy();
return;
}
this.requestContent(this._saveURLWithFileManager.bind(this, forceSaveAs));
},
/**
* @return {boolean}
*/
hasUnsavedCommittedChanges: function()
{
if (this._savedWithFileManager || this.project().canSetFileContent() || this._project.isServiceProject())
return false;
if (this._project.workspace().hasResourceContentTrackingExtensions())
return false;
return !!this._hasCommittedChanges;
},
/**
* @param {string} content
*/
addRevision: function(content)
{
this._commitContent(content, true);
},
revertToOriginal: function()
{
/**
* @this {WebInspector.UISourceCode}
* @param {?string} content
*/
function callback(content)
{
if (typeof content !== "string")
return;
this.addRevision(content);
}
this.requestOriginalContent(callback.bind(this));
},
/**
* @param {function(!WebInspector.UISourceCode)} callback
*/
revertAndClearHistory: function(callback)
{
/**
* @this {WebInspector.UISourceCode}
* @param {?string} content
*/
function revert(content)
{
if (typeof content !== "string")
return;
this.addRevision(content);
this.history = [];
callback(this);
}
this.requestOriginalContent(revert.bind(this));
},
/**
* @return {string}
*/
workingCopy: function()
{
if (this._workingCopyGetter) {
this._workingCopy = this._workingCopyGetter();
delete this._workingCopyGetter;
}
if (this.isDirty())
return this._workingCopy;
return this._content;
},
resetWorkingCopy: function()
{
this._innerResetWorkingCopy();
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyChanged);
},
_innerResetWorkingCopy: function()
{
delete this._workingCopy;
delete this._workingCopyGetter;
},
/**
* @param {string} newWorkingCopy
*/
setWorkingCopy: function(newWorkingCopy)
{
this._workingCopy = newWorkingCopy;
delete this._workingCopyGetter;
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyChanged);
},
setWorkingCopyGetter: function(workingCopyGetter)
{
this._workingCopyGetter = workingCopyGetter;
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyChanged);
},
removeWorkingCopyGetter: function()
{
if (!this._workingCopyGetter)
return;
this._workingCopy = this._workingCopyGetter();
delete this._workingCopyGetter;
},
commitWorkingCopy: function()
{
if (this.isDirty())
this._commitContent(this.workingCopy(), true);
},
/**
* @return {boolean}
*/
isDirty: function()
{
return typeof this._workingCopy !== "undefined" || typeof this._workingCopyGetter !== "undefined";
},
/**
* @return {string}
*/
highlighterType: function()
{
var lastIndexOfDot = this._name.lastIndexOf(".");
var extension = lastIndexOfDot !== -1 ? this._name.substr(lastIndexOfDot + 1) : "";
var indexOfQuestionMark = extension.indexOf("?");
if (indexOfQuestionMark !== -1)
extension = extension.substr(0, indexOfQuestionMark);
var mimeType = WebInspector.ResourceType.mimeTypesForExtensions[extension.toLowerCase()];
return mimeType || this.contentType().canonicalMimeType();
},
/**
* @return {?string}
*/
content: function()
{
return this._content;
},
/**
* @param {string} query
* @param {boolean} caseSensitive
* @param {boolean} isRegex
* @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
*/
searchInContent: function(query, caseSensitive, isRegex, callback)
{
var content = this.content();
if (content) {
var provider = new WebInspector.StaticContentProvider(this.contentType(), content);
provider.searchInContent(query, caseSensitive, isRegex, callback);
return;
}
this._project.searchInFileContent(this, query, caseSensitive, isRegex, callback);
},
/**
* @param {?string} content
*/
_fireContentAvailable: function(content)
{
this._contentLoaded = true;
this._content = content;
var callbacks = this._requestContentCallbacks.slice();
this._requestContentCallbacks = [];
for (var i = 0; i < callbacks.length; ++i)
callbacks[i](content);
},
/**
* @return {boolean}
*/
contentLoaded: function()
{
return this._contentLoaded;
},
/**
* @return {!Array.<!WebInspector.PresentationMessage>}
*/
consoleMessages: function()
{
return this._consoleMessages;
},
/**
* @param {!WebInspector.PresentationMessage} message
*/
consoleMessageAdded: function(message)
{
this._consoleMessages.push(message);
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.ConsoleMessageAdded, message);
},
/**
* @param {!WebInspector.PresentationMessage} message
*/
consoleMessageRemoved: function(message)
{
this._consoleMessages.remove(message);
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, message);
},
consoleMessagesCleared: function()
{
this._consoleMessages = [];
this.dispatchEventToListeners(WebInspector.UISourceCode.Events.ConsoleMessagesCleared);
},
/**
* @param {number} lineNumber
* @param {number=} columnNumber
* @return {!WebInspector.UILocation}
*/
uiLocation: function(lineNumber, columnNumber)
{
if (typeof columnNumber === "undefined")
columnNumber = 0;
return new WebInspector.UILocation(this, lineNumber, columnNumber);
},
__proto__: WebInspector.Object.prototype
}
/**
* @interface
*/
WebInspector.PresentationMessage = function() {}
/**
* @constructor
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
*/
WebInspector.UILocation = function(uiSourceCode, lineNumber, columnNumber)
{
this.uiSourceCode = uiSourceCode;
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
}
WebInspector.UILocation.prototype = {
/**
* @return {string}
*/
linkText: function()
{
var linkText = this.uiSourceCode.displayName();
if (typeof this.lineNumber === "number")
linkText += ":" + (this.lineNumber + 1);
return linkText;
},
/**
* @return {string}
*/
id: function()
{
return this.uiSourceCode.uri() + ":" + this.lineNumber + ":" + this.columnNumber;
},
}
/**
* @constructor
* @implements {WebInspector.ContentProvider}
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {?string|undefined} content
* @param {!Date} timestamp
*/
WebInspector.Revision = function(uiSourceCode, content, timestamp)
{
this._uiSourceCode = uiSourceCode;
this._content = content;
this._timestamp = timestamp;
}
WebInspector.Revision.prototype = {
/**
* @return {!WebInspector.UISourceCode}
*/
get uiSourceCode()
{
return this._uiSourceCode;
},
/**
* @return {!Date}
*/
get timestamp()
{
return this._timestamp;
},
/**
* @return {?string}
*/
get content()
{
return this._content || null;
},
revertToThis: function()
{
/**
* @param {string} content
* @this {WebInspector.Revision}
*/
function revert(content)
{
if (this._uiSourceCode._content !== content)
this._uiSourceCode.addRevision(content);
}
this.requestContent(revert.bind(this));
},
/**
* @return {string}
*/
contentURL: function()
{
return this._uiSourceCode.originURL();
},
/**
* @return {!WebInspector.ResourceType}
*/
contentType: function()
{
return this._uiSourceCode.contentType();
},
/**
* @param {function(string)} callback
*/
requestContent: function(callback)
{
callback(this._content || "");
},
/**
* @param {string} query
* @param {boolean} caseSensitive
* @param {boolean} isRegex
* @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
*/
searchInContent: function(query, caseSensitive, isRegex, callback)
{
callback([]);
}
}