diff --git a/.eslintrc.json b/.eslintrc.json index 0a30bab11c1..f81a815ef66 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -50,7 +50,8 @@ "valid-typeof": 2, "no-trailing-spaces": 0, - "eol-last": 0 + "eol-last": 0, + "max-len": [1, 120] }, "globals": { "appshell": false, diff --git a/README.md b/README.md index 06b9dafacf0..084656a9509 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ Having problems starting Brackets the first time, or not sure how to use Bracket review [Troubleshooting](https://github.com/adobe/brackets/wiki/Troubleshooting), which helps you to fix common problems and find extra help if needed. +For a list of common Linux issues and workarounds you can [visit this guide](https://nathanjplummer.github.io/Brackets/). + Helping Brackets ---------------- diff --git a/src/command/Menus.js b/src/command/Menus.js index e581368bb90..00b131125b7 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -179,6 +179,19 @@ define(function (require, exports, module) { return contextMenuMap[id]; } + /** + * Removes the attached event listeners from the corresponding object. + * @param {ManuItem} menuItem + */ + function removeMenuItemEventListeners(menuItem) { + menuItem._command + .off("enabledStateChange", menuItem._enabledChanged) + .off("checkedStateChange", menuItem._checkedChanged) + .off("nameChange", menuItem._nameChanged) + .off("keyBindingAdded", menuItem._keyBindingAdded) + .off("keyBindingRemoved", menuItem._keyBindingRemoved); + } + /** * Check whether a ContextMenu exists for the given id. * @param {string} id @@ -445,13 +458,15 @@ define(function (require, exports, module) { console.error("removeMenuItem(): command not found: " + command); return; } - commandID = command; } else { commandID = command.getID(); } menuItemID = this._getMenuItemId(commandID); + var menuItem = getMenuItem(menuItemID); + removeMenuItemEventListeners(menuItem); + if (_isHTMLMenu(this.id)) { // Targeting parent to get the menu item and the
  • that contains it $(_getHTMLMenuItem(menuItemID)).parent().remove(); diff --git a/src/extensions/default/HTMLCodeHints/HtmlAttributes.json b/src/extensions/default/HTMLCodeHints/HtmlAttributes.json index d7d24e12b78..70361f1fcb1 100644 --- a/src/extensions/default/HTMLCodeHints/HtmlAttributes.json +++ b/src/extensions/default/HTMLCodeHints/HtmlAttributes.json @@ -8,29 +8,29 @@ "dropzone": { "attribOption": ["copy", "move", "link"], "global": "true" }, "hidden": { "attribOption": ["hidden"], "global": "true" }, "id": { "attribOption": [], "global": "true", "type": "cssId" }, - "lang": { "attribOption": ["ab", "aa", "af", "sq", "am", "ar", "an", "hy", "as", "ay", "az", "ba", "eu", "bn", "dz", "bh", "bi", "br", - "bg", "my", "be", "km", "ca", "zh", "co", "hr", "cs", "da", "nl", "en", "eo", "et", "fo", "fa", "fi", "fr", - "fy", "gl", "gd", "gv", "ka", "de", "el", "kl", "gn", "gu", "ht", "ha", "he", "hi", "hu", "is", "io", "id", - "ia", "ie", "iu", "ik", "ga", "it", "ja", "jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", - "lv", "li", "ln", "lt", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "oc", "or", - "om", "ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sz", "sm", "sg", "sa", "sr", "sh", "st", "tn", "sn", - "ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te", "th", "bo", - "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vi", "vo", "wa", "cy", "wo", "xh", "yi", "yo", - "zu"], + "lang": { "attribOption": ["ab", "aa", "af", "sq", "am", "ar", "an", "hy", "as", "ay", "az", "ba", "eu", "bn", "dz", "bh", "bi", "br", + "bg", "my", "be", "km", "ca", "zh", "co", "hr", "cs", "da", "nl", "en", "eo", "et", "fo", "fa", "fi", "fr", + "fy", "gl", "gd", "gv", "ka", "de", "el", "kl", "gn", "gu", "ht", "ha", "he", "hi", "hu", "is", "io", "id", + "ia", "ie", "iu", "ik", "ga", "it", "ja", "jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", + "lv", "li", "ln", "lt", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "oc", "or", + "om", "ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sz", "sm", "sg", "sa", "sr", "sh", "st", "tn", "sn", + "ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te", "th", "bo", + "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vi", "vo", "wa", "cy", "wo", "xh", "yi", "yo", + "zu"], "global": "true" }, - "role": { "attribOption": ["alert", "alertdialog", "article", "application", "banner", "button", "checkbox", "columnheader", "combobox", - "complementary", "contentinfo", "definition", "directory", "dialog", "document", "form", "grid", "gridcell", - "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", - "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "note", "option", "presentation", - "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", - "separator", "slider", "spinbutton", "status", "tab", "tablist", "tabpanel", "textbox", "timer", "toolbar", - "tooltip", "tree", "treegrid", "treeitem"], + "role": { "attribOption": ["alert", "alertdialog", "article", "application", "banner", "button", "checkbox", "columnheader", "combobox", + "complementary", "contentinfo", "definition", "directory", "dialog", "document", "form", "grid", "gridcell", + "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", + "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "note", "option", "presentation", + "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", + "separator", "slider", "spinbutton", "status", "tab", "tablist", "tabpanel", "textbox", "timer", "toolbar", + "tooltip", "tree", "treegrid", "treeitem"], "global": "true" }, "spellcheck": { "attribOption": [], "global": "true", "type": "boolean" }, "style": { "attribOption": [], "global": "true", "type": "style" }, "tabindex": { "attribOption": [], "global": "true" }, "title": { "attribOption": [], "global": "true" }, - + "onabort": { "attribOption": [], "global": "true" }, "onblur": { "attribOption": [], "global": "true" }, "oncanplay": { "attribOption": [], "global": "true" }, @@ -85,7 +85,7 @@ "ontimeupdate": { "attribOption": [], "global": "true" }, "onvolumechange": { "attribOption": [], "global": "true" }, "onwaiting": { "attribOption": [], "global": "true" }, - + "accept": { "attribOption": ["text/html", "text/plain", "application/msword", "application/msexcel", "application/postscript", "application/x-zip-compressed", "application/pdf", "application/rtf", "video/x-msvideo", "video/quicktime", "video/x-mpeg2", "audio/x-pn/realaudio", "audio/x-mpeg", "audio/x-waw", "audio/x-aiff", "audio/basic", @@ -97,18 +97,26 @@ "alt": { "attribOption": [] }, "archive": { "attribOption": [] }, "async": { "attribOption": [], "type": "flag" }, - "autocomplete": { "attribOption": ["off", "on"] }, + "autocomplete": { "attribOption": ["additional-name", "address-level1", "address-level2", "address-level3", "address-level4", "address-line1", + "address-line2", "address-line3", "bday", "bday-year", "bday-day", "bday-month", "billing", + "cc-additional-name", "cc-csc", "cc-exp", "cc-exp-month", "cc-exp-year", "cc-family-name", "cc-given-name", + "cc-name", "cc-number", "cc-type", "country", "country-name", "current-password", "email", "family-name", + "fax", "given-name", "home", "honorific-prefix", "honorific-suffix", "impp", "language", "mobile", "name", + "new-password", "nickname", "off", "on", "organization", "organization-title", "pager", "photo", "postal-code", "sex", + "shipping", "street-address", "tel-area-code", "tel", "tel-country-code", "tel-extension", + "tel-local", "tel-local-prefix", "tel-local-suffix", "tel-national", "transaction-amount", + "transaction-currency", "url", "username", "work"] }, "autofocus": { "attribOption": [], "type": "flag" }, "autoplay": { "attribOption": [], "type": "flag" }, "behavior": { "attribOption": ["scroll", "slide", "alternate"] }, "bgcolor": { "attribOption": [], "type": "color" }, "border": { "attribOption": [] }, "challenge": { "attribOption": [] }, - "charset": { "attribOption": ["iso-8859-1", "utf-8", "shift_jis", "euc-jp", "big5", "gb2312", "euc-kr", "din_66003-kr", "ns_4551-1-kr", - "sen_850200_b", "csISO2022jp", "hz-gb-2312", "ibm852", "ibm866", "irv", "iso-2022-kr", "iso-8859-2", - "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-9", "koi8-r", - "ks_c_5601", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", - "windows-1256", "windows-1257", "windows-1258", "windows-874", "x-euc", "asmo-708", "dos-720", "dos-862", + "charset": { "attribOption": ["iso-8859-1", "utf-8", "shift_jis", "euc-jp", "big5", "gb2312", "euc-kr", "din_66003-kr", "ns_4551-1-kr", + "sen_850200_b", "csISO2022jp", "hz-gb-2312", "ibm852", "ibm866", "irv", "iso-2022-kr", "iso-8859-2", + "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-9", "koi8-r", + "ks_c_5601", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", + "windows-1256", "windows-1257", "windows-1258", "windows-874", "x-euc", "asmo-708", "dos-720", "dos-862", "dos-874", "cp866", "cp1256"] }, "checked": { "attribOption": [], "type": "flag" }, "cite": { "attribOption": [] }, @@ -177,9 +185,9 @@ "preload": { "attribOption": ["auto", "metadata", "none"] }, "pubdate": { "attribOption": [] }, "radiogroup": { "attribOption": [] }, - "rel": { "attribOption": ["alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", + "rel": { "attribOption": ["alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "sidebar", "tag", "external"] }, - "link/rel": { "attribOption": ["alternate", "author", "help", "icon", "license", "next", "pingback", "prefetch", "prev", "search", + "link/rel": { "attribOption": ["alternate", "author", "help", "icon", "license", "next", "pingback", "prefetch", "prev", "search", "sidebar", "stylesheet", "tag"] }, "readonly": { "attribOption": [], "type": "flag" }, "required": { "attribOption": [], "type": "flag" }, @@ -211,7 +219,7 @@ "link/type": { "attribOption": ["text/css"] }, "menu/type": { "attribOption": ["context", "list", "toolbar"] }, "ol/type": { "attribOption": ["1", "a", "A", "i", "I"] }, - "script/type": { "attribOption": ["text/javascript", "text/ecmascript", "text/jscript", "text/livescript", "text/tcl", "text/x-javascript", "text/x-ecmascript", + "script/type": { "attribOption": ["text/javascript", "text/ecmascript", "text/jscript", "text/livescript", "text/tcl", "text/x-javascript", "text/x-ecmascript", "application/x-javascript", "application/x-ecmascript", "application/javascript", "application/ecmascript", "text/babel", "text/jsx"] }, "style/type": { "attribOption": ["text/css"] }, diff --git a/test/spec/Menu-test.js b/test/spec/Menu-test.js index a1337b289dd..1fcba13130f 100644 --- a/test/spec/Menu-test.js +++ b/test/spec/Menu-test.js @@ -734,6 +734,38 @@ define(function (require, exports, module) { expect(menu).toBeTruthy(); // Verify that we got this far... }); }); + + it("should add then remove new menu item ensuring event listeners have also been detached", function () { + runs(function () { + var menuItemId = "menu-test-removeMenuItem4"; + var commandId = "Menu-test.removeMenuItem.command4"; + CommandManager.register("Brackets Test Command Custom", commandId, function () {}); + var menu = Menus.addMenu("Custom", menuItemId); + + var command = CommandManager.get(commandId); + command.on("nameChange", function () {}); + expect(Object.keys(command._eventHandlers).length).toBe(1); + expect(command._eventHandlers.nameChange.length).toBe(1); + + var menuItem = menu.addMenuItem(commandId); + expect(Object.keys(command._eventHandlers).length).toBe(5); + expect(command._eventHandlers.nameChange.length).toBe(2); + expect(command._eventHandlers.enabledStateChange.length).toBe(1); + expect(command._eventHandlers.checkedStateChange.length).toBe(1); + expect(command._eventHandlers.keyBindingAdded.length).toBe(1); + expect(command._eventHandlers.keyBindingRemoved.length).toBe(1); + + // Check if attached events have been removed + menu.removeMenuItem(command); + expect(Object.keys(command._eventHandlers).length).toBe(1); + expect(command._eventHandlers.nameChange.length).toBe(1); + expect(command._eventHandlers.enabledStateChange).toBeUndefined(); + expect(command._eventHandlers.checkedStateChange).toBeUndefined(); + expect(command._eventHandlers.keyBindingAdded).toBeUndefined(); + expect(command._eventHandlers.keyBindingRemoved).toBeUndefined(); + + }); + }); });