diff --git a/src/LiveDevelopment/LiveDevelopment.js b/src/LiveDevelopment/LiveDevelopment.js index f8aebec38f9..8b9794d6d61 100644 --- a/src/LiveDevelopment/LiveDevelopment.js +++ b/src/LiveDevelopment/LiveDevelopment.js @@ -75,6 +75,7 @@ define(function LiveDevelopment(require, exports, module) { var Async = require("utils/Async"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), DocumentManager = require("document/DocumentManager"), EditorManager = require("editor/EditorManager"), FileUtils = require("file/FileUtils"), @@ -600,7 +601,7 @@ define(function LiveDevelopment(require, exports, module) { function showWrongDocError() { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.LIVE_DEVELOPMENT_ERROR_TITLE, Strings.LIVE_DEV_NEED_HTML_MESSAGE ); @@ -617,15 +618,12 @@ define(function LiveDevelopment(require, exports, module) { } else { result.reject(); } - }) - .fail(function () { - result.reject(); }); } function showLiveDevServerNotReadyError() { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.LIVE_DEVELOPMENT_ERROR_TITLE, Strings.LIVE_DEV_SERVER_NOT_READY_MESSAGE ); @@ -647,30 +645,43 @@ define(function LiveDevelopment(require, exports, module) { if (retryCount > 6) { _setStatus(STATUS_ERROR); Dialogs.showModalDialog( - Dialogs.DIALOG_ID_LIVE_DEVELOPMENT, + DefaultDialogs.DIALOG_ID_LIVE_DEVELOPMENT, Strings.LIVE_DEVELOPMENT_RELAUNCH_TITLE, - Strings.LIVE_DEVELOPMENT_ERROR_MESSAGE - ).done(function (id) { - if (id === Dialogs.DIALOG_BTN_OK) { - // User has chosen to reload Chrome, quit the running instance - _setStatus(STATUS_INACTIVE); - NativeApp.closeLiveBrowser() - .done(function () { - browserStarted = false; - window.setTimeout(function () { - open().done(result.resolve).fail(result.reject); + Strings.LIVE_DEVELOPMENT_ERROR_MESSAGE, + [ + { + className: Dialogs.DIALOG_BTN_CLASS_LEFT, + id: Dialogs.DIALOG_BTN_CANCEL, + text: Strings.CANCEL + }, + { + className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, + id: Dialogs.DIALOG_BTN_OK, + text: Strings.RELAUNCH_CHROME + } + ] + ) + .done(function (id) { + if (id === Dialogs.DIALOG_BTN_OK) { + // User has chosen to reload Chrome, quit the running instance + _setStatus(STATUS_INACTIVE); + NativeApp.closeLiveBrowser() + .done(function () { + browserStarted = false; + window.setTimeout(function () { + open().done(result.resolve).fail(result.reject); + }); + }) + .fail(function (err) { + // Report error? + _setStatus(STATUS_ERROR); + browserStarted = false; + result.reject("CLOSE_LIVE_BROWSER"); }); - }) - .fail(function (err) { - // Report error? - _setStatus(STATUS_ERROR); - browserStarted = false; - result.reject("CLOSE_LIVE_BROWSER"); - }); - } else { - result.reject("CANCEL"); - } - }); + } else { + result.reject("CANCEL"); + } + }); return; } retryCount++; @@ -699,7 +710,7 @@ define(function LiveDevelopment(require, exports, module) { } Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_LAUNCHING_BROWSER_TITLE, message ); @@ -872,7 +883,7 @@ define(function LiveDevelopment(require, exports, module) { .fail(function () { close(); Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.LIVE_DEVELOPMENT_ERROR_TITLE, Strings.LIVE_DEV_LOADING_ERROR_MESSAGE ); diff --git a/src/LiveDevelopment/main.js b/src/LiveDevelopment/main.js index d431b0de336..6da20ce84d4 100644 --- a/src/LiveDevelopment/main.js +++ b/src/LiveDevelopment/main.js @@ -46,6 +46,7 @@ define(function main(require, exports, module) { CommandManager = require("command/CommandManager"), PreferencesManager = require("preferences/PreferencesManager"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), UrlParams = require("utils/UrlParams").UrlParams, Strings = require("strings"), ExtensionUtils = require("utils/ExtensionUtils"), @@ -120,7 +121,7 @@ define(function main(require, exports, module) { if (!params.get("skipLiveDevelopmentInfo") && !prefs.getValue("afterFirstLaunch")) { prefs.setValue("afterFirstLaunch", "true"); Dialogs.showModalDialog( - Dialogs.DIALOG_ID_INFO, + DefaultDialogs.DIALOG_ID_INFO, Strings.LIVE_DEVELOPMENT_INFO_TITLE, Strings.LIVE_DEVELOPMENT_INFO_MESSAGE ).done(function (id) { diff --git a/src/brackets.js b/src/brackets.js index 53d480d93f8..1d0a0768d86 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -83,6 +83,7 @@ define(function (require, exports, module) { MainViewHTML = require("text!htmlContent/main-view.html"), Strings = require("strings"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), ExtensionLoader = require("utils/ExtensionLoader"), SidebarView = require("project/SidebarView"), Async = require("utils/Async"), @@ -165,7 +166,7 @@ define(function (require, exports, module) { // Let the user know Brackets doesn't run in a web browser yet if (brackets.inBrowser) { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_IN_BROWSER_TITLE, Strings.ERROR_IN_BROWSER ); diff --git a/src/document/DocumentCommandHandlers.js b/src/document/DocumentCommandHandlers.js index 1e5109f189b..a5431b2d413 100644 --- a/src/document/DocumentCommandHandlers.js +++ b/src/document/DocumentCommandHandlers.js @@ -34,16 +34,15 @@ define(function (require, exports, module) { var AppInit = require("utils/AppInit"), CommandManager = require("command/CommandManager"), Commands = require("command/Commands"), - KeyBindingManager = require("command/KeyBindingManager"), NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem, ProjectManager = require("project/ProjectManager"), DocumentManager = require("document/DocumentManager"), EditorManager = require("editor/EditorManager"), - FileViewController = require("project/FileViewController"), FileUtils = require("file/FileUtils"), StringUtils = require("utils/StringUtils"), Async = require("utils/Async"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), Strings = require("strings"), PreferencesManager = require("preferences/PreferencesManager"), PerfUtils = require("utils/PerfUtils"), @@ -209,8 +208,6 @@ define(function (require, exports, module) { // Prompt the user with a dialog NativeFileSystem.showOpenDialog(true, false, Strings.OPEN_FILE, _defaultOpenDialogFullPath, null, function (paths) { - var i; - if (paths.length > 0) { // Add all files to the working set without verifying that // they still exist on disk (for faster opening) @@ -383,9 +380,16 @@ define(function (require, exports, module) { _handleNewItemInProject(true); } - function showSaveFileError(name, path) { + /** + * @private + * Shows an Error modal dialog + * @param {string} name + * @param {string} path + * @return {Dialog} + */ + function _showSaveFileError(name, path) { return Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_SAVING_FILE_TITLE, StringUtils.format( Strings.ERROR_SAVING_FILE, @@ -400,8 +404,8 @@ define(function (require, exports, module) { var result = new $.Deferred(); function handleError(error, fileEntry) { - showSaveFileError(error.name, fileEntry.fullPath) - .always(function () { + _showSaveFileError(error.name, fileEntry.fullPath) + .done(function () { result.reject(error); }); } @@ -517,7 +521,7 @@ define(function (require, exports, module) { }) .fail(function (error) { FileUtils.showFileOpenError(error.name, doc.file.fullPath) - .always(function () { + .done(function () { result.reject(error); }); }); @@ -576,41 +580,59 @@ define(function (require, exports, module) { var filename = PathUtils.parseUrl(doc.file.fullPath).filename; Dialogs.showModalDialog( - Dialogs.DIALOG_ID_SAVE_CLOSE, + DefaultDialogs.DIALOG_ID_SAVE_CLOSE, Strings.SAVE_CLOSE_TITLE, StringUtils.format( Strings.SAVE_CLOSE_MESSAGE, StringUtils.breakableUrl(filename) - ) - ).done(function (id) { - if (id === Dialogs.DIALOG_BTN_CANCEL) { - result.reject(); - } else if (id === Dialogs.DIALOG_BTN_OK) { - // "Save" case: wait until we confirm save has succeeded before closing - doSave(doc) - .done(function () { - doClose(file); - result.resolve(); - }) - .fail(function () { - result.reject(); - }); - } else { - // "Don't Save" case: even though we're closing the main editor, other views of - // the Document may remain in the UI. So we need to revert the Document to a clean - // copy of whatever's on disk. - doClose(file); - - // Only reload from disk if we've executed the Close for real, - // *and* if at least one other view still exists - if (!promptOnly && DocumentManager.getOpenDocumentForPath(file.fullPath)) { - doRevert(doc) - .pipe(result.resolve, result.reject); + ), + [ + { + className : Dialogs.DIALOG_BTN_CLASS_LEFT, + id : Dialogs.DIALOG_BTN_DONTSAVE, + text : Strings.DONT_SAVE + }, + { + className : Dialogs.DIALOG_BTN_CLASS_NORMAL, + id : Dialogs.DIALOG_BTN_CANCEL, + text : Strings.CANCEL + }, + { + className : Dialogs.DIALOG_BTN_CLASS_PRIMARY, + id : Dialogs.DIALOG_BTN_OK, + text : Strings.SAVE + } + ] + ) + .done(function (id) { + if (id === Dialogs.DIALOG_BTN_CANCEL) { + result.reject(); + } else if (id === Dialogs.DIALOG_BTN_OK) { + // "Save" case: wait until we confirm save has succeeded before closing + doSave(doc) + .done(function () { + doClose(file); + result.resolve(); + }) + .fail(function () { + result.reject(); + }); } else { - result.resolve(); + // "Don't Save" case: even though we're closing the main editor, other views of + // the Document may remain in the UI. So we need to revert the Document to a clean + // copy of whatever's on disk. + doClose(file); + + // Only reload from disk if we've executed the Close for real, + // *and* if at least one other view still exists + if (!promptOnly && DocumentManager.getOpenDocumentForPath(file.fullPath)) { + doRevert(doc) + .pipe(result.resolve, result.reject); + } else { + result.resolve(); + } } - } - }); + }); result.always(function () { EditorManager.focusEditor(); }); @@ -671,24 +693,42 @@ define(function (require, exports, module) { message += ""; Dialogs.showModalDialog( - Dialogs.DIALOG_ID_SAVE_CLOSE, + DefaultDialogs.DIALOG_ID_SAVE_CLOSE, Strings.SAVE_CLOSE_TITLE, - message - ).done(function (id) { - if (id === Dialogs.DIALOG_BTN_CANCEL) { - result.reject(); - } else if (id === Dialogs.DIALOG_BTN_OK) { - // Save all unsaved files, then if that succeeds, close all - saveAll().done(function () { - result.resolve(); - }).fail(function () { + message, + [ + { + className : Dialogs.DIALOG_BTN_CLASS_LEFT, + id : Dialogs.DIALOG_BTN_DONTSAVE, + text : Strings.DONT_SAVE + }, + { + className : Dialogs.DIALOG_BTN_CLASS_NORMAL, + id : Dialogs.DIALOG_BTN_CANCEL, + text : Strings.CANCEL + }, + { + className : Dialogs.DIALOG_BTN_CLASS_PRIMARY, + id : Dialogs.DIALOG_BTN_OK, + text : Strings.SAVE + } + ] + ) + .done(function (id) { + if (id === Dialogs.DIALOG_BTN_CANCEL) { result.reject(); - }); - } else { - // "Don't Save" case--we can just go ahead and close all files. - result.resolve(); - } - }); + } else if (id === Dialogs.DIALOG_BTN_OK) { + // Save all unsaved files, then if that succeeds, close all + saveAll().done(function () { + result.resolve(); + }).fail(function () { + result.reject(); + }); + } else { + // "Don't Save" case--we can just go ahead and close all files. + result.resolve(); + } + }); } // If all the unsaved-changes confirmations pan out above, then go ahead & close all editors diff --git a/src/extensibility/ExtensionManagerDialog.js b/src/extensibility/ExtensionManagerDialog.js index aeb4d5f8788..90bc470dac6 100644 --- a/src/extensibility/ExtensionManagerDialog.js +++ b/src/extensibility/ExtensionManagerDialog.js @@ -57,7 +57,7 @@ define(function (require, exports, module) { // Open the dialog. Dialogs.showModalDialogUsingTemplate( Mustache.render(dialogTemplate, Strings) - ).always(function () { + ).done(function () { view.dispose(); }); diff --git a/src/extensibility/ExtensionManagerView.js b/src/extensibility/ExtensionManagerView.js index a0451c6b4e4..00d719f82c4 100644 --- a/src/extensibility/ExtensionManagerView.js +++ b/src/extensibility/ExtensionManagerView.js @@ -35,6 +35,7 @@ define(function (require, exports, module) { registry_utils = require("extensibility/registry_utils"), InstallExtensionDialog = require("extensibility/InstallExtensionDialog"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), StringUtils = require("utils/StringUtils"), CommandManager = require("command/CommandManager"), Commands = require("command/Commands"), @@ -299,8 +300,23 @@ define(function (require, exports, module) { // If an extension was removed, prompt the user to quit Brackets. if (!_skipRemoval && this.model.hasExtensionsToRemove()) { - Dialogs.showModalDialog("remove-marked-extensions", Strings.REMOVE_AND_QUIT_TITLE, - Strings.REMOVE_AND_QUIT_MESSAGE) + Dialogs.showModalDialog( + DefaultDialogs.DIALOG_ID_REMOVE_EXTENSIONS, + Strings.REMOVE_AND_QUIT_TITLE, + Strings.REMOVE_AND_QUIT_MESSAGE, + [ + { + className : Dialogs.DIALOG_BTN_CLASS_NORMAL, + id : Dialogs.DIALOG_BTN_CANCEL, + text : Strings.CANCEL + }, + { + className : Dialogs.DIALOG_BTN_CLASS_PRIMARY, + id : Dialogs.DIALOG_BTN_OK, + text : Strings.REMOVE_AND_QUIT + } + ] + ) .done(function (buttonId) { if (buttonId === "ok") { self.model.removeMarkedExtensions() @@ -315,12 +331,14 @@ define(function (require, exports, module) { errorArray.forEach(function (errorObj) { ids.push(errorObj.item); }); - Dialogs.showModalDialog("error-dialog", Strings.EXTENSION_MANAGER_REMOVE, - StringUtils.format(Strings.EXTENSION_MANAGER_REMOVE_ERROR, ids.join(", "))) - .done(function () { - // We still have to quit even if some of the removals failed. - CommandManager.execute(Commands.FILE_QUIT); - }); + Dialogs.showModalDialog( + DefaultDialogs.DIALOG_ID_ERROR, + Strings.EXTENSION_MANAGER_REMOVE, + StringUtils.format(Strings.EXTENSION_MANAGER_REMOVE_ERROR, ids.join(", ")) + ).done(function () { + // We still have to quit even if some of the removals failed. + CommandManager.execute(Commands.FILE_QUIT); + }); }); } else { self.model.dispose(); diff --git a/src/extensibility/InstallExtensionDialog.js b/src/extensibility/InstallExtensionDialog.js index 387ebc07021..209bef5f1df 100644 --- a/src/extensibility/InstallExtensionDialog.js +++ b/src/extensibility/InstallExtensionDialog.js @@ -287,12 +287,7 @@ define(function (require, exports, module) { // We ignore the promise returned by showModalDialogUsingTemplate, since we're managing the // lifecycle of the dialog ourselves. - Dialogs.showModalDialogUsingTemplate( - Mustache.render(InstallDialogTemplate, Strings), - null, - null, - false - ); + Dialogs.showModalDialogUsingTemplate(Mustache.render(InstallDialogTemplate, Strings), false); this.$dlg = $(".install-extension-dialog.instance"); this.$url = this.$dlg.find(".url").focus(); diff --git a/src/extensions/default/DebugCommands/main.js b/src/extensions/default/DebugCommands/main.js index 8d982098326..00c1c60fdc7 100644 --- a/src/extensions/default/DebugCommands/main.js +++ b/src/extensions/default/DebugCommands/main.js @@ -198,7 +198,7 @@ define(function (require, exports, module) { }); $dialog = $(".switch-language.instance"); - $submit = $dialog.find(".dialog-button[data-button-id='ok']"); + $submit = $dialog.find(".dialog-button[data-button-id='" + Dialogs.DIALOG_BTN_OK + "']"); $select = $dialog.find("select"); $select.on("change", setLanguage).val(curLocale); diff --git a/src/extensions/samples/LocalizationExample/htmlContent/sampleHTMLFragment.html b/src/extensions/samples/LocalizationExample/htmlContent/sampleHTMLFragment.html index 10a35c2ef88..9cde6a38eb5 100644 --- a/src/extensions/samples/LocalizationExample/htmlContent/sampleHTMLFragment.html +++ b/src/extensions/samples/LocalizationExample/htmlContent/sampleHTMLFragment.html @@ -1,26 +1,4 @@ - - - diff --git a/src/htmlContent/dialog-template.html b/src/htmlContent/dialog-template.html new file mode 100644 index 00000000000..531379d4ba4 --- /dev/null +++ b/src/htmlContent/dialog-template.html @@ -0,0 +1,14 @@ + diff --git a/src/htmlContent/main-view.html b/src/htmlContent/main-view.html index a66abe19c62..f2e0deb0f3e 100644 --- a/src/htmlContent/main-view.html +++ b/src/htmlContent/main-view.html @@ -103,80 +103,6 @@ - - - - - -
diff --git a/src/htmlContent/project-settings-dialog.html b/src/htmlContent/project-settings-dialog.html index 72ba7358d2e..a4db1135951 100644 --- a/src/htmlContent/project-settings-dialog.html +++ b/src/htmlContent/project-settings-dialog.html @@ -8,7 +8,7 @@

diff --git a/src/htmlContent/update-dialog.html b/src/htmlContent/update-dialog.html index ad79727ac62..b15a3c42625 100644 --- a/src/htmlContent/update-dialog.html +++ b/src/htmlContent/update-dialog.html @@ -11,7 +11,7 @@

{{UPDATE_AVAILABLE_TITLE}}

diff --git a/src/preferences/PreferencesDialogs.js b/src/preferences/PreferencesDialogs.js index 74292b00d2a..a8d854d244e 100644 --- a/src/preferences/PreferencesDialogs.js +++ b/src/preferences/PreferencesDialogs.js @@ -43,12 +43,12 @@ define(function (require, exports, module) { /** * Validate that text string is a valid base url which should map to a server folder - * @param {String} url - * @return {String} empty string if valid, otherwise error string + * @param {string} url + * @return {string} Empty string if valid, otherwise error string */ function _validateBaseUrl(url) { var result = ""; - // empty url means "no server mapping; use file directly" + // Empty url means "no server mapping; use file directly" if (url === "") { return result; } @@ -74,19 +74,18 @@ define(function (require, exports, module) { /** * Show a dialog that shows the project preferences - * @param {String} baseUrl - initial value - * @param {String} errorMessage - error to display - * @return {$.Promise} A promise object that will be resolved when user successfully enters - * project settings and clicks OK, or rejected if user clicks Cancel. + * @param {string} baseUrl Initial value + * @param {string} errorMessage Error to display + * @return {Dialog} A Dialog object with an internal promise that will be resolved with the ID + * of the clicked button when the dialog is dismissed. Never rejected. */ function showProjectPreferencesDialog(baseUrl, errorMessage) { - var $dlg, $title, $baseUrlControl, - promise; + dialog; - promise = Dialogs.showModalDialogUsingTemplate(Mustache.render(SettingsDialogTemplate, Strings)) + dialog = Dialogs.showModalDialogUsingTemplate(Mustache.render(SettingsDialogTemplate, Strings)) .done(function (id) { if (id === Dialogs.DIALOG_BTN_OK) { var baseUrlValue = $baseUrlControl.val(); @@ -128,7 +127,7 @@ define(function (require, exports, module) { // Give focus to first control $baseUrlControl.focus(); - return promise; + return dialog; } // For unit testing diff --git a/src/project/FileIndexManager.js b/src/project/FileIndexManager.js index da727b3e2da..e004cc19c42 100644 --- a/src/project/FileIndexManager.js +++ b/src/project/FileIndexManager.js @@ -42,6 +42,7 @@ define(function (require, exports, module) { var PerfUtils = require("utils/PerfUtils"), ProjectManager = require("project/ProjectManager"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), CollectionUtils = require("utils/CollectionUtils"), Strings = require("strings"); @@ -95,17 +96,16 @@ define(function (require, exports, module) { /** - * Adds a new index to _indexList and marks the list dirty - * - * A future performance optimization is to only build the new index rather than - * marking them all dirty - * - * @private - * @param {!string} indexName must be unque - * @param {!function({entry} filterFunction should return true to include an - * entry in the index - - */ + * Adds a new index to _indexList and marks the list dirty + * + * A future performance optimization is to only build the new index rather than + * marking them all dirty + * + * @private + * @param {!string} indexName must be unque + * @param {!function({entry} filterFunction should return true to include an + * entry in the index + */ function _addIndex(indexName, filterFunction) { if (_indexList.hasOwnProperty(indexName)) { console.error("Duplicate index name"); @@ -123,13 +123,13 @@ define(function (require, exports, module) { /** - * Checks the entry against the filterFunction for each index and adds - * a fileInfo to the index if the entry meets the criteria. FileInfo's are - * shared between indexes. - * - * @private - * @param {!entry} entry to be added to the indexes - */ + * Checks the entry against the filterFunction for each index and adds + * a fileInfo to the index if the entry meets the criteria. FileInfo's are + * shared between indexes. + * + * @private + * @param {!entry} entry to be added to the indexes + */ // future use when files are incrementally added // function _addFileToIndexes(entry) { @@ -149,23 +149,24 @@ define(function (require, exports, module) { }); } - /** - * Error dialog when max files in index is hit - */ + /** + * Error dialog when max files in index is hit + * @return {Dialog} + */ function _showMaxFilesDialog() { return Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_MAX_FILES_TITLE, Strings.ERROR_MAX_FILES ); } /* Recursively visits all files that are descendent of dirEntry and adds - * files files to each index when the file matches the filter critera - * @private - * @param {!DirectoryEntry} dirEntry - * @returns {$.Promise} - */ + * files files to each index when the file matches the filter critera + * @private + * @param {!DirectoryEntry} dirEntry + * @returns {$.Promise} + */ function _scanDirectorySubTree(dirEntry) { if (!dirEntry) { console.error("Bad dirEntry passed to _scanDirectorySubTree"); @@ -273,9 +274,9 @@ define(function (require, exports, module) { /** - * Clears the fileInfo array for all the indexes in _indexList - * @private - */ + * Clears the fileInfo array for all the indexes in _indexList + * @private + */ function _clearIndexes() { CollectionUtils.forEach(_indexList, function (index, indexName) { index.fileInfos = []; @@ -296,9 +297,9 @@ define(function (require, exports, module) { var _ongoingSyncPromise = null; /** - * Clears and rebuilds all of the fileIndexes and sets _indexListDirty to false - * @return {$.Promise} resolved when index has been updated - */ + * Clears and rebuilds all of the fileIndexes and sets _indexListDirty to false + * @return {$.Promise} resolved when index has been updated + */ function syncFileIndex() { // If we're already syncing, don't kick off a second one @@ -328,10 +329,10 @@ define(function (require, exports, module) { } /** - * Returns the FileInfo array for the specified index - * @param {!string} indexname - * @return {$.Promise} a promise that is resolved with an Array of FileInfo's - */ + * Returns the FileInfo array for the specified index + * @param {!string} indexname + * @return {$.Promise} a promise that is resolved with an Array of FileInfo's + */ function getFileInfoList(indexName) { var result = new $.Deferred(); @@ -392,8 +393,8 @@ define(function (require, exports, module) { } /** - * Add the indexes - */ + * Add the indexes + */ _addIndex( "all", diff --git a/src/project/FileSyncManager.js b/src/project/FileSyncManager.js index 7c2e9a98d75..10c96d9615c 100644 --- a/src/project/FileSyncManager.js +++ b/src/project/FileSyncManager.js @@ -47,6 +47,7 @@ define(function (require, exports, module) { CommandManager = require("command/CommandManager"), Async = require("utils/Async"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), Strings = require("strings"), StringUtils = require("utils/StringUtils"), FileUtils = require("file/FileUtils"), @@ -207,11 +208,11 @@ define(function (require, exports, module) { /** * @param {FileError} error * @param {!Document} doc - * @return {$.Promise} + * @return {Dialog} */ function showReloadError(error, doc) { return Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_RELOADING_FILE_TITLE, StringUtils.format( Strings.ERROR_RELOADING_FILE, @@ -255,33 +256,58 @@ define(function (require, exports, module) { return promise; } - var message; - var dialogId; var toClose; + var dialogId; + var message; + var buttons; // Prompt UI varies depending on whether the file on disk was modified vs. deleted if (i < editConflicts.length) { toClose = false; - dialogId = Dialogs.DIALOG_ID_EXT_CHANGED; + dialogId = DefaultDialogs.DIALOG_ID_EXT_CHANGED; message = StringUtils.format( Strings.EXT_MODIFIED_MESSAGE, StringUtils.breakableUrl( ProjectManager.makeProjectRelativeIfPossible(doc.file.fullPath) ) ); + buttons = [ + { + className: Dialogs.DIALOG_BTN_CLASS_LEFT, + id: Dialogs.DIALOG_BTN_DONTSAVE, + text: Strings.RELOAD_FROM_DISK + }, + { + className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, + id: Dialogs.DIALOG_BTN_CANCEL, + text: Strings.KEEP_CHANGES_IN_EDITOR + } + ]; } else { toClose = true; - dialogId = Dialogs.DIALOG_ID_EXT_DELETED; + dialogId = DefaultDialogs.DIALOG_ID_EXT_DELETED; message = StringUtils.format( Strings.EXT_DELETED_MESSAGE, StringUtils.breakableUrl( ProjectManager.makeProjectRelativeIfPossible(doc.file.fullPath) ) ); + buttons = [ + { + className: Dialogs.DIALOG_BTN_CLASS_LEFT, + id: Dialogs.DIALOG_BTN_DONTSAVE, + text: Strings.CLOSE_DONT_SAVE + }, + { + className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, + id: Dialogs.DIALOG_BTN_CANCEL, + text: Strings.KEEP_CHANGES_IN_EDITOR + } + ]; } - Dialogs.showModalDialog(dialogId, title, message) + Dialogs.showModalDialog(dialogId, title, message, buttons) .done(function (id) { if (id === Dialogs.DIALOG_BTN_DONTSAVE) { if (toClose) { @@ -297,7 +323,7 @@ define(function (require, exports, module) { .fail(function (error) { // Unable to load changed version from disk - show error UI showReloadError(error, doc) - .always(function () { + .done(function () { // After user dismisses, move on to next conflict prompt result.reject(); }); @@ -344,8 +370,8 @@ define(function (require, exports, module) { // Close dialog if it was open. This will 'unblock' presentConflict(), which bails back // to us immediately upon seeing _restartPending. We then restart the sync - see below - Dialogs.cancelModalDialogIfOpen(Dialogs.DIALOG_ID_EXT_CHANGED); - Dialogs.cancelModalDialogIfOpen(Dialogs.DIALOG_ID_EXT_DELETED); + Dialogs.cancelModalDialogIfOpen(DefaultDialogs.DIALOG_ID_EXT_CHANGED); + Dialogs.cancelModalDialogIfOpen(DefaultDialogs.DIALOG_ID_EXT_DELETED); return; } diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index cd51eeab6cb..4ce83674885 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -56,6 +56,7 @@ define(function (require, exports, module) { CommandManager = require("command/CommandManager"), Commands = require("command/Commands"), Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), Menus = require("command/Menus"), StringUtils = require("utils/StringUtils"), Strings = require("strings"), @@ -763,7 +764,7 @@ define(function (require, exports, module) { processEntries(entries); } else { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_LOADING_PROJECT, StringUtils.format( Strings.READ_DIRECTORY_ENTRIES_ERROR, @@ -920,7 +921,7 @@ define(function (require, exports, module) { }, function (error) { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_LOADING_PROJECT, StringUtils.format( Strings.REQUEST_NATIVE_FILE_SYSTEM_ERROR, @@ -1084,7 +1085,7 @@ define(function (require, exports, module) { }, function (error) { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_LOADING_PROJECT, StringUtils.format(Strings.OPEN_DIALOG_ERROR, error.name) ); @@ -1103,6 +1104,7 @@ define(function (require, exports, module) { /** * Invoke project settings dialog. + * @return {Dialog} */ function _projectSettings() { return PreferencesDialogs.showProjectPreferencesDialog(getBaseUrl()); @@ -1121,7 +1123,7 @@ define(function (require, exports, module) { // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if (filename.search(/[\/?*:;\{\}<>\\|]+/) !== -1) { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.INVALID_FILENAME_TITLE, Strings.INVALID_FILENAME_MESSAGE ); @@ -1245,7 +1247,7 @@ define(function (require, exports, module) { if ((error.name === NativeFileError.PATH_EXISTS_ERR) || (error.name === NativeFileError.TYPE_MISMATCH_ERR)) { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.INVALID_FILENAME_TITLE, StringUtils.format( Strings.FILE_ALREADY_EXISTS, @@ -1258,7 +1260,7 @@ define(function (require, exports, module) { StringUtils.format(Strings.GENERIC_ERROR, error.name); Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_CREATING_FILE_TITLE, StringUtils.format( Strings.ERROR_CREATING_FILE, @@ -1385,7 +1387,7 @@ define(function (require, exports, module) { } else { // Show an error alert Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_RENAMING_FILE_TITLE, StringUtils.format( Strings.ERROR_RENAMING_FILE, diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 568961ea4a8..9a3cdcf027e 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -357,12 +357,16 @@ } .modal-footer { + text-align: right; background-color: @tc-gray-panel; border-top: 1px solid rgba(0,0,0,0.09); box-shadow: none; white-space: nowrap; } +.modal-footer .btn { + float: none; +} .modal-footer .btn.left { float: left; @@ -373,6 +377,16 @@ margin-right: 5px; } +.platform-win { + /* Reverse Save/Cancel button order on Win */ + .modal-footer { + text-align: left; + } + .modal-footer .btn:not(.left) { + float: right; + } +} + .modal-body ul { /* Bootstrap's type.less defines a heavy margin-bottom on ul/ol that we don't want in dialogs since they have heavy padding instead. */ diff --git a/src/utils/UpdateNotification.js b/src/utils/UpdateNotification.js index 0b66e41e962..19c22ed91d3 100644 --- a/src/utils/UpdateNotification.js +++ b/src/utils/UpdateNotification.js @@ -32,6 +32,7 @@ define(function (require, exports, module) { "use strict"; var Dialogs = require("widgets/Dialogs"), + DefaultDialogs = require("widgets/DefaultDialogs"), NativeApp = require("utils/NativeApp"), PreferencesManager = require("preferences/PreferencesManager"), Strings = require("strings"), @@ -303,7 +304,7 @@ define(function (require, exports, module) { } else if (force) { // No updates are available. If force == true, let the user know. Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.NO_UPDATE_TITLE, Strings.NO_UPDATE_MESSAGE ); @@ -326,7 +327,7 @@ define(function (require, exports, module) { // Error fetching the update data. If this is a forced check, alert the user if (force) { Dialogs.showModalDialog( - Dialogs.DIALOG_ID_ERROR, + DefaultDialogs.DIALOG_ID_ERROR, Strings.ERROR_FETCHING_UPDATE_INFO_TITLE, Strings.ERROR_FETCHING_UPDATE_INFO_MSG ); diff --git a/src/widgets/DefaultDialogs.js b/src/widgets/DefaultDialogs.js new file mode 100644 index 00000000000..d31556f36df --- /dev/null +++ b/src/widgets/DefaultDialogs.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + + +/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ +/*global define */ + +define(function (require, exports, module) { + "use strict"; + + /** + * List of constants for the default dialogs IDs. + */ + exports.DIALOG_ID_ERROR = "error-dialog"; + exports.DIALOG_ID_INFO = "error-dialog"; // uses the same template for now--could be different in future + exports.DIALOG_ID_SAVE_CLOSE = "save-close-dialog"; + exports.DIALOG_ID_EXT_CHANGED = "ext-changed-dialog"; + exports.DIALOG_ID_EXT_DELETED = "ext-deleted-dialog"; + exports.DIALOG_ID_LIVE_DEVELOPMENT = "live-development-error-dialog"; + exports.DIALOG_ID_REMOVE_EXTENSIONS = "remove-marked-extensions"; +}); diff --git a/src/widgets/Dialogs.js b/src/widgets/Dialogs.js index 4c3bf7e9b76..921e1298af3 100644 --- a/src/widgets/Dialogs.js +++ b/src/widgets/Dialogs.js @@ -23,7 +23,7 @@ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, $, brackets, window */ +/*global define, $, brackets, window, Mustache */ /** * Utilities for creating and managing standard modal dialogs. @@ -35,28 +35,34 @@ define(function (require, exports, module) { var KeyBindingManager = require("command/KeyBindingManager"), KeyEvent = require("utils/KeyEvent"), - NativeApp = require("utils/NativeApp"); - - var DIALOG_BTN_CANCEL = "cancel", - DIALOG_BTN_OK = "ok", - DIALOG_BTN_DONTSAVE = "dontsave", - DIALOG_CANCELED = "_canceled", - DIALOG_BTN_DOWNLOAD = "download"; + NativeApp = require("utils/NativeApp"), + Strings = require("strings"), + DialogTemplate = require("text!htmlContent/dialog-template.html"); - // TODO: (issue #258) In future, we should templatize the HTML for the dialogs rather than having - // it live directly in the HTML. - var DIALOG_ID_ERROR = "error-dialog", - DIALOG_ID_INFO = "error-dialog", // uses the same template for now--could be different in future - DIALOG_ID_SAVE_CLOSE = "save-close-dialog", - DIALOG_ID_EXT_CHANGED = "ext-changed-dialog", - DIALOG_ID_EXT_DELETED = "ext-deleted-dialog", - DIALOG_ID_LIVE_DEVELOPMENT = "live-development-error-dialog"; + /** + * Dialog Buttons IDs + * @const {string} + */ + var DIALOG_BTN_CANCEL = "cancel", + DIALOG_BTN_OK = "ok", + DIALOG_BTN_DONTSAVE = "dontsave", + DIALOG_CANCELED = "_canceled", + DIALOG_BTN_DOWNLOAD = "download"; + + /** + * Dialog Buttons Class Names + * @const {string} + */ + var DIALOG_BTN_CLASS_PRIMARY = "primary", + DIALOG_BTN_CLASS_NORMAL = "", + DIALOG_BTN_CLASS_LEFT = "left"; + /** * A stack of keydown event handler functions that corresponds to the * current stack of modal dialogs. * - * @type {Array.} + * @type {Array.} */ var _keydownListeners = []; @@ -118,23 +124,60 @@ define(function (require, exports, module) { } }; + + + /** + * @constructor + * @private + * + * @param {$.Element} $dlg The dialog jQuery element + * @param {$.Promise} promise A promise that will be resolved with the ID of the clicked button when the dialog + * is dismissed. Never rejected. + */ + function Dialog($dlg, promise) { + this._$dlg = $dlg; + this._promise = promise; + } + + /** @type {$.Element} The dialog jQuery element */ + Dialog.prototype.getElement = function () { + return this._$dlg; + }; + + /** @type {$.Promise} The dialog promise */ + Dialog.prototype.getPromise = function () { + return this._promise; + }; + + /** + * Closes the dialog if is visible + */ + Dialog.prototype.close = function () { + if (this._$dlg.is(":visible")) { // Bootstrap breaks if try to hide dialog that's already hidden + _dismissDialog(this._$dlg, DIALOG_CANCELED); + } + }; + + /** + * Adds a done callback to the dialog promise + */ + Dialog.prototype.done = function (callback) { + this._promise.done(callback); + }; + + + /** - * Like showModalDialog(), but takes a template as a parameter rather than assuming the template is embedded - * in the current DOM. The template can either be a string or a jQuery object representing a DOM node that is - * *not* in the current DOM. + * Creates a new modal dialog from a given template. + * The template can either be a string or a jQuery object representing a DOM node that is *not* in the current DOM. * * @param {string} template A string template or jQuery object to use as the dialog HTML. - * @param {string=} title The title of the error dialog. Can contain HTML markup. If unspecified, title in - * the HTML template is used unchanged. - * @param {string=} message The message to display in the error dialog. Can contain HTML markup. If - * unspecified, body in the HTML template is used unchanged. * @param {boolean=} autoDismiss Whether to automatically dismiss the dialog when one of the buttons * is clicked. Default true. If false, you'll need to manually handle button clicks and the Esc * key, and dismiss the dialog yourself when ready with `cancelModalDialogIfOpen()`. - * @return {$.Promise} a promise that will be resolved with the ID of the clicked button when the dialog - * is dismissed. Never rejected. + * @return {Dialog} */ - function showModalDialogUsingTemplate(template, title, message, autoDismiss) { + function showModalDialogUsingTemplate(template, autoDismiss) { if (autoDismiss === undefined) { autoDismiss = true; } @@ -149,14 +192,6 @@ define(function (require, exports, module) { // Save the dialog promise for unit tests $dlg.data("promise", promise); - // Set title and message - if (title) { - $(".dialog-title", $dlg).html(title); - } - if (message) { - $(".dialog-message", $dlg).html(message); - } - $(".clickable-link", $dlg).on("click", function _handleLink(e) { // Links use data-href (not href) attribute so Brackets itself doesn't redirect if (e.currentTarget.dataset && e.currentTarget.dataset.href) { @@ -239,42 +274,39 @@ define(function (require, exports, module) { keyboard: false // handle the ESC key ourselves so we can deal with nested dialogs }); - return promise; + return (new Dialog($dlg, promise)); } + /** - * General purpose modal dialog. Assumes that: - * -- the root tag of the dialog is marked with a unique class name (passed as dlgClass), as well as the - * classes "template modal hide". - * -- the HTML for the dialog contains elements with "title" and "message" classes, as well as a number - * of elements with "dialog-button" class, each of which has a "data-button-id". + * Creates a new general purpose modal dialog using the default template and the template variables given + * as parameters as described. * - * @param {string} dlgClass The class of the dialog node in the HTML. - * @param {string=} title The title of the error dialog. Can contain HTML markup. If unspecified, title in - * the HTML template is used unchanged. - * @param {string=} message The message to display in the error dialog. Can contain HTML markup. If - * unspecified, body in the HTML template is used unchanged. - * @return {$.Promise} a promise that will be resolved with the ID of the clicked button when the dialog - * is dismissed. Never rejected. + * @param {string} dlgClass A class name identifier for the dialog. + * @param {string=} title The title of the dialog. Can contain HTML markup. If unspecified, the title + * in the JSON file is used unchanged. + * @param {string=} message The message to display in the dialog. Can contain HTML markup. If + * unspecified, the message in the JSON file is used. + * @param {Array.<{className: string, id: string, text: string>=} buttons An array of buttons where each button + * has a class, id and text property. The id is used in "data-button-id". It defaults to an Ok button + * @return {Dialog} */ - function showModalDialog(dlgClass, title, message) { - // We clone the HTML rather than using it directly so that if two dialogs of the same - // type happen to show up, they can appear at the same time. (This is an edge case that - // shouldn't happen often, but we can't prevent it from happening since everything is - // asynchronous.) - var $template = $("." + dlgClass + ".template") - .clone() - .removeClass("template"); - if ($template.length === 0) { - console.error("Dialog id " + dlgClass + " does not exist"); - return; - } - return showModalDialogUsingTemplate($template, title, message); + function showModalDialog(dlgClass, title, message, buttons) { + var templateVars = { + dlgClass: dlgClass, + title: title || "", + message: message || "", + buttons: buttons || [{ className: DIALOG_BTN_CLASS_PRIMARY, id: DIALOG_BTN_OK, text: Strings.OK }] + }; + var template = Mustache.render(DialogTemplate, templateVars); + + return showModalDialogUsingTemplate(template); } /** * Immediately closes any dialog instances with the given class. The dialog callback for each instance will * be called with the special buttonId DIALOG_CANCELED (note: callback is run asynchronously). + * @param {string} dlgClass The class name identifier for the dialog. */ function cancelModalDialogIfOpen(dlgClass) { $("." + dlgClass + ".instance").each(function (index, dlg) { @@ -284,20 +316,18 @@ define(function (require, exports, module) { }); } - exports.DIALOG_BTN_CANCEL = DIALOG_BTN_CANCEL; - exports.DIALOG_BTN_OK = DIALOG_BTN_OK; - exports.DIALOG_BTN_DONTSAVE = DIALOG_BTN_DONTSAVE; - exports.DIALOG_CANCELED = DIALOG_CANCELED; - exports.DIALOG_BTN_DOWNLOAD = DIALOG_BTN_DOWNLOAD; - exports.DIALOG_ID_ERROR = DIALOG_ID_ERROR; - exports.DIALOG_ID_INFO = DIALOG_ID_INFO; - exports.DIALOG_ID_SAVE_CLOSE = DIALOG_ID_SAVE_CLOSE; - exports.DIALOG_ID_EXT_CHANGED = DIALOG_ID_EXT_CHANGED; - exports.DIALOG_ID_EXT_DELETED = DIALOG_ID_EXT_DELETED; - exports.DIALOG_ID_LIVE_DEVELOPMENT = DIALOG_ID_LIVE_DEVELOPMENT; + exports.DIALOG_BTN_CANCEL = DIALOG_BTN_CANCEL; + exports.DIALOG_BTN_OK = DIALOG_BTN_OK; + exports.DIALOG_BTN_DONTSAVE = DIALOG_BTN_DONTSAVE; + exports.DIALOG_CANCELED = DIALOG_CANCELED; + exports.DIALOG_BTN_DOWNLOAD = DIALOG_BTN_DOWNLOAD; + + exports.DIALOG_BTN_CLASS_PRIMARY = DIALOG_BTN_CLASS_PRIMARY; + exports.DIALOG_BTN_CLASS_NORMAL = DIALOG_BTN_CLASS_NORMAL; + exports.DIALOG_BTN_CLASS_LEFT = DIALOG_BTN_CLASS_LEFT; exports.showModalDialog = showModalDialog; exports.showModalDialogUsingTemplate = showModalDialogUsingTemplate; exports.cancelModalDialogIfOpen = cancelModalDialogIfOpen; -}); \ No newline at end of file +});