Source: constructs/svg/grdnt.js

/*!
 * VERSION: 1.0.2
 * DATE: 2018-03-03
 * UPDATES AND DOCS AT: https://chris-moody.github.io/mkr
 *
 * @license copyright 2017-2018 Christopher C. Moody
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
 *  this software and associated documentation files (the "Software"), to deal in the
 *  Software without restriction, including without limitation the rights to use, copy,
 *  modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 *  and to permit persons to whom the Software is furnished to do so, subject to the
 *  following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 *  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 *  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 *  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 * @author: Christopher C. Moody, chris@moodydigital.com
 */
 
(function(global, className) {
    var _instances = {}, _count=-1;

    /**
     * @class grdnt
     * @classdesc Shortcut for creating SVG gradients
     * @description Initializes a new grdnt instance.
     * @param {Object} options - Options used to customize the grdnt
     * @param {*=} options.parent - Preferably an SVGElement. When an HTMLElement is provided, a new SVGElement is appended to it, which becomes the parent for the grdnt.
     * @param {String=} options.id - The id of the instance. Auto-generated when not provided
     * @param {String} [options.type=grdnt.LINEAR] - The type of gradient to create. Can be grdnt.LINEAR or grdnt.RADIAL.
     * @param {Array} [options.stops=[]] - An array of objects containing stop element attributes. Used to initialize the stops for the gradient
     * @param {Object=} options.attr - Attributes to apply to the grdnt's gradient element.
     * @param {Object=} options.css - CSS Properties to apply to the grdnt's gradient element.

     * @requires {@link  mkr}
     * @returns {grdnt} A new grdnt instance.
    **/
    var grdnt = function(options) {
        options = options || {};
        _count++;
        var id = this._id = options.id || className+'-'+_count;
        this._parent = mkr.setDefault(options, 'parent', mkr.default(mkr.query('svg'), mkr.create('svg', {css:{overflow:'visible'}})));
        var p = typeof this._parent == 'string' ? mkr.query(this._parent) : this._parent;
        if(!(p instanceof SVGElement)) {
            this._parent = mkr.query('svg', p) || mkr.create('svg', {css:{overflow:'visible'}}, this._parent);
        }
        /*if(this._parent instanceof SVGElement) {
            s = this._svg = this._parent;
        }*/
        
        mkr.setDefault(options, 'attr', {});
        mkr.setDefault(options.attr, 'id', id);
        mkr.setDefault(options, 'css', {});

        var type = mkr.default(options.type, grdnt.LINEAR);
        var stops = mkr.default(options.stops, []);
        
        var d = this._defs = mkr.query('defs', this._parent) || mkr.create('defs', {}, this._parent)
            var grad = this._grad = mkr.create(type+'Gradient', {attr:options.attr, css:options.css}, d)
                for(var i=0; i<stops.length; i++) {
                    this.addStop(stops[i]);
                }
        
        _instances[id] = this;
    };
    
    grdnt.prototype = {
        /**
         * @name grdnt#el
         * @public
         * @readonly
         * @type SVGElement
         * @description The gradient element associated with this instance
        **/
        get el() {return this._grad;},

        /**
         * @name grdnt#id
         * @public
         * @readonly
         * @type String
         * @description The id of this instance's gradient element
        **/
        get id() {return this.el.id;},

        /**
         * @name grdnt#parent
         * @public
         * @readonly
         * @type SVGElement
         * @description The parent of the defs element
        **/
        get parent() {return this.defs.parentNode;},

        /**
         * @name grdnt#defs
         * @public
         * @readonly
         * @type SVGElement
         * @description The SVGDefsElement where the gradient is stored
        **/
        get defs() {return this._defs;},

        /**
         * @name grdnt#url
         * @public
         * @readonly
         * @type String
         * @description The url function string used to assign the gradient as a fill, stroke, etc
        **/
        get url() {return 'url(#'+this.id+')';},

        /**
         * @name grdnt#svg
         * @public
         * @readonly
         * @type NodeList
         * @description A nodelist of the grdnt's stop elements
        **/
        get stops() {return mkr.queryAll('#'+this.id+' stop')},

        /**
         * @function addStop
         * @memberof grdnt.prototype
         * @public
         * @description Updates the points attribute of the polygon element.
         * @param {Object} attr - Attributes to set on the stop element
         * @param {Object} attr.color - Shortcut for 'stop-color'
         * @param {Object} attr.alpha - Shortcut for 'stop-opacity'
         * @param {int=} index - The insertion index, defaults to the number of stops. When negative, becomes the sum of itself and the number of stops
         * @returns {SVGElement} The new stop element
        **/
        addStop: function(attr, index) {
            index = mkr.default(index, this.stops.length);
            if(index < 0) index = this.stops.length + index;
            mkr.setDefault(attr, 'stop-color', attr.color);
            mkr.setDefault(attr, 'stop-opacity', attr.alpha);
            return mkr.create('stop', {attr:attr}, this._grad, index);
        },

        /**
         * @function removeStop
         * @memberof grdnt.prototype
         * @public
         * @description Removes the stop element at the specified index
         * @param {int} [index=-1] - The index of the target stop. When negative, becomes the sum of itself and the number of stops
         * @returns {SVGElement} The stop element that was removed
        **/
        removeStop: function(index) {
            index = mkr.default(index, -1);
            if(index < 0) index = this.stops.length + index;
            return mkr.remove(this.stops[index]);
        },

        /**
         * @function getStop
         * @memberof grdnt.prototype
         * @public
         * @description Returns the stop element at the specified index
         * @param {int} [index=-1] - The index of the target stop. When negative, becomes the sum of itself and the number of stops
         * @returns {SVGElement} The stop element at the specified index
        **/
        getStop: function(index) {
            index = mkr.default(index, -1);
            if(index < 0) index = this.stops.length + index;
            return this.stops[index];
        },

        /**
         * @function setStop
         * @memberof grdnt.prototype
         * @public
         * @description Updates the stop element at the specified index
         * @param {Object} attr - Attributes to set on the stop element
         * @param {Object} attr.color - Shortcut for 'stop-color'
         * @param {Object} attr.alpha - Shortcut for 'stop-opacity'
         * @param {int} [index=-1] - The index of the target stop. When negative, becomes the sum of itself and the number of stops
         * @returns {SVGElement} The updated stop element
        **/
        setStop: function(attr, index) {
            index = mkr.default(index, -1);
            if(index < 0) index = this.stops.length + index;
            mkr.setDefault(attr, 'stop-color', attr.color);
            mkr.setDefault(attr, 'stop-opacity', attr.alpha);
            var stop = this.getStop(index);
            TweenMax.set(stop, {attr:attr});
            return stop;
        }
    };
    
    /**
    * @alias grdnt.LINEAR
    * @memberof grdnt
    * @static
    * @readonly
    * @type String
    * @description returns 'linear'
    **/
    Object.defineProperty(grdnt, 'LINEAR', {
        get: function() {
          return 'linear';
        }
    });

    /**
    * @alias grdnt.RADIAL
    * @memberof grdnt
    * @static
    * @readonly
    * @type String
    * @description returns 'radial'
    **/
    Object.defineProperty(grdnt, 'RADIAL', {
        get: function() {
          return 'radial';
        }
    });

    /**
    * @function getInstance
    * @memberof grdnt
    * @static
    * @description returns the grdnt instance associated with the id
    * @param {String} id - The lookup id
    * @returns {grdnt} The associate grdnt, if it exists
    **/
    grdnt.getInstance = function(id) {
        return _instances[id];
    };

    /**
    * @function getElInstance
    * @memberof grdnt
    * @static
    * @description returns the grdnt instance associated with the provided element's id attribute
    * @param {Element} el - The element with the lookup id
    * @returns {grdnt} The associate grdnt, if it exists
    **/    
    grdnt.getElInstance = function(el) {
        return _instances[el.id];
    };

    /**
    * @alias grdnt.VERSION
    * @memberof grdnt
    * @static
    * @readonly
    * @type String
    * @description returns grdnt's version number
    **/
    Object.defineProperty(grdnt, 'VERSION', {
        get: function() {
          return '1.0.2';
        }
    });

    if(typeof define === 'function' && define.amd){ //AMD
        define(function () { return grdnt; });
    } else if (typeof module !== 'undefined' && module.exports){ //node
        module.exports = grdnt;
    } else { //browser
        global[className] = grdnt;
    }
})(mkr._constructs, 'grdnt');