API Docs for: 1.0.1

File: js\grape\etc\tag.js

define(['../class', '../collections/bag'], function (Class, Bag) {
    /**
     * A container for tagging. You can get items by tags.
     *
     * @class Grape.TagContainer
     * @constructor
     * @see Grape.Tag
     */
    var TagContainer = Class('TagContainer', {
        init: function () {
            this._tags = {};
        },
        /**
         * Gets items stored in the container by a tag.
         *
         * @example
         *      var container = new Grape.TagContainer();
         *      var obj = new Grape.Taggable();
         *      obj.setTagContainer(container);
         *      obj.addTag('my tag');
         *      container.get('my tag'); //returns an array containing obj
         *
         *
         * @method get
         * @param {String} tag The tag
         * @return {Array} Items containing the tag
         */
        get: function (tag) { //TODOv2 multiple tags
            var i, instances, result = this.createResultContainer();
            instances = this._tags[tag];
            if (instances) {
                for (i = instances.length - 1; i >= 0; i--) {
                    result.push(instances[i]);
                }
            }
            return result;
        },
        _getTag: function (tag) {
            return this._tags[tag] || this.createResultContainer();
        },
        /**
         * Creates an array-like object. If you want to redefine the result type of the get method, you can override
         * this method.
         *
         * @method createResultContainer
         * @return {Array} An initial array.
         * @see get
         */
        createResultContainer: function () {
            return [];
        },
        'final _add': function (taggable, tag) {
            var tags = this._tags,
                items = tags[tag];
            if (!items) {
                items = tags[tag] = new Bag();
            }
            return items.add(taggable) - 1;
        },
        'final _remove': function (taggable, tag) {
            var idx = taggable._tags[tag], bag = this._tags[tag], moved = bag.remove(idx);

            if (moved) {
                moved._tags[tag] = idx;
            }

            if (bag.length === 0) {
                delete this._tags[tag];
            }
        }
    });

    /**
     * A taggable class. If you add a tag to an instance, you can get it from the container.
     *
     * @see Grape.TagContainer
     * @class Grape.Taggable
     * @constructor
     */
    var Taggable = Class('TagContainer', {
        init: function () {
            this._tags = {};
        },
        /**
         * Sets the tag container for the instance. If tags are added already, they will appear in the new container.
         * If the instance already has a tagContainer, it will be removed first.
         *
         * @method setTagContainer
         * @param {TagContainer} container The container
         */
        setTagContainer: function (container) { //todov2 instanceOf check
            var i;
            if (this._tagContainer) { //should remove old tag container first
                if (this._tagContainer === container) {
                    return;
                }
                this.removeTagContainer(); //todov2 better move
            }
            this._tagContainer = container;
            for (i in this._tags) {
                container._add(this, i);
            }
        },
        /**
         * Adds a tag to a taggable object.
         *
         * @method addTag
         * @param {String} name Tag name
         * @return {boolean} true, if a new tag is added, false, if the tag was already added.
         */
        addTag: function (name) {
            var container = this._tagContainer;
            if (this.hasTag(name)) { //already added
                return false;
            }
            if (container) { //have container
                this._tags[name] = container._add(this, name); //store the index for removal purpose
            } else {
                this._tags[name] = true;
            }
            return true;
        },
        /**
         * Checks if a tag is added or not.
         *
         * @method hasTag
         * @param {String} name Tag name
         * @return {boolean} true, if the instance has the tag
         */
        hasTag: function (name) {
            return this._tags[name] !== undefined;
        },
        /**
         * Removes a tag from a taggable object. If the tag is not added, does nothing.
         *
         * @method removeTag
         * @param {String} name Tag name
         */
        removeTag: function (name) {
            if (!this.hasTag(name)) {
                return;
            }

            if (this._tagContainer) {
                this._tagContainer._remove(this, name);
            }
            delete this._tags[name];
        },
        /**
         * Detaches the TagContainer. The instance is no more queryable through the container.
         *
         * @method removeTagContainer
         */
        removeTagContainer: function () {
            var name;
            if (!this._tagContainer) { //nothing to do
                return;
            }
            for (name in this._tags) {
                this._tagContainer._remove(this, name);
            }
        }
    });

    return {
        TagContainer: TagContainer,
        Taggable: Taggable
    };
});