/*
* 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
*/
/**
* The <code>useraction</code> event is emitted when a widget gets modified by user interaction.
* The event is emitted for the option <code>value</code>.
*
* @event Slider#useraction
*
* @param {string} name - The name of the option which was changed due to the users action
* @param {mixed} value - The new value of the option
*/
import { Widget } from './widget.js';
import { DragValue } from '../modules/dragvalue.js';
import { ScrollValue } from '../modules/scrollvalue.js';
import { warning } from '../utils/warning.js';
import { element, addClass, outerWidth, outerHeight } from '../utils/dom.js';
import {
rangedEvents,
rangedOptionsDefaults,
rangedOptionsTypes,
rangedRenderers,
} from '../utils/ranged.js';
import { focusMoveDefault, announceFocusMoveKeys } from '../utils/keyboard.js';
import { warn } from '../utils/log.js';
import { defineRender } from '../renderer.js';
import { mergeStaticEvents } from '../widget_helpers.js';
function dblClick() {
this.userset('value', this.options.reset);
/**
* Is fired when the slider receives a double click in order to reset to initial value.
*
* @event Slider#doubleclick
*
* @param {number} value - The value of the widget.
*/
this.emit('doubleclick', this.options.value);
}
function setBackground(style, horiz, vert, size) {
style['background-position'] = '-' + horiz + 'px -' + vert + 'px';
style['-webkit-background-size'] = size;
style['-moz-background-size'] = size;
style['-ms-background-size'] = size;
style['-o-background-size'] = size;
style['background-size'] = size;
}
/**
* Slider is a {@link Widget} moving its background image
* according to its value. It can be used to show strips of
* e.g. 3D-rendered faders or knobs. It's important to set the
* width and height of the widget in CSS according to the frames in
* the background file. If alignment is `horizontal` the background image
* is as height as the widget, the width keeps the ratio intact. Overall
* width of the image should be frames * width. If alignment is `vertical`
* the background image is as wide as the widget and the height of the
* image keeps the ratio intact. The height should be height of widget
* times the number of frames.
* Slider uses {@link DragValue} and {@link ScrollValue}
* for setting its value.
* It inherits all options of {@link DragValue} and {@link Ranged}.
*
* @class Slider
*
* @extends Widget
*
* @param {Object} [options={ }] - An object containing initial options.
*
* @property {Number} [options.value=0] - The current value.
* @property {Integer} [options.frames=1] - The amount of frames contained
* in the background image.
* @property {String} [options.alignment="horizontal"] - The direction
* of the frames in the image, next to (`horizontal`) or among each other (`vertical`).
* @property {String|Booelan} [options.image=false] - The image containing all frames for the slider.
* Set to `false` to set the background image via external CSS.
*
*/
export class Slider extends Widget {
static get _options() {
return [
rangedOptionsTypes,
DragValue.getOptionTypes(),
{
value: 'number',
frames: 'int',
alignment: 'string',
image: 'string|boolean',
_width: 'number',
_height: 'number',
},
];
}
static get options() {
return [
rangedOptionsDefaults,
{
value: 0,
frames: 1,
alignment: 'horizontal',
image: false,
direction: 'polar',
rotation: 45,
blind_angle: 20,
basis: 300,
role: 'slider',
tabindex: 0,
set_ariavalue: true,
},
];
}
static get static_events() {
return mergeStaticEvents(rangedEvents, {
dblclick: dblClick,
focus_move: focusMoveDefault(),
});
}
static get renderers() {
return [
...rangedRenderers,
defineRender('image', function (image) {
const style = this.element.style;
if (image) style['background-image'] = "url('" + image + "')";
else style['background-image'] = void 0;
}),
defineRender(
[
'value',
'alignment',
'frames',
'transformation',
'_width',
'_height',
'snap_module',
],
function (
value,
alignment,
frames,
transformation,
_width,
_height,
snap_module
) {
value = snap_module.snap(value);
const coef = transformation.valueToCoef(value);
const frame = Math.round(Math.max(0, frames - 1) * coef);
const style = this.element.style;
switch (alignment) {
default:
warn(
"Unknown alignment, only 'vertical' and 'horizontal' are allowed"
);
break;
case 'vertical':
setBackground(style, 0, frame * _width, '100% auto');
break;
case 'horizontal':
setBackground(style, frame * _height, 0, 'auto 100%');
break;
}
}
),
];
}
initialize(options) {
if (!options.element) options.element = element('div');
super.initialize(options);
options = this.options;
const E = this.element;
/**
* @member {HTMLDivElement} Slider#element - The main DIV container.
* Has class <code>.aux-slider</code>.
*/
/**
* @member {DragValue} Knob#drag - Instance of {@link DragValue} used for
* interaction.
*/
this.drag = new DragValue(this, {
node: E,
classes: E,
direction: this.options.direction,
rotation: this.options.rotation,
blind_angle: this.options.blind_angle,
});
/**
* @member {ScrollValue} Knob#scroll - Instance of {@link ScrollValue} used for
* interaction.
*/
this.scroll = new ScrollValue(this, {
node: E,
classes: E,
});
if (options.reset === void 0) options.reset = options.value;
}
destroy() {
this.drag.destroy();
this.scroll.destroy();
super.destroy();
}
draw(O, element) {
addClass(element, 'aux-slider');
announceFocusMoveKeys.call(this);
super.draw(O, element);
}
getResizeElements() {
return [this.element];
}
resize() {
const E = this.element;
this.set('_width', outerWidth(E, false, undefined, true));
this.set('_height', outerHeight(E, false, undefined, true));
}
set(key, value) {
const O = this.options;
if (key === 'value') {
if (value > O.max || value < O.min) warning(this.element);
value = O.snap_module.snap(Math.max(O.min, Math.min(O.max, value)));
}
if (DragValue.hasOption(key)) this.drag.set(key, value);
return super.set(key, value);
}
}