13
0
livetrax/share/web_surfaces/builtin/mixer/toolkit/widgets/multimeter.js

248 lines
8.7 KiB
JavaScript
Raw Normal View History

2020-06-21 17:29:27 -04:00
/*
* 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){
function add_meters (cnt, options) {
for (var i = 0; i < cnt; i++)
this.add_meter(options);
}
function add_meter (options) {
var l = this.meters.length;
var O = options;
var opt = extract_child_options(O, l);
var m = new TK.LevelMeter(opt);
this.meters.push(m);
this.append_child(m);
}
function remove_meter (meter) {
/* meter can be int or meter instance */
var I = this.invalid;
var M = this.meters;
var m = -1;
if (typeof meter == "number") {
m = meter;
} else {
for (var i = 0; i < M.length; i++) {
if (M[i] == meter) {
m = i;
break;
}
}
}
if (m < 0 || m > M.length - 1) return;
this.remove_child(M[m]);
M[m].set("container", null);
// TODO: no destroy function in levelmeter at this point?
//this.meters[m].destroy();
M = M.splice(m, 1);
}
TK.MultiMeter = TK.class({
/**
* TK.MultiMeter is a collection of {@link TK.LevelMeter}s to show levels of channels
* containing multiple audio streams. It offers all options of {@link TK.LevelMeter} and
* {@link TK.MeterBase} which are passed to all instantiated level meters.
*
* @class TK.MultiMeter
*
* @extends TK.Container
*
* @param {Object} [options={ }] - An object containing initial options.
*
* @property {Number} [options.count=2] - The amount of level meters.
* @property {String} [options.title=""] - The title of the multi meter. Set to `false` to hide the title from the DOM.
* @property {Array<String>} [options.titles=["L", "R"]] - An Array containing titles for the level meters. Their order is the same as the meters.
* @property {Array<Number>} [options.values=[]] - An Array containing values for the level meters. Their order is the same as the meters.
* @property {Array<Number>} [options.labels=[]] - An Array containing label values for the level meters. Their order is the same as the meters.
* @property {Array<Boolean>} [options.clips=[]] - An Array containing clippings for the level meters. Their order is the same as the meters.
* @property {Array<Number>} [options.peaks=[]] - An Array containing peak values for the level meters. Their order is the same as the meters.
* @property {Array<Number>} [options.tops=[]] - An Array containing values for top for the level meters. Their order is the same as the meters.
* @property {Array<Number>} [options.bottoms=[]] - An Array containing values for bottom for the level meters. Their order is the same as the meters.
*/
_class: "MultiMeter",
Extends: TK.Container,
/* TODO: The following sucks cause we need to maintain it according to
LevelMeters and MeterBases options. */
_options: Object.assign(Object.create(TK.Container.prototype._options), {
count: "int",
title: "boolean|string",
titles: "array",
layout: "string",
show_scale: "boolean",
}),
options: {
count: 2,
title: false,
titles: ["L", "R"],
layout: "left",
show_scale: true,
},
initialize: function (options) {
TK.Container.prototype.initialize.call(this, options, true);
/**
* @member {HTMLDivElement} TK.MultiMeter#element - The main DIV container.
* Has class <code>toolkit-multi-meter</code>.
*/
TK.add_class(this.element, "toolkit-multi-meter");
this.meters = [];
var O = this.options;
},
redraw: function () {
var O = this.options;
var I = this.invalid;
var E = this.element;
var M = this.meters;
if (I.count) {
while (M.length > O.count)
remove_meter.call(this, M[M.length-1]);
while (M.length < O.count)
add_meter.call(this, O);
E.setAttribute("class", E.getAttribute("class").replace(/toolkit-count-[0-9]*/g, ""));
E.setAttribute("class", E.getAttribute("class").replace(/ +/g, " "));
TK.add_class(E, "toolkit-count-" + O.count);
}
if (I.layout || I.count) {
I.count = I.layout = false;
TK.remove_class(E, "toolkit-vertical", "toolkit-horizontal", "toolkit-left",
"toolkit-right", "toolkit-top", "toolkit-bottom");
switch (O.layout) {
case "left":
TK.add_class(E, "toolkit-vertical", "toolkit-left");
break;
case "right":
TK.add_class(E, "toolkit-vertical", "toolkit-right");
break;
case "top":
TK.add_class(E, "toolkit-horizontal", "toolkit-top");
break;
case "bottom":
TK.add_class(E, "toolkit-horizontal", "toolkit-bottom");
break;
default:
throw("unsupported layout");
}
switch (O.layout) {
case "top":
case "left":
for (var i = 0; i < M.length - 1; i++)
M[i].set("show_scale", false);
if (M.length)
M[this.meters.length - 1].set("show_scale", O.show_scale);
break;
case "bottom":
case "right":
for (var i = 0; i < M.length; i++)
M[i].set("show_scale", false);
if (M.length)
M[0].set("show_scale", O.show_scale);
break;
}
}
TK.Container.prototype.redraw.call(this);
},
});
/**
* @member {HTMLDivElement} TK.MultiMeter#title - The {@link TK.Label} widget displaying the meters title.
*/
TK.ChildWidget(TK.MultiMeter, "title", {
create: TK.Label,
show: false,
option: "title",
default_options: { "class" : "toolkit-title" },
map_options: { "title" : "label" },
toggle_class: true,
});
/*
* This could be moved into TK.ChildWidgets(),
* which could in similar ways be used in the buttonarray,
* pager, etc.
*
*/
var mapped_options = {
titles: "title",
layout: "layout",
};
function map_child_option_simple(value, key) {
var M = this.meters, i;
for (i = 0; i < M.length; i++) M[i].set(key, value);
}
function map_child_option(value, key) {
var M = this.meters, i;
if (Array.isArray(value)) {
for (i = 0; i < M.length && i < value.length; i++) M[i].set(key, value[i]);
} else {
for (i = 0; i < M.length; i++) M[i].set(key, value);
}
}
TK.add_static_event(TK.MultiMeter, "set_titles", function(value, key) {
map_child_option.call(this, value, "title");
});
for (var key in TK.object_sub(TK.LevelMeter.prototype._options, TK.Container.prototype._options)) {
if (TK.MultiMeter.prototype._options[key]) continue;
var type = TK.LevelMeter.prototype._options[key];
if (type.search("array") !== -1) {
TK.MultiMeter.prototype._options[key] = type;
mapped_options[key] = key;
TK.add_static_event(TK.MultiMeter, "set_"+key, map_child_option_simple);
} else {
TK.MultiMeter.prototype._options[key] = "array|"+type;
mapped_options[key] = key;
TK.add_static_event(TK.MultiMeter, "set_"+key, map_child_option);
}
if (key in TK.LevelMeter.prototype.options)
TK.MultiMeter.prototype.options[key] = TK.LevelMeter.prototype.options[key];
}
function extract_child_options(O, i) {
var o = {}, value, type;
for (var key in mapped_options) {
var ckey = mapped_options[key];
if (!O.hasOwnProperty(key)) continue;
value = O[key];
type = TK.LevelMeter.prototype._options[key] || "";
if (Array.isArray(value) && type.search("array") === -1) {
if (i < value.length) o[ckey] = value[i];
} else {
o[ckey] = value;
}
}
return o;
}
})(this, this.TK);