"use strict";
/**
* An abstract class containing some separate potentionally universal methods used in the DT library.
* (It is not necessary for the class to be abstract but there is no point in instantiating it.)
* @abstract
*/
class CommonFormFunctions {
/**
* Create CommonFormFunctions.
*/
constructor() {
/* http://stackoverflow.com/a/30560792 */
if(new.target === CommonFormMethods) {
throw new TypeError("Cannot construct CommonFormFunctions instance, abstract class");
}
return this;
}
/**
* Strip a DOM element of all its children.
* @param {dom} element - A dom element to be emptied.
* @return {number} - A number of child elements deleted.
*/
static empty(element) {
let counter = 0;
while(element.hasChildNodes()) {
element.removeChild(element.lastChild);
counter++;
}
return counter;
}
/**
* Get DOM element's top, right, bottom and left borders, optionally counting in margins.
* @param {dom} element - A dom element to have the borders measured.
* @return {(Object|boolean)} - An object containing all DOM element positions, with keys: top, left, right, bottom - or false if no element was passed.
*/
static getElementPosition(element, countMargins) {
if(element) {
/* Get element's computed style property */
let getStyle = function(property) {
return window.getComputedStyle(element, null).getPropertyValue(property);
}
/* Get object with element positions {top, right, bottom, left} */
let getPosition = function(element) {
/* https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect */
return element.getBoundingClientRect();
}
let marginLeftRight = 0;
let marginTopBottom = 0;
if(countMargins === true) {
/* Compute Element margins */
marginLeftRight = parseFloat(getStyle("margin-left")) + parseFloat(getStyle("margin-right"));
marginTopBottom = parseFloat(getStyle("margin-top")) + parseFloat(getStyle("margin-bottom"));
}
return {
top: getPosition(element).top - marginLeftRight/2,
left: getPosition(element).left - marginTopBottom/2,
right: getPosition(element).right + marginLeftRight/2,
bottom: getPosition(element).bottom + marginTopBottom/2
};
}
return false;
}
/**
* Get DOM element's top, right, bottom and left borders, counting it its margins, width and height.
* Requires jQuery to run!
* @param {dom} element - A dom element to have the borders measured.
* @return {(Object|boolean)} - An object containing all DOM element positions, with keys: top, left, right, bottom - or false if no element was passed.
*/
static getElementPositionUsingjQuery(element) {
if(element) {
element = $(element);
let marginLeftRight = element.outerWidth(true) - element.width();
let marginTopBottom = element.outerHeight(true) - element.height();
return {
top: element.position().top + marginLeftRight/2,
left: element.position().left + marginTopBottom/2,
right: element.position().left + element.innerWidth() + marginLeftRight/2,
bottom: element.position().top + element.innerHeight() + marginTopBottom/2
};
}
return false;
}
/**
* Append inline style to a DOM object
* @param {element} element - A dom element to be emptied.
* @param {string} style - A style to be appended.
* @return {boolean} - Return true if append was possible or false if element or syle is not defined.
*/
static appendStyle(element, style) {
if(element && style) {
let originalStyle = element.getAttribute("style") ? element.getAttribute("style") : "";
element.setAttribute("style", originalStyle + style);
return true;
}
return false;
}
/**
* Call to trigger an event on a DOM element with passed name and optionally data.
* @param {element} eventElement - A dom element for the event to be called on.
* @param {string} eventName - A name for the to-be-called event.
* @param {(string|Object)} [eventData] - An optional attribute containing string or object with additional data to be passed to the event.
* @return {boolean} - Return true if event creation was sccessful or false ig no eventElement was passed.
*/
static triggerEvent(eventElement, eventName, eventData) {
eventName = eventName || "generalEvent";
if(eventElement) {
let event = null;
if(eventData) {
event = new CustomEvent(eventName, {detail: eventData});
}
else {
event = new Event(eventName);
}
eventElement.dispatchEvent(event);
return true;
}
return false;
}
/**
* Confirm passed input type by returning it or default to the text.
* Supports only the ones defined in the HTML standard.
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input|More about input tag on Mozilla Developer Network}
* @param {string} type - Input type to be checked.
* @return {string} - Return the same input type passed or default to "text" if there was no match.
*/
static filterProperInputTypes(type) {
type = type || "";
switch(type) {
case "password":
return "password";
case "radio":
return "radio";
case "checkbox":
return "checkbox";
case "number":
return "number";
case "date":
return "date";
case "color":
return "color";
case "range":
return "range";
case "month":
return "month";
case "week":
return "week";
case "time":
return "time";
case "datetime-local":
return "datetime-local";
case "email":
return "email";
case "search":
return "search";
case "tel":
return "tel";
case "url":
return "url";
case "button":
return "button";
case "reset":
return "reset";
case "submit":
return "submit";
default:
return "text";
}
}
/**
* Join two strings together.
* @param {string} stringA - First string.
* @param {string} stringB - Second string.
* @param {string} [delimiter] - A delimiter to divide the two string parts. Default is comma.
* @return {string} - Return the two strings joined, using the default or passed delimiter.
*/
static joinStrings(stringA, stringB, delimiter) {
if(typeof delimiter === undefined) {
delimiter = ",";
}
let finalString = "";
if(stringA) {
finalString += stringA;
if(stringB) {
if(delimiter) {
finalString += delimiter;
}
finalString += stringB;
}
}
return finalString;
}
/**
* Post passed data to a specified url
* @param {string} url - Specify URL to submit the data to.
* @param {string} data - Data to be submitted.
* @return {Promise} - A promise for the XMLHttp submit request
*/
static postData(url, data) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState == 4) {
if(xhr.status == 200) {
resolve(xhr.response);
}
else {
reject(Error(xhr.statusText));
}
}
};
xhr.onerror = function() {
reject(Error("Network Error"));
};
xhr.open("POST", url, true);
xhr.send(data);
});
}
/**
* A generic function to make items draggable using any plugin defined.
* @param {Object} data - Define draggable behaviour.
* @param {dom} data.item - Defines the element which should be draggable.
* @param {dom} [data.container] - Defines the outer element in which the dragging should be contained.
* @param {function} data.plugin - Defines a function that should take care of draggable items.
* @param {function} [data.onPosChange] - Defines a function that should be executed on draggable item position change.
* @param {function} [data.onEnd] - Defines a function that should be executed once draggable event ends.
* @return {mixed} - Returns plugin function result.
*/
static draggableSnippet(data) {
data = data || {};
if(!data.item || !data.plugin) {
throw "draggableSnippet must have draggable item and plugin defined!";
}
return data.plugin({
item: data.item,
container: data.container || null,
plugin: data.plugin,
onPosChange: data.onPosChange || null,
onEnd: data.onEnd || null
});
}
/**
* jQuery draggable plugin, requires jQuery Draggable
* @see {@link https://jqueryui.com/draggable/|jQuery Draggable}
* @param {Object} data - Define draggable behaviour.
* @param {dom} data.item - Defines the element which should be draggable.
* @param {dom} [data.container] - Defines the outer element in which the dragging should be contained.
* @param {function} [data.onEnd] - Defines a function that should be executed once draggable event ends.
*/
static draggablePlugin_jQuery(data) {
data = data || {};
if(!data.item) {
throw "Plugin must have draggable item defined!";
}
if(typeof $ !== "function") {
throw "jQuery must be loaded prior to using draggable in the DT library!";
}
if(typeof $().draggable !== "function") {
throw "jQuery Draggable must be loaded prior to using draggable in the DT library!";
}
$(data.item).draggable({
containment: data.container || null,
stop: data.onEnd || null
});
}
/**
* jQuery draggable plugin, requires interact.js
* @see {@link http://interactjs.io/|interact.js}
* @param {Object} data - Define draggable behaviour.
* @param {dom} data.item - Defines the element which should be draggable.
* @param {dom} [data.container] - Defines the outer element in which the dragging should be contained.
* @param {function} [data.onPosChange] - Defines a function that should be executed on draggable item position change.
* @param {function} [data.onEnd] - Defines a function that should be executed once draggable event ends.
*/
static draggablePlugin_interact(data) {
data = data || {};
if(!data.item) {
throw "Plugin must have draggable item defined!";
}
if(typeof interact !== "function") {
throw "interact.js must be loaded prior to using draggable in the DT library!";
}
/* source: http://interactjs.io/ */
interact(data.item).draggable({
// keep the element within the area of it's parent
restrict: {
restriction: data.container || null,
endOnly: false,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
// call this function on every dragmove event
onmove: (event) => {
dragMoveListener(event);
if(typeof data.onPosChange === "function") {
data.onPosChange();
}
},
// call this function on every dragend event
onend: data.onEnd || null
});
function dragMoveListener(event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.webkitTransform =
target.style.transform = "translate(" + x + "px, " + y + "px)";
// update the posiion attributes
target.setAttribute("data-x", x);
target.setAttribute("data-y", y);
}
// this is used later in the resizing and gesture demos
window.dragMoveListener = dragMoveListener;
return this;
}
}