Went through todos

This commit is contained in:
Dennis Eichhorn 2024-05-02 22:54:40 +00:00
parent d3337d327d
commit b167ae5696
34 changed files with 588 additions and 372 deletions

View File

@ -239,6 +239,11 @@ export class Request
this.data = data;
};
addData(name, data)
{
this.data[name] = data;
};
/**
* Get request data.
*
@ -316,24 +321,17 @@ export class Request
// @question Consider to change to fetch
if (this.xhr.readyState !== 1) {
if (this.type === RequestType.FORM_DATA) {
// GET request doesn't allow body/payload. Therefor we have to put the data into the uri
if (this.method === RequestMethod.GET) {
let url = this.uri;
for (const pair of this.data.entries()) {
url += '&' + pair[0] + '=' + pair[1];
}
this.xhr.open(this.method, UriFactory.build(url));
} else {
this.xhr.open(this.method, UriFactory.build(this.uri));
let url = this.uri;
if (this.method === RequestMethod.GET) {
for (const pair of Object.entries(this.data)) {
url += '&' + pair[0] + '=' + pair[1];
}
} else {
console.log(UriFactory.build(this.uri));
this.xhr.open(this.method, UriFactory.build(this.uri));
url = this.uri;
}
this.xhr.open(this.method, UriFactory.build(url));
for (const p in this.requestHeader) {
if (Object.prototype.hasOwnProperty.call(this.requestHeader, p) && this.requestHeader[p] !== '') {
if (this.requestHeader[p] !== 'multipart/form-data') {

View File

@ -1,4 +1,4 @@
import { jsOMS } from '../../Utils/oLib.js';
import { Logger } from '../../Log/Logger.js';
/**
* @typedef {import('../Request/Request.js').Request} Request
*/
@ -72,7 +72,7 @@ export class ResponseManager
} else if (typeof this.messages[key] !== 'undefined') {
this.messages[key].null(data);
} else {
jsOMS.Log.Logger.instance.warning('Undefined type: ' + key);
Logger.instance.warning('Undefined type: ' + key);
}
};
};

View File

@ -21,5 +21,7 @@ export function domAddElement (action, callback, id)
e.appendChild.removeChild(i);
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -25,5 +25,7 @@ export function domClickAction (action, callback, id)
i.click();
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -22,5 +22,7 @@ export function datalistAppend (action, callback)
datalist.appendChild(option);
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -16,5 +16,7 @@ export function datalistClear (action, callback)
e.removeChild(e.firstChild);
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -18,5 +18,7 @@ export function focusAction (action, callback)
focus.focus();
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -29,5 +29,7 @@ export function formSubmitAction (action, callback, id)
formManager.submit(formManager.get(i.id));
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -20,5 +20,7 @@ export function hideAction (action, callback)
/** global: jsOMS */
jsOMS.addClass(hide, 'vh');
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -81,5 +81,7 @@ export function popupButtonAction (action, callback, id)
}
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -13,10 +13,16 @@ export function redirectMessage (action, callback, id)
{
setTimeout(function ()
{
const url = action.uri === '' ? '' : UriFactory.build(action.uri);
if (action.src) {
document.getElementById(action.src).src = UriFactory.build(action.uri);
document.getElementById(action.src).src = url;
} else {
window.location = UriFactory.build(action.uri);
if (url === window.location.href || url === '') {
document.location.reload();
} else {
window.location.href = url;
}
}
}, parseInt(action.delay));
};

View File

@ -1,30 +0,0 @@
import { UriFactory } from '../../../Uri/UriFactory.js';
/**
* Reload page.
*
* @param {Object} action Action data
* @param {function} callback Callback
* @param {string} id Action element
*
* @since 1.0.0
*/
export function reloadButtonAction (action, callback, id)
{
'use strict';
setTimeout(function () {
if (action.src) {
console.log(document.getElementById(action.src).hasAttribute('data-src'));
console.log(UriFactory.build(document.getElementById(action.src).getAttribute('data-src')));
document.getElementById(action.src).src = document.getElementById(action.src).hasAttribute('data-src')
? UriFactory.build(document.getElementById(action.src).getAttribute('data-src'))
: document.getElementById(action.src).src;
} else {
document.location.reload();
}
}, parseInt(action.delay));
callback();
};

View File

@ -48,5 +48,7 @@ export function removeButtonAction (action, callback, id)
}, 200);
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -26,5 +26,7 @@ export function domRemoveElement (action, callback, id)
e.parentElement.removeChild(e);
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -38,5 +38,7 @@ export function domRemoveValue (action, callback, id)
}
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -21,5 +21,7 @@ export function showAction (action, callback, id)
/** global: jsOMS */
jsOMS.removeClass(show, 'vh');
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -43,5 +43,7 @@ export function tableAppend (action, callback)
}
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -16,5 +16,7 @@ export function tableClear (action, callback)
e.removeChild(e.firstChild);
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -41,5 +41,7 @@ export function ifAction (action, callback, id)
}
}
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -16,5 +16,7 @@ export function preventEvent (action, callback, id)
jsOMS.preventAll(action.data);
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -16,11 +16,13 @@ export function logAction (action, callback, id)
window.omsApp.notifyManager.send(
new NotificationMessage(
action.data.status,
action.data.title,
action.data.message
action.data[0].status,
action.data[0].title,
action.data[0].message
), NotificationType.APP_NOTIFICATION
);
callback();
if (typeof callback === 'function') {
callback();
}
};

View File

@ -8,10 +8,14 @@ import { Request } from '../../../Message/Request/Request.js';
*
* @since 1.0.0
*/
export function requestAction (action, callback)
export function requestAction (action, callback, id)
{
'use strict';
if (action.uri === '') {
action.uri = document.getElementById(id).href;
}
/** global: jsOMS */
const request = new Request(action.uri, action.method, action.request_type);

113
UI/Component/CodeArea.js Normal file
View File

@ -0,0 +1,113 @@
import { jsOMS } from '../../Utils/oLib.js';
/**
* Code text area.
*
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @since 1.0.0
*/
export class CodeArea {
/**
* @constructor
*
* @param {string} id Form id
*
* @since 1.0.0
*/
constructor(e) {
const self = this;
this.div = e;
this.isMouseDown = false;
// LAYOUT (table 2 panels)
const table = document.createElement('table');
table.setAttribute('cellspacing', '0');
table.setAttribute('cellpadding', '0');
const tr = document.createElement('tr');
const td1 = document.createElement('td');
const td2 = document.createElement('td');
tr.appendChild(td1);
tr.appendChild(td2);
table.appendChild(tr);
this.ta = this.div.querySelector('.codeTextarea');
// TEXTAREA NUMBERS (Canvas)
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 500;
this.ta.canvasLines = canvas;
td1.appendChild(canvas);
td2.appendChild(this.ta);
this.div.appendChild(table);
this.ta.addEventListener('scroll', function (e) {
self.render();
});
this.ta.addEventListener('mousedown', function (e) {
self.isMouseDown = true;
});
this.ta.addEventListener('mouseup', function (e) {
self.isMouseDown = false;
self.render();
});
this.ta.addEventListener('mousemove', function (e) {
if (self.isMouseDown) {
self.render();
}
});
};
render () {
try {
const canvas = this.ta.canvasLines;
if (canvas.height != this.ta.clientHeight) {
canvas.height = this.ta.clientHeight; // on resize
}
const ctx = canvas.getContext('2d');
const fontSize = parseInt(window.getComputedStyle(this.ta).fontSize);
const lineHeight = parseInt(window.getComputedStyle(this.ta).lineHeight);
ctx.fillStyle = window.getComputedStyle(this.div).background;
ctx.fillRect(0, 0, 42, this.ta.scrollHeight + 1);
ctx.fillStyle = window.getComputedStyle(this.div).color;
ctx.font = window.getComputedStyle(this.ta).fontSize + ' monospace';
const startIndex = Math.floor(this.ta.scrollTop / lineHeight, 0);
const endIndex = startIndex + Math.ceil(this.ta.clientHeight / lineHeight, 0);
let ph = 0;
let text = '';
for (let i = startIndex; i < endIndex; i++) {
ph = fontSize - this.ta.scrollTop + i * lineHeight;
text = '' + (1 + i);
ctx.fillText(text, 40 - text.length * fontSize / 2, ph);
}
} catch (e) {
console.log(e.message);
}
};
};
jsOMS.ready(function ()
{
'use strict';
const textareas = document.querySelectorAll('.codeArea');
const length = textareas.length;
for (let i = 0; i < length; ++i) {
const textarea = new CodeArea(textareas[i]);
textarea.render();
}
});

View File

@ -1284,7 +1284,7 @@ export class Form
if (redirect !== null
&& (statusCode === 200 || statusCode === null)
) {
const redirectUrl = UriFactory.build(redirect, responseData)
const redirectUrl = UriFactory.build(redirect, responseData);
fetch(redirectUrl)
.then((response) => response.text())
.then((html) => {

View File

@ -1,139 +0,0 @@
import { Request } from '../../Message/Request/Request.js';
import { Response } from '../../Message/Response/Response.js';
import { RequestMethod } from '../../Message/Request/RequestMethod.js';
import { ResponseType } from '../../Message/Response/ResponseType.js';
/**
* Form manager class.
*
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @since 1.0.0
*/
export class Input
{
/**
* @constructor
*
* @since 1.0.0
*/
constructor ()
{
this.visObs = null;
};
/**
* Unbind input element
*
* @param {Element} input Input element
*
* @return {void}
*
* @since 1.0.0
*/
static unbind (input)
{
this.app.inputManager.getKeyboardManager().unbind(input);
/** global: changeBind */
// input.removeEventListener('change', changeBind, false);
};
/**
* Bind input element
*
* @param {Element} input Input element
*
* @return {void}
*
* @since 1.0.0
*/
static bindElement (input = null)
{
if (input === null) {
throw new Error('Input element required');
}
const type = input.type;
const removeContentButton = input.parentNode.querySelector('.close');
if (removeContentButton !== null
&& type !== 'submit' && type !== 'button') {
removeContentButton.addEventListener('click', function () {
input.value = '';
input.focus();
});
}
};
/**
* Add remote datalist options
*
* This only applies for datalists that have remote options
*
* @param {Element} input Input element
* @param {Element} datalist Datalist element
*
* @return {void}
*
* @since 1.0.0
*/
static addRemoteDatalistOptions (input, datalist)
{
this.clearDatalistOptions(datalist);
const request = new Request();
request.setData(input.value);
request.setType(ResponseType.JSON);
request.setUri(datalist.getAttribute('data-list-src'));
request.setMethod(RequestMethod.POST);
request.setRequestHeader('Content-Type', 'application/json');
request.setSuccess(function (xhr)
{
try {
const o = JSON.parse(xhr.response);
const response = new Response(o);
const responseLength = response.count();
let tempResponse = null;
for (let k = 0; k < responseLength; ++k) {
tempResponse = response.getByIndex(k);
let option = null;
const data = tempResponse.getData();
const length = data.length;
for (let i = 0; i < length; ++i) {
option = document.createElement('option');
option.value = tempResponse.value;
option.text = tempResponse.text;
datalist.appendChild(option);
}
}
} catch (exception) {
Logger.instance.error('Invalid JSON object: ' + xhr, 'FormManager');
}
});
request.send();
};
/**
* Remove all datalist options from datalist
*
* @param {Element} datalist Datalist element
*
* @return {void}
*
* @since 1.0.0
*/
static clearDatalistOptions (datalist)
{
const length = datalist.options.length;
for (let i = 0; i < length; ++i) {
datalist.remove(0);
}
};
};

View File

@ -0,0 +1,329 @@
import { jsOMS } from '../../Utils/oLib.js';
import { Request } from '../../Message/Request/Request.js';
/**
* @typedef {import('../../Event/EventManager.js').EventManager} EventManager
*/
/**
* Smart input class.
*
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @since 1.0.0
*/
export class SmartTextInput
{
/**
* @constructor
*
* @param {Element} e Element to bind
*
* @since 1.0.0
*/
constructor (e)
{
/** @type {string} id */
this.id = e.id;
/** @type {Element} e */
this.inputComponent = e;
this.inputField = this.inputComponent.getElementsByClassName('input-div')[0];
// @todo Implement. Then find all data-tpl-value and data-tpl-text which are the elements to fill
this.dataContainer = this.inputField.getAttribute('data-container') === ''
? this.inputComponent
: this.inputField.closest(this.inputField.getAttribute('data-container'));
this.dataList = this.inputComponent.getElementsByClassName('input-datalist')[0];
this.dataListBody = this.inputComponent.getElementsByClassName('input-datalist-body')[0];
this.dataTpl = document.getElementsByClassName('input-data-tpl')[0];
this.src = this.inputComponent.getAttribute('data-src');
const self = this;
this.inputField.addEventListener('focus', function (e) {
self.dataList.classList.remove('vh');
});
this.inputField.addEventListener('click', function (e) {
self.dataList.classList.remove('vh');
});
this.inputField.addEventListener('focusout', function (e) {
setTimeout(function () {
self.dataList.classList.add('vh');
self.clearDataListSelection(self);
if (self.inputField.textContent === '') {
self.inputField.setAttribute('data-value', '');
}
if (self.inputField.classList.contains('required') && self.inputField.getAttribute('data-value') === '') {
self.inputField.classList.add('invalid');
} else {
self.inputField.classList.remove('invalid');
}
const list = self.dataListBody.getElementsByTagName('div');
const length = list.length;
if (length > 0 && self.inputField.getAttribute('data-value') !== '') {
let isValid = false;
for (let i = 0; i < length; ++i) {
if (list[i].textContent === self.inputField.textContent) {
isValid = true;
break;
}
}
if (!isValid) {
self.inputField.classList.add('invalid');
} else {
self.inputField.classList.remove('invalid');
}
}
}, 100);
});
this.inputField.addEventListener('keydown', function (e) {
if (self.dataList.classList.contains("vh")) {
self.dataList.classList.remove('vh');
}
if (e.keyCode === 13 || e.keyCode === 40) {
jsOMS.preventAll(e);
}
if (e.keyCode === 40) {
// down-key
self.selectOption(self.dataListBody.firstElementChild);
self.dataList.focus();
jsOMS.preventAll(e);
} else {
// handle change delay
self.inputTimeDelay({ id: self.id, delay: 300 }, self.changeCallback, self, e);
}
});
// @bug This is never getting run?!
this.dataList.addEventListener('keydown', function (e) {
jsOMS.preventAll(e);
if (e.code === 'Escape' || e.code === 'Delete' || e.code === 'Backspace') {
// handle esc, del to go back to input field
self.inputField.focus();
self.clearDataListSelection(self);
} else if (e.code === 'ArrowUp') {
// handle up-click
if (document.activeElement.previousElementSibling !== null) {
self.clearDataListSelection(self);
self.selectOption(document.activeElement.previousElementSibling);
}
} else if (e.code === 'ArrowDown') {
// handle down-click
if (document.activeElement.nextElementSibling !== null) {
self.clearDataListSelection(self);
self.selectOption(document.activeElement.nextElementSibling);
}
} else if (e.code === 'Enter' || e.code === 'Tab') {
self.clearDataListSelection(self);
self.addToResultList(self, document.activeElement);
}
});
this.dataList.addEventListener('click', function (e) {
self.clearDataListSelection(self);
self.addToResultList(self, e.target);
self.dataList.classList.add('vh');
});
};
/**
* Handle remote data response result
*
* This method adds remote results to the dropdown list for selecting
*
* @param {SmartTextInput} self This reference
* @param {Object} data Response data
*
* @return {void}
*
* @since 1.0.0
*/
remoteCallback (self, data)
{
window.omsApp.logger.log(data);
data = JSON.parse(data.response)[0];
const dataLength = data.length;
// if dropdown == true
if (self.dataList.getAttribute('data-active') !== 'true') {
return;
}
if (self.inputField.textContent === '') {
self.inputField.setAttribute('data-value', '');
}
while (self.dataListBody.firstChild) {
self.dataListBody.removeChild(self.dataListBody.firstChild);
}
let matchFound = false;
for (let i = 0; i < dataLength; ++i) {
// set readable value
const newRow = self.dataTpl.content.cloneNode(true);
let fields = newRow.querySelectorAll('[data-tpl-text]');
let fieldLength = fields.length;
for (let j = 0; j < fieldLength; ++j) {
fields[j].appendChild(
document.createTextNode(
jsOMS.getArray(fields[j].getAttribute('data-tpl-text'), data[i])
)
);
}
// set internal value
fields = newRow.querySelectorAll('[data-tpl-value]');
fieldLength = fields.length;
for (let j = 0; j < fieldLength; ++j) {
fields[j].setAttribute(
'data-value',
jsOMS.getArray(fields[j].getAttribute('data-tpl-value'), data[i])
);
}
// set data cache
newRow.firstElementChild.setAttribute('data-data', JSON.stringify(data[i]));
if (!matchFound && self.inputField.textContent === newRow.firstElementChild.textContent) {
newRow.firstElementChild.classList.add('active');
self.inputField.setAttribute('data-value', newRow.firstElementChild.getAttribute('data-value'));
matchFound = true;
}
self.dataListBody.appendChild(newRow);
}
if (!matchFound && self.inputField.getAttribute('data-value') !== '') {
self.inputField.classList.add('invalid');
} else {
self.inputField.classList.remove('invalid');
}
};
/**
* Callback for input field content change
*
* @param {SmartTextInput} self This reference
*
* @return {void}
*
* @since 1.0.0
*/
changeCallback (self)
{
// if remote data
if (typeof self.src !== 'undefined' && self.src !== '') {
const request = new Request(self.src);
request.addData(self.inputField.getAttribute('data-name'), self.inputField.textContent)
request.addData('limit', self.inputField.getAttribute('data-limit'))
request.setSuccess(function (data) { self.remoteCallback(self, data); });
request.send();
}
};
/**
* Select element in dropdown (only mark it as selected)
*
* @param {Element} e Element to select in dropdown
*
* @return {void}
*
* @since 1.0.0
*/
selectOption (e)
{
e.focus();
jsOMS.addClass(e, 'active');
};
/**
* Clear all selected/marked options in dropdown
*
* @param {SmartTextInput} self This reference
*
* @return {void}
*
* @since 1.0.0
*/
clearDataListSelection (self)
{
const list = self.dataListBody.getElementsByTagName('div');
const length = list.length;
for (let i = 0; i < length; ++i) {
jsOMS.removeClass(list[i], 'active');
}
};
/**
* Add selected dropdown elements to some final result list
*
* This can add the selected dropdown elements to a table, badge list etc. depending on the template structure.
*
* @param {SmartTextInput} self This reference
* @param {Element} e Element
*
* @return {void}
*
* @since 1.0.0
*/
addToResultList (self, e) {
const data = JSON.parse(e.getAttribute('data-data'));
if (self.inputField.getAttribute('data-autocomplete') === 'true') {
self.inputField.value = jsOMS.getArray(self.inputField.getAttribute('data-value'), data);
}
self.inputField.setAttribute('data-value', e.getAttribute('data-value'));
self.inputField.textContent = e.textContent;
self.inputField.focus();
self.dataList.classList.add('vh');
};
/**
* Delay handler (e.g. delay after finishing typing)
*
* After waiting for a delay a callback can be triggered.
*
* @param {Object} action Action type
* @param {function} callback Callback to be triggered
* @param {SmartTextInput} self This reference (passed to callback)
* @param {Object} data Data (passed to callback)
*
* @return {void}
*
* @since 1.0.0
*/
inputTimeDelay (action, callback, self, data)
{
if (SmartTextInput.timerDelay[action.id]) {
clearTimeout(SmartTextInput.timerDelay[action.id]);
delete SmartTextInput.timerDelay[action.id];
}
SmartTextInput.timerDelay[action.id] = setTimeout(function () {
delete SmartTextInput.timerDelay[action.id];
callback(self, data);
}, action.delay);
};
};
SmartTextInput.timerDelay = {};

View File

@ -13,7 +13,7 @@ import { Request } from '../../Message/Request/Request.js';
* @version 1.0.0
* @since 1.0.0
*/
export class AdvancedInput
export class TagInput
{
/**
* @constructor
@ -149,7 +149,7 @@ export class AdvancedInput
*
* This method adds remote results to the dropdown list for selecting
*
* @param {AdvancedInput} self This reference
* @param {TagInput} self This reference
* @param {Object} data Response data
*
* @return {void}
@ -217,7 +217,7 @@ export class AdvancedInput
/**
* Callback for input field content change
*
* @param {AdvancedInput} self This reference
* @param {TagInput} self This reference
*
* @return {void}
*
@ -257,7 +257,7 @@ export class AdvancedInput
/**
* Clear all selected/marked options in dropdown
*
* @param {AdvancedInput} self This reference
* @param {TagInput} self This reference
*
* @return {void}
*
@ -283,7 +283,7 @@ export class AdvancedInput
*
* This can add the selected dropdown elements to a table, badge list etc. depending on the template structure.
*
* @param {AdvancedInput} self This reference
* @param {TagInput} self This reference
* @param {Element} e Element
*
* @return {void}
@ -379,7 +379,7 @@ export class AdvancedInput
*
* @param {Object} action Action type
* @param {function} callback Callback to be triggered
* @param {AdvancedInput} self This reference (passed to callback)
* @param {TagInput} self This reference (passed to callback)
* @param {Object} data Data (passed to callback)
*
* @return {void}
@ -388,16 +388,16 @@ export class AdvancedInput
*/
inputTimeDelay (action, callback, self, data)
{
if (AdvancedInput.timerDelay[action.id]) {
clearTimeout(AdvancedInput.timerDelay[action.id]);
delete AdvancedInput.timerDelay[action.id];
if (TagInput.timerDelay[action.id]) {
clearTimeout(TagInput.timerDelay[action.id]);
delete TagInput.timerDelay[action.id];
}
AdvancedInput.timerDelay[action.id] = setTimeout(function () {
delete AdvancedInput.timerDelay[action.id];
TagInput.timerDelay[action.id] = setTimeout(function () {
delete TagInput.timerDelay[action.id];
callback(self, data);
}, action.delay);
};
};
AdvancedInput.timerDelay = {};
TagInput.timerDelay = {};

View File

@ -1,7 +1,8 @@
import { jsOMS } from '../Utils/oLib.js';
import { UriFactory } from '../Uri/UriFactory.js';
import { AdvancedInput } from './Component/AdvancedInput.js';
import { TagInput } from './Component/TagInput.js';
import { SmartTextInput } from './Component/SmartTextInput.js';
// import { NotificationLevel } from '../Message/Notification/NotificationLevel.js';
// import { NotificationMessage } from '../Message/Notification/NotificationMessage.js';
// import { NotificationType } from '../Message/Notification/NotificationType.js';
@ -196,7 +197,7 @@ export class GeneralUI
}
e[i].addEventListener('load', function () {
const spinner = this.parentElement.getElementsByClassName('ispinner');
const spinner = this.parentElement.getElementsByClassName('spinner');
if (spinner.length > 0) {
spinner[0].style.display = 'none';
@ -256,14 +257,28 @@ export class GeneralUI
*/
bindInput (e = null)
{
e = e !== null
let l = e !== null
? [e]
: document.getElementsByClassName('advIpt');
const length = e.length;
let length = l.length;
for (let i = 0; i < length; ++i) {
new AdvancedInput(e[i], this.app.eventManager, this.app.uiManager.getDOMObserver()); // eslint-disable-line no-new
new TagInput(l[i], this.app.eventManager, this.app.uiManager.getDOMObserver()); // eslint-disable-line no-new
}
l = e !== null
? [e]
: document.querySelectorAll('.smart-input-wrapper');
length = l.length;
for (let i = 0; i < length; ++i) {
if (!l[i].querySelector('.input-div').hasAttribute('contenteditable')) {
continue;
}
new SmartTextInput(l[i]); // eslint-disable-line no-new
}
};

View File

@ -60,6 +60,11 @@ export class KeyboardManager
for (let i = 0; i < length; ++i) {
elements[i].addEventListener('keydown', function (event)
{
if (self.down.includes(event.keyCode)) {
// Already fired
return;
}
self.down.push(event.keyCode);
self.run(element, event);
});
@ -90,7 +95,7 @@ export class KeyboardManager
run (element, event)
{
if (typeof this.elements[element] === 'undefined') {
throw new Error('Unexpected elmenet!');
throw new Error('Unexpected element!');
}
const actions = this.elements[element].concat(this.elements['']);

View File

@ -212,14 +212,16 @@ export class UriFactory
}
}
let parsed = uri.replace(new RegExp('\{[\/#\?%@\.\$\!].*?\}', 'g'), function (match) {
let parsed = uri.replace(new RegExp('\{[\/#\?%@°\.\$\!].*?\}', 'g'), function (match) {
match = match.substring(1, match.length - 1);
if (toMatch !== null
&& (Object.prototype.hasOwnProperty.call(toMatch, match)
|| match.includes('/'))
) {
return match.includes('/') ? jsOMS.getArray(match, toMatch) : toMatch[match];
return typeof toMatch[match] === 'undefined'
? (match.includes('/') ? jsOMS.getArray(match, toMatch) : toMatch[match])
: toMatch[match]
} else if (typeof UriFactory.uri[match] !== 'undefined') {
return UriFactory.uri[match];
} else if (match.indexOf('!') === 0) {
@ -268,6 +270,20 @@ export class UriFactory
return current.query();
} else if (match.indexOf('/') === 0 && match.length === 1) {
return current.path;
} else if (match.indexOf('°') === 0 && match.length === 1) {
if(!navigator.geolocation) {
return;
}
var lat = 0.0;
var lon = 0.0;
navigator.geolocation.getCurrentPosition(position => {
lat = position.coords.latitude;
lon = position.coords.longitude;
});
return lat === 0.0 && lon === 0.0 ? '' : lat + ',' + lon;
} else if (match.indexOf(':user') === 0) {
return current.user;
} else if (match.indexOf(':pass') === 0) {

View File

@ -1,113 +0,0 @@
const visited = [];
const findings = {};
const cssSelectors = {};
const cssFiles = ['http://127.0.0.1/cssOMS/styles.css'];
const cssFilesLength = cssFiles.length;
const domain = window.location.hostname;
const pageLimit = 10;
const cssRequest = new XMLHttpRequest();
cssRequest.onreadystatechange = function ()
{
if (cssRequest.readyState === 4 && cssRequest.status === 200) {
const cssText = this.responseText;
const result = cssText.match(/[a-zA-Z0-9\ :>~\.\"'#,\[\]=\-\(\)\*]+{/g);
const resultLength = result.length;
for (let i = 1; i < resultLength; ++i) {
const sel = result[i].substring(0, result[i].length - 1).trimLeft().trimRight().trimRight();
if (!Object.prototype.hasOwnProperty.call(cssSelectors, sel)) {
cssSelectors[sel] = 0;
}
}
}
};
for (let i = 0; i < cssFilesLength; ++i) {
cssRequest.open('GET', cssFiles[i], true);
cssRequest.send();
}
const validatePage = function (url)
{
if (visited.includes(url) || visited.length > pageLimit - 1) {
return;
}
// mark url as visited
visited.push(url);
findings[url] = {};
// web request
const webRequest = new XMLHttpRequest();
webRequest.onreadystatechange = function ()
{
if (webRequest.readyState === 4 && webRequest.status === 200) {
// replace content
document.open();
document.write(this.responseText);
document.close();
// analyze img alt attribute
const imgAlt = document.querySelectorAll('img:not([alt]), img[alt=""], img[alt=" "]');
findings[url].img_alt = imgAlt.length;
// analyze img src
const imgSrc = document.querySelectorAll('img:not([src]), img[src=""], img[src=" "]');
findings[url].img_src = imgSrc.length;
// analyze empty link
const aHref = document.querySelectorAll('a:not([alt]), a[href=""], a[href=" "], a[href="#"]');
findings[url].href_empty = aHref.length;
/* eslint-disable max-len */
// analyze inline on* function
const onFunction = document.querySelectorAll('[onafterprint], [onbeforeprint], [onbeforeunload], [onerror], [onhaschange], [onload], [onmessage], [onoffline], [ononline], [onpagehide], [onpageshow], [onpopstate], [onredo], [onresize], [onstorage], [onundo], [onunload], [onblur], [onchage], [oncontextmenu], [onfocus], [onformchange], [onforminput], [oninput], [oninvalid], [onreset], [onselect], [onsubmit], [onkeydown], [onkeypress], [onkeyup], [onclick], [ondblclick], [ondrag], [ondragend], [ondragenter], [ondragleave], [ondragover], [ondragstart], [ondrop], [onmousedown], [onmousemove], [onmouseout], [onmouseover], [onmouseup], [onmousewheel], [onscroll], [onabort], [oncanplay], [oncanplaythrough], [ondurationchange], [onemptied], [onended], [onerror], [onloadeddata], [onloadedmetadata], [onloadstart], [onpause], [onplay], [onplaying], [onprogress], [onratechange], [onreadystatechange], [onseeked], [onseeking], [onstalled], [onsuspend], [ontimeupdate], [onvolumechange], [onwaiting]');
findings[url].js_on = onFunction.length;
// analyze missing form element attributes
const formElements = document.querySelectorAll('input:not([id]), input[type=""], select:not([id]), textarea:not([id]), label:not([for]), label[for=""], label[for=" "], input:not([name]), select:not([name]), textarea:not([name]), form:not([id]), form:not([action]), form[action=""], form[action=" "], form[action="#"]');
findings[url].form_elements = formElements.length;
// analyze invalid container-children relationship (e.g. empty containers, invalid children)
const invalidContainerChildren = document.querySelectorAll(':not(tr) > td, :not(tr) > th, colgroup *:not(col), :not(colgroup) > col, tr > :not(td):not(th), optgroup > :not(option), :not(select) > option, :not(fieldset) > legend, select > :not(option):not(optgroup), :not(select):not(optgroup) > option, table > *:not(thead):not(tfoot):not(tbody):not(tr):not(colgroup):not(caption)');
findings[url].invalid_container_children = invalidContainerChildren.length;
/* eslint-enable max-len */
// has inline styles
const hasInlineStyles = document.querySelectorAll('*[style]');
findings[url].form_elements = hasInlineStyles.length;
// analyze css usage
let cssFound;
for (const i in cssSelectors) {
try {
cssFound = document.querySelectorAll(i.replace(/:hover|:active/gi, ''));
cssSelectors[i] = cssFound === null ? 0 : cssFound.length;
} catch (e) {}
}
// check other pages
const links = document.querySelectorAll('a');
const linkLength = links.length;
for (let i = 0; i < linkLength; ++i) {
if (visited.includes(links[i].href)
|| (!links[i].href.startsWith('/') && links[i].href.startsWith('http') && !links[i].href.includes(domain))
) {
continue;
}
validatePage(links[i].href);
}
}
};
webRequest.open('GET', url, true);
webRequest.send();
};
validatePage(location.href);
console.table(findings);
console.table(cssSelectors);

View File

@ -1,5 +1,4 @@
import { jsOMS } from '../Utils/oLib.js';
import { Input } from '../UI/Component/Input.js';
/**
* Form view.
@ -13,14 +12,14 @@ import { Input } from '../UI/Component/Input.js';
* @version 1.0.0
* @since 1.0.0
*
* @tood Karaka/jsOMS#60
* On change listener
* Allow to add a on change listener in a form. This should result in automatic submits after changing a form.
* Consider the following cases to submit the form:
* * on Enter (all except textarea)
* * on Change (by using a timer)
* * on Leave (all elements)
* The listener should be defined in the form definition once and in js be applied to all form elements.
* @todo On change listener
* Allow to add a on change listener in a form. This should result in automatic submits after changing a form.
* Consider the following cases to submit the form:
* * on Enter (all except textarea)
* * on Change (by using a timer)
* * on Leave (all elements)
* The listener should be defined in the form definition once and in js be applied to all form elements.
* https://github.com/Karaka-Management/jsOMS/issues/60
*/
export class FormView
{
@ -776,27 +775,6 @@ export class FormView
} else {
this.action = 'EMPTY';
}
const elements = this.getFormElements();
const 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:
}
}
};
getElementsToBind (e = null)