/*
* 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 { toggleClass, element } from './utils/dom.js';
import { defineRender } from './renderer.js';
export function addEvent(to, event, fun) {
const tmp = to[event];
if (!tmp) {
to[event] = fun;
} else if (Array.isArray(tmp)) {
to[event] = tmp.concat([fun]);
} else {
to[event] = [tmp, fun];
}
}
export function removeEvent(from, event, fun) {
const tmp = from[event];
if (!tmp) return;
if (Array.isArray(tmp)) {
from[event] = tmp.filter((f) => f !== fun);
if (tmp.length === 1) from[event] = tmp[0];
else if (tmp.length === 0) delete from[event];
} else if (tmp === fun) {
delete from[event];
}
}
function arrayify(x) {
if (!Array.isArray(x)) x = [x];
return x;
}
export function mergeStaticEvents(a, b) {
if (!a || !b) return Object.assign({}, a || b || {});
const result = Object.assign({}, a, b);
for (let event in a) {
if (!(event in b)) continue;
result[event] = arrayify(a[event]).concat(arrayify(b[event]));
}
return result;
}
export function addStaticEvent(w, event, fun) {
w.addStaticEvent(event, fun);
}
export function defineChildElement(widget, name, config) {
/**
* @function defineChildElement
*
* @description Creates a HTMLElement as a child for a widget. Is used to simplify
* widget definitions. E.g. the tiny marker used to display the back-end
* value is a simple DIV added using child element. The generic element
* is a DIV added to Widget.element with the class
* <code>.aux-[name]</code>. Default creating and adding can be
* overwritten with custom callback functions.
*
* @param {Widget} widget - The {@link Widget} to add the child element to.
* @param {string} name - The identifier of the element. It will be prefixed
* by an underscore <code>Widget["_" + config.name]</code>.
* @param {object} config - The configuration of the child element.
*
* @param {boolean} [config.show=false] - Show/hide the child element on initialization.
* @param {string} [config.option="show_"+config.name] - A custom option of the parent widget
* to determine the visibility of the child element. If this is
* <code>null</code>, <code>Widget.options["show_"+ config.name]</code>
* is used to toggle its visibility. The child element is visible, if
* this options is <code>!== false</code>.
* @param {function} [config.display_check] - A function overriding the
* generic <code>show_option</code> behavior. If set, this function
* is called with the value of <code>show_option</code> as argument
* as soon as it gets set and is supposed to return a boolean
* defining the visibility of the element.
* @param {function} [config.append] - A function overriding the generic
* append mechanism. If not <code>null</code>, this function is
* supposed to take care of adding the child element to the parent
* widget's DOM.
* @param {function} [config.create] - A function overriding the generic
* creation mechanism. If not <code>null</code>, this function is
* supposed to create and return a DOM element to be added to the
* parent widget.
* @param {boolean} [config.toggle_class=false] - Defines if the parent widget
* receives the class <code>.aux-has-[name]</code> as soon as
* the child element is shown.
* @param {array} [config.draw_options] - A list of options of the parent
* widget which are supposed to trigger a check if the element has to
* be added or removed.
* @param {function} [config.draw] - A function to be called on redraw.
*
*/
const show_option = config.option || 'show_' + name;
const index = '_' + name;
const display_check = config.display_check;
/* This is done to make sure that the object property is created
* inside of the constructor. Otherwise, if we add the widget later
* might be turned into a generic mapping.
*/
widget.addStaticEvent('initialize', function () {
this[index] = null;
});
/* trigger child element creation after initialization */
widget.addStaticEvent('initialize_children', function () {
this.set(show_option, this.options[show_option]);
});
/* clean up on destroy */
widget.addStaticEvent('destroy', function () {
if (this[index]) {
this[index].remove();
this[index] = null;
}
});
let append = config.append;
let create = config.create;
if (create === void 0)
create = function () {
return element('div', 'aux-' + name);
};
if (append === void 0)
append = function () {
this.element.appendChild(this[index]);
};
const dependency = config.dependency;
widget.addStaticEvent('set_' + show_option, function (value) {
let C = this[index];
const show = display_check ? display_check(value) : value !== false;
if (!show || show === !!C) return;
if (show && !C) {
C = create.call(this);
this[index] = C;
}
});
widget.addTask(
defineRender([show_option], function (value) {
const childElement = this[index];
const show = display_check ? display_check(value) : value !== false;
if (show) {
if (!childElement.parentNode) append.call(this, this.options);
} else if (childElement !== null) {
this[index] = null;
childElement.remove();
}
if (config.toggle_class)
toggleClass(this.element, 'aux-has-' + name, show);
this.triggerResize();
if (dependency) this.invalidate(dependency);
})
);
if (config.draw) {
let draw_options = config.draw_options;
if (!draw_options) draw_options = [show_option];
else draw_options = [show_option, ...draw_options];
// filter out options which are not unique
draw_options = draw_options.filter((name, i, a) => i === a.indexOf(name));
widget.addTask(
defineRender(draw_options, function (value) {
const show = display_check ? display_check(value) : value !== false;
if (show) config.draw.call(this, this.options);
})
);
}
if (!widget.hasOption(show_option)) {
widget.defineOption(show_option, 'boolean', !!config.show);
}
}