mirror of
https://github.com/Karaka-Management/jsOMS.git
synced 2026-01-11 09:58:39 +00:00
599 lines
14 KiB
JavaScript
599 lines
14 KiB
JavaScript
import { Input } from '../UI/Component/Input.js';
|
|
|
|
/**
|
|
* Form view.
|
|
*
|
|
* The form view contains a single form and it's data elements. Form actions are no longer handled by
|
|
* the browser but through this view. The view also provides additional functionality for non-default
|
|
* form elements such as canvas etc.
|
|
*
|
|
* @copyright Dennis Eichhorn
|
|
* @license OMS License 1.0
|
|
* @version 1.0.0
|
|
* @since 1.0.0
|
|
*
|
|
* @todo expand this so any element can become a "form" e.g. table, div etc.
|
|
* Simply add data-method, data-action
|
|
* Expand the getData() and some other function to consider this
|
|
*/
|
|
export class FormView {
|
|
/**
|
|
* @constructor
|
|
*
|
|
* @param {string} id Form id
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
constructor (id)
|
|
{
|
|
this.id = id;
|
|
|
|
this.initializeMembers();
|
|
this.bind();
|
|
|
|
this.success = null;
|
|
this.finally = null;
|
|
this.lastSubmit = 0;
|
|
};
|
|
|
|
/**
|
|
* Initialize members
|
|
*
|
|
* Pulled out since this is used in a cleanup process
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
initializeMembers ()
|
|
{
|
|
this.submitInjects = [];
|
|
this.method = 'POST';
|
|
this.action = '';
|
|
};
|
|
|
|
/**
|
|
* Get method
|
|
*
|
|
* @return {string}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getMethod ()
|
|
{
|
|
return this.method;
|
|
};
|
|
|
|
/**
|
|
* Get action
|
|
*
|
|
* @return {string}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getAction ()
|
|
{
|
|
return this.action;
|
|
};
|
|
|
|
/**
|
|
* Get time of last submit
|
|
*
|
|
* @return {int}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getLastSubmit ()
|
|
{
|
|
return this.lastSubmit;
|
|
};
|
|
|
|
/**
|
|
* Update last submit time
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
updateLastSubmit ()
|
|
{
|
|
this.lastSubmit = Math.floor(Date.now());
|
|
};
|
|
|
|
/**
|
|
* Get submit elements
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getSubmit ()
|
|
{
|
|
// todo: question, exclude save/remove button? maybe not because they also submit data right?
|
|
return document.querySelectorAll(
|
|
'#' + this.id + ' input[type=submit], '
|
|
+ 'button[form=' + this.id + '][type=submit], '
|
|
+ '#' + this.id + ' button[type=submit], '
|
|
+ '.submit[data-form=' + this.id + '], '
|
|
+ '#' + this.id + ' .submit'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get submit elements
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getImagePreviews() {
|
|
// todo: question, exclude save/remove button? maybe not because they also submit data right?
|
|
return document.querySelectorAll(
|
|
'#' + this.id + ' input[type=file].preview'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get edit elements
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getUpdate ()
|
|
{
|
|
return document.querySelectorAll(
|
|
'button[form=' + this.id + '].update, '
|
|
+ '.update[data-form=' + this.id + '], '
|
|
+ '#' + this.id + ' .update'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get save elements
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getSave ()
|
|
{
|
|
return document.querySelectorAll(
|
|
'button[form=' + this.id + '].save, '
|
|
+ '.save[data-form=' + this.id + '], '
|
|
+ '#' + this.id + ' .save'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get save elements
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getCancel ()
|
|
{
|
|
return document.querySelectorAll(
|
|
'button[form=' + this.id + '].cancel, '
|
|
+ '.cancel[data-form=' + this.id + '], '
|
|
+ '#' + this.id + ' .cancel'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get remove buttons
|
|
*
|
|
* @return {NodeListOf<any>}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getRemove ()
|
|
{
|
|
return document.querySelectorAll(
|
|
'button[form=' + this.id + '].remove, '
|
|
+ '.remove[data-form=' + this.id + '], '
|
|
+ '#' + this.id + ' .remove'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get remove buttons
|
|
*
|
|
* @return {NodeListOf<any>}
|
|
*
|
|
* @since 1.0.0
|
|
* @todo isn't this the same as submit in some cases? form below table?
|
|
*/
|
|
getAdd ()
|
|
{
|
|
return document.querySelectorAll(
|
|
'button[form=' + this.id + '].add, '
|
|
+ '.add[data-form=' + this.id + '], '
|
|
+ '#' + this.id + ' .add'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get success callback
|
|
*
|
|
* @return {callback}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getSuccess ()
|
|
{
|
|
return this.success;
|
|
};
|
|
|
|
/**
|
|
* Set success callback
|
|
*
|
|
* @param {callback} callback Callback
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
setSuccess (callback)
|
|
{
|
|
this.success = callback;
|
|
};
|
|
|
|
/**
|
|
* Get finally callback
|
|
*
|
|
* @return {callback}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getFinally ()
|
|
{
|
|
return this.finally;
|
|
};
|
|
|
|
/**
|
|
* Set finally callback
|
|
*
|
|
* @param {callback} callback Callback
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
setFinally(callback) {
|
|
this.finally = callback;
|
|
};
|
|
|
|
/**
|
|
* Inject submit with post callback
|
|
*
|
|
* @param {callback} callback Callback
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
injectSubmit (callback)
|
|
{
|
|
this.submitInjects.push(callback);
|
|
};
|
|
|
|
/**
|
|
* Get form elements
|
|
*
|
|
* @return {Array}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getFormElements ()
|
|
{
|
|
const form = document.getElementById(this.id);
|
|
|
|
if (!form) {
|
|
return [];
|
|
}
|
|
|
|
const selects = form.getElementsByTagName('select'),
|
|
textareas = form.getElementsByTagName('textarea'),
|
|
inputs = [].slice.call(form.getElementsByTagName('input')),
|
|
buttons = form.getElementsByTagName('button'),
|
|
canvas = form.getElementsByTagName('canvas'),
|
|
external = [].slice.call(document.querySelectorAll(':not(#' + this.id + ') [form=' + this.id + ']')),
|
|
special = form.querySelectorAll('[data-name]'),
|
|
specialExt = document.querySelectorAll('form:not(#' + this.id + ') [data-form=' + this.id + '] [data-name]'),
|
|
inputLength = inputs.length,
|
|
externalLength = external.length;
|
|
|
|
// todo: handle trigger element. check which element triggered the submit and pass it's name+value
|
|
// the reason for this is, there may be multiple buttons in a form which trigger a send
|
|
// sometimes even a checkbox or drop down could trigger a send
|
|
// Maybe it makes sense to do this however at a different place e.g. the actual data submit
|
|
|
|
for (let i = 0; i < inputLength; ++i) {
|
|
if ((inputs[i].type === 'checkbox' || inputs[i].type === 'radio') && !inputs[i].checked) {
|
|
delete inputs[i];
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < externalLength; ++i) {
|
|
if ((external[i].type === 'checkbox' || external[i].type === 'radio') && !external[i].checked) {
|
|
delete external[i];
|
|
}
|
|
}
|
|
|
|
return Array.prototype.slice.call(inputs).concat(
|
|
Array.prototype.slice.call(selects),
|
|
Array.prototype.slice.call(textareas),
|
|
Array.prototype.slice.call(buttons),
|
|
Array.prototype.slice.call(external),
|
|
Array.prototype.slice.call(special),
|
|
Array.prototype.slice.call(specialExt),
|
|
Array.prototype.slice.call(canvas)
|
|
).filter(function(val) { return val; });
|
|
};
|
|
|
|
/**
|
|
* Get unique form elements
|
|
*
|
|
* @param {Array} arr Form element array
|
|
*
|
|
* @return {Array}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getUniqueFormElements (arr)
|
|
{
|
|
let seen = {};
|
|
|
|
return arr.filter(function(item) {
|
|
return seen.hasOwnProperty(item.name) ? false : (seen[item.name] = true);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Get form data
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getData ()
|
|
{
|
|
const data = {},
|
|
elements = this.getFormElements(),
|
|
length = elements.length;
|
|
|
|
let value = null;
|
|
|
|
for (let i = 0; i < length; ++i) {
|
|
if (elements[i].tagName.toLowerCase() === 'canvas') {
|
|
value = elements[i].toDataURL('image/png');
|
|
} else {
|
|
if (typeof elements[i].value !== 'undefined') {
|
|
value = elements[i].value;
|
|
} else if (typeof elements[i].getAttribute('data-value') !== 'undefined') {
|
|
value = elements[i].getAttribute('data-value');
|
|
}
|
|
}
|
|
|
|
const id = FormView.getElementId(elements[i]);
|
|
if (id === null) {
|
|
continue;
|
|
}
|
|
|
|
// handle array data (e.g. table rows with same name)
|
|
if (data.hasOwnProperty(id)) {
|
|
if (data[id].constructor !== Array) {
|
|
data[id] = [data[id]];
|
|
}
|
|
|
|
data[id].push(value);
|
|
} else {
|
|
data[id] = value;
|
|
}
|
|
}
|
|
|
|
// Create FormData
|
|
/* todo: implement once we know how to handle this in the backend/php
|
|
const formData = new FormData(),
|
|
dataLength = data.length;
|
|
|
|
for (let key in data) {
|
|
if (data.hasOwnProperty(key)) {
|
|
formData.append(key, data[key].constructor === Array ? JSON.stringify(data[key]) : data[key]);
|
|
}
|
|
} */
|
|
|
|
return data;
|
|
};
|
|
|
|
/**
|
|
* Get form id
|
|
*
|
|
* @return {string}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getId ()
|
|
{
|
|
return this.id;
|
|
};
|
|
|
|
/**
|
|
* Validate form
|
|
*
|
|
* @return {boolean}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
isValid ()
|
|
{
|
|
const elements = this.getFormElements(),
|
|
length = elements.length;
|
|
|
|
try {
|
|
for (let i = 0; i < length; ++i) {
|
|
if ((elements[i].required && elements[i].value === '')
|
|
|| (typeof elements[i].pattern !== 'undefined'
|
|
&& elements[i].pattern !== ''
|
|
&& !(new RegExp(elements[i].pattern)).test(elements[i].value))
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
jsOMS.Log.Logger.instance.error(e);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Get form element
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getElement ()
|
|
{
|
|
return document.getElementById(this.getId());
|
|
};
|
|
|
|
/**
|
|
* Get form element id
|
|
*
|
|
* @return {string}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
static getElementId (e)
|
|
{
|
|
if (e.getAttribute('name') !== null) {
|
|
return e.getAttribute('name');
|
|
} else if (e.getAttribute('id') !== null) {
|
|
return e.getAttribute('id');
|
|
} else if (e.getAttribute('data-name') !== null) {
|
|
return e.getAttribute('data-name');
|
|
} else if (e.getAttribute('type') !== null) {
|
|
return e.getAttribute('type');
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Get submit injects
|
|
*
|
|
* @return {Object}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
getSubmitInjects ()
|
|
{
|
|
return this.submitInjects;
|
|
};
|
|
|
|
/**
|
|
* Bind form
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
* @todo: check bind functionality maybe remove!!!
|
|
*/
|
|
bind ()
|
|
{
|
|
this.clean();
|
|
|
|
const e = document.getElementById(this.id);
|
|
|
|
if (!e) {
|
|
return;
|
|
}
|
|
|
|
if (typeof e.attributes['method'] !== 'undefined') {
|
|
this.method = e.attributes['method'].value;
|
|
} else if (typeof e.attributes['data-method'] !== 'undefined') {
|
|
this.method = e.attributes['data-method'].value;
|
|
} else {
|
|
this.method = 'EMPTY';
|
|
}
|
|
|
|
if (typeof e.attributes['action'] !== 'undefined') {
|
|
this.action = e.attributes['action'].value;
|
|
} else if (typeof e.attributes['data-uri'] !== 'undefined') {
|
|
this.action = e.attributes['data-uri'].value;
|
|
} else {
|
|
this.action = 'EMPTY';
|
|
}
|
|
|
|
const elements = this.getFormElements(),
|
|
length = elements.length;
|
|
|
|
for (let i = 0; i < length; ++i) {
|
|
switch (elements[i].tagName.toLowerCase()) {
|
|
case 'input':
|
|
Input.bindElement(elements[i]);
|
|
break;
|
|
case 'select':
|
|
//this.bindSelect(elements[i]);
|
|
break;
|
|
case 'textarea':
|
|
//this.bindTextarea(elements[i]);
|
|
break;
|
|
case 'button':
|
|
//this.bindButton(elements[i]);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Unbind form
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
* @todo: check unbind functionality maybe remove = everything!!!
|
|
*/
|
|
unbind ()
|
|
{
|
|
const elements = this.getFormElements(),
|
|
length = elements.length;
|
|
|
|
for (let i = 0; i < length; ++i) {
|
|
switch (elements[i].tagName) {
|
|
case 'input':
|
|
Input.unbind(elements[i]);
|
|
break;
|
|
case 'select':
|
|
this.bindSelect(elements[i]);
|
|
break;
|
|
case 'textarea':
|
|
this.bindTextarea(elements[i]);
|
|
break;
|
|
case 'button':
|
|
this.bindButton(elements[i]);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Clean form
|
|
*
|
|
* @return {void}
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
clean ()
|
|
{
|
|
this.unbind();
|
|
this.initializeMembers();
|
|
};
|
|
};
|