src/dom/dom.js
import * as utils from '../utils/Utils';
/**
* Parent class for handling DOM elements.
* @private
* @class DOMElement
*/
export default class DOMElement {
/**
* @constructor
* @param {string} id HTML Id of the element.
*/
constructor(id) {
/**
* jQuery reference to the element.
* @type {object}
*/
this.obj = $(utils.fixId(id));
/**
* Stores if the element is enabled or disabled.
* @type {boolean}
*/
this.isEnabled = !this.obj.prop('disabled');
}
/**
* Enable or disable the DOM element.
* @param {boolean} state Desired state of the element.
*/
enabled(state) {
if (this.isEnabled !== state) {
this.isEnabled = state;
this.obj.prop('disabled', !this.isEnabled);
}
}
}
/**
* Class used to handle a checkbox (<input type="checkbox">).
* @public
* @class Option
* @example
* // Given the following HTML checkbox:
* // <input type="checkbox" id="showPoints">
* var showPoints = new Option("showPoints", function(val) {
* console.log(val);
* });
*/
export class Option extends DOMElement {
/**
* @constructor
* @param {string} id HTML id of the checkbox.
* @param {function} onClick Callback function for when the checkbox changes state.
*/
constructor(id, onClick) {
// Extend DOMElement.
super(id);
/**
* Callback function for when the checkbox changes state. The new state of the
* checkbox is passed to the callback function as a parameter.
* @type {function}
*/
this.onClick = onClick;
/**
* State of the option.
* @type {boolean}
*/
this.value = this.obj.prop('checked');
// Set the callback.
this.obj.on('click', () => {
this.value = this.obj.prop('checked');
if (utils.isFunction(this.onClick))
this.onClick(this.obj.prop('checked'));
});
}
}
/**
* Class used to handle a button (<button>).
* @public
* @class Button
* @example
* // Given the following HTML button:
* // <button type="button" id="start"></button>
* var start = new Button("start", function() {
* console.log("clicked");
* });
*/
export class Button extends DOMElement {
/**
* @constructor
* @param {string} id HTML id of the button.
* @param {function} onClick Callback function for when button is pressed.
*/
constructor(id, onClick) {
// Extend DOMElement.
super(id);
/**
* Callback function for when the button gets pressed.
* @type {function}
*/
this.onClick = onClick;
// Set the callback.
this.obj.on('click', () => {
if (utils.isFunction(this.onClick)) this.onClick();
});
}
}
/**
* Class used to handle an Input (<input>).
* @public
* @class Input
* @example
* // Given the following HTML input element:
* // <input id="value"></button>
* var value = new Input("value", function(val) {
* console.log(val);
* });
*/
export class Input extends DOMElement {
/**
* @constructor
* @param {string} id HTML id of the input.
* @param {function} onChange Callback function for when input value changes.
*/
constructor(id, onChange, opts) {
// Extend DOMElement.
super(id);
/**
* Callback function for when the input value changes.
* @type {function}
*/
this.onChange = onChange;
/**
* Callback function for when the focus is lost.
* @type {function}
*/
this.onFocusout = undefined;
/**
* Defines if the parsed value must be casted to a number, otherwise the input is invalid.
* @type {boolean}
*/
this.isNumber = false;
/**
* Flag for remembering if the input is invalid.
* @type {boolean}
*/
this.isInvalid = false;
/**
* Stores the parsed value of the input.
* @type {(number|string)}
*/
this.value = '';
/**
* Default value for the input.
* @type {(number|string)}
*/
this.default = '';
// Apply user settings.
utils.loadOptions(this, opts);
this.set(this.default);
// Callback whenever the input is changed by the user.
this.obj.on('input', () => {
const rawValue = this.obj.val();
if (this.isNumber) {
if (rawValue === '') {
this.value = this.default;
this.invalid(false);
} else {
const parsedValue = parseFloat(rawValue);
this.invalid(isNaN(parsedValue));
if (!isNaN(parsedValue)) this.value = parsedValue;
}
} else {
this.value = rawValue;
}
if (utils.isFunction(this.onChange)) this.onChange(this.value);
});
// Watch for the focusout event.
// After the user leaves the input, set the value to the parsed one.
this.obj.on('focusout', () => {
if (this.isNumber) {
this.set(this.value);
if (utils.isFunction(this.onChange)) this.onChange(this.value);
if (utils.isFunction(this.onFocusout)) this.onFocusout();
}
});
// When the user presses enter, focusout.
this.obj.on('keyup', e => {
if (e.keyCode === 13) {
this.obj.blur();
}
});
}
set(value) {
this.value = value || this.default;
this.obj.val(this.value);
this.invalid(false);
}
invalid(state) {
if (this.isInvalid !== state) {
this.isInvalid = state;
if (this.isInvalid) {
this.obj.addClass('invalid');
} else {
this.obj.removeClass('invalid');
}
}
}
}
/**
* Class used to handle select elements (<select>).
* @public
* @class Select
* @example
* // Given the following HTML select element:
* // <select id="fx" name="fx">
* // <option value="1">Option 1</option>
* // <option value="2">Option 2</option>
* // <option value="3">Option 3</option>
* // </select>
* var fx = new Select("fx", function(val) {
* console.log(val);
* });
*/
export class Select extends DOMElement {
/**
* @constructor
* @param {string} id HTML id of the select element.
* @param {function} onChange Callback function for when the selected option changes.
*/
constructor(id, onChange) {
// Extend DOMElement.
super(id);
/**
* Callback function for when the selected option changes. The value of the selected
* option is passed to the callback function as a parameter.
* @type {function}
*/
this.onChange = onChange;
/**
* Current option selected.
* @type {number}
*/
this.value = 0;
// Set the callback.
this.obj.on('change', () => {
this.value = this.obj.find(':selected').val();
if (utils.isFunction(this.onChange)) this.onChange(this.value);
});
}
/**
* Sets the options for the select element.
* @param {Object} options Object that will define the options in the select element.
* @param {Number} select Key of the option to select.
*/
setOptions(options, select) {
// Remove old options.
this.obj.empty();
// Add new options.
for (let [key, value] of Object.entries(options)) {
this.obj.append(
$('<option></option>')
.attr('value', value)
.attr('selected', value === select)
.text(key)
);
}
this.obj.trigger('change');
}
}
/**
* Class used to handle a set of multiple radio buttons (<input type="radio">).
* @public
* @class Options
* @example
* // Given the following set of HTML radio buttons:
* // <input type="radio" name="graph_type" value="pos">
* // <input type="radio" name="graph_type" value="vel">
* // <input type="radio" name="graph_type" value="accel">
* var graph_type = new Options("graph_type", function(val) {
* console.log(val);
* });
*/
export class Options {
/**
* @constructor
* @param {string} name Name of the radio buttons.
* @param {function} onChange Callback function for when any of the radio buttons changes state.
*/
constructor(name, onChange) {
/**
* Sets the id of the input using the name as an argument. This id is then used to obtain the
* jQuery object of the input.
* @type {string}
*/
this.name = `input[name="${name}"]`;
/**
* jQuery reference to the input.
* @type {object}
*/
this.obj = $(this.name);
/**
* Value of the currently selected radio option.
* @type {string}
*/
this.value = $(`${this.name}:checked`).val();
/**
* Callback function for when a radio button is selected. The value of the selected
* radio button is passed to the callback function as a parameter.
* @type {function}
*/
this.onChange = onChange;
// Set the callback.
this.obj.change(() => {
this.value = $(`${this.name}:checked`).val();
if (utils.isFunction(this.onChange)) {
this.onChange(this.value);
}
});
}
/**
* Selects a radio button using its value.
* @param {string} value Value of the radio button to select.
*/
select(value) {
this.value = value;
// prettier-ignore
this.obj.each(function () {
if ($(this).val() === value) {
// prettier-ignore
$(this).parent().addClass('active');
$(this).prop('checked', true);
} else {
// prettier-ignore
$(this).parent().removeClass('active');
$(this).prop('checked', false);
}
});
}
/**
* Enables or disables the radio options.
* @param {boolean} state Desired state of the element.
*/
enabled(state) {
if (state) {
this.obj.parent().removeClass('disabled');
} else {
this.obj.parent().addClass('disabled');
}
}
}