412 lines
14 KiB
JavaScript
412 lines
14 KiB
JavaScript
/*
|
|
* 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
|
|
*/
|
|
|
|
/**
|
|
* 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 TK.Fader#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
|
|
*/
|
|
"use strict";
|
|
(function(w, TK){
|
|
|
|
function vert(O) {
|
|
return O.layout === "left" || O.layout === "right";
|
|
}
|
|
function get_value(ev) {
|
|
var is_vertical = vert(this.options);
|
|
var pos, real, hsize, pad;
|
|
hsize = this._handle_size / 2;
|
|
pad = this._padding;
|
|
|
|
if (is_vertical) {
|
|
real = this.options.basis - (ev.offsetY - hsize) + pad.bottom;
|
|
} else {
|
|
real = ev.offsetX - hsize + pad.left;
|
|
}
|
|
return this.px2val(real);
|
|
}
|
|
function tooltip_by_position(ev, tt) {
|
|
if (this._handle.contains(ev.target)) {
|
|
tooltip_by_value.call(this, ev, tt);
|
|
return;
|
|
}
|
|
var val = this.snap(get_value.call(this, ev));
|
|
TK.set_text(tt, this.options.tooltip(val));
|
|
}
|
|
function tooltip_by_value(ev, tt) {
|
|
TK.set_text(tt, this.options.tooltip(this.options.value));
|
|
}
|
|
function mouseenter (ev) {
|
|
if (!this.options.tooltip) return;
|
|
TK.tooltip.add(1, this.tooltip_by_position);
|
|
}
|
|
function clicked(ev) {
|
|
var value;
|
|
if (this._handle.contains(ev.target)) return;
|
|
if (this.value && this.value.element.contains(ev.target)) return;
|
|
if (this.label && this.label.element.contains(ev.target)) return;
|
|
if (this.scale && this.scale.element.contains(ev.target)) return;
|
|
value = this.userset("value", get_value.call(this, ev));
|
|
if (this.options.tooltip && TK.tooltip._entry)
|
|
TK.set_text(TK.tooltip._entry, this.options.tooltip(this.options.value));
|
|
}
|
|
function mouseleave (ev) {
|
|
TK.tooltip.remove(1, this.tooltip_by_position);
|
|
}
|
|
function startdrag(ev) {
|
|
if (!this.options.tooltip) return;
|
|
TK.tooltip.add(0, this.tooltip_by_value);
|
|
}
|
|
function stopdrag(ev) {
|
|
TK.tooltip.remove(0, this.tooltip_by_value);
|
|
}
|
|
function scrolling(ev) {
|
|
if (!this.options.tooltip) return;
|
|
TK.set_text(TK.tooltip._entry, this.options.tooltip(this.options.value));
|
|
}
|
|
function dblclick(ev) {
|
|
this.userset("value", this.options.reset);
|
|
/**
|
|
* Is fired when the handle receives a double click.
|
|
*
|
|
* @event TK.Fader#doubleclick
|
|
*
|
|
* @param {number} value - The value of the {@link TK.Fader}.
|
|
*/
|
|
this.fire_event("doubleclick", this.options.value);
|
|
}
|
|
function activate_tooltip() {
|
|
if (!this.tooltip_by_position) {
|
|
this.tooltip_by_position = tooltip_by_position.bind(this);
|
|
this.tooltip_by_value = tooltip_by_value.bind(this);
|
|
this.__startdrag = startdrag.bind(this);
|
|
this.__stopdrag = stopdrag.bind(this);
|
|
this.__scrolling = scrolling.bind(this);
|
|
}
|
|
this.add_event("mouseenter", mouseenter);
|
|
this.add_event("mouseleave", mouseleave);
|
|
this.drag.add_event("startdrag", this.__startdrag);
|
|
this.drag.add_event("stopdrag", this.__stopdrag);
|
|
this.scroll.add_event("scrolling", this.__scrolling);
|
|
}
|
|
|
|
function deactivate_tooltip() {
|
|
if (!this.tooltip_by_position) return;
|
|
TK.tooltip.remove(0, this.tooltip_by_value);
|
|
TK.tooltip.remove(1, this.tooltip_by_position);
|
|
this.remove_event("mouseenter", mouseenter);
|
|
this.remove_event("mouseleave", mouseleave);
|
|
this.drag.remove_event("startdrag", this.__startdrag);
|
|
this.drag.remove_event("stopdrag", this.__stopdrag);
|
|
this.scroll.remove_event("scrolling", this.__scrolling);
|
|
}
|
|
/**
|
|
* TK.Fader is a slidable control with a {@link TK.Scale} next to it which
|
|
* can be both dragged and scrolled. TK.Fader implements {@link TK.Ranged},
|
|
* {@link TK.Warning} and {@link TK.GlobalCursor} and inherits their options.
|
|
* A {@link TK.Label} and a {@link TK.Value} are available optionally.
|
|
*
|
|
* @class TK.Fader
|
|
*
|
|
* @extends TK.Widget
|
|
*
|
|
* @param {Object} [options={ }] - An object containing initial options.
|
|
*
|
|
* @property {Number} [options.value] - The faders position. This options is
|
|
* modified by user interaction.
|
|
* @property {Function} [options.tooltip=false] - An optional formatting function for
|
|
* the tooltip value. The tooltip will show the value the mouse cursor is
|
|
* currently hovering over. If this option is not set, no tooltip will be shown.
|
|
* @property {Boolean} [options.bind_click=false] - If true, a <code>click</code>
|
|
* on the fader will move the handle to the pointed position.
|
|
* @property {Boolean} [options.bind_dblclick=true] - If true, a <code>dblclick</code>
|
|
* on the fader will reset the fader value to <code>options.reset</code>.
|
|
* @property {Number} [options.reset=options.value] - The reset value, which is used by
|
|
* the <code>dblclick</code> event and the {@link TK.Fader#reset} method.
|
|
* @property {Boolean} [options.show_scale=true] - If true, a {@link TK.Scale} is added to the fader.
|
|
* @property {Boolean} [options.show_value=false] - If true, a {@link TK.Value} widget is added to the fader.
|
|
* @property {String|Boolean} [options.label=false] - Add a label to the fader. Set to `false` to remove the label from the DOM.
|
|
*/
|
|
TK.Fader = TK.class({
|
|
_class: "Fader",
|
|
Extends: TK.Widget,
|
|
Implements: [TK.Ranged, TK.Warning, TK.GlobalCursor],
|
|
_options: Object.assign(Object.create(TK.Widget.prototype._options),
|
|
TK.Ranged.prototype._options, TK.Scale.prototype._options, {
|
|
value: "number",
|
|
division: "number",
|
|
levels: "array",
|
|
gap_dots: "number",
|
|
gap_labels: "number",
|
|
show_labels: "boolean",
|
|
labels: "function",
|
|
tooltip: "function",
|
|
layout: "string",
|
|
direction: "int",
|
|
reset: "number",
|
|
bind_click: "boolean",
|
|
bind_dblclick: "boolean",
|
|
}),
|
|
options: {
|
|
value: 0,
|
|
division: 1,
|
|
levels: [1, 6, 12, 24],
|
|
gap_dots: 3,
|
|
gap_labels: 40,
|
|
show_labels: true,
|
|
labels: function (val) { return val.toFixed(2); },
|
|
tooltip: false,
|
|
layout: "left",
|
|
bind_click: false,
|
|
bind_dblclick: true,
|
|
label: false,
|
|
},
|
|
static_events: {
|
|
set_bind_click: function(value) {
|
|
if (value) this.add_event("click", clicked);
|
|
else this.remove_event("click", clicked);
|
|
},
|
|
set_bind_dblclick: function(value) {
|
|
if (value) this.add_event("dblclick", dblclick);
|
|
else this.remove_event("dblclick", dblclick);
|
|
},
|
|
set_tooltip: function(value) {
|
|
(value ? activate_tooltip : deactivate_tooltip).call(this);
|
|
},
|
|
set_layout: function(value) {
|
|
this.options.direction = vert(this.options) ? "vertical" : "horizontal";
|
|
this.drag.set("direction", this.options.direction);
|
|
this.scroll.set("direction", this.options.direction);
|
|
},
|
|
},
|
|
initialize: function (options) {
|
|
this.__tt = false;
|
|
TK.Widget.prototype.initialize.call(this, options);
|
|
|
|
var E, O = this.options;
|
|
|
|
/**
|
|
* @member {HTMLDivElement} TK.Fader#element - The main DIV container.
|
|
* Has class <code>toolkit-fader</code>.
|
|
*/
|
|
if (!(E = this.element)) this.element = E = TK.element("div");
|
|
TK.add_class(E, "toolkit-fader");
|
|
this.widgetize(E, true, true, true);
|
|
|
|
/**
|
|
* @member {HTMLDivElement} TK.Fader#_track - The track for the handle. Has class <code>toolkit-track</code>.
|
|
*/
|
|
this._track = TK.element("div", "toolkit-track");
|
|
this.element.appendChild(this._track);
|
|
|
|
/**
|
|
* @member {HTMLDivElement} TK.Fader#_handle - The handle of the fader. Has class <code>toolkit-handle</code>.
|
|
*/
|
|
this._handle = TK.element("div", "toolkit-handle");
|
|
this._handle_size = 0;
|
|
this._track.appendChild(this._handle);
|
|
|
|
if (O.reset === void(0))
|
|
O.reset = O.value;
|
|
|
|
if (O.direction === void(0))
|
|
O.direction = vert(O) ? "vertical" : "horizontal";
|
|
/**
|
|
* @member {TK.DragValue} TK.Fader#drag - Instance of {@link TK.DragValue} used for the handle
|
|
* interaction.
|
|
*/
|
|
this.drag = new TK.DragValue(this, {
|
|
node: this._handle,
|
|
classes: this.element,
|
|
direction: O.direction,
|
|
limit: true,
|
|
});
|
|
/**
|
|
* @member {TK.ScrollValue} TK.Fader#scroll - Instance of {@link TK.ScrollValue} used for the
|
|
* handle interaction.
|
|
*/
|
|
this.scroll = new TK.ScrollValue(this, {
|
|
node: this.element,
|
|
classes: this.element,
|
|
limit: true,
|
|
});
|
|
|
|
this.set("bind_click", O.bind_click);
|
|
this.set("bind_dblclick", O.bind_dblclick);
|
|
this.set("tooltip", O.tooltip);
|
|
},
|
|
|
|
redraw: function () {
|
|
TK.Widget.prototype.redraw.call(this);
|
|
var I = this.invalid;
|
|
var O = this.options;
|
|
var E = this.element;
|
|
var value;
|
|
var tmp;
|
|
|
|
if (I.layout) {
|
|
I.layout = false;
|
|
value = O.layout;
|
|
TK.remove_class(E, "toolkit-vertical", "toolkit-horizontal", "toolkit-left",
|
|
"toolkit-right", "toolkit-top", "toolkit-bottom");
|
|
TK.add_class(E, vert(O) ? "toolkit-vertical" : "toolkit-horizontal");
|
|
TK.add_class(E, "toolkit-"+value);
|
|
|
|
if (TK.supports_transform)
|
|
this._handle.style.transform = null;
|
|
else {
|
|
if (vert(O))
|
|
this._handle.style.left = null;
|
|
else
|
|
this._handle.style.bottom = null;
|
|
}
|
|
I.value = false;
|
|
}
|
|
|
|
if (I.validate.apply(I, Object.keys(TK.Ranged.prototype._options)) || I.value) {
|
|
I.value = false;
|
|
// TODO: value is snapped already in set(). This is not enough for values which are set during
|
|
// initialization.
|
|
tmp = this.val2px(this.snap(O.value)) + "px"
|
|
|
|
if (vert(O)) {
|
|
if (TK.supports_transform)
|
|
this._handle.style.transform = "translateY(-"+tmp+")";
|
|
else
|
|
this._handle.style.bottom = tmp;
|
|
} else {
|
|
if (TK.supports_transform)
|
|
this._handle.style.transform = "translateX("+tmp+")";
|
|
else
|
|
this._handle.style.left = tmp;
|
|
}
|
|
}
|
|
},
|
|
resize: function () {
|
|
var O = this.options;
|
|
var T = this._track, H = this._handle;
|
|
var basis;
|
|
|
|
TK.Widget.prototype.resize.call(this);
|
|
|
|
this._padding = TK.css_space(T, "padding", "border");
|
|
|
|
if (vert(O)) {
|
|
this._handle_size = TK.outer_height(H, true);
|
|
basis = TK.inner_height(T) - this._handle_size;
|
|
} else {
|
|
this._handle_size = TK.outer_width(H, true);
|
|
basis = TK.inner_width(T) - this._handle_size;
|
|
}
|
|
|
|
this.set("basis", basis);
|
|
},
|
|
destroy: function () {
|
|
this._handle.remove();
|
|
TK.Widget.prototype.destroy.call(this);
|
|
TK.tooltip.remove(0, this.tooltip_by_value);
|
|
TK.tooltip.remove(1, this.tooltip_by_position);
|
|
},
|
|
|
|
/**
|
|
* Resets the fader value to <code>options.reset</code>.
|
|
*
|
|
* @method TK.Fader#reset
|
|
*/
|
|
reset: function() {
|
|
this.set("value", this.options.reset);
|
|
},
|
|
|
|
// GETTER & SETTER
|
|
set: function (key, value) {
|
|
if (key === "value") {
|
|
if (value > this.options.max || value < this.options.min)
|
|
this.warning(this.element);
|
|
value = this.snap(value);
|
|
}
|
|
|
|
return TK.Widget.prototype.set.call(this, key, value);
|
|
},
|
|
userset: function (key, value) {
|
|
if (key == "value") {
|
|
if (value > this.options.max || value < this.options.min)
|
|
this.warning(this.element);
|
|
value = this.snap(value);
|
|
}
|
|
return TK.Widget.prototype.userset.call(this, key, value);
|
|
}
|
|
});
|
|
/**
|
|
* @member {TK.Scale} TK.Fader#scale - A {@link TK.Scale} to display a scale next to the fader.
|
|
*/
|
|
TK.ChildWidget(TK.Fader, "scale", {
|
|
create: TK.Scale,
|
|
show: true,
|
|
inherit_options: true,
|
|
toggle_class: true,
|
|
static_events: {
|
|
set: function(key, value) {
|
|
/**
|
|
* Is fired when the scale was changed.
|
|
*
|
|
* @event TK.Fader#scalechanged
|
|
*
|
|
* @param {string} key - The key of the option.
|
|
* @param {mixed} value - The value to which it was set.
|
|
*/
|
|
if (this.parent)
|
|
this.parent.fire_event("scalechanged", key, value);
|
|
},
|
|
},
|
|
});
|
|
/**
|
|
* @member {TK.Label} TK.Fader#label - A {@link TK.label} to display a title.
|
|
*/
|
|
TK.ChildWidget(TK.Fader, "label", {
|
|
create: TK.Label,
|
|
show: false,
|
|
toggle_class: true,
|
|
option: "label",
|
|
map_options: {
|
|
label: "label",
|
|
},
|
|
});
|
|
/**
|
|
* @member {TK.Label} TK.Fader#value - A {@link TK.Value} to display the current value, offering a way to enter a value via keyboard.
|
|
*/
|
|
TK.ChildWidget(TK.Fader, "value", {
|
|
create: TK.Value,
|
|
show: false,
|
|
static_events: {
|
|
"valueset" : function (v) { this.parent.set("value", v); }
|
|
},
|
|
map_options: {
|
|
value: "value",
|
|
format: "format",
|
|
},
|
|
toggle_class: true,
|
|
userset_delegate: true,
|
|
});
|
|
|
|
})(this, this.TK);
|