1.started removal of CDNs.\n 2. Improved modular structure for all parts of project including database.
This commit is contained in:
181
static/js/components/input_date.js
Normal file
181
static/js/components/input_date.js
Normal file
@@ -0,0 +1,181 @@
|
||||
|
||||
|
||||
// Date picker inputs
|
||||
/*
|
||||
function hookupInputDatePickers(dateInputs, notFuture, notPast, parent, addClearOption) {
|
||||
|
||||
if (!isEmpty(dateInputs)) {
|
||||
|
||||
let currentInput, currentDateString, currentDate, exceptionsArray;
|
||||
|
||||
for (let i = 0; i < dateInputs.length; i++) {
|
||||
|
||||
currentInput = document.querySelectorAll(dateInputs[i]);
|
||||
currentDateString = currentInput.val();
|
||||
currentDate = (!isEmpty(currentDateString)) ? convertDDMMYYYYString2Date(currentDateString, false) : null;
|
||||
exceptionsArray = (currentDate != null) ? [currentDate] : null;
|
||||
|
||||
turnInputIntoDatePicker(currentInput, notFuture, notPast, exceptionsArray);
|
||||
}
|
||||
|
||||
if (!isEmpty(parent)) {
|
||||
// stop user from manually typing date except backspace and delete
|
||||
// which will clear the whole value to ensure we either have a whole
|
||||
// date string or none
|
||||
|
||||
parent.addEventListener("keydown", isDatePickerSelector, function(event) {
|
||||
if (event.keyCode == 46 | event.keyCode == 8) { // delete or backspace
|
||||
this.val('');
|
||||
}
|
||||
else {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
return false
|
||||
});
|
||||
|
||||
if (addClearOption) {
|
||||
|
||||
// if user right-clicks in date input, give option to clear the date
|
||||
parent.contextMenu({
|
||||
selector: isDatePickerSelector,
|
||||
delay: 100,
|
||||
autoHide: true,
|
||||
position: function(opt, x, y) {
|
||||
var event = opt.$trigger[0]?.ownerDocument?.defaultView?.event || event;
|
||||
opt.$menu.position({ my: 'center top', at: 'center top', of: event });
|
||||
},
|
||||
items: {
|
||||
"clears": {
|
||||
name: "Clear Date",
|
||||
icon: "delete",
|
||||
disabled: function(key, opt) { return isEmpty(document.querySelectorAll(opt.$trigger)); }, // if it's already empty, don't do anything
|
||||
callback: function(itemKey, opt, rootMenu, originalEvent) { var input = document.querySelectorAll(opt.$trigger); input.val(''); input.trigger('change'); }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function turnInputIntoDatePicker(input, notFuture, notPast, exceptionValueArray) {
|
||||
|
||||
var beforeShowDayCallBack = null;
|
||||
|
||||
if (notFuture || notPast) {
|
||||
|
||||
var today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
var tomorrow = new Date();
|
||||
tomorrow.setDate(today.getDate() + 1);
|
||||
tomorrow.setHours(0, 0, 0, 0);
|
||||
|
||||
var hasExceptions = !isEmpty(exceptionValueArray);
|
||||
|
||||
beforeShowDayCallBack = function(date) {
|
||||
|
||||
var selectedDate = date.getTime();
|
||||
var fieldHasException = hasExceptions && arrayContainsItem(exceptionValueArray, date);
|
||||
|
||||
if (notFuture && (tomorrow < selectedDate) && fieldHasException) return [false, 'redday', 'You cannot choose a future date'];
|
||||
if (notPast && (selectedDate < today) && fieldHasException) return [false, 'redday', 'You cannot choose a past date'];
|
||||
|
||||
return [true, '', ''];
|
||||
};
|
||||
}
|
||||
|
||||
input.datepicker({
|
||||
dateFormat: 'dd-mm-yy',
|
||||
navigationAsDateFormat: true,
|
||||
beforeShowDay: beforeShowDayCallBack
|
||||
});
|
||||
|
||||
// prevent datepicker from appearing on right click
|
||||
input.addEventListener('contextmenu', function() { this.datepicker('hide'); });
|
||||
|
||||
// Disable autocomplete suggestions appearing when clicking on input
|
||||
input.attr('autocomplete', 'off');
|
||||
}
|
||||
|
||||
function setDatePickerDate(input, objDate) {
|
||||
if (!isEmpty(objDate)) {
|
||||
input.val('');
|
||||
}
|
||||
else {
|
||||
input.datepicker('setDate', objDate);
|
||||
}
|
||||
}
|
||||
|
||||
function getDatePickerDate(input, adjust4DayLightSavings) {
|
||||
|
||||
var date = null;
|
||||
|
||||
if (!isEmpty(input)) {
|
||||
date = input.datepicker('getDate');
|
||||
|
||||
if (adjust4DayLightSavings) {
|
||||
formatDateDayLightSavingsTime(date);
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function formatDateDayLightSavingsTime(date) {
|
||||
// JSON.stringify removes hour delta for daylight savings
|
||||
// e.g. 13/11/2023 01:00:00 goes to 13/11/2023 00:00:00
|
||||
// this adds an hour so it becomes the correct time when stringified
|
||||
if (!isEmpty(date)) {
|
||||
date.setTime(date.getTime() - date.getTimezoneOffset() * 60 * 1000)
|
||||
}
|
||||
}
|
||||
*/
|
||||
function convertJSONDateString2Date(dateStr) {
|
||||
if (isEmpty(dateStr)) return null;
|
||||
if (dateStr instanceof Date) return dateStr;
|
||||
return new Date(parseInt(dateStr.substr(6)));
|
||||
}
|
||||
|
||||
function convertDDMMYYYYString2Date(dateStr, adjust4DayLightSavings) {
|
||||
var date = null;
|
||||
|
||||
if (!isEmpty(dateStr)) {
|
||||
if (dateStr instanceof Date) {
|
||||
date = dateStr;
|
||||
}
|
||||
else {
|
||||
var dateParts = dateStr.split('-');
|
||||
|
||||
if (dateParts.length == 3) {
|
||||
date = new Date(dateParts[2], dateParts[1] - 1, dateParts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (adjust4DayLightSavings && !isEmpty(date)) {
|
||||
formatDateDayLightSavingsTime(date);
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function convertDate2DDMMYYYYString(date) {
|
||||
if (isEmpty(date)) return '';
|
||||
|
||||
try {
|
||||
var dd = date.getDate();
|
||||
var mm = date.getMonth() + 1;
|
||||
var yyyy = date.getFullYear();
|
||||
|
||||
if (dd < 10) dd = '0' + dd;
|
||||
if (dd < 10) mm = '0' + mm;
|
||||
|
||||
return dd + '-' + mm + '-' + yyyy;
|
||||
}
|
||||
catch (err) {
|
||||
return 'Formatting error';
|
||||
}
|
||||
}
|
||||
16
static/js/components/select.js
Normal file
16
static/js/components/select.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
function handleSelectCollapse(elementSelect) {
|
||||
let optionSelected = document.querySelectorAll(elementSelect).querySelector('option:selected');
|
||||
optionSelected.text(optionSelected.attr(attrTextCollapsed));
|
||||
console.log('collapsed: ', optionSelected.text());
|
||||
optionSelected.classList.remove(flagExpanded);
|
||||
optionSelected.classList.add(flagCollapsed);
|
||||
}
|
||||
function handleSelectExpand(elementSelect) {
|
||||
let optionSelected = document.querySelectorAll(elementSelect).querySelector('option:selected');
|
||||
optionSelected.text(optionSelected.attr(attrTextExpanded));
|
||||
console.log('expanded: ', optionSelected.text());
|
||||
optionSelected.classList.remove(flagCollapsed);
|
||||
optionSelected.classList.add(flagExpanded);
|
||||
}
|
||||
18
static/js/components/table.js
Normal file
18
static/js/components/table.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
// Data tables
|
||||
function getDataTableCellByNode(table, elRow, indexColumn) {
|
||||
// normal jQuery selector won't pick up hidden columns
|
||||
return document.querySelectorAll(table.DataTable().cells(elRow, indexColumn, null).nodes());
|
||||
}
|
||||
|
||||
function outputTableElementDateInput(table, elRow, indexColumn, value) {
|
||||
|
||||
let currentCell = getDataTableCellByNode(table, elRow, indexColumn);
|
||||
|
||||
let dateTxt = '';
|
||||
|
||||
if (!isEmpty(value)) {
|
||||
if (typeof value === 'string') value = convertJSONDateString2Date(value);
|
||||
}
|
||||
}
|
||||
41
static/js/components/textarea.js
Normal file
41
static/js/components/textarea.js
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
function removeBlankTextAreaLines(textarea) {
|
||||
textarea.val(textarea.val.replace(/(?:(?:\r\n|\r|\n)\s*){2}/gm, ''));
|
||||
}
|
||||
|
||||
function fitTextAreasToContent(parent) {
|
||||
var textareas = parent.querySelector('textarea');
|
||||
|
||||
if (!isEmpty(textareas)) {
|
||||
for (var t = 0; t < textareas.length; t++) {
|
||||
fitTextAreaToContent(document.querySelectorAll(textareas[t]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fitTextAreaToContent(textarea) {
|
||||
// Trim new text
|
||||
var txtNew = textarea.val().trim();
|
||||
textarea.val(txtNew);
|
||||
|
||||
var elTextarea = textarea[0];
|
||||
|
||||
// Clear style height and set rows = 1
|
||||
elTextarea.style.removeProperty('height');
|
||||
textarea.attr('rows', 1);
|
||||
|
||||
const paddingTop = parseCSSPropertyToFloat(textarea, 'padding-top');
|
||||
const paddingBottom= parseCSSPropertyToFloat(textarea, 'padding-bottom');
|
||||
const borderTop = parseCSSPropertyToFloat(textarea, 'border-top');
|
||||
const borderBottom = parseCSSPropertyToFloat(textarea, 'border-bottom');
|
||||
let heightDelta = paddingTop + paddingBottom + borderTop + borderBottom;
|
||||
let heightNew = elTextarea.scrollHeight + heightDelta;
|
||||
|
||||
// If new height is less than 1 linem default to single line height
|
||||
const heightSingleLine = parseCSSPropertyToFloat(textarea, 'line-height') + heightDelta;
|
||||
if (heightNew < heightSingleLine) heightNew = heightSingleLine;
|
||||
|
||||
elTextarea.style.height = heightNew + 'px';
|
||||
}
|
||||
|
||||
|
||||
13
static/js/components/video.js
Normal file
13
static/js/components/video.js
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
function videoPlay(elemVideo) {
|
||||
if (!_loading) { // elemVideo.paused &&
|
||||
elemVideo.play();
|
||||
if (_verbose) { console.log("Playing video element: " + elemVideo.name)};
|
||||
}
|
||||
}
|
||||
|
||||
function videoPause(elemVideo) {
|
||||
elemVideo.pause();
|
||||
if (_verbose) { console.log("Pausing video element: " + elemVideo.name)};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user