Modules (181)

ExtensionManagerViewModel

Description

Dependencies

Variables

Private

_searchFields

Type
Array
    var _searchFields = [["metadata", "name"], ["metadata", "title"], ["metadata", "description"],
                         ["metadata", "author", "name"], ["metadata", "keywords"], ["owner"]];

Functions

Public API

DefaultViewModel

Model for displaying default extensions that come bundled with Brackets

    function DefaultViewModel() {
        ExtensionManagerViewModel.call(this);
    }

    // Inheritance setup
    DefaultViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
    DefaultViewModel.prototype.constructor = DefaultViewModel;

Classes

Constructor

ExtensionManagerViewModel

The base model for the ExtensionManagerView. Keeps track of the extensions that are currently visible and manages sorting/filtering them. Must be disposed with dispose() when done. Events:

  • change - triggered when the data for a given extension changes. Second parameter is the extension id.
  • filter - triggered whenever the filtered set changes (including on initialize).
    function ExtensionManagerViewModel() {
        this._handleStatusChange = this._handleStatusChange.bind(this);

        // Listen for extension status changes.
        ExtensionManager
            .on("statusChange." + this.source, this._handleStatusChange)
            .on("registryUpdate." + this.source, this._handleStatusChange);
    }
    EventDispatcher.makeEventDispatcher(ExtensionManagerViewModel.prototype);

Properties

SOURCE_DEFAULT

Type
string
    ExtensionManagerViewModel.prototype.SOURCE_DEFAULT = "default";

SOURCE_INSTALLED

Type
string
    ExtensionManagerViewModel.prototype.SOURCE_INSTALLED = "installed";

SOURCE_REGISTRY

Type
string
    ExtensionManagerViewModel.prototype.SOURCE_REGISTRY = "registry";

SOURCE_THEMES

Type
string
    ExtensionManagerViewModel.prototype.SOURCE_THEMES = "themes";
Private

_initializeFromSourcePromise

    ExtensionManagerViewModel.prototype._initializeFromSourcePromise = null;
Private

_lastQuery

Type
string
    ExtensionManagerViewModel.prototype._lastQuery = null;

extensions

Type
Object
    ExtensionManagerViewModel.prototype.extensions = null;

filterSet

Type
Array.<Object>
    ExtensionManagerViewModel.prototype.filterSet = null;

infoMessage

Type
string
    ExtensionManagerViewModel.prototype.infoMessage = null;

message

Type
string
    ExtensionManagerViewModel.prototype.message = null;

notifyCount

Type
number
    ExtensionManagerViewModel.prototype.notifyCount = 0;

sortedFullSet

Type
Object
    ExtensionManagerViewModel.prototype.sortedFullSet = null;

source

Type
string
    ExtensionManagerViewModel.prototype.source = null;

Methods

Private

_entryMatchesQuery

entry Object
The extension entry to test.
query string
The query to match against.
Returns: boolean
Whether the query matches.
    ExtensionManagerViewModel.prototype._entryMatchesQuery = function (entry, query) {
        return query === "" ||
            _searchFields.some(function (fieldSpec) {
                var i, cur = entry;
                for (i = 0; i < fieldSpec.length; i++) {
                    // Recurse downward through the specified fields to the leaf value.
                    cur = cur[fieldSpec[i]];
                    if (!cur) {
                        return false;
                    }
                }
                // If the leaf value is an array (like keywords), search each item, otherwise
                // just search in the string.
                if (Array.isArray(cur)) {
                    return cur.some(function (keyword) {
                        return keyword.toLowerCase().indexOf(query) !== -1;
                    });
                } else if (fieldSpec[fieldSpec.length - 1] === "owner") {
                    // Special handling: ignore the authentication source when querying,
                    // since it's not useful to search on
                    var components = cur.split(":");
                    if (components[1].toLowerCase().indexOf(query) !== -1) {
                        return true;
                    }
                } else if (cur.toLowerCase().indexOf(query) !== -1) {
                    return true;
                }
            });
    };

    ExtensionManagerViewModel.prototype._setSortedExtensionList = function (extensions, isTheme) {
        this.filterSet = this.sortedFullSet = registry_utils.sortRegistry(extensions, "registryInfo", PreferencesManager.get("extensions.sort"))
            .filter(function (entry) {
                if (!isTheme) {
                    return entry.registryInfo && !entry.registryInfo.metadata.theme;
                } else {
                    return entry.registryInfo && entry.registryInfo.metadata.theme;
                }
            })
            .map(function (entry) {
                return entry.registryInfo.metadata.name;
            });
    };
Private

_getEntry

id string
of the extension
Returns: Object?
extension metadata or null if there's no matching extension
    ExtensionManagerViewModel.prototype._getEntry = function (id) {
        return null;
    };
Private

_handleStatusChange

e $.Event
The jQuery event object.
id string
The id of the extension whose status changed.
    ExtensionManagerViewModel.prototype._handleStatusChange = function (e, id) {
        this.trigger("change", id);
    };
Private

_setInitialFilter

    ExtensionManagerViewModel.prototype._setInitialFilter = function () {
        // Initial filtered list is the same as the sorted list.
        this.filterSet = _.clone(this.sortedFullSet);
        this.trigger("filter");
    };
Private

_sortFullSet

    ExtensionManagerViewModel.prototype._sortFullSet = function () { };
Private

_updateMessage

    ExtensionManagerViewModel.prototype._updateMessage = function () {
        if (this._initializeFromSourcePromise && this._initializeFromSourcePromise.state() === "rejected") {
            this.message = Strings.EXTENSION_MANAGER_ERROR_LOAD;
        } else if (this.filterSet && this.filterSet.length === 0) {
            this.message = this.sortedFullSet && this.sortedFullSet.length ? Strings.NO_EXTENSION_MATCHES : Strings.NO_EXTENSIONS;
        } else {
            this.message = null;
        }
    };

dispose

Unregisters listeners when we're done.

    ExtensionManagerViewModel.prototype.dispose = function () {
        ExtensionManager.off("." + this.source);
    };
Private

filter

query string
The string to search for.
force boolean
If true, always filter starting with the full set, not the last query's filter.
    ExtensionManagerViewModel.prototype.filter = function (query, force) {
        var self = this, initialList;
        if (!force && this._lastQuery && query.indexOf(this._lastQuery) === 0) {
            // This is the old query with some new letters added, so we know we can just
            // search in the current filter set. (This is true even if query has spaces).
            initialList = this.filterSet;
        } else {
            // This is a new query, so start with the full list.
            initialList = this.sortedFullSet;
        }

        var keywords = query.toLowerCase().split(/\s+/);

        // Takes 'extensionList' and returns a version filtered to only those that match 'keyword'
        function filterForKeyword(extensionList, word) {
            var filteredList = [];
            extensionList.forEach(function (id) {
                var entry = self._getEntry(id);
                if (entry && self._entryMatchesQuery(entry, word)) {
                    filteredList.push(id);
                }
            });
            return filteredList;
        }

        // "AND" the keywords together: successively filter down the result set by each keyword in turn
        var i, currentList = initialList;
        for (i = 0; i < keywords.length; i++) {
            currentList = filterForKeyword(currentList, keywords[i]);
        }

        this._lastQuery = query;
        this.filterSet = currentList;

        this._updateMessage();

        this.trigger("filter");
    };

initialize

Initializes the model from the source.

    ExtensionManagerViewModel.prototype.initialize = function () {
        var self = this;

        this._initializeFromSourcePromise = this._initializeFromSource().always(function () {
            self._updateMessage();
        });

        return this._initializeFromSourcePromise;
    };
Constructor

RegistryViewModel

The model for the ExtensionManagerView that is responsible for handling registry-based extensions. This extends ExtensionManagerViewModel. Must be disposed with dispose() when done.

Events:

  • change - triggered when the data for a given extension changes. Second parameter is the extension id.
  • filter - triggered whenever the filtered set changes (including on initialize).
    function RegistryViewModel() {
        ExtensionManagerViewModel.call(this);
        this.infoMessage = Strings.REGISTRY_SANITY_CHECK_WARNING;
    }

    RegistryViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
    RegistryViewModel.prototype.constructor = RegistryViewModel;

Properties

source

Type
string
    RegistryViewModel.prototype.source = ExtensionManagerViewModel.prototype.SOURCE_REGISTRY;

Methods

Private

_getEntry

id string
of the extension
Returns: Object?
extension metadata or null if there's no matching extension
    RegistryViewModel.prototype._getEntry = function (id) {
        var entry = this.extensions[id];
        if (entry) {
            return entry.registryInfo;
        }
        return entry;
    };
Private

_initializeFromSource

Initializes the model from the remote extension registry.

Returns: $.Promise
a promise that's resolved with the registry JSON data or rejected if the server can't be reached.
    RegistryViewModel.prototype._initializeFromSource = function () {
        var self = this;
        return ExtensionManager.downloadRegistry()
            .done(function () {
                self.extensions = ExtensionManager.extensions;

                // Sort the registry by last published date and store the sorted list of IDs.
                self._setSortedExtensionList(ExtensionManager.extensions, false);
                self._setInitialFilter();
            })
            .fail(function () {
                self.extensions = [];
                self.sortedFullSet = [];
                self.filterSet = [];
            });
    };
Constructor

InstalledViewModel

The model for the ExtensionManagerView that is responsible for handling previously-installed extensions. This extends ExtensionManagerViewModel. Must be disposed with dispose() when done.

Events:

  • change - triggered when the data for a given extension changes. Second parameter is the extension id.
  • filter - triggered whenever the filtered set changes (including on initialize).
    function InstalledViewModel() {
        ExtensionManagerViewModel.call(this);

        // when registry is downloaded, sort extensions again - those with updates will be before others
        var self = this;
        ExtensionManager.on("registryDownload." + this.source, function () {
            self._sortFullSet();
            self._setInitialFilter();
        });
    }

    InstalledViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
    InstalledViewModel.prototype.constructor = InstalledViewModel;

Properties

source

Type
string
    InstalledViewModel.prototype.source = ExtensionManagerViewModel.prototype.SOURCE_INSTALLED;

Methods

Private

_countUpdates

    InstalledViewModel.prototype._countUpdates = function () {
        var self = this;
        this.notifyCount = 0;
        this.sortedFullSet.forEach(function (key) {
            if (self.extensions[key].installInfo.updateCompatible && !ExtensionManager.isMarkedForUpdate(key)) {
                self.notifyCount++;
            }
        });
    };
Private

_getEntry

id string
of the extension
Returns: Object?
extension metadata or null if there's no matching extension
    InstalledViewModel.prototype._getEntry = function (id) {
        var entry = this.extensions[id];
        if (entry) {
            return entry.installInfo;
        }
        return entry;
    };
Private

_handleStatusChange

e $.Event
The jQuery event object.
id string
The id of the extension whose status changed.
    InstalledViewModel.prototype._handleStatusChange = function (e, id) {
        var index = this.sortedFullSet.indexOf(id),
            refilter = false;
        if (index !== -1 && !this.extensions[id].installInfo) {
            // This was in our set, but was uninstalled. Remove it.
            this.sortedFullSet.splice(index, 1);
            this._countUpdates();  // may also affect update count
            refilter = true;
        } else if (index === -1 && this.extensions[id].installInfo) {
            // This was not in our set, but is now installed. Add it and resort.
            this.sortedFullSet.push(id);
            this._sortFullSet();
            refilter = true;
        }
        if (refilter) {
            this.filter(this._lastQuery || "", true);
        }

        if (this.extensions[id].installInfo) {
            // If our count of available updates may have been affected, re-count
            this._countUpdates();
        }

        ExtensionManagerViewModel.prototype._handleStatusChange.call(this, e, id);
    };
Private

_initializeFromSource

Initializes the model from the set of locally installed extensions, sorted alphabetically by id (or name of the extension folder for legacy extensions).

Returns: $.Promise
a promise that's resolved when we're done initializing.
    InstalledViewModel.prototype._initializeFromSource = function () {
        var self = this;
        this.extensions = ExtensionManager.extensions;
        this.sortedFullSet = Object.keys(this.extensions)
            .filter(function (key) {
                return self.extensions[key].installInfo &&
                    self.extensions[key].installInfo.locationType !== ExtensionManager.LOCATION_DEFAULT;
            });
        this._sortFullSet();
        this._setInitialFilter();
        this._countUpdates();

        return new $.Deferred().resolve().promise();
    };
Private

_sortFullSet

    InstalledViewModel.prototype._sortFullSet = function () {
        var self = this;

        this.sortedFullSet = this.sortedFullSet.sort(function (key1, key2) {
            // before sorting by name, put first extensions that have updates
            var ua1 = self.extensions[key1].installInfo.updateAvailable,
                ua2 = self.extensions[key2].installInfo.updateAvailable;

            if (ua1 && !ua2) {
                return -1;
            } else if (!ua1 && ua2) {
                return 1;
            }

            var metadata1 = self.extensions[key1].installInfo.metadata,
                metadata2 = self.extensions[key2].installInfo.metadata,
                id1 = (metadata1.title || metadata1.name).toLocaleLowerCase(),
                id2 = (metadata2.title || metadata2.name).toLocaleLowerCase();

            return id1.localeCompare(id2);
        });
    };
Constructor

ThemesViewModel

The model for the ExtensionManagerView that is responsible for handling registry-based theme extensions. This extends ExtensionManagerViewModel. Must be disposed with dispose() when done.

Events:

  • change - triggered when the data for a given extension changes. Second parameter is the extension id.
  • filter - triggered whenever the filtered set changes (including on initialize).
    function ThemesViewModel() {
        ExtensionManagerViewModel.call(this);
    }

    // Inheritance setup
    ThemesViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
    ThemesViewModel.prototype.constructor = ThemesViewModel;

Properties

source

Type
string
    ThemesViewModel.prototype.source = ExtensionManagerViewModel.prototype.SOURCE_THEMES;

Methods

Private

_getEntry

id string
of the theme extension
Returns: Object?
extension metadata or null if there's no matching extension
    ThemesViewModel.prototype._getEntry = function (id) {
        var entry = this.extensions[id];
        if (entry) {
            return entry.registryInfo;
        }
        return entry;
    };

    exports.RegistryViewModel = RegistryViewModel;
    exports.ThemesViewModel = ThemesViewModel;
    exports.InstalledViewModel = InstalledViewModel;
    exports.DefaultViewModel = DefaultViewModel;
});
Private

_initializeFromSource

Initializes the model from the remote extension registry.

Returns: $.Promise
a promise that's resolved with the registry JSON data.
    ThemesViewModel.prototype._initializeFromSource = function () {
        var self = this;
        return ExtensionManager.downloadRegistry()
            .done(function () {
                self.extensions = ExtensionManager.extensions;

                // Sort the registry by last published date and store the sorted list of IDs.
                self._setSortedExtensionList(ExtensionManager.extensions, true);
                self._setInitialFilter();
            })
            .fail(function () {
                self.extensions = [];
                self.sortedFullSet = [];
                self.filterSet = [];
            });
    };