airbug/bugcore

View on GitHub
libraries/bugcore/js/src/observable/data/ObservableCollection.js

Summary

Maintainability
C
1 day
Test Coverage
/*
 * Copyright (c) 2016 airbug Inc. http://airbug.com
 *
 * bugcore may be freely distributed under the MIT license.
 */


//-------------------------------------------------------------------------------
// Annotations
//-------------------------------------------------------------------------------

//@Export('ObservableCollection')

//@Require('AddChange')
//@Require('ArgumentBug')
//@Require('Class')
//@Require('ClearChange')
//@Require('Collection')
//@Require('IArrayable')
//@Require('ICollection')
//@Require('IIterable')
//@Require('Obj')
//@Require('Observable')
//@Require('Proxy')
//@Require('RemoveChange')
//@Require('TypeUtil')


//-------------------------------------------------------------------------------
// Context
//-------------------------------------------------------------------------------

require('bugpack').context("*", function(bugpack) {

    //-------------------------------------------------------------------------------
    // BugPack
    //-------------------------------------------------------------------------------

    var AddChange       = bugpack.require('AddChange');
    var ArgumentBug     = bugpack.require('ArgumentBug');
    var Class           = bugpack.require('Class');
    var ClearChange     = bugpack.require('ClearChange');
    var Collection      = bugpack.require('Collection');
    var IArrayable      = bugpack.require('IArrayable');
    var ICollection     = bugpack.require('ICollection');
    var IIterable       = bugpack.require('IIterable');
    var Obj             = bugpack.require('Obj');
    var Observable      = bugpack.require('Observable');
    var Proxy           = bugpack.require('Proxy');
    var RemoveChange    = bugpack.require('RemoveChange');
    var TypeUtil        = bugpack.require('TypeUtil');


    //-------------------------------------------------------------------------------
    // Declare Class
    //-------------------------------------------------------------------------------

    /**
     * @class
     * @extends {Observable}
     * @implements {ICollection.<I>}
     * @template I
     */
    var ObservableCollection = Class.extend(Observable, /** @lends {ObservableCollection.prototype} */{

        _name: "ObservableCollection",


        //-------------------------------------------------------------------------------
        // Constructor
        //-------------------------------------------------------------------------------

        /**
         * @constructs
         * @param {(ICollection.<I> | Array.<I>)} items
         */
        _constructor: function(items) {

            this._super();


            //-------------------------------------------------------------------------------
            // Private Properties
            //-------------------------------------------------------------------------------

            /**
             * @private
             * @type {Collection.<I>}
             */
            this.observed = this.factoryObserved(items);
        },


        //-------------------------------------------------------------------------------
        // Getters and Setters
        //-------------------------------------------------------------------------------

        /**
         * @return {Collection.<I>}
         */
        getObserved: function() {
            return this.observed;
        },


        //-------------------------------------------------------------------------------
        // IArrayable Implementation
        //-------------------------------------------------------------------------------

        /**
         * @override
         * @return (Array.<I>)
         */
        toArray: function() {
            return this.observed.toArray();
        },


        //-------------------------------------------------------------------------------
        // ICollection Implementation
        //-------------------------------------------------------------------------------

        /**
         * @param {I} value
         * @return {boolean}
         */
        add: function(value) {
            var result = this.observed.add(value);
            if (result) {
                this.notifyObservers(new AddChange(value))
            }
            return result;
        },

        /**
         * @param {(ICollection.<I> | Array.<I>)} items
         */
        addAll: function(items) {
            if (Class.doesImplement(items, ICollection) || TypeUtil.isArray(items)) {
                var _this = this;
                items.forEach(function(value) {
                    _this.add(value);
                });
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "items", items, "parameter must implement ICollection or be an Array");
            }
        },

        /**
         *
         */
        clear: function() {
            this.observed.clear();
            this.notifyObservers(new ClearChange());
        },

        /**
         * @param {*} value
         * @return {boolean}
         */
        contains: function(value) {
            return this.observed.contains(value);
        },

        /**
         * @param {(ICollection.<*> | Array.<*>)} items
         * @return {boolean}
         */
        containsAll: function(items) {
            return this.observed.contains(items);
        },

        /**
         * @param {(ICollection.<*> | Array.<*>)} items
         * @return {boolean}
         */
        containsEqual: function(items) {
            return this.observed.containsEqual(items);
        },

        /**
         * @return {number}
         */
        getCount: function() {
            return this.observed.getCount();
        },

        /**
         * @param {*} value
         * @return {number}
         */
        countValue: function(value) {
            return this.observed.countValue(value);
        },

        /**
         * @return {boolean}
         */
        isEmpty: function() {
            return this.observed.isEmpty();
        },

        /**
         * @param {*} value
         * @return {boolean}
         */
        remove: function(value) {
            var result = this.observed.remove(value);
            if (result) {
                this.notifyObservers(new RemoveChange(value));
            }
            return result;
        },

        /**
         * @param {(ICollection.<*> | Array.<*>)} items
         */
        removeAll: function(items) {
            if (Class.doesImplement(items, ICollection) || TypeUtil.isArray(items)) {
                var _this = this;
                items.forEach(function(value) {
                    _this.remove(value);
                });
            } else {
                throw new ArgumentBug(ArgumentBug.ILLEGAL, "items", items, "parameter must implement ICollection or be an Array");
            }
        },


        //-------------------------------------------------------------------------------
        // IIterable Implementation
        //-------------------------------------------------------------------------------

        /**
         * NOTE BRN: If a value is modified in one iteration and then visited at a later time, its value in the loop is
         * its value at that later time. A value that is deleted before it has been visited will not be visited later.
         * Values added to the Collection over which iteration is occurring may either be visited or omitted from iteration.
         *
         * @param {function(I)} func
         */
        forEach: function(func) {
            this.observed.forEach(func);
        },

        /**
         * NOTE BRN: If a value is modified in one iteration and then visited at a later time, its value in the loop is
         * its value at that later time. A value that is deleted before it has been visited will not be visited later.
         * Values added to the Collection over which iteration is occurring may either be visited or omitted from iteration.
         *
         * @return {IIterator.<I>}
         */
        iterator: function() {
            return this.observed.iterator();
        },


        //-------------------------------------------------------------------------------
        // Obj Methods
        //-------------------------------------------------------------------------------

        /**
         * @param {boolean} deep
         * @return {ObservableCollection.<I>}
         */
        clone: function(deep) {
            var cloneCollection = new ObservableCollection();
            if (deep) {
                this.forEach(function(item){
                    cloneCollection.add(Obj.clone(item, deep));
                });
            } else {
                cloneCollection.addAll(this);
            }
            return cloneCollection;
        },


        //-------------------------------------------------------------------------------
        // Protected Methods
        //-------------------------------------------------------------------------------

        /**
         * @protected
         * @param {(ICollection.<I> | Array.<I>)=} items
         * @return {Collection.<I>}
         */
        factoryObserved: function(items) {
            return new Collection(items);
        }
    });


    //-------------------------------------------------------------------------------
    // Implement Interfaces
    //-------------------------------------------------------------------------------

    Class.implement(ObservableCollection, IArrayable);
    Class.implement(ObservableCollection, ICollection);
    Class.implement(ObservableCollection, IIterable);


    //-------------------------------------------------------------------------------
    // Exports
    //-------------------------------------------------------------------------------

    bugpack.export('ObservableCollection', ObservableCollection);
});