Source: implements/gradient.js

/*
 * This file is part of Toolkit.
 *
 * Toolkit is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General
 * Public License along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */
"use strict";
(function(w, TK){
TK.Gradient = TK.class({
    /**
     * TK.Gradient provides a function to set the background of a
     * DOM element to a CSS gradient according on the users browser and version.
     * TK.Gradient needs a {@link TK.Range} to be implemented on.
     *
     * @mixin TK.Gradient
     * 
     * @param {Object} [options={ }] - An object containing initial options.
     * 
     * @property {Object|Boolean} options.gradient - Gradient definition for the background.
     *   Keys are ints or floats as string corresponding to the widgets scale.
     *   Values are valid css color strings like <code>#ff8000</code> or <code>rgb(0,56,103)</code>.
     *   If set to false the css style color is used.
     * @property {String|Boolean} [options.background="#000000"] - Background color if no gradient is used.
     *   Values are valid css color strings like <code>#ff8000</code> or <code>rgb(0,56,103)</code>.
     *   If set to false the css style color is used.
     */
    _class: "Gradient",
    Implements: TK.Ranged,
    _options: Object.assign(TK.Ranged.prototype._options, {
        gradient: "mixed",
        background: "mixed",
    }),
    options: {
        gradient:        false,
        background:      false
    },
    draw_gradient: function (element, gradient, fallback, range) {
        /**
         * This function generates a string from a given
         * gradient object to set as a CSS background for a DOM element.
         * If element is given, the function automatically sets the
         * background. If gradient is omitted, the gradient is taken from
         * options. Fallback is used if no gradient can be created.
         * If fallback is omitted, <code>options.background</code> is used. if no range
         * is set Gradient assumes that the implementing instance has
         * {@link TK.Range} functionality.
         *
         * @method TK.Gradient#draw_gradient
         * 
         * @param {DOMNode} element - The DOM node to apply the gradient to.
         * @param {Object} [gradient=this.options.gradient] - Gradient definition for the background, e.g. <code>{"-96": "rgb(30,87,153)", "-0.001": "rgb(41,137,216)", "0": "rgb(32,124,202)", "24": "rgb(125,185,232)"}</code>.
         * @param {string} [fallback=this.options.background] - If no gradient can be applied, use this as background color string.
         * @param {TK.Range} [range=this] - If a specific range is set, it is used for the calculations. If not, we expect the widget itself provides {@link TK.Ranged} functionality.
         * 
         * @returns {string} A string to be used as background CSS.
         *
         * @mixes TK.Ranged
         * 
         * @emits TK.Gradient#backgroundchanged
         */
        
        //  {"-96": "rgb(30,87,153)", "-0.001": "rgb(41,137,216)", "0": "rgb(32,124,202)", "24": "rgb(125,185,232)"}
        // becomes:
//         background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><linearGradient id='gradient'><stop offset='10%' stop-color='#F00'/><stop offset='90%' stop-color='#fcc'/> </linearGradient><rect fill='url(#gradient)' x='0' y='0' width='100%' height='100%'/></svg>");
//         background: -moz-linear-gradient(top, rgb(30,87,153) 0%, rgb(41,137,216) 50%, rgb(32,124,202) 51%, rgb(125,185,232) 100%);
//         background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgb(30,87,153)), color-stop(50%,rgb(41,137,216)), color-stop(51%,rgb(32,124,202)), color-stop(100%,rgb(125,185,232)));
//         background: -webkit-linear-gradient(top, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%);
//         background: -o-linear-gradient(top, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%);
//         background: -ms-linear-gradient(top, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%);
//         background: linear-gradient(to bottom, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%);
//         filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e5799', endColorstr='#7db9e8',GradientType=0 );
        
        var bg = "";
        range = range || this;
        if (!gradient && !this.options.gradient)
            bg = fallback || this.options.background;
        else {
            gradient = gradient || this.options.gradient;
            
            var ms_first   = "";
            var ms_last    = "";
            var m_svg      = "";
            var m_regular  = "";
            var m_webkit   = "";
            var s_ms       = "background filter: progid:DXImageTransform."
                           + "Microsoft.gradient( startColorstr='%s', "
                           + "endColorstr='%s',GradientType='%d' )";
            var s_svg      = "url(\"data:image/svg+xml;utf8,<svg "
                           + "xmlns='http://www.w3.org/2000/svg' width='100%%' "
                           + "height='100%%'>"
                           + "<linearGradient id='%s_gradient' %s>%s"
                           + "</linearGradient><rect fill='url(#%s_gradient)' "
                           + "x='0' y='0' width='100%%' "
                           + "height='100%%'/></svg>\")";
            var s_regular  = "%slinear-gradient(%s, %s)";
            var s_webkit   = "-webkit-gradient(linear, %s, %s)";
            var c_svg      = "<stop offset='%s%%' stop-color='%s'/>";
            var c_regular  = "%s %s%%, ";
            var c_webkit   = "color-stop(%s%%, %s), ";
            
            var d_w3c = {};
            d_w3c["s"+"left"]       = "to top";
            d_w3c["s"+"right"]      = "to top";
            d_w3c["s"+"top"]        = "to right";
            d_w3c["s"+"bottom"]     = "to right";
            
            var d_regular = {};
            d_regular["s"+"left"]   = "bottom";
            d_regular["s"+"right"]  = "bottom";
            d_regular["s"+"top"]    = "left";
            d_regular["s"+"bottom"] = "left";
            
            var d_webkit = {};
            d_webkit["s"+"left"]    = "left bottom, left top";
            d_webkit["s"+"right"]   = "left bottom, left top";
            d_webkit["s"+"top"]     = "left top, right top";
            d_webkit["s"+"bottom"]  = "left top, right top";
            
            var d_ms = {};
            d_ms["s"+"left"]     = 'x1="0%" y1="0%" x2="0%" y2="100%"';
            d_ms["s"+"right"]    = 'x1="0%" y1="0%" x2="0%" y2="100%"';
            d_ms["s"+"top"]      = 'x1="100%" y1="0%" x2="0%" y2="0%"';
            d_ms["s"+"bottom"]   = 'x1="100%" y1="0%" x2="0%" y2="0%"';
            
            var keys = Object.keys(gradient);
            for (var i = 0; i < keys.length; i++) {
                keys[i] = parseFloat(keys[i]);
            }
            keys = keys.sort(this.options.reverse ?
                function (a,b) { return b-a } : function (a,b) { return a-b });
            
            for (var i = 0; i < keys.length; i++) {
                var ps = (100*range.val2coef(range.snap(keys[i]))).toFixed(2);
                if (!ms_first) ms_first = gradient[i];
                ms_last = gradient[keys[i] + ""];
                
                m_svg     += TK.sprintf(c_svg, ps, gradient[keys[i] + ""]);
                m_regular += TK.sprintf(c_regular, gradient[keys[i] + ""], ps);
                m_webkit  += TK.sprintf(c_webkit, ps, gradient[keys[i] + ""]);
            }
            m_regular = m_regular.substr(0, m_regular.length -2);
            m_webkit  = m_regular.substr(0, m_webkit.length -2);
            
            if (TK.browser.name === "IE" && TK.browser.version <= 8)
                    bg = (TK.sprintf(s_ms, ms_last, ms_first, this._vert() ? 0:1));
                
            else if (TK.browser.name === "IE" && TK.browser.version === 9)
                bg = (TK.sprintf(s_svg, this.options.id,
                      d_ms["s"+this.options.layout],
                      m_svg, this.options.id));
            
            else if (TK.browser.name === "IE" && TK.browser.version >= 10)
                bg = (TK.sprintf(s_regular, "-ms-",
                      d_regular["s" + this.options.layout],
                      m_regular));
            
            else if (TK.browser.name=="Firefox")
                bg = (TK.sprintf(s_regular, "-moz-",
                      d_regular["s"+this.options.layout],
                      m_regular));
            
            else if (TK.browser.name === "Opera" && TK.browser.version >= 11)
                bg = (TK.sprintf(s_regular, "-o-",
                      d_regular["s"+this.options.layout],
                      m_regular));
            
            else if (TK.browser.name === "Chrome" && TK.browser.version < 10
                  || TK.browser.name === "Safari" && TK.browser.version < 5.1)
                bg = (TK.sprintf(s_webkit,
                      d_webkit["s"+this.options.layout],
                      m_regular));
            
            else if (TK.browser.name === "Chrome" || TK.browser.name === "Safari")
                bg = (TK.sprintf(s_regular, "-webkit-",
                      d_regular["s"+this.options.layout],
                      m_regular));
            
            else
                bg = (TK.sprintf(s_regular, "",
                      d_w3c["s"+this.options.layout],
                      m_regular));
        }
        
        if (element) {
            element.style.background = bg ? bg : void 0;
            /**
             * Is fired when the gradient was created.
             *
             * @event TK.Gradient#backgroundchanged
             * 
             * @param {HTMLElement} element - The element which background has changed.
             * @param {string} background - The background of the element as CSS color string.
             */
            this.fire_event("backgroundchanged", element, bg);
        }
        return bg;
    }
});
})(this, this.TK);