diff --git a/src/extensibility/ExtensionManager.js b/src/extensibility/ExtensionManager.js index ebc8b38613e..00f67ca5b68 100644 --- a/src/extensibility/ExtensionManager.js +++ b/src/extensibility/ExtensionManager.js @@ -22,7 +22,7 @@ */ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, window, $, brackets */ +/*global define, window, $, brackets, semver */ /*unittests: ExtensionManager*/ /** @@ -42,6 +42,9 @@ define(function (require, exports, module) { NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem, ExtensionLoader = require("utils/ExtensionLoader"); + // semver isn't a proper AMD module, so it will just load into the global namespace. + require("extensibility/node/node_modules/semver/semver"); + /** * Extension status constants. */ @@ -85,7 +88,11 @@ define(function (require, exports, module) { */ function getRegistry(forceDownload) { if (!_registry || forceDownload) { - return $.getJSON(brackets.config.extension_registry, {cache: false}) + return $.ajax({ + url: brackets.config.extension_registry, + dataType: "json", + cache: false + }) .done(function (data) { _registry = data; }); @@ -155,12 +162,45 @@ define(function (require, exports, module) { return (_extensions[id] && _extensions[id].status) || NOT_INSTALLED; } + /** + * Returns information about whether the given entry is compatible with the given Brackets API version. + * @param {Object} entry The registry entry to check. + * @param {string} apiVersion The Brackets API version to check against. + * @return {{isCompatible: boolean, requiresNewer}} Result contains an + * "isCompatible" member saying whether it's compatible. If not compatible, then + * "requiresNewer" says whether it requires an older or newer version of Brackets. + */ + function getCompatibilityInfo(entry, apiVersion) { + var requiredVersion = entry.metadata.engines && entry.metadata.engines.brackets, + result = {}; + result.isCompatible = !requiredVersion || semver.satisfies(apiVersion, requiredVersion); + if (!result.isCompatible) { + if (requiredVersion.charAt(0) === '<') { + result.requiresNewer = false; + } else if (requiredVersion.charAt(0) === '>') { + result.requiresNewer = true; + } else if (requiredVersion.charAt(0) === "~") { + var compareVersion = requiredVersion.slice(1); + // Need to add .0s to this style of range in order to compare (since valid version + // numbers must have major/minor/patch). + if (compareVersion.match(/^[0-9]+$/)) { + compareVersion += ".0.0"; + } else if (compareVersion.match(/^[0-9]+\.[0-9]+$/)) { + compareVersion += ".0"; + } + result.requiresNewer = semver.lt(apiVersion, compareVersion); + } + } + return result; + } + // Listen to extension load events $(ExtensionLoader).on("load", _handleExtensionLoad); // Public exports exports.getRegistry = getRegistry; exports.getStatus = getStatus; + exports.getCompatibilityInfo = getCompatibilityInfo; exports.NOT_INSTALLED = NOT_INSTALLED; exports.ENABLED = ENABLED; diff --git a/src/extensibility/ExtensionManagerView.js b/src/extensibility/ExtensionManagerView.js index c966b01fa51..6365c5d8d49 100644 --- a/src/extensibility/ExtensionManagerView.js +++ b/src/extensibility/ExtensionManagerView.js @@ -22,7 +22,7 @@ */ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, window, $, brackets, Mustache, semver */ +/*global define, window, $, brackets, Mustache */ /*unittests: ExtensionManager*/ define(function (require, exports, module) { @@ -36,12 +36,11 @@ define(function (require, exports, module) { registry_utils = require("extensibility/registry_utils"), itemTemplate = require("text!htmlContent/extension-manager-view-item.html"); - // semver isn't a proper AMD module, so it will just load into the global namespace. - require("extensibility/node/node_modules/semver/semver"); - /** * @constructor * Creates a view enabling the user to install and manage extensions. + * Events: + * "render": whenever the view fully renders itself. */ function ExtensionManagerView() { var self = this; @@ -81,7 +80,7 @@ define(function (require, exports, module) { // Show the busy spinner and access the registry. var $spinner = $("
") .appendTo(this.$el); - ExtensionManager.getRegistry().done(function (registry) { + ExtensionManager.getRegistry(true).done(function (registry) { // Display the registry view. self._render(registry_utils.sortRegistry(registry)); }).fail(function () { @@ -116,10 +115,17 @@ define(function (require, exports, module) { // Create a Mustache context object containing the entry data and our helper functions. var context = $.extend({}, entry), status = ExtensionManager.getStatus(entry.metadata.name); + + // Normally we would merge the strings into the context we're passing into the template, + // but since we're instantiating the template for every item, it seems wrong to take the hit + // of copying all the strings into the context, so we just make it a subfield. + context.Strings = Strings; + context.isInstalled = (status === ExtensionManager.ENABLED); - var requiredVersion = entry.metadata.engines && entry.metadata.engines.brackets; - context.isCompatible = !requiredVersion || semver.satisfies(brackets.metadata.apiVersion, requiredVersion); + var compatInfo = ExtensionManager.getCompatibilityInfo(entry, brackets.metadata.apiVersion); + context.isCompatible = compatInfo.isCompatible; + context.requiresNewer = compatInfo.requiresNewer; context.allowInstall = context.isCompatible && !context.isInstalled; @@ -146,6 +152,7 @@ define(function (require, exports, module) { $item.appendTo($table); }); $table.appendTo(this.$el); + $(this).triggerHandler("render"); }; /** diff --git a/src/htmlContent/extension-manager-view-item.html b/src/htmlContent/extension-manager-view-item.html index dbefa7342e6..a58b529409f 100644 --- a/src/htmlContent/extension-manager-view-item.html +++ b/src/htmlContent/extension-manager-view-item.html @@ -3,27 +3,30 @@ {{#metadata.title}}{{metadata.title}}{{/metadata.title}}{{^metadata.title}}{{metadata.name}}{{/metadata.title}} {{metadata.version}}