diff --git a/.travis.yml b/.travis.yml index b2803e1d9..464408502 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ language: node_js sudo: false # use container-based Travis infrastructure node_js: - "6" -before_install: - - phpenv global 7.0 #switch to php7, since that's what php-Tooling extension requires before_script: - npm install -g grunt-cli - npm install -g jasmine-node diff --git a/Gruntfile.js b/Gruntfile.js index 0a276991b..b6598f4e3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -50,6 +50,16 @@ module.exports = function (grunt) { 'src/styles/brackets.css' ] }] + }, + node_modules_test_dir : { + files: [{ + dot: true, + src: [ + 'dist/node_modules/npm/test/fixtures', + 'dist/node_modules/npm/node_modules/tar/test', + 'dist/node_modules/npm/node_modules/npm-registry-client/test' + ] + }] } }, copy: { @@ -412,7 +422,8 @@ module.exports = function (grunt) { 'npm-install', 'cleanempty', 'usemin', - 'build-config' + 'build-config', + 'clean:node_modules_test_dir' ]); // task: build @@ -430,3 +441,4 @@ module.exports = function (grunt) { // Default task. grunt.registerTask('default', ['test']); }; + diff --git a/README.md b/README.md index 481d454ef..a71783965 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + +| :warning: On September 1, 2021, Adobe will end support for Brackets. If you would like to continue using, maintaining, and improving Brackets, you may fork the project on [GitHub](https://github.com/adobe/brackets). Through Adobe’s partnership with Microsoft, we encourage users to migrate to [Visual Studio Code](https://aka.ms/brackets-to-vscode), Microsoft’s free code editor built on open source. +| --- + + Welcome to Brackets! [![Build Status](https://travis-ci.org/adobe/brackets.svg?branch=master)](https://travis-ci.org/adobe/brackets) ------------------- diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 3ac8cb728..acfb3ca93 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1946,9 +1946,9 @@ "dev": true }, "lodash": { - "version": "4.17.4", - "from": "lodash@4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "version": "4.17.15", + "from": "lodash@4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz" }, "longest": { "version": "1.0.1", diff --git a/package.json b/package.json index 805dff574..84ce74f28 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "chokidar": "1.6.1", "decompress-zip": "0.3.0", "fs-extra": "2.0.0", - "lodash": "4.17.4", + "lodash": "4.17.15", "npm": "3.10.10", "opn": "4.0.2", "request": "2.79.0", diff --git a/src/brackets.config.dist.json b/src/brackets.config.dist.json index c3411b31a..e6168390a 100644 --- a/src/brackets.config.dist.json +++ b/src/brackets.config.dist.json @@ -3,7 +3,7 @@ "analyticsDataServerURL" : "https://cc-api-data.adobe.io/ingest", "serviceKey" : "brackets-service", "environment" : "production", - "update_info_url" : "https://getupdates.brackets.io/getupdates/", + "update_info_url" : "https://getupdates.brackets.io/getupdates?locale=", "notification_info_url" : "https://getupdates.brackets.io/getnotifications?locale=", "buildtype" : "production" } diff --git a/src/brackets.js b/src/brackets.js index 27c543879..c291a3221 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -257,6 +257,23 @@ define(function (require, exports, module) { ); } + brackets.app.getRemoteDebuggingPort(function (err, remote_debugging_port){ + var InfoBar = require('widgets/infobar'), + StringUtils = require("utils/StringUtils"); + if ((!err) && remote_debugging_port && remote_debugging_port > 0) { + InfoBar.showInfoBar({ + type: "warning", + title: `${Strings.REMOTE_DEBUGGING_ENABLED}${remote_debugging_port}`, + description: "" + }); + } else if (err) { + InfoBar.showInfoBar({ + type: "error", + title: StringUtils.format(Strings.REMOTE_DEBUGGING_PORT_INVALID, err, 1024, 65534), + description: "" + }); + } + }); // Use quiet scrollbars if we aren't on Lion. If we're on Lion, only // use native scroll bars when the mouse is not plugged in or when // using the "Always" scroll bar setting. diff --git a/src/config.json b/src/config.json index cc1c874f7..d954955a7 100644 --- a/src/config.json +++ b/src/config.json @@ -47,7 +47,7 @@ "chokidar": "1.6.1", "decompress-zip": "0.3.0", "fs-extra": "2.0.0", - "lodash": "4.17.4", + "lodash": "4.17.15", "npm": "3.10.10", "opn": "4.0.2", "request": "2.79.0", diff --git a/src/document/DocumentCommandHandlers.js b/src/document/DocumentCommandHandlers.js index ed875376d..bcc235f9a 100644 --- a/src/document/DocumentCommandHandlers.js +++ b/src/document/DocumentCommandHandlers.js @@ -1642,28 +1642,33 @@ define(function (require, exports, module) { if (brackets.inBrowser) { result.resolve(); } else { - var port = brackets.app.getRemoteDebuggingPort ? brackets.app.getRemoteDebuggingPort() : 9234; - Inspector.getDebuggableWindows("127.0.0.1", port) - .fail(result.reject) - .done(function (response) { - var page = response[0]; - if (!page || !page.webSocketDebuggerUrl) { - result.reject(); - return; - } - var _socket = new WebSocket(page.webSocketDebuggerUrl); - // Disable the cache - _socket.onopen = function _onConnect() { - _socket.send(JSON.stringify({ id: 1, method: "Network.setCacheDisabled", params: { "cacheDisabled": true } })); - }; - // The first message will be the confirmation => disconnected to allow remote debugging of Brackets - _socket.onmessage = function _onMessage(e) { - _socket.close(); - result.resolve(); - }; - // In case of an error - _socket.onerror = result.reject; - }); + brackets.app.getRemoteDebuggingPort(function (err, port){ + if ((!err) && port && port > 0) { + Inspector.getDebuggableWindows("127.0.0.1", port) + .fail(result.reject) + .done(function (response) { + var page = response[0]; + if (!page || !page.webSocketDebuggerUrl) { + result.reject(); + return; + } + var _socket = new WebSocket(page.webSocketDebuggerUrl); + // Disable the cache + _socket.onopen = function _onConnect() { + _socket.send(JSON.stringify({ id: 1, method: "Network.setCacheDisabled", params: { "cacheDisabled": true } })); + }; + // The first message will be the confirmation => disconnected to allow remote debugging of Brackets + _socket.onmessage = function _onMessage(e) { + _socket.close(); + result.resolve(); + }; + // In case of an error + _socket.onerror = result.reject; + }); + } else { + result.reject(); + } + }); } return result.promise(); diff --git a/src/extensions/default/CSSCodeHints/CSSProperties.json b/src/extensions/default/CSSCodeHints/CSSProperties.json index d9bd8370f..342b9875d 100644 --- a/src/extensions/default/CSSCodeHints/CSSProperties.json +++ b/src/extensions/default/CSSCodeHints/CSSProperties.json @@ -140,6 +140,8 @@ "image-resolution": {"values": ["from-image", "snap"]}, "isolation": {"values": ["auto", "isolate"]}, "justify-content": {"values": ["center", "flex-end", "flex-start", "space-around", "space-between"]}, + "justify-items": {"values": ["auto", "normal", "stretch", "center", "start", "end", "flex-start", "flex-end", "self-start", "self-end", "left", "right", "baseline", "first", "last", "safe", "unsafe", "legacy", "inherit", "initial"]}, + "justify-self": {"values": ["auto", "normal", "stretch", "center", "start", "end", "flex-start", "flex-end", "self-start", "self-end", "left", "right", "baseline", "first", "last", "safe", "unsafe", "inherit", "initial"]}, "left": {"values": ["auto", "inherit"]}, "letter-spacing": {"values": ["normal", "inherit"]}, "line-height": {"values": ["normal", "inherit"]}, @@ -190,6 +192,7 @@ "resize": {"values": ["both", "horizontal", "none", "vertical", "inherit"]}, "right": {"values": ["auto", "inherit"]}, "scroll-behavior": {"values": ["auto", "smooth"]}, + "scroll-snap-type": {"values": ["none", "x", "y", "block", "inline", "both", "mandatory", "proximity"]}, "src": {"values": [ "url()"]}, "shape-image-threshold": {"values": []}, "shape-inside": {"values": ["auto", "circle()", "ellipse()", "inherit", "outside-shape", "polygon()", "rectangle()"]}, diff --git a/src/extensions/default/InAppNotifications/main.js b/src/extensions/default/InAppNotifications/main.js index f51684006..b4c2e582c 100644 --- a/src/extensions/default/InAppNotifications/main.js +++ b/src/extensions/default/InAppNotifications/main.js @@ -213,6 +213,11 @@ define(function (require, exports, module) { } function _checkExtensions(filters) { + //if no property called extensions then it's a universal notification + if (filters.extensions === undefined) { + return true; + } + var allExtensions = ExtensionManager.extensions, allExtnsMatched = true, userExtensionKeys = Object.keys(allExtensions).filter(function(k) { @@ -220,10 +225,16 @@ define(function (require, exports, module) { }); if (!filters.extensions) { - allExtnsMatched = userExtensionKeys.size === 0; + //if property called extensions exists but has a falsy value + //then number of user extensions must be zero + allExtnsMatched = userExtensionKeys.length === 0; } else if (filters.extensions.length === 0) { + //if property called extensions exists but is an empty array + //then number of user extensions must greater than zero allExtnsMatched = userExtensionKeys.length > 0; } else { + //if property called extensions exists but is a non empty array + //then notification is targetted to users having the fitered extensions var filteredExtns = filters.extensions, extnIterator = null; for (var i=0; i < filteredExtns.length; i++) { diff --git a/src/extensions/default/OpenWithExternalApplication/GraphicsFile.js b/src/extensions/default/OpenWithExternalApplication/GraphicsFile.js new file mode 100644 index 000000000..1ffc72c11 --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/GraphicsFile.js @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2013 - present 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. + * + */ + +define(function (require, exports, module) { + "use strict"; + + + var PreferencesManager = brackets.getModule("preferences/PreferencesManager"), + Strings = brackets.getModule("strings"), + StringsUtils = brackets.getModule("utils/StringUtils"), + ProjectManager = brackets.getModule("project/ProjectManager"), + Dialogs = brackets.getModule("widgets/Dialogs"), + DefaultDialogs = brackets.getModule("widgets/DefaultDialogs"), + HealthLogger = brackets.getModule("utils/HealthLogger"); + + + var _requestID = 0, + _initialized = false; + + var _graphicsFileTypes = ["jpg", "jpeg", "png", "svg", "xd", "psd", "ai"]; + + var _nodeDomain; + + function init(nodeDomain) { + + if (_initialized) { + return; + } + _initialized = true; + + _nodeDomain = nodeDomain; + + _nodeDomain.on('checkFileTypesInFolderResponse', function (event, response) { + if (response.id !== _requestID) { + return; + } + _graphicsFilePresentInProject(response.present); + }); + + ProjectManager.on("projectOpen", function () { + _checkForGraphicsFileInPrjct(); + }); + + _checkForGraphicsFileInPrjct(); + + } + + + function _checkForGraphicsFileInPrjct() { + + if (PreferencesManager.getViewState("AssociateGraphicsFileDialogShown")) { + return; + } + + var userUuid = PreferencesManager.getViewState("UUID"), + olderUuid = PreferencesManager.getViewState("OlderUUID"); + + if(!(userUuid || olderUuid)) { + return; + } + + _nodeDomain.exec("checkFileTypesInFolder", { + extensions: _graphicsFileTypes.join(), + folder: ProjectManager.getProjectRoot().fullPath, + reqId: ++_requestID + }); + + } + + function _graphicsFilePresentInProject(isPresent) { + + if (!isPresent) { + return; + } + + Dialogs.showModalDialog( + DefaultDialogs.DIALOG_ID_INFO, + Strings.ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_TITLE, + Strings.ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_MSG, + [ + { 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.OK + } + ] + ).done(function (id) { + + if (id !== Dialogs.DIALOG_BTN_OK) { + HealthLogger.sendAnalyticsData( + "externalEditorsCancelled", + "usage", + "externalEditors", + "Cancelled", + "" + ); + return; + } + HealthLogger.sendAnalyticsData( + "LinkExternalEditors", + "usage", + "externalEditors", + "LinkExternalEditors", + "" + ); + + brackets.app.getSystemDefaultApp(_graphicsFileTypes.join(), function (err, out) { + + if (err) { + return; + } + var associateApp = out.split(','), + fileTypeToAppMap = {}, + AppToFileTypeMap = {}; + + associateApp.forEach(function (item) { + + var filetype = item.split('##')[0], + app = item.split('##')[1]; + + if (!filetype) { + return; + } + + if (filetype === "xd") { + if (app.toLowerCase() !== "adobe xd" && app.toLowerCase() !== "adobe.cc.xd") { + return; + } + + app = "Adobe XD"; + } + fileTypeToAppMap[filetype] = app; + + if (brackets.platform === "win" && app.toLowerCase().endsWith('.exe')) { + app = app.substring(app.lastIndexOf('\\') + 1, app.length - 4); + } + if (AppToFileTypeMap[app]) { + AppToFileTypeMap[app].push(filetype); + } else { + AppToFileTypeMap[app] = [filetype]; + } + }); + + var prefs = PreferencesManager.get('externalApplications'); + + for (var key in fileTypeToAppMap) { + if (fileTypeToAppMap.hasOwnProperty(key)) { + if(key && !prefs[key]) { + prefs[key] = fileTypeToAppMap[key]; + if(brackets.platform === "win" && !fileTypeToAppMap[key].toLowerCase().endsWith('.exe')) { + prefs[key] = "default"; + } + HealthLogger.sendAnalyticsData( + "AddExternalEditorForFileType_" + key.toUpperCase(), + "usage", + "externalEditors", + "AddExternalEditorForFileType_" + key.toUpperCase(), + "" + ); + } + } + } + + PreferencesManager.set('externalApplications', prefs); + + var str = ""; + for(var app in AppToFileTypeMap) { + str += AppToFileTypeMap[app].join() + "->" + app + "
"; + } + + if(!str) { + return; + } + + str+="
"; + + Dialogs.showModalDialog( + DefaultDialogs.DIALOG_ID_INFO, + Strings.ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_TITLE, + StringsUtils.format(Strings.ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_CNF_MSG, str), + [ + { className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, id: Dialogs.DIALOG_BTN_OK, + text: Strings.OK + } + ] + ); + }); + }); + PreferencesManager.setViewState("AssociateGraphicsFileDialogShown", true); + + } + + exports.init = init; + +}); diff --git a/src/extensions/default/OpenWithExternalApplication/main.js b/src/extensions/default/OpenWithExternalApplication/main.js new file mode 100644 index 000000000..3589f994c --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/main.js @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013 - present 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. + * + */ + +define(function (require, exports, module) { + "use strict"; + + + var AppInit = brackets.getModule("utils/AppInit"), + PreferencesManager = brackets.getModule("preferences/PreferencesManager"), + Strings = brackets.getModule("strings"), + FileViewController = brackets.getModule("project/FileViewController"), + ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), + NodeDomain = brackets.getModule("utils/NodeDomain"), + FileUtils = brackets.getModule("file/FileUtils"), + FileSystem = brackets.getModule("filesystem/FileSystem"), + GraphicsFile = require("GraphicsFile"); + + /** + * @private + * @type {string} fullPath of the OpenWithExternalEditor Domain implementation + */ + var _domainPath = ExtensionUtils.getModulePath(module, "node/OpenWithExternalApplicationDomain"); + + /** + * @private + * @type {NodeDomain} + */ + var _nodeDomain = new NodeDomain("OpenWithExternalApplication", _domainPath); + + var extensionToExtApplicationMap = {}; + + function convertUnixPathToWindowsPath(path) { + if (brackets.platform === "win" && path && FileSystem.isAbsolutePath(path)) { + path = path.replace(RegExp('/','g'), '\\'); + } + return path; + } + + function _openWithExternalApplication(event, path) { + _nodeDomain.exec("open", { + path: convertUnixPathToWindowsPath(path), + app: extensionToExtApplicationMap[FileUtils.getFileExtension(path).toLowerCase()] + }); + } + + PreferencesManager.definePreference("externalApplications", "object", {}, { + description: Strings.DESCRIPTION_EXTERNAL_APPLICATION_ASSOCIATE + }); + + PreferencesManager.on("change", "externalApplications", function () { + extensionToExtApplicationMap = PreferencesManager.get("externalApplications"); + FileUtils.addExtensionToExternalAppList(Object.keys(extensionToExtApplicationMap)); + }); + + FileViewController.on("openWithExternalApplication", _openWithExternalApplication); + + AppInit.appReady(function () { + + GraphicsFile.init(_nodeDomain); + extensionToExtApplicationMap = PreferencesManager.get("externalApplications"); + FileUtils.addExtensionToExternalAppList(Object.keys(extensionToExtApplicationMap)); + }); +}); diff --git a/src/extensions/default/OpenWithExternalApplication/node/OpenWithExternalApplicationDomain.js b/src/extensions/default/OpenWithExternalApplication/node/OpenWithExternalApplicationDomain.js new file mode 100644 index 000000000..e5a7e4145 --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/node/OpenWithExternalApplicationDomain.js @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2012 - present 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. + * + */ + +/*eslint-env node */ +/*jslint node: true */ +"use strict"; + +var open = require("open"), + Glob = require("glob").Glob, + path = require('path'); + +var _domainManager; + +/** + * @private + * + * @param {Object} params Object to use + */ +function _openWithExternalApplication(params) { + var application = "default" === params.app ? "": params.app; + open(params.path, application); +} + +/** + * @private + * + * @param {Object} params Object to use + */ +function _checkFileTypesInFolder(params) { + + var extList = params.extensions, + dirPath = path.normalize(params.folder), + + pattern = dirPath + "/**/*.+(" + extList.replace(/,/g, "|") + ")"; + + var globMgr = new Glob(pattern, function (err, matches) { + + var respObj = { + id: params.reqId, + present: matches.length > 0 ? true : false + }; + _domainManager.emitEvent('OpenWithExternalApplication', 'checkFileTypesInFolderResponse', [respObj]); + }); + + globMgr.on("match", function() { + globMgr.abort(); + var respObj = { + id: params.reqId, + present: true + }; + _domainManager.emitEvent('OpenWithExternalApplication', 'checkFileTypesInFolderResponse', [respObj]); + }); + +} + + +/** + * Initializes the OpenWithExternalEditor domain with its commands. + * @param {DomainManager} domainManager The DomainManager for the server + */ +function init(domainManager) { + _domainManager = domainManager; + + if (!domainManager.hasDomain("OpenWithExternalApplication")) { + domainManager.registerDomain("OpenWithExternalApplication", {major: 0, minor: 1}); + } + + _domainManager.registerCommand( + "OpenWithExternalApplication", + "open", + _openWithExternalApplication, + true, + "open document with External Application.", + [{ + name: "params", + type: "object", + description: "Params Object having document and App Path." + }], + [] + ); + + _domainManager.registerCommand( + "OpenWithExternalApplication", + "checkFileTypesInFolder", + _checkFileTypesInFolder, + true, + "looks for File Types in a folder.", + [{ + name: "params", + type: "object", + description: "Params Object having File Extensions and Folder Path." + }], + [] + ); + + _domainManager.registerEvent( + "OpenWithExternalApplication", + "checkFileTypesInFolderResponse", + [ + { + name: "msgObj", + type: "object", + description: "json object containing message info to pass to brackets" + } + ] + ); +} + +exports.init = init; diff --git a/src/extensions/default/OpenWithExternalApplication/node/package.json b/src/extensions/default/OpenWithExternalApplication/node/package.json new file mode 100644 index 000000000..0e6464959 --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/node/package.json @@ -0,0 +1,7 @@ +{ + "name": "brackets-open-external_application", + "dependencies": { + "open": "0.0.5", + "glob": "7.1.1" + } +} diff --git a/src/features/FindReferencesManager.js b/src/features/FindReferencesManager.js index 50b8cdfe5..8b9470a95 100644 --- a/src/features/FindReferencesManager.js +++ b/src/features/FindReferencesManager.js @@ -121,6 +121,16 @@ define(function (require, exports, module) { searchModel.clear(); } + /** + * @public + * Closes the references panel + */ + function closeReferencesPanel() { + if (_resultsView) { + _resultsView.close(); + } + } + function setMenuItemStateForLanguage(languageId) { CommandManager.get(Commands.CMD_FIND_ALL_REFERENCES).setEnabled(false); if (!languageId) { @@ -207,4 +217,5 @@ define(function (require, exports, module) { exports.registerFindReferencesProvider = registerFindReferencesProvider; exports.removeFindReferencesProvider = removeFindReferencesProvider; exports.setMenuItemStateForLanguage = setMenuItemStateForLanguage; + exports.closeReferencesPanel = closeReferencesPanel; }); diff --git a/src/file/FileUtils.js b/src/file/FileUtils.js index 308c389cd..284c839b4 100644 --- a/src/file/FileUtils.js +++ b/src/file/FileUtils.js @@ -59,6 +59,11 @@ define(function (require, exports, module) { */ var MAX_FILE_SIZE = MAX_FILE_SIZE_MB * 1024 * 1024; + /** + * @const {List} list of File Extensions which will be opened in external Application + */ + var extListToBeOpenedInExtApp = []; + /** * Asynchronously reads a file as UTF-8 encoded text. @@ -526,6 +531,28 @@ define(function (require, exports, module) { return pathArray.join("/"); } + /** + * @param {string} ext extension string a file + * @return {string} returns true If file to be opened in External Application. + * + */ + function shouldOpenInExternalApplication(ext) { + return extListToBeOpenedInExtApp.includes(ext); + } + + /** + * @param {string} ext File Extensions to be added in External App List + * + */ + function addExtensionToExternalAppList(ext) { + + if(Array.isArray(ext)) { + extListToBeOpenedInExtApp = ext; + } else if (typeof ext === 'string'){ + extListToBeOpenedInExtApp.push(ext); + } + } + // Asynchronously load DocumentCommandHandlers // This avoids a temporary circular dependency created // by relocating showFileOpenError() until deprecation is over @@ -568,4 +595,6 @@ define(function (require, exports, module) { exports.comparePaths = comparePaths; exports.MAX_FILE_SIZE = MAX_FILE_SIZE; exports.encodeFilePath = encodeFilePath; + exports.shouldOpenInExternalApplication = shouldOpenInExternalApplication; + exports.addExtensionToExternalAppList = addExtensionToExternalAppList; }); diff --git a/src/htmlContent/infobar-template.html b/src/htmlContent/infobar-template.html new file mode 100644 index 000000000..ab73c367b --- /dev/null +++ b/src/htmlContent/infobar-template.html @@ -0,0 +1,13 @@ +
+
+ +
+
+

{{title}}  {{{description}}}

+
+ {{^buttons}} +
+ +
+ {{/buttons}} +
\ No newline at end of file diff --git a/src/language/languages.json b/src/language/languages.json index 1de22f94d..5c8e9ef0b 100644 --- a/src/language/languages.json +++ b/src/language/languages.json @@ -291,7 +291,7 @@ "jsz", "lib", "mpeg", "mpg", "mp4", "msi", "node", "o", "obj", "odc", "odb", "odf", "odg", "odp", "ods", "odt", "otf", "pak", "pdb", "pdf", "pdi", "ppt", "pptx", "psd", "rar", "sdf", "so", "sqlite", "suo", "svgz", - "swf", "tar", "tif", "tiff", "ttf", "woff", "xls", "xlsx", "zip" + "swf", "tar", "tif", "tiff", "ttf", "woff", "xls", "xlsx", "zip", "xd" ], "isBinary": true }, diff --git a/src/nls/fr/strings.js b/src/nls/fr/strings.js index 866b74cba..7b164ab12 100644 --- a/src/nls/fr/strings.js +++ b/src/nls/fr/strings.js @@ -897,5 +897,20 @@ define({ "REFERENCES_NO_RESULTS": "Références non disponibles pour la position actuelle du curseur", "CMD_FIND_DOCUMENT_SYMBOLS": "Rechercher des symboles de document", - "CMD_FIND_PROJECT_SYMBOLS": "Rechercher des symboles de projet" + "CMD_FIND_PROJECT_SYMBOLS": "Rechercher des symboles de projet", + + // Remote debugging enabled + "REMOTE_DEBUGGING_ENABLED": "Débogage à distance activé sur localhost:", + + // Remote debugging port argument is invalid + "REMOTE_DEBUGGING_PORT_INVALID": "Impossible d’activer le débogage à distance sur le port {0}. Les numéros de port doivent être compris entre {1} et {2}.", + + //Associate File Type to External App + "DESCRIPTION_EXTERNAL_APPLICATION_ASSOCIATE": "Mappages d’extension de fichier avec des applications externes. Syntaxe : \"\": \"\", Utiliser « default » pour ouvrir les fichiers dans l’application par défaut du système pour le type de fichier.", + + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_TITLE": "Ouvrez les fichiers graphiques dans des éditeurs externes.", + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_MSG": "Votre dossier actuel comporte des types de fichier graphique non pris en charge par {APP_NAME}.
Vous pouvez à présent associer des types de fichiers spécifiques avec des éditeurs externes. Une fois l’association établie, vous pouvez ouvrir des fichiers graphiques tels que .xd, .psd, .jpg, .png, .ai et .svg, dans leur application par défaut en double-cliquant sur ces fichiers dans l’arborescence de fichiers.

Cliquez sur le bouton OK pour associer les types de fichier graphique avec leur application par défaut respective.", + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_CNF_MSG": "Les types de fichiers suivants ont été associés à des applications par défaut.
{0} Vous pouvez modifier votre préférence concernant l’éventuel(le) suppression/ajout d’associations de type de fichier dans brackets.json via le menu Déboguer->Ouvrir le fichier des préférences." + + }); diff --git a/src/nls/ja/strings.js b/src/nls/ja/strings.js index 5a2a88733..7a128abfb 100644 --- a/src/nls/ja/strings.js +++ b/src/nls/ja/strings.js @@ -897,5 +897,20 @@ define({ "REFERENCES_NO_RESULTS": "現在のカーソル位置で利用可能な参照はありません", "CMD_FIND_DOCUMENT_SYMBOLS": "ドキュメント記号を検索", - "CMD_FIND_PROJECT_SYMBOLS": "プロジェクト記号を検索" + "CMD_FIND_PROJECT_SYMBOLS": "プロジェクト記号を検索", + + // Remote debugging enabled + "REMOTE_DEBUGGING_ENABLED": "次のローカルホストでリモートデバッグが有効になりました。localhost:", + + // Remote debugging port argument is invalid + "REMOTE_DEBUGGING_PORT_INVALID": "ポート {0} でリモートデバッグを有効にできません。ポート番号は、{1} から {2} の間で指定してください。", + + //Associate File Type to External App + "DESCRIPTION_EXTERNAL_APPLICATION_ASSOCIATE": "ファイル拡張子の外部アプリケーションへのマッピング。構文: \"\": \"\"。「default」を指定すると、そのファイルタイプに対してシステムでデフォルトに設定されているアプリケーションを使用してファイルが開きます。", + + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_TITLE": "外部エディターでグラフィックファイルを開きます。", + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_MSG": "現在のフォルダーには、{APP_NAME}でサポートされていないタイプのグラフィックファイルがあります。
ここで、特定のファイルタイプを外部エディターに関連付けることができます。関連付けが完了すると、xd、.psd、.jpg、.png、.ai、.svgなどのグラフィックファイルをファイルツリーでダブルクリックすることで、デフォルトのアプリケーションで開くことができます。

「OK」ボタンをクリックして、グラフィックファイルタイプをそれぞれのデフォルトアプリケーションに関連付けてください。", + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_CNF_MSG": "次のファイルタイプが、デフォルトのアプリケーションに関連付けられました。
{0} この設定は、brackets.json でファイルタイプの関連付けを削除し、新しい関連付けを追加するか、デバッグ/環境設定ファイルを開くメニューにアクセスして変更できます。" + + }); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index dc74e2f35..bcd120c68 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -897,5 +897,20 @@ define({ "REFERENCES_NO_RESULTS" : "No References available for current cursor position", "CMD_FIND_DOCUMENT_SYMBOLS" : "Find Document Symbols", - "CMD_FIND_PROJECT_SYMBOLS" : "Find Project Symbols" + "CMD_FIND_PROJECT_SYMBOLS" : "Find Project Symbols", + + // Remote debugging enabled + "REMOTE_DEBUGGING_ENABLED" : "Remote debugging enabled on localhost:", + + // Remote debugging port argument is invalid + "REMOTE_DEBUGGING_PORT_INVALID" : "Cannot enable remote debugging on port {0}. Port numbers should be between {1} and {2}.", + + //Associate File Type to External App + "DESCRIPTION_EXTERNAL_APPLICATION_ASSOCIATE" : "Mappings for file extension to external applications. Syntax: \"\": \"\", Use \"default\" to open files in system default application for the file type.", + + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_TITLE" : "Open Graphic Files in External Editors.", + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_MSG" : "Your current folder has graphic file types which are not supported by {APP_NAME}.
You can now associate specific file types with external editors. Once associated, you can open graphic files like .xd, .psd, .jpg, .png, .ai, .svg in their default applications by double clicking on the files in File Tree.

Please click on ‘Ok’ button to associate the graphic file types with their respective default applications.", + "ASSOCIATE_GRAPHICS_FILE_TO_DEFAULT_APP_CNF_MSG" : "Following file types have been successfully associated with default applications.
{0} You have the option to change your preference on whether you delete/add new file type associations in brackets.json by going to “Debug->Open Preferences File” menu." + + }); diff --git a/src/project/FileTreeView.js b/src/project/FileTreeView.js index 4efeef4a6..d7ec88b98 100644 --- a/src/project/FileTreeView.js +++ b/src/project/FileTreeView.js @@ -39,7 +39,8 @@ define(function (require, exports, module) { LanguageManager = require("language/LanguageManager"), FileTreeViewModel = require("project/FileTreeViewModel"), ViewUtils = require("utils/ViewUtils"), - KeyEvent = require("utils/KeyEvent"); + KeyEvent = require("utils/KeyEvent"), + PreferencesManager = require("preferences/PreferencesManager"); var DOM = Preact.DOM; @@ -554,7 +555,16 @@ define(function (require, exports, module) { }); } } else { - this.props.actions.setSelected(this.myPath()); + var language = LanguageManager.getLanguageForPath(this.myPath()), + doNotOpen = false; + if (language && language.isBinary() && "image" !== language.getId() && + FileUtils.shouldOpenInExternalApplication( + FileUtils.getFileExtension(this.myPath()).toLowerCase() + ) + ) { + doNotOpen = true; + } + this.props.actions.setSelected(this.myPath(), doNotOpen); } e.stopPropagation(); e.preventDefault(); @@ -569,6 +579,12 @@ define(function (require, exports, module) { if (this.state.clickTimer !== null) { this.clearTimer(); } + if (FileUtils.shouldOpenInExternalApplication( + FileUtils.getFileExtension(this.myPath()).toLowerCase() + )) { + this.props.actions.openWithExternalApplication(this.myPath()); + return; + } this.props.actions.selectInWorkingSet(this.myPath()); } }, diff --git a/src/project/FileViewController.js b/src/project/FileViewController.js index e8079a0eb..ac616e943 100644 --- a/src/project/FileViewController.js +++ b/src/project/FileViewController.js @@ -226,6 +226,13 @@ define(function (require, exports, module) { return result.promise(); } + /** + * Opens the specified document with its associated external editor, + */ + function openWithExternalApplication(fullPath) { + exports.trigger("openWithExternalApplication", fullPath); + } + /** * Opens the specified document if it's not already open, adds it to the working set, * and selects it in the WorkingSetView @@ -275,4 +282,5 @@ define(function (require, exports, module) { exports.setFileViewFocus = setFileViewFocus; exports.WORKING_SET_VIEW = WORKING_SET_VIEW; exports.PROJECT_MANAGER = PROJECT_MANAGER; + exports.openWithExternalApplication = openWithExternalApplication; }); diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index db0cc7375..f6a799adf 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -280,6 +280,14 @@ define(function (require, exports, module) { this.model.selectInWorkingSet(path); }; + /** + * See `FileViewController.openWithExternalApplication` + */ + ActionCreator.prototype.openWithExternalApplication = function (path) { + FileViewController.openWithExternalApplication(path); + }; + + /** * See `ProjectModel.setContext` */ diff --git a/src/search/FindInFilesUI.js b/src/search/FindInFilesUI.js index 4633fe326..7d1cc0462 100644 --- a/src/search/FindInFilesUI.js +++ b/src/search/FindInFilesUI.js @@ -450,6 +450,15 @@ define(function (require, exports, module) { } } + /** + * @public + * Closes the search results panel + */ + function closeResultsPanel() { + _resultsView.close(); + _closeFindBar(); + } + // Initialize items dependent on HTML DOM AppInit.htmlReady(function () { var model = FindInFiles.searchModel; @@ -495,6 +504,7 @@ define(function (require, exports, module) { // Public exports exports.searchAndShowResults = searchAndShowResults; exports.searchAndReplaceResults = searchAndReplaceResults; + exports.closeResultsPanel = closeResultsPanel; // For unit testing exports._showFindBar = _showFindBar; diff --git a/src/styles/brackets_shared.less b/src/styles/brackets_shared.less index 7c7dd2cab..94665a160 100644 --- a/src/styles/brackets_shared.less +++ b/src/styles/brackets_shared.less @@ -57,3 +57,5 @@ // Styling for scrollbars @import url("brackets_scrollbars.less"); + +@import url("infobar-styles.less"); diff --git a/src/styles/images/infobar-alert.svg b/src/styles/images/infobar-alert.svg new file mode 100644 index 000000000..2390583d9 --- /dev/null +++ b/src/styles/images/infobar-alert.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/styles/images/infobar-checkmarkcircle.svg b/src/styles/images/infobar-checkmarkcircle.svg new file mode 100644 index 000000000..fc0706d5d --- /dev/null +++ b/src/styles/images/infobar-checkmarkcircle.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/styles/images/infobar-info.svg b/src/styles/images/infobar-info.svg new file mode 100644 index 000000000..5b23b6491 --- /dev/null +++ b/src/styles/images/infobar-info.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/styles/infobar-styles.less b/src/styles/infobar-styles.less new file mode 100644 index 000000000..246cda67b --- /dev/null +++ b/src/styles/infobar-styles.less @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 - present 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. + * + */ + +/*info Bar*/ +#info-bar-template { + display: block; + background-color: #105F9C; + box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.53); + height: 38px; + width: 100%; + position: absolute; + z-index: 15; + left: 0px; + bottom: 25px; + outline: none; + overflow: hidden; +} + +#info-bar-template #icon-container { + width: auto; + height: auto; + padding: 11px; + float: left; +} +#info-bar-template #icon-container #info-icon { + background: url("images/infobar-info.svg") no-repeat 0 0; + width: 16px; + height: 16px; + display: block; +} + +#info-bar-template #content-container { + padding: 10px 7px; + float: left; + max-width: 78%; +} + +#info-bar-template #content-container #info-content { + margin: 0px !important; /*Check if this important is necessary*/ + line-height: 18px; + font-size: 14px; + font-family: 'SourceSansPro'; + color: #FFFFFF; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +#info-bar-template #content-container #info-content #heading{ + font-weight: bold; +} +/*For focussed link of brackets.io*/ +#info-bar-template #content-container #info-content #description a:focus{ + box-shadow: none; +} + +#info-bar-template #content-container #info-content #description a{ + text-decoration: underline; + color: #FFFFFF; +} + +#info-bar-template #close-icon-container { + height: auto; + padding: 9px; + position: fixed; + float: right; + text-align: center; + width: auto; + min-width: 66px; + right: 30px; + background-color: #105F9C; +} + +#info-bar-template #close-icon-container #close-icon { + display: block; + color: white; + font-size: 18px; + line-height: 18px; + text-decoration: none; + width: 18px; + height: 18px; + background-color: transparent; + border: none; + padding: 0px; /*This is needed to center the icon*/ + float: right; +} + +#info-bar-template #close-icon-container #close-icon:hover { + background-color: rgba(255, 255, 255 ,0.16); + border-radius: 50%; +} + +#info-bar-template #close-icon-container #close-icon:focus { + background-color: rgba(255, 255, 255 ,0.16); + border-radius: 50%; + border: 1px solid #C3E3FF; + outline: 0; +} + +#info-bar-template #close-icon-container #close-icon:focus:active { + background-color: rgba(255, 255, 255 ,0.32); + border: none; +} + +/*Warning Message in info Bar*/ +#info-bar-template.warning, #info-bar-template.warning #close-icon-container { + background-color: #DA7A12; +} + +.dark #info-bar-template.warning, .dark #info-bar-template.warning #close-icon-container { + background-color: #E6851A; +} + +#info-bar-template.warning #icon-container #info-icon, +#info-bar-template.error #icon-container #info-icon { + background: url("images/infobar-alert.svg") no-repeat 0 0; +} + +/*Error message in info Bar*/ +#info-bar-template.error, #info-bar-template.error #close-icon-container { + background-color: #D7373F; +} + +.dark #info-bar-template.error, .dark #info-bar-template.error #close-icon-container{ + background-color: #E4484F; +} +/*Success message in info Bar*/ +#info-bar-template.success, #info-bar-template.success #close-icon-container { + background-color: #278E6B; +} + +.dark #info-bar-template.success, .dark #info-bar-template.success #close-icon-container { + background-color: #2E9D77; +} + +#info-bar-template.success #icon-container #info-icon{ + background: url("images/infobar-checkmarkcircle.svg") no-repeat 0 0; +} diff --git a/src/utils/UpdateNotification.js b/src/utils/UpdateNotification.js index ed0ec018a..262dc8e6f 100644 --- a/src/utils/UpdateNotification.js +++ b/src/utils/UpdateNotification.js @@ -115,33 +115,7 @@ define(function (require, exports, module) { locale = locale.substring(0, 2); } - //AUTOUPDATE_PRERELEASE_BEGIN - // The following code is needed for supporting Auto Update in prerelease, - //and will be removed eventually for stable releases - { - if (locale) { - if(locale.length > 2) { - locale = locale.substring(0, 2); - } - switch(locale) { - case "de": - break; - case "es": - break; - case "fr": - break; - case "ja": - break; - case "en": - default: - locale = "en"; - } - return brackets.config.update_info_url.replace("", locale); - } - } - //AUTOUPDATE_PRERELEASE_END - - return brackets.config.update_info_url + '?locale=' + locale; + return brackets.config.update_info_url.replace('', locale || 'en'); } /** diff --git a/src/widgets/infobar.js b/src/widgets/infobar.js new file mode 100644 index 000000000..4a266d633 --- /dev/null +++ b/src/widgets/infobar.js @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018 - present 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. + * + */ + +define(function (require, exports, module) { + "use strict"; + + var MainViewManager = require("view/MainViewManager"), + Mustache = require("thirdparty/mustache/mustache"), + EventDispatcher = require("utils/EventDispatcher"), + InfoBarHtml = require("text!htmlContent/infobar-template.html"), + _ = require("thirdparty/lodash"); + + EventDispatcher.makeEventDispatcher(exports); + + // Key handlers for buttons in UI + var ESC_KEY = 27; // keycode for escape key + + /** + * Generates the json to be used by Mustache for rendering + * @param {object} msgObj - json object containing message information to be displayed + * @returns {object} - the generated json object + */ + function generateJsonForMustache(msgObj) { + var msgJsonObj = {}; + if (msgObj.type) { + msgJsonObj.type = "'" + msgObj.type + "'"; + } + msgJsonObj.title = msgObj.title; + msgJsonObj.description = msgObj.description; + return msgJsonObj; + } + /** + * Removes and cleans up the info bar from DOM + */ + function cleanInfoBar() { + var $infoBar = $('#info-bar-template'); + if ($infoBar.length > 0) { + $infoBar.remove(); + } + $(window.document).off("keydown.InfoBarTemplateDoc"); + $(window).off('resize.InfoBarTemplate'); + } + + /** + * Displays the Info Bar UI + * @param {object} msgObj - json object containing message info to be displayed + * + */ + function showInfoBar(msgObj) { + var jsonToMustache = generateJsonForMustache(msgObj), + $infoBarElement = $(Mustache.render(InfoBarHtml, jsonToMustache)); + + cleanInfoBar(); //Remove an already existing info bar, if any + $infoBarElement.prependTo(".content"); + + var $infoBar = $('#info-bar-template'), + $infoContent = $infoBar.find('#info-content'), + $contentContainer = $infoBar.find('#content-container'), + $iconContainer = $infoBar.find('#icon-container'), + $closeIconContainer = $infoBar.find('#close-icon-container'), + $heading = $infoBar.find('#heading'), + $description = $infoBar.find('#description'), + $closeIcon = $infoBar.find('#close-icon'); + + if ($infoContent.length > 0) { + if ($infoContent[0].scrollWidth > $infoContent.innerWidth()) { + //Text has over-flown, show the info content as tooltip message + if ($contentContainer.length > 0 && + $heading.length > 0 && + $description.length > 0) { + $contentContainer.attr("title", $heading.text() + $description.text()); + } + } + } + // Content Container Width between Icon Container and Button Container or Close Icon Container + // will be assigned when window will be rezied. + var resizeContentContainer = function () { + if($infoContent.length > 0 && $contentContainer.length > 0 && $infoBar.length > 0) { + var newWidth = $infoBar.outerWidth() - 38; + if($iconContainer.length > 0) { + newWidth = newWidth - $iconContainer.outerWidth(); + } + if($closeIconContainer.length > 0) { + newWidth = newWidth - $closeIconContainer.outerWidth(); + } + + $contentContainer.css({ + "maxWidth": newWidth + }); + } + }; + + resizeContentContainer(); + $(window).on('resize.InfoBarTemplate', _.debounce(resizeContentContainer, 150)); + + //Event handlers on the Info Bar + // Click and key handlers on Close button + if ($closeIcon.length > 0) { + $closeIcon.click(function () { + cleanInfoBar(); + MainViewManager.focusActivePane(); + }); + } + $(window.document).on("keydown.InfoBarTemplateDoc", function (event) { + var code = event.which; + if (code === ESC_KEY) { + // Keyboard input of Esc key on Info Bar dismisses and removes the bar + cleanInfoBar(); + MainViewManager.focusActivePane(); + event.stopImmediatePropagation(); + } + }); + } + exports.showInfoBar = showInfoBar; +});