/*
* This file is part of AUX.
*
* AUX 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.
*
* AUX 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
* 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
*/
import { defineClass } from '../widget_helpers.js';
import { Ranged } from './ranged.js';
import { sprintf } from '../utils/sprintf.js';
export const Gradient = defineClass({
/**
* Gradient provides a function to set the background of a
* DOM element to a CSS gradient according to a {@link Range}.
* If the element to draw on is a `CANVAS`, the canvas API is used
* for drawing.
*
* @mixin Gradient
*
* @mixes Ranged
*
* @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.
*/
Implements: Ranged,
_options: Object.assign(Object.create(Ranged.prototype._options), {
gradient: 'mixed',
background: 'mixed',
}),
options: {
gradient: false,
background: false,
},
drawGradient: 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 Range} functionality.
*
* @method Gradient#drawGradient
*
* @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 {Range} [range=this] - If a specific range is set, it is used for the calculations. If not, we expect the widget itself provides {@link Ranged} functionality.
*
* @returns {string} A string to be used as background CSS.
*
* @mixes Ranged
*
* @emits 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: linear-gradient(to bottom, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%);
const O = this.options;
let bg = '';
range = range || this;
if (!gradient && !O.gradient) {
bg = fallback || O.background;
if (element.tagName === 'CANVAS') {
const ctx = element.getContext('2d');
ctx.fillStyle = bg;
ctx.fillRect(0, 0, O._width, O._height);
this.emit('backgroundchanged', element, bg);
return;
}
} else {
gradient = gradient || this.options.gradient;
let keys = Object.keys(gradient);
for (let i = 0; i < keys.length; i++) {
keys[i] = parseFloat(keys[i]);
}
keys = keys.sort(
O.reverse
? function (a, b) {
return b - a;
}
: function (a, b) {
return a - b;
}
);
if (element.tagName === 'CANVAS') {
const vert = O.layout == 'left' || O.layout == 'right';
const ctx = element.getContext('2d');
const grd = ctx.createLinearGradient(
0,
0,
vert ? 0 : O._width || 0,
vert ? O._height || 0 : 0
);
for (let i = 0; i < keys.length; i++) {
let pos = range.valueToCoef(range.snap(keys[i]));
pos = Math.min(1, Math.max(0, pos));
if (vert) pos = 1 - pos;
grd.addColorStop(pos, gradient[keys[i] + '']);
}
ctx.fillStyle = grd;
ctx.fillRect(0, 0, O._width, O._height);
this.emit('backgroundchanged', element, gradient);
return;
}
let m_regular = '';
const s_regular = 'linear-gradient(%s, %s)';
const c_regular = '%s %s%%, ';
var d_w3c = {};
d_w3c.sleft = 'to top';
d_w3c.sright = 'to top';
d_w3c.stop = 'to right';
d_w3c.sbottom = 'to right';
for (let i = 0; i < keys.length; i++) {
const ps = (100 * range.valueToCoef(range.snap(keys[i]))).toFixed(2);
m_regular += sprintf(c_regular, gradient[keys[i] + ''], ps);
}
m_regular = m_regular.substr(0, m_regular.length - 2);
bg = 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 Gradient#backgroundchanged
*
* @param {HTMLElement} element - The element which background has changed.
* @param {string} background - The background of the element as CSS color string.
*/
this.emit('backgroundchanged', element, bg);
}
return bg;
},
});