;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.jSuites = factory();
}(this, (function () {
var jSuites;
/******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 791:
/***/ (function(module) {
/**
* (c) jSuites Javascript Plugins and Web Components (v6)
*
* Website: https://jsuites.net
* Description: Create amazing web based applications.
*
* MIT License
*/
;(function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
'use strict';
const Helpers = (function () {
const component = {};
// Excel like dates
const excelInitialTime = Date.UTC(1900, 0, 0);
const excelLeapYearBug = Date.UTC(1900, 1, 29);
const millisecondsPerDay = 86400000;
// Transform in two digits
component.two = function (value) {
value = '' + value;
if (value.length === 1) {
value = '0' + value;
}
return value;
};
component.isValidDate = function (d) {
return d instanceof Date && !isNaN(d.getTime());
};
component.isValidDateFormat = function(date) {
if (typeof date === 'string') {
// Check format: YYYY-MM-DD using regex
const match = date.substring(0, 10).match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (match) {
const year = Number(match[1]);
const month = Number(match[2]) - 1;
const day = Number(match[3]);
const parsed = new Date(Date.UTC(year, month, day));
// Return
return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month && parsed.getUTCDate() === day;
}
}
return false;
}
component.toString = function (date, dateOnly) {
let y = null;
let m = null;
let d = null;
let h = null;
let i = null;
let s = null;
if (Array.isArray(date)) {
y = date[0];
m = date[1];
d = date[2];
h = date[3];
i = date[4];
s = date[5];
} else {
if (!date) {
date = new Date();
}
y = date.getUTCFullYear();
m = date.getUTCMonth() + 1;
d = date.getUTCDate();
h = date.getUTCHours();
i = date.getUTCMinutes();
s = date.getUTCSeconds();
}
if (dateOnly === true) {
return component.two(y) + '-' + component.two(m) + '-' + component.two(d);
} else {
return (
component.two(y) + '-' + component.two(m) + '-' + component.two(d) + ' ' + component.two(h) + ':' + component.two(i) + ':' + component.two(s)
);
}
};
component.toArray = function (value) {
let date = value.split(value.indexOf('T') !== -1 ? 'T' : ' ');
let time = date[1];
date = date[0].split('-');
let y = parseInt(date[0]);
let m = parseInt(date[1]);
let d = parseInt(date[2]);
let h = 0;
let i = 0;
if (time) {
time = time.split(':');
h = parseInt(time[0]);
i = parseInt(time[1]);
}
return [y, m, d, h, i, 0];
};
component.arrayToStringDate = function (arr) {
return component.toString(arr, false);
};
component.dateToNum = function (jsDate) {
if (typeof jsDate === 'string') {
jsDate = new Date(jsDate + ' GMT+0');
}
let jsDateInMilliseconds = jsDate.getTime();
if (jsDateInMilliseconds >= excelLeapYearBug) {
jsDateInMilliseconds += millisecondsPerDay;
}
jsDateInMilliseconds -= excelInitialTime;
return jsDateInMilliseconds / millisecondsPerDay;
};
component.numToDate = function (excelSerialNumber, asArray) {
// allow 0; only bail on null/undefined/empty
if (excelSerialNumber === null || excelSerialNumber === undefined || excelSerialNumber === '') {
return '';
}
const MS_PER_DAY = 86_400_000;
const SEC_PER_DAY = 86_400;
// Excel day 0 is 1899-12-31 (with the fake 1900-02-29 at serial 60)
const EXCEL_DAY0_UTC_MS = Date.UTC(1899, 11, 31);
let wholeDays = Math.floor(excelSerialNumber);
let fractionalDay = excelSerialNumber - wholeDays;
// Fix the 1900 leap-year bug: shift serials >= 60 back one day
if (wholeDays >= 60) wholeDays -= 1;
// Build midnight UTC of the day
let ms = EXCEL_DAY0_UTC_MS + wholeDays * MS_PER_DAY;
// Add time part using integer seconds to avoid FP jitter
const seconds = Math.round(fractionalDay * SEC_PER_DAY);
ms += seconds * 1000;
const d = new Date(ms);
const arr = [
d.getUTCFullYear(),
component.two(d.getUTCMonth() + 1),
component.two(d.getUTCDate()),
component.two(d.getUTCHours()),
component.two(d.getUTCMinutes()),
component.two(d.getUTCSeconds()),
];
return asArray ? arr : component.toString(arr, false);
};
component.prettify = function (d, texts) {
if (!texts) {
texts = {
justNow: 'Just now',
xMinutesAgo: '{0}m ago',
xHoursAgo: '{0}h ago',
xDaysAgo: '{0}d ago',
xWeeksAgo: '{0}w ago',
xMonthsAgo: '{0} mon ago',
xYearsAgo: '{0}y ago',
};
}
if (d.indexOf('GMT') === -1 && d.indexOf('Z') === -1) {
d += ' GMT';
}
let d1 = new Date();
let d2 = new Date(d);
let total = parseInt((d1 - d2) / 1000 / 60);
const format = (t, o) => {
return t.replace('{0}', o);
};
if (!total) {
return texts.justNow;
} else if (total < 90) {
return format(texts.xMinutesAgo, total);
} else if (total < 1440) {
// One day
return format(texts.xHoursAgo, Math.round(total / 60));
} else if (total < 20160) {
// 14 days
return format(texts.xDaysAgo, Math.round(total / 1440));
} else if (total < 43200) {
// 30 days
return format(texts.xWeeksAgo, Math.round(total / 10080));
} else if (total < 1036800) {
// 24 months
return format(texts.xMonthsAgo, Math.round(total / 43200));
} else {
// 24 months+
return format(texts.xYearsAgo, Math.round(total / 525600));
}
};
component.prettifyAll = function () {
let elements = document.querySelectorAll('.prettydate');
for (let i = 0; i < elements.length; i++) {
if (elements[i].getAttribute('data-date')) {
elements[i].innerHTML = component.prettify(elements[i].getAttribute('data-date'));
} else {
if (elements[i].innerHTML) {
elements[i].setAttribute('title', elements[i].innerHTML);
elements[i].setAttribute('data-date', elements[i].innerHTML);
elements[i].innerHTML = component.prettify(elements[i].innerHTML);
}
}
}
};
// Compatibility with jSuites
component.now = component.toString;
const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const translate = function (t) {
if (typeof document !== 'undefined' && document.dictionary) {
return document.dictionary[t] || t;
} else {
return t;
}
};
Object.defineProperty(component, 'weekdays', {
get: function () {
return weekdays.map(function (v) {
return translate(v);
});
},
});
Object.defineProperty(component, 'weekdaysShort', {
get: function () {
return weekdays.map(function (v) {
return translate(v).substring(0, 3);
});
},
});
Object.defineProperty(component, 'months', {
get: function () {
return months.map(function (v) {
return translate(v);
});
},
});
Object.defineProperty(component, 'monthsShort', {
get: function () {
return months.map(function (v) {
return translate(v).substring(0, 3);
});
},
});
return component;
})();
const Mask = (function() {
// Currency
const tokens = {
// Escape
escape: [ '\\\\[.\\s\\S]' ],
// Text
text: [ '@', '&' ],
// Number
fraction: [ '#{0,1}.*?\\?+\\/[0-9?]+' ],
// Currency tokens
currency: [ '#(.{1})##0?(.{1}[0#]+)?( ?;(.*)?)?' ],
// Scientific
scientific: [ '[0#]+([.,]{1}0*#*)?E{1}\\+0+' ],
// Percentage
percentage: [ '[0#]+([.,]{1}0*#*)?%' ],
// Number
numeric: [ '[0#]+([.,]{1}0*#*)?', '#+' ],
// Data tokens
datetime: [ 'YYYY', 'YYY', 'YY', 'MMMMM', 'MMMM', 'MMM', 'MM', 'DDDDD', 'DDDD', 'DDD', 'DD', 'DY', 'DAY', 'WD', 'D', 'Q', 'MONTH', 'MON', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'S', 'Y', 'M', 'I' ],
// Other
general: [ 'A', '0', '\\?', '\\*', ',,M', ',,,B', '[a-zA-Z\\$]+', '_[.\\s\\S]', '\\(', '\\)', '.']
}
const countryCodes = {
"0409": [
"$",
",",
"."
],
"0809": [
"£",
",",
"."
],
"0C09": [
"$",
",",
"."
],
"1009": [
"$",
",",
"."
],
"1409": [
"$",
",",
"."
],
"1809": [
"€",
",",
"."
],
"1C09": [
"R",
" ",
"."
],
"040C": [
"€",
" ",
","
],
"080C": [
"€",
" ",
","
],
"100C": [
"CHF",
"'",
"."
],
"140C": [
"€",
" ",
","
],
"0C0C": [
"$",
" ",
","
],
"0407": [
"€",
".",
","
],
"0C07": [
"€",
".",
","
],
"0807": [
"CHF",
"'",
"."
],
"1007": [
"€",
".",
","
],
"0413": [
"€",
".",
","
],
"0813": [
"€",
" ",
","
],
"0410": [
"€",
".",
","
],
"0810": [
"CHF",
"'",
"."
],
"0C0A": [
"€",
".",
","
],
"080A": [
"$",
",",
"."
],
"2C0A": [
"$",
".",
","
],
"340A": [
"$",
".",
","
],
"240A": [
"$",
".",
","
],
"300A": [
"$",
",",
"."
],
"280A": [
"S/",
",",
"."
],
"200A": [
"Bs.",
".",
","
],
"140A": [
"₡",
",",
"."
],
"100A": [
"Q",
",",
"."
],
"1C0A": [
"RD$",
",",
"."
],
"3C0A": [
"$",
",",
"."
],
"440A": [
"C$",
",",
"."
],
"4C0A": [
"B/.",
",",
"."
],
"480A": [
"L",
",",
"."
],
"0816": [
"€",
".",
","
],
"0416": [
"R$",
".",
","
],
"0406": [
"kr",
".",
","
],
"041D": [
"kr",
" ",
","
],
"0414": [
"kr",
" ",
","
],
"040B": [
"€",
" ",
","
],
"040F": [
"kr",
".",
","
],
"0415": [
"zł",
" ",
","
],
"0405": [
"Kč",
" ",
","
],
"041B": [
"€",
" ",
","
],
"040E": [
"Ft",
" ",
","
],
"0424": [
"€",
".",
","
],
"041A": [
"€",
".",
","
],
"0418": [
"lei",
".",
","
],
"0402": [
"лв.",
" ",
","
],
"0425": [
"€",
" ",
","
],
"0426": [
"€",
" ",
","
],
"0427": [
"€",
" ",
","
],
"0408": [
"€",
".",
","
],
"043A": [
"€",
",",
"."
],
"043C": [
"€",
",",
"."
],
"0419": [
"₽",
" ",
","
],
"0422": [
"₴",
" ",
","
],
"0423": [
"Br",
" ",
","
],
"041F": [
"₺",
".",
","
],
"042C": [
"₼",
" ",
","
],
"042F": [
"ден",
".",
","
],
"0441": [
"Lek",
".",
","
],
"141A": [
"KM",
".",
","
],
"0401": [
"ر.س",
",",
"."
],
"0C01": [
"ج.م",
",",
"."
],
"1401": [
"دج",
" ",
","
],
"1801": [
"د.م.",
" ",
","
],
"1C01": [
"د.ت",
" ",
","
],
"2001": [
"﷼",
",",
"."
],
"3401": [
"KD",
",",
"."
],
"3801": [
"د.إ",
",",
"."
],
"3C01": [
"BD",
",",
"."
],
"4001": [
"ر.ق",
",",
"."
],
"2801": [
"£",
",",
"."
],
"2C01": [
"د.ا",
",",
"."
],
"3001": [
"ل.ل",
",",
"."
],
"2401": [
"﷼",
",",
"."
],
"1001": [
"ل.د",
",",
"."
],
"0429": [
"﷼",
",",
"."
],
"040D": [
"₪",
",",
"."
],
"0439": [
"₹",
",",
"."
],
"0445": [
"৳",
",",
"."
],
"0461": [
"रु",
",",
"."
],
"045B": [
"Rs",
",",
"."
],
"044E": [
"₹",
",",
"."
],
"0444": [
"₹",
",",
"."
],
"0449": [
"₹",
",",
"."
],
"044B": [
"₹",
",",
"."
],
"0421": [
"Rp",
".",
","
],
"043E": [
"RM",
",",
"."
],
"0464": [
"₱",
",",
"."
],
"041E": [
"฿",
",",
"."
],
"042A": [
"₫",
".",
","
],
"0453": [
"៛",
",",
"."
],
"0454": [
"₭",
",",
"."
],
"0455": [
"K",
",",
"."
],
"0404": [
"NT$",
",",
"."
],
"0C04": [
"HK$",
",",
"."
],
"0804": [
"¥",
",",
"."
],
"0411": [
"¥",
",",
"."
],
"0412": [
"₩",
",",
"."
],
"0437": [
"₾",
" ",
","
],
"042B": [
"֏",
" ",
","
],
"043F": [
"₸",
" ",
","
],
"0443": [
"so'm",
" ",
","
],
"0428": [
"ЅМ",
" ",
","
],
"0440": [
"сом",
" ",
","
],
"0466": [
"₦",
",",
"."
],
"0469": [
"₦",
",",
"."
],
"0468": [
"GH₵",
",",
"."
],
"180C": [
"F CFA",
" ",
","
]
}
/**
* Detect decimal and thousand separators in a mask pattern
*/
const detectMaskSeparators = function(mask) {
// Decimal: separator in pattern 0[sep]0+ (can have trailing chars like _ )
// Look for the rightmost occurrence of this pattern
const decimalMatches = mask.match(/0([.,\s'])0+/g);
let decimal = null;
if (decimalMatches && decimalMatches.length > 0) {
// Get the last match (rightmost decimal pattern)
const lastMatch = decimalMatches[decimalMatches.length - 1];
const sepMatch = lastMatch.match(/0([.,\s'])0/);
decimal = sepMatch ? sepMatch[1] : null;
}
// Thousand: other separator that appears in #[sep]# or 0[sep]# patterns
let thousand = null;
for (const sep of [',', '.', ' ', "'"]) {
if (sep !== decimal) {
const escapedSep = sep === '.' ? '\\.' : sep;
const regex = new RegExp('[#0]' + escapedSep + '[#0]');
if (regex.test(mask)) {
thousand = sep;
break;
}
}
}
return { decimal, thousand };
}
/**
* Transform Excel currency locale patterns like [$$-409]#,##0.00
* countryCodes format: { "0409": ["$", ",", "."] } // [currency, thousand, decimal]
* [$$ = use locale's default currency, [$X = use literal X]
*/
const transformExcelLocaleMask = function(mask) {
// Handle multiple patterns (e.g., positive;negative)
// Accept any alphanumeric for locale code to handle invalid codes gracefully
const pattern = /\[\$(.?)-([0-9A-Z]+)\]/gi;
let transformation = null;
// Find first pattern to determine locale
const firstMatch = mask.match(/\[\$(.?)-([0-9A-Z]+)\]/i);
if (!firstMatch) return mask;
const symbolChar = firstMatch[1] || '';
let localeCode = firstMatch[2].toUpperCase();
// Pad with leading zero if 3 digits (e.g., "409" → "0409")
if (localeCode.length === 3) {
localeCode = '0' + localeCode;
}
// Look up locale in countryCodes
const localeData = countryCodes[localeCode];
if (!localeData) {
// Unknown locale - fallback: strip ALL patterns, use literal symbol (or $)
const fallbackSymbol = symbolChar || '$';
// Replace all occurrences of the pattern with the symbol
return mask.replace(/\[\$(.?)-([0-9A-Z]+)\]/gi, fallbackSymbol);
}
// Extract from array: [currency, thousand, decimal]
const localeCurrency = localeData[0];
const localeThousand = localeData[1];
const localeDecimal = localeData[2];
// Determine currency symbol: $$ means use locale's default, else use literal
let currencySymbol;
if (symbolChar === '$') {
// $$ pattern - use locale's default currency
currencySymbol = localeCurrency;
} else {
// Literal symbol provided (e.g., [$€-407])
currencySymbol = symbolChar;
}
// Replace all locale patterns with currency symbol
let result = mask.replace(pattern, currencySymbol);
// Detect current separators in mask (after removing patterns)
const current = detectMaskSeparators(result);
// Transform separators to match locale using placeholders to avoid conflicts
const temp1 = '\uFFF0'; // Placeholder for thousand
const temp2 = '\uFFF1'; // Placeholder for decimal
// Step 1: Replace current separators with placeholders
if (current.thousand && current.thousand !== localeThousand) {
const from = current.thousand === '.' ? '\\.' : (current.thousand === ' ' ? ' ' : current.thousand);
result = result.replace(new RegExp(from, 'g'), temp1);
}
if (current.decimal && current.decimal !== localeDecimal) {
const from = current.decimal === '.' ? '\\.' : (current.decimal === ' ' ? ' ' : current.decimal);
result = result.replace(new RegExp(from, 'g'), temp2);
}
// Step 2: Replace placeholders with target separators
result = result.replace(new RegExp(temp1, 'g'), localeThousand);
result = result.replace(new RegExp(temp2, 'g'), localeDecimal);
return result;
}
// All expressions
const allExpressions = [].concat(tokens.escape, tokens.fraction, tokens.currency, tokens.datetime, tokens.percentage, tokens.scientific, tokens.numeric, tokens.text, tokens.general).join('|');
// Pre-compile all regexes once at initialization for better performance
const compiledTokens = {};
const tokenPriority = ['escape', 'fraction', 'currency', 'scientific', 'percentage', 'numeric', 'datetime', 'text', 'general'];
// Cache for getMethod results
const methodCache = {};
// Cache for getTokens results
const tokensCache = {};
// Cache for autoCasting results
const autoCastingCache = {};
// Initialize compiled regexes
for (const type of tokenPriority) {
compiledTokens[type] = tokens[type].map(pattern => ({
regex: new RegExp('^' + pattern + '$', 'i'),
method: pattern
}));
}
// Pre-compile regex for getTokens function
const allExpressionsRegex = new RegExp(allExpressions, 'gi');
const hiddenCaret = "\u200B";
// Locale for date parsing
const userLocale = (typeof navigator !== 'undefined' && navigator.language) || 'en-US';
// Helpers
const focus = function(el) {
if (el.textContent.length) {
// Handle contenteditable elements
const range = document.createRange();
const sel = window.getSelection();
let node = el;
// Go as deep as possible to the last text node
while (node.lastChild) node = node.lastChild;
// Ensure it's a text node
if (node.nodeType === Node.TEXT_NODE) {
range.setStart(node, node.length);
} else {
range.setStart(node, node.childNodes.length);
}
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.scrollLeft = el.scrollWidth;
}
}
/**
* Returns if the given value is considered blank
*/
const isBlank = function(v) {
return v === null || v === '' || typeof(v) === 'undefined';
}
/**
* Clean mask - extremely fast implementation using only char operations
* Removes quotes, detects parenthesis for negative numbers, and removes brackets (except time format codes)
* Sets control.parenthesisForNegativeNumbers and returns cleaned mask
*/
const cleanMask = function(mask, control) {
const len = mask.length;
let result = '';
for (let i = 0; i < len; i++) {
const char = mask[i];
// Remove quotes
if (char === '"') {
continue;
}
// Handle brackets - remove them unless they're time format codes
if (char === '[') {
// Check if it's a time format code: [s], [ss], [h], [hh], [m], [mm]
let isTimeFormat = false;
if (i + 2 < len && mask[i + 2] === ']') {
const c = mask[i + 1];
if (c === 's' || c === 'h' || c === 'm') {
isTimeFormat = true;
}
} else if (i + 3 < len && mask[i + 3] === ']') {
const c1 = mask[i + 1];
const c2 = mask[i + 2];
if ((c1 === 's' && c2 === 's') || (c1 === 'h' && c2 === 'h') || (c1 === 'm' && c2 === 'm')) {
isTimeFormat = true;
}
}
if (isTimeFormat) {
result += char;
} else {
// Skip content and closing bracket
while (i < len && mask[i] !== ']') {
i++;
}
continue;
}
}
// Check for parenthesis (not preceded by underscore and no underscore inside)
else if (char === '(') {
if (i === 0 || mask[i - 1] !== '_') {
let hasUnderscore = false;
let depth = 1;
for (let j = i + 1; j < len && depth > 0; j++) {
if (mask[j] === '(') depth++;
if (mask[j] === ')') depth--;
if (mask[j] === '_') {
hasUnderscore = true;
break;
}
}
if (! hasUnderscore) {
control.parenthesisForNegativeNumbers = true;
}
}
result += char;
} else {
result += char;
}
}
return result;
}
/**
* Receives a string from a method type and returns if it's a numeric method
*/
const isNumeric = function(t) {
return t === 'currency' || t === 'percentage' || t === 'numeric' || t === 'scientific';
}
const adjustPrecision = function(num) {
if (typeof num === 'number' && ! Number.isInteger(num)) {
const v = num.toString().split('.');
if (v[1] && v[1].length > 10) {
let t0 = 0;
const t1 = v[1][v[1].length - 2];
if (t1 == 0 || t1 == 9) {
for (let i = v[1].length - 2; i > 0; i--) {
if (t0 >= 0 && v[1][i] == t1) {
t0++;
if (t0 > 6) {
break;
}
} else {
t0 = 0;
break;
}
}
if (t0) {
return parseFloat(parseFloat(num).toFixed(v[1].length - 1));
}
}
}
}
return num;
}
/**
* Get the decimal defined in the mask configuration
*/
const getDecimal = function(v) {
let decimal;
if (this.decimal) {
decimal = this.decimal;
} else {
if (this.locale) {
let t = Intl.NumberFormat(this.locale).format(1.1);
decimal = t[1];
} else {
if (! v) {
v = this.mask;
}
// Fixed regex: 0* means zero or more 0s before decimal separator
let e = new RegExp('0*([,.])0+', 'ig');
let t = e.exec(v);
if (t && t[1] && t[1].length === 1) {
decimal = t[1];
} else {
// Try the second pattern for # formats
e = new RegExp('#{1}(.{1})#+', 'ig');
t = e.exec(v);
if (t && t[1] && t[1].length === 1) {
if (t[1] === ',') {
decimal = '.';
} else if (t[1] === "'" || t[1] === '.') {
decimal = ',';
}
}
}
if (! decimal) {
decimal = '1.1'.toLocaleString().substring(1, 2);
}
}
}
if (decimal) {
return decimal;
} else {
return null;
}
}
/**
* Caret position getter
* `this` in this function should be the element with a caret
*/
const getCaretPosition = function(editableDiv) {
let caretPos = 0;
let sel = window.getSelection();
if (sel && sel.rangeCount > 0) {
let range = sel.getRangeAt(0);
let preRange = range.cloneRange();
preRange.selectNodeContents(editableDiv);
preRange.setEnd(range.endContainer, range.endOffset);
caretPos = preRange.toString().length;
}
return caretPos;
}
/**
* Caret position getter
* `this` in this function should be the element with a caret
*/
const getCaret = function(el) {
if (el.tagName === 'DIV') {
return getCaretPosition(el);
} else {
return el.selectionStart;
}
}
/**
* Caret position setter
* `this` should be the element (input/textarea or contenteditable div)
*/
const setCaret = function(index) {
if (typeof index !== 'number') index = Number(index) || 0;
if (this.tagName !== 'DIV' || this.isContentEditable !== true) {
const n = this.value ?? '';
if (index < 0) index = 0;
if (index > n.length) index = n.length;
this.focus();
this.selectionStart = index;
this.selectionEnd = index;
return;
}
// Contenteditable DIV
const el = /** @type {HTMLElement} */ (this);
const totalLen = (el.textContent || '').length;
if (index < 0) index = 0;
if (index > totalLen) index = totalLen;
const sel = window.getSelection();
if (!sel) return;
const range = document.createRange();
el.focus();
// Empty element → ensure a text node to place the caret into
if (totalLen === 0) {
if (!el.firstChild) el.appendChild(document.createTextNode(''));
// place at start
range.setStart(el.firstChild, 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return;
}
// If caret is at the very end, this is fastest/cleanest
if (index === totalLen) {
range.selectNodeContents(el);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
return;
}
// Walk text nodes to find the node that contains the index-th character
const walker = document.createTreeWalker(
el,
NodeFilter.SHOW_TEXT,
{
acceptNode(node) {
// skip empty/whitespace-only nodes if you want; or just accept all text
return node.nodeValue ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
}
);
let pos = 0;
let node = walker.nextNode();
while (node) {
const nextPos = pos + node.nodeValue.length;
if (index <= nextPos) {
const offset = index - pos; // char offset within this text node
range.setStart(node, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return;
}
pos = nextPos;
node = walker.nextNode();
}
// Fallback: collapse at end if something unexpected happened
range.selectNodeContents(el);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
};
/**
* Methods to deal with different types of data
*/
const parseMethods = {
'FIND': function(v, a) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
// TODO: tratar eventos
if (this.event && this.event.inputType && this.event.inputType.indexOf('delete') > -1) {
this.values[this.index] += v;
return;
}
let pos = 0;
let count = 0;
let value = (this.values[this.index] + v).toLowerCase();
for (let i = 0; i < a.length; i++) {
if (a[i].toLowerCase().indexOf(value) === 0) {
pos = i;
count++;
}
}
if (count > 1) {
this.values[this.index] += v;
} else if (count === 1) {
// Jump a number of chars
let t = (a[pos].length - this.values[this.index].length) - 1;
this.position += t;
this.values[this.index] = a[pos];
this.index++;
return pos;
}
},
'YEAR': function(v, s) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
let number = parseInt(v);
if (number >= 0 && number <= 10) {
if (this.values[this.index].length < s) {
this.values[this.index] += v;
}
}
if (this.values[this.index].length === s) {
let y = new Date().getFullYear().toString();
if (s === 2) {
y = y.substring(0,2) + this.values[this.index];
} else if (s === 3) {
y = y.substring(0,1) + this.values[this.index];
} else if (s === 4) {
y = this.values[this.index];
}
this.date[0] = y;
this.index++;
}
},
'YYYY': function(v) {
parseMethods.YEAR.call(this, v, 4);
},
'YYY': function(v) {
parseMethods.YEAR.call(this, v, 3);
},
'YY': function(v) {
parseMethods.YEAR.call(this, v, 2);
},
'MMMMM': function(v) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
let value = (this.values[this.index] + v).toLowerCase();
for (var i = 0; i < Helpers.months.length; i++) {
if (Helpers.months[i][0].toLowerCase().indexOf(value) === 0) {
this.values[this.index] = Helpers.months[i][0];
this.date[1] = i + 1;
this.index++;
break;
}
}
},
'MMMM': function(v) {
let ret = parseMethods.FIND.call(this, v, Helpers.months);
if (typeof(ret) !== 'undefined') {
this.date[1] = ret + 1;
}
},
'MMM': function(v) {
let ret = parseMethods.FIND.call(this, v, Helpers.monthsShort);
if (typeof(ret) !== 'undefined') {
this.date[1] = ret + 1;
}
},
'MM': function(v, single) {
const commit = () => {
this.date[1] = this.values[this.index];
this.index++;
}
let number = parseInt(v);
if (isBlank(this.values[this.index])) {
if (number > 1 && number < 10) {
if (! single) {
v = '0' + v;
}
this.values[this.index] = v;
commit();
} else if (number < 2) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 1 && number < 3) {
this.values[this.index] += v;
commit();
} else if (this.values[this.index] == 0 && number > 0 && number < 10) {
this.values[this.index] += v;
commit();
} else {
let test = parseInt(this.values[this.index]);
if (test > 0 && test <= 12) {
if (! single) {
test = '0' + test;
}
this.values[this.index] = test;
commit();
return false;
}
}
}
},
'M': function(v) {
return parseMethods['MM'].call(this, v, true);
},
'MONTH': function(v) {
return parseMethods['MMMM'].call(this, v);
},
'MON': function(v) {
return parseMethods['MMM'].call(this, v);
},
'DDDD': function(v) {
return parseMethods.FIND.call(this, v, Helpers.weekdays);
},
'DDD': function(v) {
return parseMethods.FIND.call(this, v, Helpers.weekdaysShort);
},
'DD': function(v, single) {
const commit = () => {
this.date[2] = this.values[this.index];
this.index++;
}
let number = parseInt(v);
if (isBlank(this.values[this.index])) {
if (number > 3 && number < 10) {
if (! single) {
v = '0' + v;
}
this.values[this.index] = v;
commit();
} else if (number < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 3 && number < 2) {
this.values[this.index] += v;
commit();
} else if ((this.values[this.index] == 1 || this.values[this.index] == 2) && number < 10) {
this.values[this.index] += v;
commit();
} else if (this.values[this.index] == 0 && number > 0 && number < 10) {
this.values[this.index] += v;
commit();
} else {
let test = parseInt(this.values[this.index]);
if (test > 0 && test <= 31) {
if (! single) {
test = '0' + test;
}
this.values[this.index] = test;
commit();
return false;
}
}
}
},
'D': function(v) {
return parseMethods['DD'].call(this, v, true);
},
'DY': function(v) {
return parseMethods['DDD'].call(this, v);
},
'DAY': function(v) {
return parseMethods['DDDD'].call(this, v);
},
'HH12': function(v, two) {
let test = false;
let number = parseInt(v);
if (isBlank(this.values[this.index])) {
if (number > 1 && number < 10) {
if (two) {
v = 0 + v;
}
this.date[3] = this.values[this.index] = v;
this.index++;
} else if (number < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 1 && number < 3) {
this.date[3] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] < 1 && number < 10) {
this.date[3] = this.values[this.index] += v;
this.index++;
} else {
test = true;
}
}
// Re-test
if (test === true) {
var t = parseInt(this.values[this.index]);
if (t >= 0 && t <= 12) {
this.date[3] = this.values[this.index];
this.index++;
return false;
}
}
},
'HH24': function(v, two = true) {
let test = false;
let number = parseInt(v)
if (number >= 0 && number < 10) {
if (isBlank(this.values[this.index])) {
if (number > 2 && number < 10) {
if (two !== false) {
v = 0 + v;
}
this.date[3] = this.values[this.index] = v;
this.index++;
} else if (number < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 2 && number < 4) {
if (two === false && this.values[this.index] === '0') {
this.values[this.index] = '';
}
this.date[3] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] < 2 && number < 10) {
if (two === false && this.values[this.index] === '0') {
this.values[this.index] = '';
}
this.date[3] = this.values[this.index] += v;
this.index++;
} else {
test = true;
}
}
} else {
test = true;
}
// Re-test
if (test === true) {
var t = parseInt(this.values[this.index]);
if (t >= 0 && t < 24) {
// Pad single digit with leading zero for HH24 format
if (two !== false && this.values[this.index] && this.values[this.index].length === 1) {
this.values[this.index] = '0' + this.values[this.index];
}
this.date[3] = this.values[this.index];
this.index++;
return false;
}
}
},
'HH': function(v) {
parseMethods['HH24'].call(this, v, true);
},
'H': function(v) {
parseMethods['HH24'].call(this, v, false);
},
'\\[H\\]': function(v) {
if (this.values[this.index] == undefined) {
this.values[this.index] = '';
}
if (v.match(/[0-9]/g)) {
this.date[3] = this.values[this.index] += v;
} else {
if (this.values[this.index].match(/[0-9]/g)) {
this.date[3] = this.values[this.index];
this.index++;
return false;
}
}
},
'N60': function(v, i, two) {
let test = false;
let number = parseInt(v);
if (number >= 0 && number < 10) {
if (isBlank(this.values[this.index])) {
if (number > 5 && number < 10) {
if (two) {
v = '0' + v;
}
this.date[i] = this.values[this.index] = v;
this.index++;
} else if (number < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] < 6 && number < 10) {
if (! two && this.values[this.index] === '0') {
this.values[this.index] = '';
}
this.date[i] = this.values[this.index] += v;
this.index++;
} else {
test = true;
}
}
} else {
test = true;
}
// Re-test
if (test === true) {
var t = parseInt(this.values[this.index]);
if (t >= 0 && t < 60) {
this.date[i] = this.values[this.index];
this.index++;
return false;
}
}
},
'MI': function(v) {
parseMethods.N60.call(this, v, 4, true);
},
'SS': function(v) {
parseMethods.N60.call(this, v, 5, true);
},
'I': function(v) {
parseMethods.N60.call(this, v, 4, false);
},
'S': function(v) {
parseMethods.N60.call(this, v, 5, false);
},
'AM/PM': function(v) {
if (typeof(this.values[this.index]) === 'undefined') {
this.values[this.index] = '';
}
if (this.values[this.index] === '') {
if (v.match(/a/i) && this.date[3] < 13) {
this.values[this.index] += 'A';
} else if (v.match(/p/i)) {
this.values[this.index] += 'P';
}
} else if (this.values[this.index] === 'A' || this.values[this.index] === 'P') {
this.values[this.index] += 'M';
this.index++;
}
},
'WD': function(v) {
if (typeof(this.values[this.index]) === 'undefined') {
this.values[this.index] = '';
}
let number = parseInt(v);
if (number >= 0 && number < 7) {
this.values[this.index] = v;
}
if (this.values[this.index].length == 1) {
this.index++;
}
},
// Numeric Methods
'[0#]+([.,]{1}0*#*)?': function(v) {
if (v === '.' && inputIsANumber(this.raw)) {
v = this.decimal;
}
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
if (v === '-') {
// Transform the number into negative if it is not already
if (this.values[this.index][0] != '-') {
this.values[this.index] = '-' + this.values[this.index];
}
} else if (v === '+') {
// Transform the number into positive if it is negative
if (this.values[this.index][0] == '-') {
this.values[this.index] = this.values[this.index].replace('-', '');
}
} else if (v == '0') {
// Only adds zero if there's a non-zero number before
if (this.values[this.index] != '0' && this.values[this.index] != '-0') {
this.values[this.index] += v;
}
} else if (v > 0 && v < 10) {
// Verify if there's a zero to remove it, avoiding left zeros
if (this.values[this.index] == '0' || this.values[this.index] == '-0') {
this.values[this.index] = this.values[this.index].replace('0', '');
}
this.values[this.index] += v;
} else if (v === this.decimal) {
// Only adds decimal when there's a number value on its left
if (! this.values[this.index].includes(this.decimal)) {
if (! this.values[this.index].replace('-', '').length) {
this.values[this.index] += '0';
}
this.values[this.index] += this.decimal;
}
}
},
'[0#]+([.,]{1}0*#*)?%': function(v) {
parseMethods['[0#]+([.,]{1}0*#*)?'].call(this, v);
// Adds the % only if it has a number value
if (this.values[this.index].match(/[\-0-9]/g)) {
if (this.values[this.index].indexOf('%') !== -1) {
this.values[this.index] = this.values[this.index].replaceAll('%', '');
}
this.values[this.index] += '%';
} else {
this.values[this.index] = '';
}
},
'#(.{1})##0?(.{1}[0#]+)?( ?;(.*)?)?': function(v) {
// Process first the number
parseMethods['[0#]+([.,]{1}0*#*)?'].call(this, v, true);
// Create the separators
let separator = this.tokens[this.index].substring(1,2);
let currentValue = this.values[this.index];
// Remove existing separators and negative sign
currentValue = currentValue.replaceAll(separator, '');
// Process separators
let val = currentValue.split(this.decimal);
if (val[0].length > 3) {
let number = [];
let count = 0;
for (var j = val[0].length - 1; j >= 0 ; j--) {
let c = val[0][j];
if (c >= 0 && c <= 9) {
if (count && ! (count % 3)) {
number.unshift(separator);
}
count++;
}
number.unshift(c);
}
val[0] = number.join('');
}
// Reconstruct the value
this.values[this.index] = val.join(this.decimal);
},
'[0#]+([.,]{1}0*#*)?E{1}\\+0+': function(v) {
parseMethods['[0#]+([.,]{1}0*#*)?'].call(this, v);
},
'#{0,1}.*?\\?+\\/[0-9?]+': function (v) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
const token = this.tokens[this.index]; // e.g. "# ?/?", "?/2", "# ??/16"
let cur = this.values[this.index];
// Parse RHS of mask to decide denominator rule
const rhsRaw = (token.split('/')[1] || '').replace(/\s+/g, '');
const allowDen = /^\d+$/.test(rhsRaw) ? rhsRaw : /^\?+$/.test(rhsRaw) ? '?' : '?';
// ----- NEW: allow '-' as first char -----
if (v === '-') {
if (cur.length === 0) {
this.values[this.index] = '-';
}
return; // never return false
}
// ----------------------------------------
// Only accept digits / space / slash; ignore everything else
if (!(/[0-9\/ ]/.test(v))) {
return;
}
// If we already have a slash and denominator is fixed but not yet appended,
// auto-complete immediately regardless of what the user typed now.
const hasSlashNow = cur.includes('/');
if (hasSlashNow && allowDen !== '?') {
const afterSlash = cur.slice(cur.indexOf('/') + 1);
if (afterSlash.length === 0) {
this.values[this.index] = cur + allowDen;
this.index++; // move to next token
return;
}
}
// Empty -> only digits (or a leading '-' handled above)
if (cur.length === 0) {
if (/\d/.test(v)) this.values[this.index] = v;
return;
}
const hasSpace = cur.includes(' ');
const hasSlash = cur.includes('/');
const last = cur[cur.length - 1];
// Space rules: only one, must be before slash, must follow a digit
if (v === ' ') {
if (!hasSpace && !hasSlash && /\d/.test(last)) {
this.values[this.index] = cur + ' ';
}
return;
}
// Slash rules: only one slash, not right after a space, must follow a digit
if (v === '/') {
if (!hasSlash && last !== ' ' && /\d/.test(last)) {
if (allowDen === '?') {
this.values[this.index] = cur + '/';
} else {
this.values[this.index] = cur + '/' + allowDen;
this.index++; // conclude this token
}
}
return;
}
// Digit rules
if (/\d/.test(v)) {
if (!hasSlash) {
// Before slash: digits always fine
this.values[this.index] = cur + v;
return;
}
// After slash
if (allowDen === '?') {
this.values[this.index] = cur + v;
return;
}
// Fixed denominator: enforce prefix and advance when complete
const afterSlash = cur.slice(cur.indexOf('/') + 1);
const nextDen = afterSlash + v;
if (allowDen.startsWith(nextDen)) {
this.values[this.index] = cur + v;
if (nextDen.length === allowDen.length) {
this.index++;
}
}
}
},
'[a-zA-Z\\$]+': function(v) {
// Token to be added to the value
let word = this.tokens[this.index];
// Value
if (typeof(this.values[this.index]) === 'undefined') {
this.values[this.index] = '';
}
if (v === null) {
let size = this.values[this.index].length;
v = word.substring(size, size+1);
}
// Add the value
this.values[this.index] += v;
// Only if caret is before the change
let current = this.values[this.index];
// Add token to the values
if (current !== word.substring(0,current.length)) {
this.values[this.index] = word;
// Next token to process
this.index++;
return false;
} else if (current === word) {
// Next token to process
this.index++;
}
},
'A': function(v) {
return parseMethods['[a-zA-Z\\$]+'].call(this, v);
},
'a': function(v) {
return parseMethods['[a-zA-Z\\$]+'].call(this, v);
},
'.': function(v) {
return parseMethods['[a-zA-Z\\$]+'].call(this, v);
},
'&': function(v) {
if (v.match(/^[a-zA-Z ]+$/)) {
this.values[this.index] = v;
this.index++;
}
},
'\\*': function() {
this.values[this.index] = '';
this.index++;
return false;
},
'C': function(v) {
parseMethods['&'].call(this, v);
},
// General Methods
'0': function(v) {
if (v.match(/[0-9]/g)) {
this.values[this.index] = v;
this.index++;
}
},
'9': function(v) {
parseMethods['0'].call(this, v);
},
'#': function(v) {
parseMethods['0'].call(this, v);
},
'L': function(v) {
if (v.match(/[a-zA-Z]/gi)) {
this.values[this.index] = v;
this.index++;
}
},
'\\?': function(v) {
if (v.match(/[1-9]/g)) {
this.values[this.index] = v;
this.index++;
}
},
'@': function(v) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
this.values[this.index] += v;
},
'_[.\\s\\S]': function() {
this.values[this.index] = ' ';
this.index++;
return false;
},
'\\(': function() {
if (this.type === 'currency' && this.parenthesisForNegativeNumbers) {
this.values[this.index] = '';
} else {
this.values[this.index] = '(';
}
this.index++;
return false;
},
'\\)': function() {
if (this.type === 'currency' && this.parenthesisForNegativeNumbers) {
this.values[this.index] = '';
} else {
this.values[this.index] = ')';
}
this.index++;
return false;
},
',,M': function() {
this.values[this.index] = 'M';
this.index++;
return false;
},
',,,B': function() {
this.values[this.index] = 'B';
this.index++;
return false;
},
'\\\\[.\\s\\S]': function(v) {
this.values[this.index] = this.tokens[this.index].replace('\\', '');
this.index++;
return false;
}
}
const extractDate = function() {
let v = '';
if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
v = parseInt(this.date[3]);
} else {
let h = parseInt(this.date[3]);
if (h < 13 && this.values.indexOf('PM') !== -1) {
v = (h+12) % 24;
} else {
v = h % 24;
}
}
if (this.date[4]) {
v += parseFloat(this.date[4] / 60);
}
if (this.date[5]) {
v += parseFloat(this.date[5] / 3600);
}
v /= 24;
} else if (this.date[0] || this.date[1] || this.date[2] || this.date[3] || this.date[4] || this.date[5]) {
if (this.date[0] && this.date[1] && ! this.date[2]) {
this.date[2] = 1;
}
var t = Helpers.now(this.date);
v = Helpers.dateToNum(t);
}
if (isNaN(v)) {
v = '';
}
return v;
}
// Types TODO: Generate types so we can garantee that text,scientific, numeric,percentage, current are not duplicates. If they are, it will be general or broken.
const getTokens = function(str) {
// Check cache first - direct access, undefined check is fast
let result = tokensCache[str];
if (result !== undefined) {
return result;
}
allExpressionsRegex.lastIndex = 0; // Reset for global regex
result = str.match(allExpressionsRegex);
tokensCache[str] = result;
return result;
}
/**
* Get the method of one given token
*/
const getMethod = function(str, temporary) {
str = str.toString().toUpperCase();
// Check cache first - direct access, undefined check is fast
let cached = methodCache[str];
if (cached !== undefined) {
return cached;
}
// Check for datetime mask
const datetime = temporary.every(t => t.type === 'datetime' || t.type === 'general' || t.type === 'escape');
// Use priority order for faster matching with pre-compiled regexes
for (const type of tokenPriority) {
if (!datetime && type === 'datetime') continue;
for (const compiled of compiledTokens[type]) {
if (compiled.regex.test(str)) {
const result = { type: type, method: compiled.method };
methodCache[str] = result;
return result;
}
}
}
methodCache[str] = null;
return null;
}
const fixMinuteToken = function(t) {
const len = t.length;
for (let i = 0; i < len; i++) {
const token = t[i];
if (token === 'M' || token === 'MM') {
// Check if this M is a minute (near H or S) rather than month
let isMinute = false;
// Check previous 2 tokens for H (hour indicator)
if (i > 0) {
const prev1 = t[i - 1];
// Use includes for fast check - covers H, HH, HH24, HH12, [H], etc
if (prev1 && prev1.includes('H')) {
isMinute = true;
} else if (i > 1) {
const prev2 = t[i - 2];
if (prev2 && prev2.includes('H')) {
isMinute = true;
}
}
}
// Check next 2 tokens for S (seconds indicator) if not already determined
if (!isMinute && i < len - 1) {
const next1 = t[i + 1];
// Use includes for fast check - covers S, SS, MS, etc
if (next1 && next1.includes('S')) {
isMinute = true;
} else if (i < len - 2) {
const next2 = t[i + 2];
if (next2 && next2.includes('S')) {
isMinute = true;
}
}
}
if (isMinute) {
t[i] = token === 'M' ? 'I' : 'MI';
}
}
}
}
/**
* Identify each method for each token
*/
const getMethodsFromTokens = function(t) {
// Uppercase
t = t.map(v => {
return v.toString().toUpperCase();
});
// Compatibility with Excel
fixMinuteToken(t);
let result = [];
for (let i = 0; i < t.length; i++) {
var m = getMethod(t[i], result);
if (m) {
result.push(m);
} else {
result.push(null);
}
}
return result;
}
const getMethodByPosition = function(control) {
let methodName;
if (control.methods[control.index] && typeof(control.value[control.position]) !== 'undefined') {
methodName = control.methods[control.index].method;
}
let m = parseMethods[methodName];
if (typeof(m) === 'function') {
return m;
}
return false;
}
const processPaddingZeros = function(token, value, decimal) {
if (! value) {
return value;
}
let m = token.split(decimal);
let desiredNumOfPaddingZeros = m[0].match(/[0]+/g);
if (desiredNumOfPaddingZeros && desiredNumOfPaddingZeros[0]) {
desiredNumOfPaddingZeros = desiredNumOfPaddingZeros[0].length
let v = value.toString().split(decimal);
let len = v[0].length;
if (desiredNumOfPaddingZeros > len) {
v[0] = v[0].padStart(desiredNumOfPaddingZeros, '0');
return v.join(decimal);
}
}
}
const processNumOfPaddingZeros = function(control) {
let negativeSignal = false;
control.methods.forEach((method, k) => {
if (method) {
if (method.type === 'numeric' || method.type === 'percentage' || method.type === 'scientific') {
let ret = processPaddingZeros(control.tokens[k], control.values[k], control.decimal);
if (ret) {
control.values[k] = ret;
}
}
if (isNumeric(control.type) && control.parenthesisForNegativeNumbers === true) {
if (isNumeric(method.type)) {
if (control.values[k] && control.values[k].toString().includes('-')) {
control.values[k] = control.values[k].replace('-', '');
negativeSignal = true;
}
}
}
}
});
if (isNumeric(control.type) && control.parenthesisForNegativeNumbers === true && negativeSignal) {
control.methods.forEach((method, k) => {
if (! control.values[k] && control.tokens[k] === '(') {
control.values[k] = '(';
} else if (! control.values[k] && control.tokens[k] === ')') {
control.values[k] = ')';
}
});
}
}
const getValue = function(control) {
let value = control.values.join('');
if (isNumeric(control.type)) {
if (value.indexOf('--') !== false) {
value = value.replace('--', '-');
}
if (Number(control.raw) < 0 && value.includes('-')) {
value = '-' + value.replace('-', '');
}
}
return value;
}
const inputIsANumber = function(num) {
if (typeof(num) === 'string') {
num = num.trim();
}
return !isNaN(num) && num !== null && num !== '';
}
const getType = function(control) {
// Mask type
let type = 'general';
// Process other types
for (var i = 0; i < control.methods.length; i++) {
let m = control.methods[i];
if (m) {
let t = m.type;
if (t !== 'general' && t !== 'escape' && t !== type) {
if (type === 'general') {
type = t;
} else {
type = 'general';
break;
}
}
}
}
return type;
}
const isNumber = function(num) {
if (typeof(num) === 'string') {
num = num.trim();
}
return !isNaN(num) && num !== null && num !== '';
}
// TODO, get negative mask automatically based on the input sign?
const getConfig = function(config, value) {
// Internal default control of the mask system
const control = {
// Mask options
options: {},
// New values for each token found
values: [],
// Token position
index: 0,
// Character position
position: 0,
// Date raw values
date: [0,0,0,0,0,0],
// Raw number for the numeric values
number: 0,
}
if (typeof(value) === 'undefined' || value === null) {
value = '';
}
// Value to be masked
control.value = value.toString();
control.raw = value;
// Options defined by the user
if (typeof(config) == 'string') {
// Mask
control.mask = config;
} else if (config) {
// Mask
let k = Object.keys(config);
for (var i = 0; i < k.length; i++) {
control[k[i]] = config[k[i]];
}
}
// Controls of Excel that should be ignored
let mask = control.mask;
if (mask) {
if (mask.indexOf(';') !== -1) {
let d = mask.split(';');
// Mask
mask = d[0];
if (typeof (value) === 'number' || isNumber(value)) {
if (Number(value) < 0 && d[1]) {
mask = d[1];
} else if (Number(value) === 0 && d[2]) {
mask = d[2];
} else {
mask = d[0];
}
} else {
if (typeof(d[3]) !== 'undefined') {
mask = d[3];
}
}
}
// Transform Excel locale patterns (e.g., [$$-409]#,##0.00) - only if pattern exists
if (mask.indexOf('[$') !== -1) {
mask = transformExcelLocaleMask(mask);
}
// Cleaning the mask
mask = cleanMask(mask, control);
// Get only the first mask for now and remove
control.mask = mask;
// Get tokens which are the methods for parsing
let tokens = control.tokens = getTokens(mask);
// Get methods from the tokens
control.methods = getMethodsFromTokens(tokens);
// Type
control.type = getType(control);
}
// Decimal only for numbers
if (isNumeric(control.type) || control.locale) {
control.decimal = getDecimal.call(control);
}
return control;
}
const toPlainString = function(num) {
// Convert number to string if it isn't already
num = String(num);
// If it's not in exponential form, return as-is
if (!/e/i.test(num)) return num;
// Decompose scientific notation
const [coefficient, exponent] = num.toLowerCase().split('e');
const exp = parseInt(exponent, 10);
// Handle sign
const sign = coefficient[0] === '-' ? '-' : '';
const [intPart, fracPart = ''] = coefficient.replace('-', '').split('.');
const digits = intPart + fracPart;
const decimalPos = intPart.length;
let newPos = decimalPos + exp;
if (newPos <= 0) {
// Decimal point moves left
return sign + '0.' + '0'.repeat(-newPos) + digits;
} else if (newPos >= digits.length) {
// Decimal point moves right, add trailing zeros
return sign + digits + '0'.repeat(newPos - digits.length);
} else {
// Decimal point moves into the number
return sign + digits.slice(0, newPos) + '.' + digits.slice(newPos);
}
};
const adjustNumberOfDecimalPlaces = function(config, value) {
let temp = value;
let mask = config.mask;
if (config.type === 'scientific') {
// Scientific notation handling
mask = config.mask.toUpperCase().split('E')[0];
let numOfDecimalPlaces = mask.split(config.decimal);
// Handle masks without decimal (e.g., '0E+00')
if (numOfDecimalPlaces[1]) {
numOfDecimalPlaces = numOfDecimalPlaces[1].match(/[0#]+/g);
numOfDecimalPlaces = numOfDecimalPlaces[0]?.length ?? 0;
} else {
numOfDecimalPlaces = 0;
}
temp = temp.toExponential(numOfDecimalPlaces);
// Split by 'e' to handle both positive (e+) and negative (e-) exponents
let expo = temp.toString().split('e');
// Keep coefficient as string to preserve decimal places (e.g., '1.00' not 1)
temp = expo[0];
// Process padding zeros for coefficient
let ret = processPaddingZeros(mask, temp, config.decimal);
if (ret) {
temp = ret;
}
expo[0] = temp;
// Handle both E+ and E- in mask for exponent
mask = config.mask.toUpperCase().split(/E[+-]?/)[1];
ret = processPaddingZeros(mask, expo[1]?.replace(/^[+-]/, ''), config.decimal);
if (ret) {
// Preserve the sign from the original exponent
let sign = expo[1]?.charAt(0);
expo[1] = (sign === '-' ? '-' : '+') + ret;
}
temp = expo.join('e');
} else {
// Non-scientific decimal adjustment
if (mask.indexOf(config.decimal) === -1) {
// No decimal places
if (! Number.isInteger(temp)) {
temp = temp.toFixed(0);
}
} else {
// Length of the decimal
let mandatoryDecimalPlaces = mask.split(config.decimal);
mandatoryDecimalPlaces = mandatoryDecimalPlaces[1].match(/0+/g);
if (mandatoryDecimalPlaces) {
mandatoryDecimalPlaces = mandatoryDecimalPlaces[0].length;
} else {
mandatoryDecimalPlaces = 0;
}
// Amount of decimal (use original value to check decimal separator)
let valueStr = value.toString();
let numOfDecimalPlaces = valueStr.split(valueStr.includes('.') ? '.' : (config.decimal || '.'))
numOfDecimalPlaces = numOfDecimalPlaces[1]?.length ?? 0;
// Necessary adjustment
let necessaryAdjustment = 0;
if (numOfDecimalPlaces < mandatoryDecimalPlaces) {
necessaryAdjustment = mandatoryDecimalPlaces;
} else {
// Optional
let optionalDecimalPlaces = mask.split(config.decimal);
optionalDecimalPlaces = optionalDecimalPlaces[1].match(/[0#]+/g);
if (optionalDecimalPlaces) {
optionalDecimalPlaces = optionalDecimalPlaces[0].length;
if (numOfDecimalPlaces > optionalDecimalPlaces) {
necessaryAdjustment = optionalDecimalPlaces;
}
}
}
// Adjust decimal numbers if applicable
if (necessaryAdjustment) {
let t = temp.toFixed(necessaryAdjustment);
let n = temp.toString().split('.');
let fraction = n[1];
if (fraction && fraction.length > necessaryAdjustment && fraction[fraction.length - 1] === '5') {
t = parseFloat(n[0] + '.' + fraction + '1').toFixed(necessaryAdjustment);
}
temp = t;
}
}
}
return temp;
}
const formatFraction = function(value, mask) {
let maxDenominator;
let fixedDenominator = null;
let allowWholeNumber = true;
// Check for fixed denominator like # ?/8 or ?/8
const fixed = mask.match(/\/(\d+)/);
if (fixed) {
fixedDenominator = parseInt(fixed[1], 10);
maxDenominator = fixedDenominator;
} else {
// Determine based on question marks in mask
const match = mask.match(/\?\/(\?+)/);
if (match) {
maxDenominator = Math.pow(10, match[1].length) - 1;
} else {
maxDenominator = 9; // Default for # ?/? or ?/?
}
}
// Check if mask allows whole number (e.g., ?/? or ?/8 implies no whole number)
allowWholeNumber = mask.includes('#');
// If we have a fixed denominator, use it exactly (don't simplify)
if (fixedDenominator) {
const isNegative = value < 0;
const absValue = Math.abs(value);
const numerator = Math.round(absValue * fixedDenominator);
// For masks like ?/8, always output as pure fraction (no whole number)
if (!allowWholeNumber) {
return isNegative ? `-${numerator}/${fixedDenominator}` : `${numerator}/${fixedDenominator}`;
}
// For masks like # ?/8, allow whole number
const whole = Math.floor(numerator / fixedDenominator);
const remainder = numerator % fixedDenominator;
if (remainder === 0) {
return isNegative ? `-${whole}` : `${whole}`;
}
if (whole === 0) {
return isNegative ? `-${numerator}/${fixedDenominator}` : `${numerator}/${fixedDenominator}`;
}
return isNegative ? `-${whole} ${remainder}/${fixedDenominator}` : `${whole} ${remainder}/${fixedDenominator}`;
}
// Use continued fractions algorithm for better approximation
function continuedFraction(value, maxDenom) {
if (value === 0) return [0, 1];
let sign = value < 0 ? -1 : 1;
value = Math.abs(value);
let whole = Math.floor(value);
let frac = value - whole;
if (frac === 0) return [sign * whole, 1];
let h1 = 1, h2 = 0;
let k1 = 0, k2 = 1;
let x = frac;
while (k1 <= maxDenom) {
let a = Math.floor(x);
let h0 = a * h1 + h2;
let k0 = a * k1 + k2;
if (k0 > maxDenom) break;
h2 = h1; h1 = h0;
k2 = k1; k1 = k0;
if (Math.abs(x - a) < 1e-10) break;
x = 1 / (x - a);
}
// Add the whole part back only if allowed
let finalNum = sign * (allowWholeNumber ? whole * k1 + h1 : Math.round(value * k1));
let finalDen = k1;
return [finalNum, finalDen];
}
const [numerator, denominator] = continuedFraction(value, maxDenominator);
// Handle the result
const isNegative = numerator < 0;
const absNumerator = Math.abs(numerator);
const whole = allowWholeNumber ? Math.floor(absNumerator / denominator) : 0;
const remainder = absNumerator % denominator;
const sign = isNegative ? '-' : '';
if (remainder === 0) {
return `${sign}${whole || 0}`;
}
if (whole === 0 || !allowWholeNumber) {
return `${sign}${absNumerator}/${denominator}`;
}
return `${sign}${whole} ${remainder}/${denominator}`;
}
const extractDateAndTime = function(value) {
value = value.toString().substring(0,19);
let splitStr = (value.indexOf('T') !== -1) ? 'T' : ' ';
value = value.split(splitStr);
let y = null;
let m = null;
let d = null;
let h = '0';
let i = '0';
let s = '0';
if (! value[1]) {
if (value[0].indexOf(':') !== -1) {
value[0] = value[0].split(':');
h = value[0][0];
i = value[0][1];
s = value[0][2];
} else {
value[0] = value[0].split('-');
y = value[0][0];
m = value[0][1];
d = value[0][2];
}
} else {
value[0] = value[0].split('-');
y = value[0][0];
m = value[0][1];
d = value[0][2];
value[1] = value[1].split(':');
h = value[1][0];
i = value[1][1];
s = value[1][2];
}
return [y,m,d,h,i,s];
}
const format = function(str, config) {
// Extract numeric value from formatted or unformatted string (e.g., "₹3.00" -> 3)
// Using same logic as ParseValue to handle already-formatted strings
let numericValue;
const decimal = config.decimal || '.';
let v = String(str).split(decimal);
// Detect negative sign
let signal = v[0].includes('-');
// Extract digits only
v[0] = v[0].match(/[0-9]+/g);
if (v[0]) {
if (signal) v[0].unshift('-');
v[0] = v[0].join('');
} else {
v[0] = signal ? '-0' : '0';
}
if (v[1] !== undefined) {
v[1] = v[1].match(/[0-9]+/g);
v[1] = v[1] ? v[1].join('') : '';
}
// Convert to number
numericValue = v[0] || v[1] ? parseFloat(v.join('.')) : 0;
let ret = new Intl.NumberFormat(config.locale, config.options || {}).format(numericValue);
config.values.push(ret);
}
const Component = function(str, config, returnObject) {
// Get configuration
const control = getConfig(config, str);
if (control.locale) {
format(str, control);
} else if (control.mask) {
// Walk every character on the value
let method;
while (method = getMethodByPosition(control)) {
let char = control.value[control.position];
if (char === hiddenCaret) {
control.caret = {
index: control.index,
position: control.values[control.index]?.length ?? 0,
}
control.position++;
} else {
// Get the method name to handle the current token
let ret = method.call(control, char);
// Next position
if (ret !== false) {
control.position++;
}
}
}
// Move index
if (control.methods[control.index]) {
let type = control.methods[control.index].type;
if (isNumeric(type) && control.methods[++control.index]) {
let next;
while (next = control.methods[control.index]) {
if (control.methods[control.index].type === 'general') {
let method = control.methods[control.index].method;
if (method && typeof(parseMethods[method]) === 'function') {
parseMethods[method].call(control, null);
}
} else {
break;
}
}
}
}
}
if (control.caret) {
let index = control.caret.index;
let position = control.caret.position;
let value = control.values[index] ?? '';
// Re-apply the caret to the original position
control.values[index] = value.substring(0, position) + hiddenCaret + value.substring(position);
}
control.value = getValue(control);
if (returnObject) {
return control;
} else {
return control.value;
}
}
// Helper: Compare rendered value to original input
const testMask = function(mask, value, original) {
const rendered = Component.render(value, { mask }, true);
return rendered.replace(/\s/g, '') === original.replace(/\s/g, '');
}
const autoCastingFractions = function(value) {
const fractionPattern = /^\s*(-?\d+\s+)?(-?\d+)\/(\d+)\s*$/;
const fractionMatch = value.match(fractionPattern);
if (fractionMatch) {
const sign = value.trim().startsWith('-') ? -1 : 1;
const whole = fractionMatch[1] ? Math.abs(parseInt(fractionMatch[1])) : 0;
const numerator = Math.abs(parseInt(fractionMatch[2]));
const denominator = parseInt(fractionMatch[3]);
if (denominator === 0) return null;
const decimalValue = sign * (whole + (numerator / denominator));
// Determine the mask
let mask;
if ([2, 4, 8, 16, 32].includes(denominator)) {
mask = whole !== 0 ? `# ?/${denominator}` : `?/${denominator}`;
} else if (denominator <= 9) {
mask = whole !== 0 ? '# ?/?' : '?/?';
} else {
mask = whole !== 0 ? '# ??/??' : '??/??';
}
if (testMask(mask, decimalValue, value.trim())) {
return { mask, value: decimalValue, type: 'fraction', category: 'fraction' };
}
}
return null;
}
const autoCastingPercent = function(value) {
const percentPattern = /^\s*([+-]?\d+(?:[.,]\d+)?)%\s*$/;
const percentMatch = value.match(percentPattern);
if (percentMatch) {
const rawNumber = percentMatch[1].replace(',', '.');
const decimalValue = parseFloat(rawNumber) / 100;
const decimalPart = rawNumber.split('.')[1];
const decimalPlaces = decimalPart ? decimalPart.length : 0;
const mask = decimalPlaces > 0 ? `0.${'0'.repeat(decimalPlaces)}%` : '0%';
if (testMask(mask, decimalValue, value.trim())) {
return { mask: mask, value: decimalValue, type: 'percent', category: 'numeric' };
}
}
return null;
}
const autoCastingDates = function(value) {
if (!value || typeof value !== 'string') {
return null;
}
// Smart pattern detection based on the structure of the string
// 1. Analyze the structure to determine possible formats
const analyzeStructure = function(str) {
const patterns = [];
// Check for date with forward slashes: XX/XX/XXXX or XX/XX/XX
if (str.match(/^\d{1,2}\/\d{1,2}\/\d{2,4}$/)) {
const parts = str.split('/');
const p1 = parseInt(parts[0]);
const p2 = parseInt(parts[1]);
const p3 = parseInt(parts[2]);
// Determine likely format based on values
if (p1 <= 12 && p2 <= 31 && p2 > 12) {
// Likely mm/dd/yyyy
patterns.push('mm/dd/yyyy', 'mm/dd/yy', 'm/d/yyyy', 'm/d/yy');
} else if (p1 <= 31 && p2 <= 12 && p1 > 12) {
// Likely dd/mm/yyyy
patterns.push('dd/mm/yyyy', 'dd/mm/yy', 'd/m/yyyy', 'd/m/yy');
} else if (p1 <= 12 && p2 <= 12) {
// Ambiguous - could be either, use locale preference
if (userLocale.startsWith('en-US')) {
patterns.push('mm/dd/yyyy', 'dd/mm/yyyy', 'mm/dd/yy', 'dd/mm/yy');
} else {
patterns.push('dd/mm/yyyy', 'mm/dd/yyyy', 'dd/mm/yy', 'mm/dd/yy');
}
}
// Add variations
if (p3 < 100) {
patterns.push('dd/mm/yy', 'mm/dd/yy', 'd/m/yy', 'm/d/yy');
} else {
patterns.push('dd/mm/yyyy', 'mm/dd/yyyy', 'd/m/yyyy', 'm/d/yyyy');
}
}
// Check for date with dashes: XX-XX-XXXX
else if (str.match(/^\d{1,2}-\d{1,2}-\d{2,4}$/)) {
const parts = str.split('-');
const p1 = parseInt(parts[0]);
const p2 = parseInt(parts[1]);
if (p1 <= 12 && p2 <= 31 && p2 > 12) {
patterns.push('mm-dd-yyyy', 'mm-dd-yy', 'm-d-yyyy', 'm-d-yy');
} else if (p1 <= 31 && p2 <= 12 && p1 > 12) {
patterns.push('dd-mm-yyyy', 'dd-mm-yy', 'd-m-yyyy', 'd-m-yy');
} else {
patterns.push('dd-mm-yyyy', 'mm-dd-yyyy', 'dd-mm-yy', 'mm-dd-yy');
}
}
// Check for ISO format: YYYY-MM-DD
else if (str.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
patterns.push('yyyy-mm-dd', 'yyyy-m-d');
}
// Check for format: YYYY/MM/DD
else if (str.match(/^\d{4}\/\d{1,2}\/\d{1,2}$/)) {
patterns.push('yyyy/mm/dd', 'yyyy/m/d');
}
// Check for dates with month names
else if (str.match(/\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i)) {
// DD Mon YYYY or DD Month YYYY
if (str.match(/^\d{1,2}\s+\w+\s+\d{2,4}$/i)) {
patterns.push('dd mmm yyyy', 'dd mmmm yyyy', 'd mmm yyyy', 'd mmmm yyyy',
'dd mmm yy', 'dd mmmm yy', 'd mmm yy', 'd mmmm yy');
}
// Mon DD, YYYY or Month DD, YYYY
else if (str.match(/^\w+\s+\d{1,2},?\s+\d{2,4}$/i)) {
patterns.push('mmm dd, yyyy', 'mmmm dd, yyyy', 'mmm d, yyyy', 'mmmm d, yyyy',
'mmm dd yyyy', 'mmmm dd yyyy', 'mmm d yyyy', 'mmmm d yyyy');
}
// DD-Mon-YYYY
else if (str.match(/^\d{1,2}-\w+-\d{2,4}$/i)) {
patterns.push('dd-mmm-yyyy', 'dd-mmmm-yyyy', 'd-mmm-yyyy', 'd-mmmm-yyyy',
'dd-mmm-yy', 'dd-mmmm-yy', 'd-mmm-yy', 'd-mmmm-yy');
}
}
// Check for weekday formats
else if (str.match(/^(mon|tue|wed|thu|fri|sat|sun)/i)) {
if (str.match(/^\w+,\s+\d{1,2}\s+\w+\s+\d{4}$/i)) {
patterns.push('ddd, dd mmm yyyy', 'ddd, d mmm yyyy',
'dddd, dd mmmm yyyy', 'dddd, d mmmm yyyy');
}
}
// Check for datetime formats
else if (str.includes(' ') && str.match(/\d{1,2}:\d{2}/)) {
const parts = str.split(' ');
if (parts.length >= 2) {
const datePart = parts[0];
const timePart = parts.slice(1).join(' ');
// Determine date format
let dateMasks = [];
if (datePart.includes('/')) {
dateMasks = ['dd/mm/yyyy', 'mm/dd/yyyy', 'd/m/yyyy', 'm/d/yyyy'];
} else if (datePart.includes('-')) {
if (datePart.match(/^\d{4}-/)) {
dateMasks = ['yyyy-mm-dd', 'yyyy-m-d'];
} else {
dateMasks = ['dd-mm-yyyy', 'mm-dd-yyyy', 'd-m-yyyy', 'm-d-yyyy'];
}
}
// Determine time format
let timeMasks = [];
if (timePart.match(/\d{1,2}:\d{2}:\d{2}/)) {
timeMasks = ['hh:mm:ss', 'h:mm:ss'];
} else {
timeMasks = ['hh:mm', 'h:mm'];
}
// Add AM/PM variants if present
if (timePart.match(/[ap]m/i)) {
timeMasks = timeMasks.map(t => t + ' am/pm');
}
// Combine date and time masks
for (const dateMask of dateMasks) {
for (const timeMask of timeMasks) {
patterns.push(`${dateMask} ${timeMask}`);
}
}
}
}
// Check for time-only formats
else if (str.match(/^\d{1,2}:\d{2}(:\d{2})?(\s*(am|pm))?$/i)) {
if (str.match(/:\d{2}:\d{2}/)) {
patterns.push('hh:mm:ss', 'h:mm:ss');
if (str.match(/[ap]m/i)) {
patterns.push('hh:mm:ss am/pm', 'h:mm:ss am/pm');
}
} else {
patterns.push('hh:mm', 'h:mm');
if (str.match(/[ap]m/i)) {
patterns.push('hh:mm am/pm', 'h:mm am/pm');
}
}
}
// Check for extended hour format [h]:mm:ss
else if (str.match(/^\[?\d+\]?:\d{2}:\d{2}$/)) {
patterns.push('[h]:mm:ss');
}
return [...new Set(patterns)]; // Remove duplicates
};
// Get candidate masks based on the string structure
const candidateMasks = analyzeStructure(value);
// If no patterns detected, try some common formats as fallback
if (candidateMasks.length === 0) {
if (userLocale.startsWith('en-US')) {
candidateMasks.push(
'mm/dd/yyyy', 'mm-dd-yyyy', 'yyyy-mm-dd',
'mm/dd/yy', 'mm-dd-yy',
'hh:mm:ss', 'hh:mm', 'h:mm am/pm'
);
} else {
candidateMasks.push(
'dd/mm/yyyy', 'dd-mm-yyyy', 'yyyy-mm-dd',
'dd/mm/yy', 'dd-mm-yy',
'hh:mm:ss', 'hh:mm', 'h:mm'
);
}
}
// Try each candidate mask
for (const mask of candidateMasks) {
try {
// Use Component.extractDateFromString to parse the date
const isoDate = Component.extractDateFromString(value, mask);
if (isoDate && isoDate !== '') {
// Parse the ISO date string to components
const parts = isoDate.split(' ');
const dateParts = parts[0].split('-');
const timeParts = parts[1] ? parts[1].split(':') : ['0', '0', '0'];
const year = parseInt(dateParts[0]);
const month = parseInt(dateParts[1]);
const day = parseInt(dateParts[2]);
const hour = parseInt(timeParts[0]);
const minute = parseInt(timeParts[1]);
const second = parseInt(timeParts[2]);
// Validate the date components
if (year > 0 && month >= 1 && month <= 12 && day >= 1 && day <= 31 &&
hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) {
// Convert to Excel serial number
const excelNumber = Helpers.dateToNum(isoDate);
// Verify by rendering back
const rendered = Component.render(excelNumber, { mask: mask }, true);
// Case-insensitive comparison for month names
if (rendered.toLowerCase() === value.toLowerCase()) {
return {
mask: mask,
value: excelNumber,
type: 'date',
category: 'datetime'
};
}
}
}
} catch (e) {
}
}
// No matching format found
return null;
};
const autoCastingCurrency = function (input, filterDecimal) {
if (typeof input !== 'string') return null;
const str = input.trim();
if (!str) return null;
const len = str.length;
let isNegative = false;
let hasParens = false;
let symbol = '';
let numericPart = '';
let letterBuffer = '';
let firstCommaPos = -1;
let lastCommaPos = -1;
let firstDotPos = -1;
let lastDotPos = -1;
let commaCount = 0;
let dotCount = 0;
let hasInvalidChars = false;
let hasCurrencySymbol = false;
// Single pass through the string
for (let i = 0; i < len; i++) {
const char = str[i];
// Check for negative signs and parentheses
if (char === '-' && !numericPart) {
isNegative = true;
continue;
}
if (char === '(') {
hasParens = true;
isNegative = true;
continue;
}
if (char === ')') continue;
// Skip whitespace
if (char === ' ' || char === '\t') {
if (letterBuffer) {
letterBuffer += char;
}
continue;
}
// Currency symbols
if (char === '$' || char === '€' || char === '£' || char === '¥' ||
char === '₹' || char === '₽' || char === '₩' || char === '₫' || char === '¢') {
hasCurrencySymbol = true;
// Validate letter buffer: max 2 letters before symbol
const trimmedBuffer = letterBuffer.trim();
if (trimmedBuffer.length > 2) {
return null;
}
if (letterBuffer) {
symbol = letterBuffer + char;
letterBuffer = '';
} else {
symbol = char;
}
// Check if next char is a space to include it in symbol
if (i + 1 < len && (str[i + 1] === ' ' || str[i + 1] === '\t')) {
symbol += ' ';
i++; // Skip the space
}
continue;
}
// Letters (only valid BEFORE currency symbol, max 2 letters)
if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
// Letters after symbol or numeric part are invalid
if (hasCurrencySymbol || numericPart) {
return null;
}
letterBuffer += char;
continue;
}
// Digits
if (char >= '0' && char <= '9') {
// Reject if we have letters but no currency symbol
if (letterBuffer && !hasCurrencySymbol) {
return null;
}
letterBuffer = '';
numericPart += char;
continue;
}
// Comma and dot separators
if (char === ',') {
if (firstCommaPos === -1) firstCommaPos = numericPart.length;
lastCommaPos = numericPart.length;
commaCount++;
numericPart += char;
continue;
}
if (char === '.') {
if (firstDotPos === -1) firstDotPos = numericPart.length;
lastDotPos = numericPart.length;
dotCount++;
numericPart += char;
continue;
}
// Invalid character
hasInvalidChars = true;
}
// Reject if no currency symbol was found
if (!hasCurrencySymbol) {
return null;
}
// Reject if there are any invalid characters
if (hasInvalidChars) {
return null;
}
// If no numeric part, reject
if (!numericPart) return null;
// Ensure numeric part contains at least one digit (not just separators)
if (!/\d/.test(numericPart)) return null;
// Infer decimal and group separators
let decimal = '.';
let group = ',';
// If filterDecimal is provided, use it to determine separators
if (filterDecimal) {
decimal = filterDecimal;
group = filterDecimal === ',' ? '.' : ',';
// Validate that the input matches this interpretation
if (commaCount > 0 && dotCount > 0) {
// Both present: check if filterDecimal matches the last one (which should be decimal)
const lastIsComma = lastCommaPos > lastDotPos;
if ((filterDecimal === ',' && !lastIsComma) || (filterDecimal === '.' && lastIsComma)) {
return null;
}
} else if (dotCount === 1 && commaCount === 0) {
// Only one dot: if NOT followed by exactly 3 digits, it's a decimal separator
const afterDot = numericPart.substring(lastDotPos + 1);
if (afterDot.length !== 3) {
// It's a decimal separator, must match filterDecimal
if (filterDecimal !== '.') {
return null;
}
}
} else if (commaCount === 1 && dotCount === 0) {
// Only one comma: if NOT followed by exactly 3 digits, it's a decimal separator
const afterComma = numericPart.substring(lastCommaPos + 1);
if (afterComma.length !== 3) {
// It's a decimal separator, must match filterDecimal
if (filterDecimal !== ',') {
return null;
}
}
}
} else {
// Infer from the input pattern
if (commaCount > 0 && dotCount > 0) {
// Both present: the one that appears last is decimal
if (lastCommaPos > lastDotPos) {
decimal = ',';
group = '.';
}
} else if (dotCount === 1 && commaCount === 0) {
// Only one dot: check if it's followed by exactly 3 digits
const afterDot = numericPart.substring(lastDotPos + 1);
if (afterDot.length === 3) {
// Likely a thousands separator
decimal = ',';
group = '.';
}
} else if (commaCount === 1 && dotCount === 0) {
// Only one comma: check if it's followed by exactly 3 digits
const afterComma = numericPart.substring(lastCommaPos + 1);
if (afterComma.length !== 3) {
// Likely a decimal separator
decimal = ',';
group = '.';
}
}
}
// Normalize: remove group separator, convert decimal to '.'
let normalized = '';
for (let i = 0; i < numericPart.length; i++) {
const char = numericPart[i];
if (char === group) continue;
if (char === decimal) {
normalized += '.';
} else {
normalized += char;
}
}
const parsed = parseFloat(normalized);
if (isNaN(parsed)) return null;
const finalValue = isNegative ? -parsed : parsed;
// Build mask
const dotPos = normalized.indexOf('.');
const decimalPlaces = dotPos !== -1 ? normalized.length - dotPos - 1 : 0;
const maskDecimal = decimalPlaces ? decimal + '0'.repeat(decimalPlaces) : '';
const groupMask = '#' + group + '##0';
const needsSpace = symbol && !symbol.endsWith(' ') && !symbol.endsWith('\t') && !hasParens;
let mask = symbol + (needsSpace ? ' ' : '') + groupMask + maskDecimal;
if (hasParens) {
mask = `(${mask})`;
}
return {
mask,
value: finalValue,
type: 'currency',
category: 'numeric'
};
}
const autoCastingNumber = function (input, filterDecimal) {
// If you currently support numeric inputs directly, keep this:
if (typeof input === 'number' && Number.isFinite(input)) {
return { mask: '0', value: input, type: 'number', category: 'numeric' };
}
if (typeof input !== 'string') {
return null;
}
const sRaw = input.trim();
// Check for simple integers first (with optional sign)
if (/^[+-]?\d+$/.test(sRaw)) {
const sign = /^[+-]/.test(sRaw) ? sRaw[0] : '';
const digitsClean = (sign ? sRaw.slice(1) : sRaw);
const rawDigits = sign ? sRaw.slice(1) : sRaw;
const m = rawDigits.match(/^0+/);
const leadingZeros = m ? m[0].length : 0;
const mask = leadingZeros > 0 ? '0'.repeat(rawDigits.length) : '0';
const value = Number(sign + digitsClean);
return { mask, value, type: 'number', category: 'numeric' };
}
// Check for formatted numbers with thousand separators (no letters, no symbols)
// Examples: "1,000.25", "1.000,25", "-1,234.56"
if (/^[+-]?[\d,.]+$/.test(sRaw)) {
let isNegative = sRaw[0] === '-';
let numStr = isNegative ? sRaw.slice(1) : sRaw;
// Count separators
const commaCount = (numStr.match(/,/g) || []).length;
const dotCount = (numStr.match(/\./g) || []).length;
// Must have at least one digit
if (!/\d/.test(numStr)) return null;
// Infer decimal and group separators
let decimal = '.';
let group = ',';
const lastCommaPos = numStr.lastIndexOf(',');
const lastDotPos = numStr.lastIndexOf('.');
// If filterDecimal is provided, use it to determine separators
if (filterDecimal) {
decimal = filterDecimal;
group = filterDecimal === ',' ? '.' : ',';
// Validate that the input matches this interpretation
if (commaCount > 0 && dotCount > 0) {
// Both present: check if filterDecimal matches the last one (which should be decimal)
const lastIsComma = lastCommaPos > lastDotPos;
if ((filterDecimal === ',' && !lastIsComma) || (filterDecimal === '.' && lastIsComma)) {
return null;
}
} else if (dotCount === 1 && commaCount === 0) {
// Only one dot: if NOT followed by exactly 3 digits, it's a decimal separator
const afterDot = numStr.substring(lastDotPos + 1);
if (afterDot.length !== 3 || /[,.]/.test(afterDot)) {
// It's a decimal separator, must match filterDecimal
if (filterDecimal !== '.') {
return null;
}
}
} else if (commaCount === 1 && dotCount === 0) {
// Only one comma: if NOT followed by exactly 3 digits, it's a decimal separator
const afterComma = numStr.substring(lastCommaPos + 1);
if (afterComma.length !== 3 || /[,.]/.test(afterComma)) {
// It's a decimal separator, must match filterDecimal
if (filterDecimal !== ',') {
return null;
}
}
}
} else {
// Infer from the input pattern
if (commaCount > 0 && dotCount > 0) {
// Both present: the one that appears last is decimal
if (lastCommaPos > lastDotPos) {
decimal = ',';
group = '.';
}
} else if (dotCount === 1 && commaCount === 0) {
// Only one dot: check if it's followed by exactly 3 digits (thousands separator)
const afterDot = numStr.substring(lastDotPos + 1);
if (afterDot.length === 3 && !/[,.]/.test(afterDot)) {
decimal = ',';
group = '.';
}
} else if (commaCount === 1 && dotCount === 0) {
// Only one comma: check if it's NOT followed by exactly 3 digits (decimal separator)
const afterComma = numStr.substring(lastCommaPos + 1);
if (afterComma.length !== 3 || /[,.]/.test(afterComma)) {
decimal = ',';
group = '.';
}
}
}
// Check if group separator is actually used in the input
const hasGroupSeparator = (group === ',' && commaCount > 1) || (group === '.' && dotCount > 1) ||
(group === ',' && decimal === '.' && commaCount > 0) ||
(group === '.' && decimal === ',' && dotCount > 0);
// Normalize: remove group separator, convert decimal to '.'
let normalized = numStr.replace(new RegExp('\\' + group, 'g'), '').replace(decimal, '.');
const parsed = parseFloat(normalized);
if (isNaN(parsed)) return null;
const value = isNegative ? -parsed : parsed;
// Build mask
const dotPos = normalized.indexOf('.');
const decimalPlaces = dotPos !== -1 ? normalized.length - dotPos - 1 : 0;
const maskDecimal = decimalPlaces ? decimal + '0'.repeat(decimalPlaces) : '';
const mask = (hasGroupSeparator ? '#' + group + '##0' : '0') + maskDecimal;
return { mask, value, type: 'number', category: 'numeric' };
}
return null;
};
const autoCastingScientific = function(input) {
if (typeof input !== 'string') return null;
const original = input.trim();
// Match scientific notation: 1e3, -2.5E-4, etc.
const sciPattern = /^[-+]?\d*\.?\d+[eE][-+]?\d+$/;
if (!sciPattern.test(original)) return null;
const parsed = parseFloat(original);
if (isNaN(parsed)) return null;
// Extract parts to determine mask
const [coefficient, exponent] = original.toLowerCase().split('e');
const decimalPlaces = coefficient.includes('.') ? coefficient.split('.')[1].length : 0;
const mask = `0${decimalPlaces ? '.' + '0'.repeat(decimalPlaces) : ''}E+00`;
return {
mask,
value: parsed,
type: 'scientific',
category: 'scientific'
};
}
const autoCastingTime = function (input) {
if (typeof input !== 'string') return null;
const original = input.trim();
// hh:mm[:ss][ am/pm]
const m = original.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\s*(am|pm))?$/i);
if (!m) return null;
let h = parseInt(m[1], 10);
const i = parseInt(m[2], 10);
const s = m[3] ? parseInt(m[3], 10) : 0;
const mer = m[4] && m[4].toLowerCase();
// basic range checks
if (i > 59 || s > 59) return null;
if (mer) {
if (h < 1 || h > 12) return null;
if (mer === 'pm' && h < 12) h += 12;
if (mer === 'am' && h === 12) h = 0;
} else {
if (h > 23) return null;
}
// Excel serial for time-of-day = hours/24 + minutes/1440 + seconds/86400
const excel = (h + i / 60 + s / 3600) / 24;
// Build mask according to how user typed it
const hourToken = m[1].length === 1 ? 'h' : 'hh';
const base = s !== 0 || m[3] ? `${hourToken}:mm:ss` : `${hourToken}:mm`;
const mask = mer ? `${base} am/pm` : base;
// Verify we can render back exactly what the user typed
if (testMask(mask, excel, original)) { // uses Component.render under the hood
return { mask: mask, value: excel, type: 'time', category: 'datetime' };
}
// Try alternate hour width if needed
const altHour = hourToken === 'hh' ? 'h' : 'hh';
const alt = mer
? `${altHour}${base.slice(hourToken.length)} am/pm`
: `${altHour}${base.slice(hourToken.length)}`;
if (testMask(alt, excel, original)) {
return { mask: alt, value: excel, type: 'time', category: 'datetime' };
}
return null;
};
const ParseValue = function(v, config) {
if (v === '') return '';
const originalInput = '' + v;
const decimal = config.decimal || '.';
// Validate that the input looks like a reasonable number format before extracting digits
// Reject strings that are clearly not intended to be numbers (e.g., "test123", "abc", etc.)
const hasLetters = /[a-zA-Z]/.test(originalInput);
const hasDigits = /[0-9]/.test(originalInput);
if (hasLetters && hasDigits) {
// Mixed letters and digits - check if it's a valid numeric format
// Allow currency symbols, currency codes (3 letters), percentage, and separators
// Remove all valid numeric characters and symbols
let cleaned = originalInput.replace(/[\d\s.,\-+()]/g, ''); // Remove digits, spaces, separators, signs, parentheses
cleaned = cleaned.replace(/[A-Z]{1,2}[€$£¥₹₽₩₫¢]/gi, ''); // Remove 1-2 letter prefix + currency symbol (R$, U$, etc.)
cleaned = cleaned.replace(/[€$£¥₹₽₩₫¢]/g, ''); // Remove remaining currency symbols
cleaned = cleaned.replace(/%/g, ''); // Remove percentage
cleaned = cleaned.replace(/\b[A-Z]{3}\b/g, ''); // Remove 3-letter currency codes (USD, BRL, etc.)
// If anything remains, it's likely invalid (like "test" in "test123")
if (cleaned.trim().length > 0) {
return null; // Reject patterns like "test123", "abc123", etc.
}
}
v = originalInput.split(decimal);
// Detect negative sign
let signal = v[0].includes('-');
v[0] = v[0].match(/[0-9]+/g);
if (v[0]) {
if (signal) v[0].unshift('-');
v[0] = v[0].join('');
} else {
v[0] = signal ? '-' : '';
}
if (v[1] !== undefined) {
v[1] = v[1].match(/[0-9]+/g);
v[1] = v[1] ? v[1].join('') : '';
}
return v[0] || v[1] ? v : '';
}
const Extract = function(v, config) {
const parsed = ParseValue(v, config);
if (parsed) {
if (parsed[0] === '-') {
parsed[0] = '-0';
}
return parseFloat(parsed.join('.'));
}
return null;
}
/**
* Try to get which mask that can transform the number in that format
* @param {string|number} value - The value to detect the mask for
* @param {string} [decimal] - Optional decimal separator filter ('.' or ',')
*/
Component.autoCasting = function(value, decimal) {
// Check cache first - use string value and decimal as key
const cacheKey = decimal ? String(value) + '|' + decimal : String(value);
let cached = autoCastingCache[cacheKey];
if (cached !== undefined) {
return cached;
}
const methods = [
autoCastingDates, // Most structured, the least ambiguous
autoCastingTime,
autoCastingFractions, // Specific pattern with slashes
autoCastingPercent, // Recognizable with "%"
autoCastingScientific,
(v) => autoCastingNumber(v, decimal), // Only picks up basic digits, decimals, leading 0s
(v) => autoCastingCurrency(v, decimal), // Complex formats, but recognizable
];
let result = null;
for (let method of methods) {
const test = method(value);
if (test) {
result = test;
break;
}
}
// Cache the result (even if null)
autoCastingCache[cacheKey] = result;
return result;
}
Component.extract = function(value, options, returnObject) {
if (! value || typeof options !== 'object') {
return value;
}
if (options.locale) {
const config = getConfig(options, value);
config.value = Extract(value, config);
if (value.toString().indexOf('%') !== -1) {
config.value /= 100;
}
return returnObject ? config : config.value;
} else if (options.mask) {
let mask = options.mask.split(';')[0];
if (mask) {
options.mask = mask;
}
// Get decimal, group, type, etc.
const config = getConfig(options, value);
const type = config.type;
let result;
let o = options;
if (type === 'text') {
result = value;
} else if (type === 'general') {
result = Component(value, options);
} else if (type === 'datetime') {
if (value instanceof Date) {
value = Component.getDateString(value, config.mask);
}
o = Component(value, options, true);
result = typeof o.value === 'number' ? o.value : extractDate.call(o);
} else if (type === 'scientific') {
result = typeof value === 'string' ? Number(value) : value;
} else if (type === 'fraction') {
// Parse a fraction string according to the mask (supports mixed "# ?/d" or simple "?/d")
const mask = config.mask;
// Detect fixed denominator (e.g. "# ?/16" or "?/8")
const fixedDenMatch = mask.match(/\/\s*(\d+)\s*$/);
const fixedDen = fixedDenMatch ? parseInt(fixedDenMatch[1], 10) : null;
// Whether a mask allows a whole part (e.g. "# ?/?")
const allowWhole = mask.includes('#');
let s = ('' + value).trim();
if (!s) {
result = null;
} else {
// Allow leading parentheses or '-' for negatives
let sign = 1;
if (/^\(.*\)$/.test(s)) {
sign = -1;
s = s.slice(1, -1).trim();
}
if (/^\s*-/.test(s)) {
sign = -1;
s = s.replace(/^\s*-/, '').trim();
}
let out = null;
if (s.includes('/')) {
// sign? (whole )? numerator / denominator
// Examples:
// "1 1/2" => whole=1, num=1, den=2
// "1/2" => whole=undefined, num=1, den=2
const m = s.match(/^\s*(?:(\d+)\s+)?(\d+)\s*\/\s*(\d+)\s*$/);
if (m) {
const whole = allowWhole && m[1] ? parseInt(m[1], 10) : 0;
const num = parseInt(m[2], 10);
let den = parseInt(m[3], 10);
// If mask fixes the denominator, enforce it
if (fixedDen) den = fixedDen;
if (den !== 0) {
out = sign * (whole + num / den);
}
}
} else {
// No slash → treats as a plain number (e.g., whole only)
const plain = Number(s.replace(',', '.'));
if (!Number.isNaN(plain)) {
out = sign * Math.abs(plain);
}
}
result = out;
}
} else {
// Default fallback — numeric/currency/percent/etc.
result = Extract(value, config);
// Adjust percent
if (type === 'percentage' && ('' + value).indexOf('%') !== -1) {
result = result / 100;
}
}
o.value = result;
if (!o.type && type) {
o.type = type;
}
return returnObject ? o : result;
}
return value;
};
Component.render = function(value, options, fullMask, strict) {
// Nothing to render
if (value === '' || value === undefined || value === null) {
return '';
}
// Store original value for strict mode
const originalValue = value;
// Config
const config = getConfig(options, value);
if (config.locale) {
value = Component(value, options);
} else if (config.mask) {
// Percentage
if (config.type === 'datetime') {
var t = Component.getDateString(value, config.mask);
if (t) {
value = t;
} else {
return '';
}
} else if (config.type === 'text') {
// Parse number
if (typeof (value) === 'number') {
value = value.toString();
}
} else {
// Strict mode: for numeric masks, if string input is not a valid number,
// return original value unchanged (Excel-like behavior)
if (strict && typeof(originalValue) === 'string' && !isNumber(originalValue)) {
return originalValue;
}
if (config.type === 'percentage') {
if (typeof (value) === 'string' && value.indexOf('%') !== -1) {
value = value.replace('%', '');
} else {
value = adjustPrecision(Number(value) * 100);
}
} else {
if (config.mask.includes(',,M')) {
if (typeof (value) === 'string' && value.indexOf('M') !== -1) {
value = value.replace('M', '');
} else {
value = Number(value) / 1000000;
}
} else if (config.mask.includes(',,,B')) {
if (typeof (value) === 'string' && value.indexOf('B') !== -1) {
value = value.replace('B', '');
} else {
value = Number(value) / 1000000000;
}
}
}
if (typeof (value) === 'string' && isNumber(value)) {
value = Number(value);
}
if (typeof value === 'number') {
// Temporary value
let temp = value;
if (config.type === 'fraction') {
temp = formatFraction(value, config.mask);
} else {
if (fullMask) {
temp = adjustNumberOfDecimalPlaces(config, value);
if (config.type === 'scientific') {
return temp;
}
}
}
value = toPlainString(temp);
if (config.decimal === ',') {
value = value.replace('.', config.decimal);
}
}
}
// Process mask
let control = Component(value, options, true);
// Complement render
if (fullMask) {
processNumOfPaddingZeros(control);
}
value = getValue(control);
// If numeric mask but no numbers in input, return empty
if (isNumeric(control.type)) {
// Check if any numeric digit was actually extracted
const hasNumericValue = control.values.some(v => v && /\d/.test(v));
if (! hasNumericValue) {
value = '';
}
}
if (options.input && options.input.tagName) {
if (options.input.contentEditable) {
options.input.textContent = value;
} else {
options.input.value = value;
}
focus(options.input);
}
}
return value;
}
// Helper to extract date from a string
Component.extractDateFromString = function (date, format) {
let o = Component(date, { mask: format }, true);
// Check if in format Excel (Need difference with format date or type detected is numeric)
if (date > 0 && Number(date) == date && (o.values.join("") !== o.value || o.type == "numeric")) {
var d = new Date(Math.round((date - 25569) * 86400 * 1000));
return d.getFullYear() + "-" + Helpers.two(d.getMonth()) + "-" + Helpers.two(d.getDate()) + ' 00:00:00';
}
let complete = false;
if (o.values && o.values.length === o.tokens.length && o.values[o.values.length - 1].length >= o.tokens[o.tokens.length - 1].length) {
complete = true;
}
if (o.date[0] && o.date[1] && (o.date[2] || complete)) {
if (!o.date[2]) {
o.date[2] = 1;
}
return o.date[0] + '-' + Helpers.two(o.date[1]) + '-' + Helpers.two(o.date[2]) + ' ' + Helpers.two(o.date[3]) + ':' + Helpers.two(o.date[4]) + ':' + Helpers.two(o.date[5]);
}
return '';
}
// Tokens
const dateTokens = ['DAY', 'WD', 'DDDD', 'DDD', 'DD', 'D', 'Q', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'YYYY', 'YYY', 'YY', 'Y', 'MONTH', 'MON', 'MMMMM', 'MMMM', 'MMM', 'MM', 'M', '.'];
// All date tokens
const allDateTokens = dateTokens.join('|')
Component.getDateString = function(value, options) {
if (! options) {
options = {};
}
// Labels
let format;
if (options && typeof(options) == 'object') {
if (options.format) {
format = options.format;
} else if (options.mask) {
format = options.mask;
}
} else {
format = options;
}
if (! format) {
format = 'YYYY-MM-DD';
}
format = format.toUpperCase();
// Date instance
if (value instanceof Date) {
value = Helpers.now(value);
} else if (isNumber(value)) {
value = Helpers.numToDate(value);
}
// Expression to extract all tokens from the string
let e = new RegExp(allDateTokens, 'gi');
// Extract
let t = format.match(e);
// Compatibility with Excel
fixMinuteToken(t);
// Object
const o = {
tokens: t
}
// Value
if (value) {
try {
// Data
o.data = extractDateAndTime(value);
if (o.data[1] && o.data[1] > 12) {
throw new Error('Invalid date');
} else if (o.data[4] && o.data[4] > 59) {
throw new Error('Invalid date');
} else if (o.data[5] && o.data[5] > 59) {
throw new Error('Invalid date');
} else if (o.data[0] != null && o.data[1] != null) {
let day = new Date(o.data[0], o.data[1], 0).getDate();
if (o.data[2] > day) {
throw new Error('Invalid date');
}
}
// Value
o.value = [];
// Calendar instance
let calendar = new Date(o.data[0], o.data[1] - 1, o.data[2], o.data[3], o.data[4], o.data[5]);
if (o.data[0] === null && o.data[1] === null && o.data[2] === null) {
// Do nothing
} else {
if (isNaN(calendar.getTime())) {
throw new Error('Invalid date');
}
}
// Get method
const get = function (i) {
// Token
let t = this.tokens[i];
// Case token
let s = t.toUpperCase();
let v = null;
if (s === 'YYYY') {
v = this.data[0];
} else if (s === 'YYY') {
v = this.data[0];
if (v) {
v = v.substring(1, 4);
}
} else if (s === 'YY') {
v = this.data[0];
if (v) {
v = v.substring(2, 4);
}
} else if (s === 'Y') {
v = this.data[0];
if (v) {
v = v.substring(3, 4);
}
} else if (t === 'MON') {
v = Helpers.months[calendar.getMonth()];
if (v) {
v = v.substr(0, 3).toUpperCase();
}
} else if (t === 'mon') {
v = Helpers.months[calendar.getMonth()];
if (v) {
v = v.substr(0, 3).toLowerCase();
}
} else if (t === 'MONTH') {
v = Helpers.months[calendar.getMonth()];
if (v) {
v = v.toUpperCase();
}
} else if (t === 'month') {
v = Helpers.months[calendar.getMonth()];
if (v) {
v = v.toLowerCase();
}
} else if (s === 'MMMMM') {
v = Helpers.months[calendar.getMonth()];
if (v) {
v = v.substr(0, 1);
}
} else if (s === 'MMMM' || t === 'Month') {
v = Helpers.months[calendar.getMonth()];
} else if (s === 'MMM' || t == 'Mon') {
v = Helpers.months[calendar.getMonth()];
if (v) {
v = v.substr(0, 3);
}
} else if (s === 'MM') {
v = Helpers.two(this.data[1]);
} else if (s === 'M') {
v = calendar.getMonth() + 1;
} else if (t === 'DAY') {
v = Helpers.weekdays[calendar.getDay()];
if (v) {
v = v.toUpperCase();
}
} else if (t === 'day') {
v = Helpers.weekdays[calendar.getDay()];
if (v) {
v = v.toLowerCase();
}
} else if (s === 'DDDD' || t == 'Day') {
v = Helpers.weekdays[calendar.getDay()];
} else if (s === 'DDD') {
v = Helpers.weekdays[calendar.getDay()];
if (v) {
v = v.substr(0, 3);
}
} else if (s === 'DD') {
v = Helpers.two(this.data[2]);
} else if (s === 'D') {
v = parseInt(this.data[2]);
} else if (s === 'Q') {
v = Math.floor((calendar.getMonth() + 3) / 3);
} else if (s === 'HH24' || s === 'HH') {
v = this.data[3]%24;
if (this.tokens.indexOf('AM/PM') !== -1) {
if (v > 12) {
v -= 12;
} else if (v == '0' || v == '00') {
v = 12;
}
}
v = Helpers.two(v);
} else if (s === 'HH12') {
v = this.data[3]%24;
if (v > 12) {
v = Helpers.two(v - 12);
} else {
v = Helpers.two(v);
}
} else if (s === 'H') {
v = this.data[3]%24;
if (this.tokens.indexOf('AM/PM') !== -1) {
if (v > 12) {
v -= 12;
} else if (v == '0' || v == '00') {
v = 12;
}
}
} else if (s === '[H]') {
v = this.data[3];
} else if (s === 'MI') {
v = Helpers.two(this.data[4]);
} else if (s === 'I') {
v = parseInt(this.data[4]);
} else if (s === 'SS') {
v = Helpers.two(this.data[5]);
} else if (s === 'S') {
v = parseInt(this.data[5]);
} else if (s === 'MS') {
v = calendar.getMilliseconds();
} else if (s === 'AM/PM') {
if (this.data[3] >= 12) {
v = 'PM';
} else {
v = 'AM';
}
} else if (s === 'WD') {
v = Helpers.weekdays[calendar.getDay()];
}
if (v === null) {
this.value[i] = this.tokens[i];
} else {
this.value[i] = v;
}
}
for (let i = 0; i < o.tokens.length; i++) {
get.call(o, i);
}
value = o.value.join('');
} catch (e) {
value = '';
}
}
return value;
}
Component.getDate = function(value, format) {
if (! format) {
format = 'YYYY-MM-DD';
}
let ret = value;
if (ret && Number(ret) == ret) {
ret = Helpers.numToDate(ret);
}
// Try a formatted date
if (! Helpers.isValidDateFormat(ret)) {
let tmp = Component.extractDateFromString(ret, format);
if (tmp) {
ret = tmp;
}
}
return Component.getDateString(ret, format);
}
Component.oninput = function(e, mask) {
// Element
let element = e.target;
// Property
let property = 'value';
// Get the value of the input
if (element.tagName !== 'INPUT') {
property = 'textContent';
}
// Value
let value = element[property];
// Get the mask
if (! mask) {
mask = element.getAttribute('data-mask') || element.mask;
}
// Keep the current caret position
let caret = getCaret(element);
if (caret) {
value = value.substring(0, caret) + hiddenCaret + value.substring(caret);
}
if (typeof mask === 'string') {
mask = { mask: mask };
}
if (mask.locale) {
return;
}
// Run mask
let result = Component(value, mask, true);
// New value
let newValue = result.values.join('');
// Apply the result back to the element
if (newValue !== value && ! e.inputType.includes('delete')) {
// Set the caret to the position before transformation
let caret = newValue.indexOf(hiddenCaret);
if (caret !== -1) {
// Apply value
element[property] = newValue.replace(hiddenCaret, "");
// Set caret
setCaret.call(element, caret);
} else {
// Apply value
element[property] = newValue;
// Make sure the caret is positioned in the end
focus(element);
}
}
}
Component.getType = function(config) {
// Get configuration
const control = getConfig(config, null);
return control.type;
};
Component.adjustPrecision = adjustPrecision;
return Component;
})();
return { Mask: Mask, Helpers: Helpers };
})));
/***/ }),
/***/ 763:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (! lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
if (! Modal && "function" === 'function') {
var Modal = __webpack_require__(72);
}
if (! utils && "function" === 'function') {
var utils = __webpack_require__(791);
}
const Helpers = utils.Helpers;
const Mask = utils.Mask;
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
this.dispatchEvent(new CustomEvents(type, options));
}
}
// Translations
const T = function(t) {
if (typeof(document) !== "undefined" && document.dictionary) {
return document.dictionary[t] || t;
} else {
return t;
}
}
const filterData = function(year, month) {
// Data for the month
let data = {};
if (Array.isArray(this.data)) {
this.data.map(function (v) {
let d = year + '-' + Helpers.two(month + 1);
if (v.date.substring(0, 7) === d) {
if (!data[v.date]) {
data[v.date] = [];
}
data[v.date].push(v);
}
});
}
return data;
}
// Get the short weekdays name
const getWeekdays = function(firstDayOfWeek) {
const reorderedWeekdays = [];
for (let i = 0; i < 7; i++) {
const dayIndex = (firstDayOfWeek + i) % 7;
reorderedWeekdays.push(Helpers.weekdays[dayIndex]);
}
return reorderedWeekdays.map(w => {
return { title: w.substring(0, 1) };
});
}
const Views = function(self) {
const view = {};
// Create years container
view.years = [];
view.months = [];
view.days = [];
view.hours = [];
view.minutes = [];
for (let i = 0; i < 16; i++) {
view.years.push({
title: null,
value: null,
selected: false,
});
}
for (let i = 0; i < 12; i++) {
view.months.push({
title: null,
value: null,
selected: false,
});
}
for (let i = 0; i < 42; i++) {
view.days.push({
title: null,
value: null,
selected: false,
});
}
for (let i = 0; i < 24; i++) {
view.hours.push({
title: Helpers.two(i),
value: i
});
}
for (let i = 0; i < 60; i++) {
view.minutes.push({
title: Helpers.two(i),
value: i
});
}
view.years.update = function(date) {
let year = date.getUTCFullYear();
let start = year - (year % 16);
for (let i = 0; i < 16; i++) {
let item = view.years[i];
let value = start + i;
item.title = value
item.value = value;
if (self.cursor.y === value) {
item.selected = true;
// Current item
self.cursor.current = item;
} else {
item.selected = false;
}
}
}
view.months.update = function(date) {
let year = date.getUTCFullYear();
for (let i = 0; i < 12; i++) {
let item = view.months[i];
item.title = Helpers.months[i].substring(0,3);
item.value = i;
if (self.cursor.y === year && self.cursor.m === i) {
item.selected = true;
// Current item
self.cursor.current = item;
} else {
item.selected = false;
}
}
}
view.days.update = function(date) {
let year = date.getUTCFullYear();
let month = date.getUTCMonth();
let data = filterData.call(self, year, month);
// First day
let tmp = new Date(Date.UTC(year, month, 1, 0, 0, 0));
let firstDayOfMonth = tmp.getUTCDay();
let firstDayOfWeek = self.startingDay ?? 0;
// Calculate offset based on desired first day of week. firstDayOfWeek: 0 = Sunday, 1 = Monday, 2 = Tuesday, etc.
let offset = (firstDayOfMonth - firstDayOfWeek + 7) % 7;
let index = -1 * offset;
for (let i = 0; i < 42; i++) {
index++;
// Item
let item = view.days[i];
// Get the day
tmp = new Date(Date.UTC(year, month, index, 0, 0, 0));
// Day
let day = tmp.getUTCDate();
// Create the item
item.title = day;
item.value = index;
item.number = Helpers.dateToNum(tmp.toISOString().substring(0, 10));
// Reset range properties for each item
item.start = false;
item.end = false;
item.range = false;
item.last = false;
item.disabled = false;
item.data = null;
// Check selections
if (tmp.getUTCMonth() !== month) {
// Days are not in the current month
item.grey = true;
} else {
// Check for data
let d = [ year, Helpers.two(month+1), Helpers.two(day) ].join('-');
if (data && data[d]) {
item.data = data[d];
}
item.grey = false;
}
// Month
let m = tmp.getUTCMonth();
// Select cursor
if (self.cursor.y === year && self.cursor.m === m && self.cursor.d === day) {
item.selected = true;
// Current item
self.cursor.current = item;
} else {
item.selected = false;
}
// Valid ranges
if (self.validRange) {
if (typeof self.validRange === 'function') {
let ret = self.validRange(day,m,year,item);
if (typeof ret !== 'undefined') {
item.disabled = ret;
}
} else {
let current = year + '-' + Helpers.two(m+1) + '-' + Helpers.two(day);
let test1 = !self.validRange[0] || current >= self.validRange[0].substr(0, 10);
let test2 = !self.validRange[1] || current <= self.validRange[1].substr(0, 10);
if (! (test1 && test2)) {
item.disabled = true;
}
}
}
// Select range
if (self.range && self.rangeValues) {
// Only mark start/end if the number matches
item.start = self.rangeValues[0] === item.number;
item.end = self.rangeValues[1] === item.number;
// Mark as part of range if between start and end
item.range = self.rangeValues[0] && self.rangeValues[1] && self.rangeValues[0] <= item.number && self.rangeValues[1] >= item.number;
}
}
}
return view;
}
const isTrue = function(v) {
return v === true || v === 'true';
}
const isNumber = function (num) {
if (typeof(num) === 'string') {
num = num.trim();
}
return !isNaN(num) && num !== null && num !== '';
}
const Calendar = function(children, { onchange, onload, track }) {
let self = this;
// Event
let change = self.onchange;
self.onchange = null;
// Weekdays
self.weekdays = getWeekdays(self.startingDay ?? 0);
// Cursor
self.cursor = {};
// Time
self.time = !! self.time;
// Range values
self.rangeValues = null;
// Calendar date
let date = new Date();
// Views
const views = Views(self);
const hours = views.hours;
const minutes = views.minutes;
// Initial view
self.view = 'days';
// Auto Input
if (self.input === 'auto') {
self.input = document.createElement('input');
self.input.type = 'text';
}
// Get the position of the data based on the view
const getPosition = function() {
let position = 2;
if (self.view === 'years') {
position = 0;
} else if (self.view === 'months') {
position = 1;
}
return position;
}
const setView = function(e) {
if (typeof e === 'object') {
e = this.getAttribute('data-view');
}
// Valid views
const validViews = ['days', 'months', 'years'];
// Define new view
if (validViews.includes(e) && self.view !== e) {
self.view = e;
}
}
const reloadView = function(reset) {
if (reset) {
// Update options to the view
self.options = views[self.view];
}
// Update the values of hte options of hte view
views[self.view]?.update.call(self, date);
}
const getValue = function() {
let value = null;
if (isTrue(self.range)) {
if (Array.isArray(self.rangeValues)) {
if (isTrue(self.numeric)) {
value = self.rangeValues;
} else {
value = [
Helpers.numToDate(self.rangeValues[0]).substring(0, 10),
Helpers.numToDate(self.rangeValues[1]).substring(0, 10)
];
}
}
} else {
value = getDate();
if (isTrue(self.numeric)) {
value = Helpers.dateToNum(value);
}
}
return value;
}
const setValue = function(v) {
let d = new Date();
if (v) {
if (isTrue(self.range)) {
if (v) {
if (! Array.isArray(v)) {
v = v.toString().split(',');
}
self.rangeValues = [...v];
if (v[0] && typeof (v[0]) === 'string' && v[0].indexOf('-')) {
self.rangeValues[0] = Helpers.dateToNum(v[0]);
}
if (v[1] && typeof (v[1]) === 'string' && v[1].indexOf('-')) {
self.rangeValues[1] = Helpers.dateToNum(v[1]);
}
v = v[0];
}
} else if (typeof v === 'string' && v.includes(',')) {
v = v.split(',')[0];
}
if (v) {
v = isNumber(v) ? Helpers.numToDate(v) : v;
d = new Date(v + ' GMT+0');
}
// if no date is defined
if (! Helpers.isValidDate(d)) {
d = new Date();
}
}
// Update the internal calendar date
setDate(d, true);
// Update the view
reloadView();
}
const getDate = function() {
let v = [ self.cursor.y, self.cursor.m, self.cursor.d, self.hour, self.minute ];
let d = new Date(Date.UTC(...v));
// Update the headers of the calendar
if (self.time) {
return d.toISOString().substring(0, 19).replace('T', ' ');
} else {
return d.toISOString().substring(0, 10);
}
}
const setDate = function(d, update) {
if (Array.isArray(d)) {
d = new Date(Date.UTC(...d));
} else if (typeof(d) === 'string') {
d = new Date(d);
}
// Update the date
let value = d.toISOString().substring(0,10).split('-');
let month = Helpers.months[parseInt(value[1])-1];
let year = parseInt(value[0]);
if (self.month !== month) {
self.month = month;
}
if (self.year !== year) {
self.year = year;
}
// Update the time
let time = d.toISOString().substring(11,19).split(':');
let hour = parseInt(time[0]);
let minute = parseInt(time[1]);
if (self.hour !== hour) {
self.hour = hour;
}
if (self.minute !== minute) {
self.minute = minute;
}
// Update internal date
date = d;
// Update cursor information
if (update) {
updateCursor();
}
}
const updateCursor = function() {
self.cursor.y = date.getUTCFullYear();
self.cursor.m = date.getUTCMonth();
self.cursor.d = date.getUTCDate();
}
const resetCursor = function() {
// Remove selection from the current object
let current = self.cursor.current;
// Current item
if (typeof current !== 'undefined') {
current.selected = false;
}
}
const setCursor = function(s) {
// Reset current visual cursor
resetCursor();
// Update cursor based on the object position
if (s) {
// Update current
self.cursor.current = s;
// Update selected property
s.selected = true;
}
updateCursor();
// Update range
if (isTrue(self.range)) {
updateRange(s)
}
Dispatch.call(self, self.onupdate, 'update', {
instance: self,
value: date.toISOString(),
});
}
const select = function(e, s) {
// Get new date content
let d = updateDate(s.value, getPosition());
// New date
setDate(new Date(Date.UTC(...d)))
// Based where was the click
if (self.view !== 'days') {
// Back to the days
self.view = 'days';
} else if (! s.disabled) {
if (isTrue(self.range)) {
// Start a new range
if (self.rangeValues && (self.rangeValues[0] >= s.number || self.rangeValues[1])) {
destroyRange();
}
// Range
s.range = true;
// Update range
if (! self.rangeValues) {
s.start = true;
self.rangeValues = [s.number, null];
} else {
s.end = true;
self.rangeValues[1] = s.number;
}
setCursor(s);
} else {
setCursor(s);
update(e);
}
}
}
// Update Calendar
const update = function(e) {
self.setValue(getValue());
if (! (e && e.type === 'click' && e.target.tagName === 'DIV' && self.time === true)) {
self.close({ origin: 'button' });
}
}
const reset = function() {
self.setValue('');
self.close({ origin: 'button' });
}
const updateDate = function(v, position) {
// Current internal date
let value = [date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), self.hour, self.minute, 0];
// Update internal date
value[position] = v;
// Return new value
return value;
}
const move = function(direction) {
// Reset visual cursor
resetCursor();
// Value
let value;
// Update the new internal date
if (self.view === 'days') {
// Select the new internal date
value = updateDate(date.getUTCMonth()+direction, 1);
} else if (self.view === 'months') {
// Select the new internal date
value = updateDate(date.getUTCFullYear()+direction, 0);
} else if (self.view === 'years') {
// Select the new internal date
value = updateDate(date.getUTCFullYear()+(direction*16), 0);
}
// Update view
setDate(value);
// Reload content of the view
reloadView();
}
const getJump = function(e) {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
return self.view === 'days' ? 7 : 4;
}
return 1;
}
const prev = function(e) {
if (e && e.type === 'keydown') {
// Current index
let total = self.options.length;
let position = self.options.indexOf(self.cursor.current) - getJump(e);
if (position < 0) {
// Next month
move(-1);
// New position
position = total + position;
}
// Update cursor
setCursor(self.options[position])
} else {
move(-1);
}
}
const next = function(e) {
if (e && e.type === 'keydown') {
// Current index
let total = self.options.length;
let position = self.options.indexOf(self.cursor.current) + getJump(e);
if (position >= total) {
// Next month
move(1);
// New position
position = position - total;
}
// Update cursor
setCursor(self.options[position])
} else {
move(1);
}
}
const getInput = function() {
let input = self.input;
if (input && input.current) {
input = input.current;
} else {
if (self.input) {
input = self.input;
}
}
return input;
}
const updateRange = function(s) {
if (self.range && self.view === 'days' && self.rangeValues) {
// Creating a range
if (self.rangeValues[0] && ! self.rangeValues[1]) {
let number = s.number;
if (number) {
// Update range properties
for (let i = 0; i < self.options.length; i++) {
let v = self.options[i].number;
// Update property condition
self.options[i].range = v >= self.rangeValues[0] && v <= number;
self.options[i].last = (v === number);
}
}
}
}
}
const destroyRange = function() {
if (self.range) {
for (let i = 0; i < self.options.length; i++) {
if (self.options[i].range !== false) {
self.options[i].range = false;
}
if (self.options[i].start !== false) {
self.options[i].start = false;
}
if (self.options[i].end !== false) {
self.options[i].end = false;
}
if (self.options[i].last !== false) {
self.options[i].last = false;
}
}
self.rangeValues = null;
}
}
const render = function(v) {
if (v) {
if (! Array.isArray(v)) {
v = v.toString().split(',');
}
v = v.map(entry => {
return Mask.render(entry, self.format || 'YYYY-MM-DD');
}).join(',');
}
return v;
}
const normalize = function(v) {
if (! Array.isArray(v)) {
v = v.toString().split(',');
}
return v.map(item => {
if (Number(item) == item) {
return Helpers.numToDate(item);
} else {
if (Helpers.isValidDateFormat(item)) {
return item;
} else if (self.format) {
let tmp = Mask.extractDateFromString(item, self.format);
if (tmp) {
return tmp;
}
}
}
})
}
const extractValueFromInput = function() {
let input = getInput();
if (input) {
let v;
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
v = input.value;
} else if (input.isContentEditable) {
v = input.textContent;
}
if (v) {
return normalize(v).join(',');
}
return v;
}
}
const onopen = function() {
let isEditable = false;
let value = self.value;
let input = getInput();
if (input) {
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
isEditable = !input.hasAttribute('readonly') && !input.hasAttribute('disabled');
} else if (input.isContentEditable) {
isEditable = true;
}
let ret = extractValueFromInput();
if (ret && ret !== value) {
value = ret;
}
}
if (! isEditable) {
self.content.focus();
}
// Update the internal date values
setValue(value);
// Open event
Dispatch.call(self, self.onopen, 'open', {
instance: self
});
}
const onclose = function(modal, origin) {
// Cancel range events
destroyRange();
// Close event
Dispatch.call(self, self.onclose, 'close', {
instance: self,
origin: origin,
});
}
const dispatchOnChangeEvent = function() {
// Destroy range
destroyRange();
// Update the internal controllers
setValue(self.value);
// Events
Dispatch.call(self, change, 'change', {
instance: self,
value: self.value,
});
// Update input
let input = getInput();
if (input) {
// Update input value
input.value = render(self.value);
// Dispatch event
Dispatch.call(input, null, 'change', {
instance: self,
value: self.value,
});
}
}
const events = {
focusin: (e) => {
if (self.modal && self.isClosed()) {
self.open();
}
},
focusout: (e) => {
if (self.modal && ! self.isClosed()) {
if (! (e.relatedTarget && self.modal.el.contains(e.relatedTarget))) {
self.modal.close({ origin: 'focusout' });
}
}
},
click: (e) => {
if (e.target.classList.contains('lm-calendar-input')) {
self.open();
}
},
keydown: (e) => {
if (self.modal) {
if (e.code === 'ArrowUp' || e.code === 'ArrowDown') {
if (! self.isClosed()) {
self.content.focus();
} else {
self.open();
}
} else if (e.code === 'Enter') {
if (! self.isClosed()) {
update(e);
} else {
self.open();
}
} else if (e.code === 'Escape') {
if (! self.isClosed()) {
self.modal.close({origin: 'escape'});
}
}
}
},
input: (e) => {
let input = e.target;
if (input.classList.contains('lm-calendar-input')) {
if (! isTrue(self.range)) {
// TODO: process with range
// Apply mask
if (self.format) {
Mask.oninput(e, self.format);
}
let value = null;
// Content
let content = (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') ? input.value : input.textContent;
// Check if that is a valid date
if (Helpers.isValidDateFormat(content)) {
value = content;
} else if (self.format) {
let tmp = Mask.extractDateFromString(content, self.format);
if (tmp) {
value = tmp;
}
}
// Change the calendar view
if (value) {
setValue(value);
}
}
}
}
}
// Onload
onload(() => {
if (self.type !== "inline") {
// Create modal instance
self.modal = {
width: 300,
closed: true,
focus: false,
onopen: onopen,
onclose: onclose,
position: 'absolute',
'auto-close': false,
'auto-adjust': true,
};
// Generate modal
Modal(self.el, self.modal);
}
let ret;
// Create input controls
if (self.input && self.initInput !== false) {
if (! self.input.parentNode) {
self.el.parentNode.insertBefore(self.input, self.el);
}
let input = getInput();
if (input && input.tagName) {
input.classList.add('lm-input');
input.classList.add('lm-calendar-input');
input.addEventListener('click', events.click);
input.addEventListener('input', events.input);
input.addEventListener('keydown', events.keydown);
input.addEventListener('focusin', events.focusin);
input.addEventListener('focusout', events.focusout);
if (self.placeholder) {
input.setAttribute('placeholder', self.placeholder);
}
if (self.onChange) {
input.addEventListener('change', self.onChange);
}
// Retrieve the value
if (self.value) {
input.value = render(self.value);
} else {
let value = extractValueFromInput();
if (value && value !== self.value) {
ret = value;
}
}
}
}
// Update the internal date values
if (ret) {
self.setValue(ret);
} else {
setValue(self.value);
}
// Reload view
reloadView(true);
/**
* Handler keyboard
* @param {object} e - event
*/
self.el.addEventListener('keydown', function(e) {
let prevent = false;
if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
if (e.target !== self.content) {
self.content.focus();
}
prev(e);
prevent = true;
} else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
if (e.target !== self.content) {
self.content.focus();
}
next(e);
prevent = true;
} else if (e.key === 'Enter') {
if (e.target === self.content) {
// Item
if (self.cursor.current) {
// Select
select(e, self.cursor.current);
prevent = true;
}
}
} else if (e.key === 'Escape') {
if (! self.isClosed()) {
self.close({ origin: 'escape' });
prevent = true;
}
}
if (prevent) {
e.preventDefault();
e.stopImmediatePropagation();
}
});
/**
* Mouse wheel handler
* @param {object} e - mouse event
*/
self.content.addEventListener('wheel', function(e){
if (self.wheel !== false) {
if (e.deltaY < 0) {
prev(e);
} else {
next(e);
}
e.preventDefault();
}
}, { passive: false });
/**
* Range handler
* @param {object} e - mouse event
*/
self.content.addEventListener('mouseover', function(e){
let parent = e.target.parentNode
if (parent === self.content) {
let index = Array.prototype.indexOf.call(parent.children, e.target);
updateRange(self.options[index]);
}
});
// Create event for focus out
self.el.addEventListener("focusout", (e) => {
let input = getInput();
if (e.relatedTarget !== input && ! self.el.contains(e.relatedTarget)) {
self.close({ origin: 'focusout' });
}
});
});
onchange((prop) => {
if (prop === 'view') {
reloadView(true);
} else if (prop === 'startingDay') {
self.weekdays = getWeekdays(self.startingDay ?? 0);
} else if (prop === 'value') {
dispatchOnChangeEvent();
}
})
// Tracking variables
track('value');
// Public methods
self.open = function(e) {
if (self.modal) {
if (self.type === 'auto') {
self.type = window.innerWidth > 640 ? self.type = 'default' : 'picker';
}
self.modal.open();
}
}
self.close = function(options) {
if (self.modal) {
if (options && options.origin) {
self.modal.close(options)
} else {
self.modal.close({ origin: 'button' })
}
}
}
self.isClosed = function() {
if (self.modal) {
return self.modal.isClosed();
}
}
self.getValue = function() {
return self.value;
}
self.setValue = function(v) {
// Update value
if (v) {
let ret = normalize(v);
if (isTrue(self.numeric)) {
ret = ret.map(entry => {
return Helpers.dateToNum(entry);
})
}
if (! Array.isArray(v)) {
ret = ret.join(',');
}
if (ret == Number(ret)) {
ret = Number(ret);
}
v = ret;
}
// Events
if (v !== self.value) {
self.value = v;
}
}
self.onevent = function(e) {
if (events[e.type]) {
events[e.type](e);
}
}
self.update = update;
self.next = next;
self.prev = prev;
self.reset = reset;
self.setView = setView;
self.helpers = Helpers;
self.helpers.getDate = Mask.getDate;
return render => render`<div class="lm-calendar" data-grid="{{self.grid}}" data-type="{{self.type}}" data-disabled="{{self.disabled}}" data-starting-day="{{self.startingDay}}">
<div class="lm-calendar-options">
<button type="button" onclick="${reset}">${T('Reset')}</button>
<button type="button" onclick="${update}">${T('Done')}</button>
</div>
<div class="lm-calendar-container" data-view="{{self.view}}">
<div class="lm-calendar-header">
<div>
<div class="lm-calendar-labels"><button type="button" onclick="${setView}" data-view="months">{{self.month}}</button> <button type="button" onclick="${setView}" data-view="years">{{self.year}}</button></div>
<div class="lm-calendar-navigation">
<button type="button" class="lm-calendar-icon lm-ripple" onclick="${prev}" tabindex="0">expand_less</button>
<button type="button" class="lm-calendar-icon lm-ripple" onclick="${next}" tabindex="0">expand_more</button>
</div>
</div>
<div class="lm-calendar-weekdays" :loop="self.weekdays"><div>{{self.title}}</div></div>
</div>
<div class="lm-calendar-content" :loop="self.options" tabindex="0" :ref="self.content">
<div data-start="{{self.start}}" data-end="{{self.end}}" data-last="{{self.last}}" data-range="{{self.range}}" data-event="{{self.data}}" data-grey="{{self.grey}}" data-bold="{{self.bold}}" data-selected="{{self.selected}}" data-disabled="{{self.disabled}}" onclick="${select}">{{self.title}}</div>
</div>
<div class="lm-calendar-footer" data-visible="{{self.footer}}">
<div class="lm-calendar-time" data-visible="{{self.time}}"><select :loop="${hours}" :bind="self.hour" class="lm-calendar-control"><option value="{{self.value}}">{{self.title}}</option></select>:<select :loop="${minutes}" :bind="self.minute" class="lm-calendar-control"><option value="{{self.value}}">{{self.title}}</option></select></div>
<div class="lm-calendar-update"><input type="button" value="${T('Update')}" onclick="${update}" class="lm-ripple lm-input"></div>
</div>
</div>
</div>`
}
// Register the LemonadeJS Component
lemonade.setComponents({ Calendar: Calendar });
// Register the web component
lemonade.createWebComponent('calendar', Calendar);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Calendar, root, options)
return options;
} else {
return Calendar.call(this, root)
}
}
})));
/***/ }),
/***/ 541:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (! lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
if (! Modal && "function" === 'function') {
var Modal = __webpack_require__(72);
}
if (! Tabs && "function" === 'function') {
var Tabs = __webpack_require__(560);
}
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
method(...a);
} else if (this.tagName) {
// Fallback: dispatch a custom event
const event = new CustomEvent(type, {
bubbles: true,
cancelable: true,
detail: options,
});
this.dispatchEvent(event);
}
}
const defaultPalette = [
["#ffebee", "#fce4ec", "#f3e5f5", "#e8eaf6", "#e3f2fd", "#e0f7fa", "#e0f2f1", "#e8f5e9", "#f1f8e9", "#f9fbe7", "#fffde7", "#fff8e1", "#fff3e0", "#fbe9e7", "#efebe9", "#fafafa", "#eceff1"],
["#ffcdd2", "#f8bbd0", "#e1bee7", "#c5cae9", "#bbdefb", "#b2ebf2", "#b2dfdb", "#c8e6c9", "#dcedc8", "#f0f4c3", "#fff9c4", "#ffecb3", "#ffe0b2", "#ffccbc", "#d7ccc8", "#f5f5f5", "#cfd8dc"],
["#ef9a9a", "#f48fb1", "#ce93d8", "#9fa8da", "#90caf9", "#80deea", "#80cbc4", "#a5d6a7", "#c5e1a5", "#e6ee9c", "#fff59d", "#ffe082", "#ffcc80", "#ffab91", "#bcaaa4", "#eeeeee", "#b0bec5"],
["#e57373", "#f06292", "#ba68c8", "#7986cb", "#64b5f6", "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f", "#ffb74d", "#ff8a65", "#a1887f", "#e0e0e0", "#90a4ae"],
["#ef5350", "#ec407a", "#ab47bc", "#5c6bc0", "#42a5f5", "#26c6da", "#26a69a", "#66bb6a", "#9ccc65", "#d4e157", "#ffee58", "#ffca28", "#ffa726", "#ff7043", "#8d6e63", "#bdbdbd", "#78909c"],
["#f44336", "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b"],
["#e53935", "#d81b60", "#8e24aa", "#3949ab", "#1e88e5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e", "#6d4c41", "#757575", "#546e7a"],
["#d32f2f", "#c2185b", "#7b1fa2", "#303f9f", "#1976d2", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#5d4037", "#616161", "#455a64"],
["#c62828", "#ad1457", "#6a1b9a", "#283593", "#1565c0", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#4e342e", "#424242", "#37474f"],
["#b71c1c", "#880e4f", "#4a148c", "#1a237e", "#0d47a1", "#006064", "#004d40", "#1b5e20", "#33691e", "#827717", "#f57f17", "#ff6f00", "#e65100", "#bf360c", "#3e2723", "#212121", "#263238"],
]
const Grid = function(children, { onchange }) {
const self = this;
if (! self.palette) {
self.palette = defaultPalette;
}
const select = (event) => {
if (event.target.tagName === 'TD') {
let color = event.target.getAttribute('data-value')
// Remove current selected mark
let selected = self.el.querySelector('.lm-color-selected');
if (selected) {
selected.classList.remove('lm-color-selected');
}
// Mark cell as selected
if (color) {
event.target.classList.add('lm-color-selected');
self.set(color);
}
}
}
self.constructRows = function (e) {
let tbody = [];
e.textContent = '';
for (let j = 0; j < self.palette.length; j++) {
let tr = document.createElement('tr');
e.appendChild(tr);
for (let i = 0; i < self.palette[j].length; i++) {
let color = self.palette[j][i];
let td = document.createElement('td');
td.setAttribute('data-value', color);
td.style.backgroundColor = color;
tr.appendChild(td);
}
}
}
onchange(property => {
if (property === 'palette') {
self.constructRows()
}
});
return render => render`<div class="lm-color-grid" :palette="self.palette">
<table cellpadding="7" cellspacing="0" onclick="${select}" :ref="self.table" :ready="self.constructRows"></table>
</div>`
}
const Spectrum = function(children, { onload }) {
let self = this;
let context = null;
let decToHex = function(num) {
let hex = num.toString(16);
return hex.length === 1 ? "0" + hex : hex;
}
let rgbToHex = function(r, g, b) {
return "#" + decToHex(r) + decToHex(g) + decToHex(b);
}
onload(() => {
context = self.canvas.getContext("2d", { willReadFrequently: true });
draw();
})
// Drsaw
const draw = function() {
let g = context.createLinearGradient(0, 0, self.canvas.width, 0);
// Create color gradient
g.addColorStop(0, "rgb(255,0,0)");
g.addColorStop(0.15, "rgb(255,0,255)");
g.addColorStop(0.33, "rgb(0,0,255)");
g.addColorStop(0.49, "rgb(0,255,255)");
g.addColorStop(0.67, "rgb(0,255,0)");
g.addColorStop(0.84, "rgb(255,255,0)");
g.addColorStop(1, "rgb(255,0,0)");
context.fillStyle = g;
context.fillRect(0, 0, self.canvas.width, self.canvas.height);
g = context.createLinearGradient(0, 0, 0, self.canvas.height);
g.addColorStop(0, "rgba(255,255,255,1)");
g.addColorStop(0.5, "rgba(255,255,255,0)");
g.addColorStop(0.5, "rgba(0,0,0,0)");
g.addColorStop(1, "rgba(0,0,0,1)");
context.fillStyle = g;
context.fillRect(0, 0, self.canvas.width, self.canvas.height);
}
// Moves the marquee point to the specified position
const update = (e) => {
let x;
let y;
let buttons = 1;
if (e.type === 'touchmove') {
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
} else {
buttons = e.buttons;
x = e.clientX;
y = e.clientY;
}
if (buttons === 1) {
let rect = self.el.getBoundingClientRect();
let left = x - rect.left;
let top = y - rect.top;
// Get the color in this pixel
let pixel = context.getImageData(left, top, 1, 1).data;
// Position pointer
self.point.style.left = left + 'px';
self.point.style.top = top + 'px';
// Return color
self.set(rgbToHex(pixel[0], pixel[1], pixel[2]));
}
}
return render => render`<div class="lm-color-hsl">
<canvas width="240" height="140" :ref="self.canvas" onmousedown="${update}" onmousemove="${update}" ontouchmove="${update}"></canvas>
<div class="lm-color-point" :ref="self.point"></div>
</div>`;
}
const Color = function(children, { onchange, onload }) {
let self = this;
let value = null;
const change = self.onchange;
self.onchange = null;
// Decide the type based on the size of the screen
let autoType = self.type === 'auto';
const applyValue = function(v) {
if (self.value !== v) {
self.value = v;
}
}
const onopen = function(e) {
self.open();
// Open event
Dispatch.call(self, self.onopen, 'open', {
instance: self
});
}
const onclose = function(modal, origin) {
// Close event
Dispatch.call(self, self.onclose, 'close', {
instance: self,
origin: origin,
});
}
const update = function() {
applyValue(value);
self.close({ origin: 'button' });
}
const getInput = function() {
let input = self.input;
if (input && input.current) {
input = input.current;
} else {
if (self.input) {
input = self.input;
}
}
return input;
}
const events = {
focusin: (e) => {
if (self.modal && self.isClosed()) {
self.open();
}
},
focusout: (e) => {
if (self.modal && ! self.isClosed()) {
if (! (e.relatedTarget && self.modal.el.contains(e.relatedTarget))) {
self.modal.close({ origin: 'focusout' });
}
}
},
click: (e) => {
if (e.target.classList.contains('lm-color-input')) {
self.open();
}
},
keydown: (e) => {
if (self.modal) {
if (e.code === 'ArrowUp' || e.code === 'ArrowDown') {
if (self.isClosed()) {
self.open();
}
} else if (e.code === 'Enter') {
if (! self.isClosed()) {
update();
} else {
self.open();
}
} else if (e.code === 'Escape') {
if (! self.isClosed()) {
self.modal.close({origin: 'escape'});
}
}
}
}
}
const set = function(v) {
value = v;
// Close
if (self.closeOnChange === true) {
// Update value
self.setValue(v);
// Close modal
self.close({ origin: 'select' });
}
}
self.open = function(e) {
if (self.modal) {
if (autoType) {
self.type = window.innerWidth > 640 ? self.type = 'default' : 'picker';
}
value = self.value;
// Table
let table = self.grid.table;
// Remove any selection
let o = table.querySelector('.lm-color-selected');
if (o) {
o.classList.remove('lm-color-selected');
}
// Selected
o = table.querySelector('[data-value="'+self.value+'"]');
if (o) {
o.classList.add('lm-color-selected');
}
// Open modal
self.modal.open();
}
}
/**
* Close the modal
*/
self.close = function(options) {
if (self.modal) {
if (options && options.origin) {
self.modal.close(options)
} else {
self.modal.close({ origin: 'button' })
}
}
}
self.isClosed = function() {
if (self.modal) {
return self.modal.isClosed();
}
}
self.reset = function() {
self.setValue('');
self.close({ origin: 'button' });
}
self.setValue = function(v) {
self.value = value = v;
}
self.getValue = function() {
return self.value;
}
self.onevent = function(e) {
if (events[e.type]) {
events[e.type](e);
}
}
onchange(prop => {
if (prop === 'value') {
let input = getInput();
if (input) {
input.value = self.value;
if (self.value) {
input.style.color = self.value;
} else {
input.style.color = '';
}
}
Dispatch.call(self, change, 'change', {
instance: self,
value: self.value,
});
}
});
// Input
if (self.input === 'auto') {
self.input = document.createElement('input');
self.input.type = 'text';
}
onload(() => {
if (self.type !== "inline") {
// Create modal instance
self.modal = {
closed: true,
onopen: onopen,
onclose: onclose,
focus: false,
position: 'absolute',
'auto-close': false,
'auto-adjust': true,
};
// Generate modal
Modal(self.el, self.modal);
}
// Create input controls
if (self.input && self.initInput !== false) {
if (! self.input.parentNode) {
self.el.parentNode.insertBefore(self.input, self.el);
}
let input = getInput();
if (input && input.tagName) {
input.classList.add('lm-input');
input.classList.add('lm-color-input');
input.addEventListener('click', events.click);
input.addEventListener('focusin', events.focusin);
input.addEventListener('focusout', events.focusout);
if (self.placeholder) {
input.setAttribute('placeholder', self.placeholder);
}
if (self.onChange) {
input.addEventListener('change', self.onChange);
}
// Retrieve the value
if (self.value) {
input.value = self.value;
} else if (input.value && input.value !== self.value) {
self.value = input.value;
}
}
}
// Create event for focus out
self.el.addEventListener("focusout", (e) => {
let input = getInput();
if (e.relatedTarget !== input && ! self.el.contains(e.relatedTarget)) {
self.close({ origin: 'focusout' });
}
});
});
return render => render`<div class="lm-color" :value="self.value">
<div class="lm-color-options">
<button type="button" onclick="${self.reset}">Reset</button>
<button type="button" onclick="${update}">Done</button>
</div>
<lm-tabs selected="0" position="center" :ref="self.tabs">
<div title="Grid"><${Grid} :palette="self.palette" :ref="self.grid" :set="${set}" /></div>
<div title="Spectrum"><${Spectrum} :ref="self.spectrum" :set="${set}" /></div>
</lm-tabs>
</div>`;
}
lemonade.setComponents({ Color: Color });
// Register the web component
lemonade.createWebComponent('color', Color);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Color, root, options)
return options;
} else {
return Color.call(this, root)
}
}
})));
/***/ }),
/***/ 238:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (! lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
if (! Modal && "function" === 'function') {
var Modal = __webpack_require__(72);
}
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
this.dispatchEvent(new CustomEvents(type, options));
}
}
// Get the coordinates of the action
const getCoords = function(e) {
let x;
let y;
if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
} else {
x = e.clientX;
y = e.clientY;
}
return [x,y];
}
const Item = function() {
let self = this;
self.onload = function() {
if (typeof(self.render) === 'function') {
self.render.call(self, self.el);
}
}
// Initialize expanded state
self.expanded = false;
if (self.type === 'line' || self.type === 'divisor') {
return `<hr role="separator" />`;
} else if (self.type === 'inline') {
return `<div></div>`;
} else {
return `<div class="lm-menu-item" role="menuitem" data-disabled="{{self.disabled}}" data-cursor="{{self.cursor}}" data-icon="{{self.icon}}" title="{{self.tooltip}}" data-submenu="${!!self.submenu}" aria-haspopup="${!!self.submenu}" aria-expanded="{{self.expanded}}" aria-label="{{self.title}}" tabindex="-1" onmouseup="self.parent.mouseUp" onmouseenter="self.parent.mouseEnter" onmouseleave="self.parent.mouseLeave">
<span>{{self.title}}</span> <div>{{self.shortcut}}</div>
</div>`;
}
}
const Create = function() {
let self = this;
// Delay on open
let delayTimer;
// Save the position of this modal
let index = self.parent.modals.length;
// Blank options
self.options = [];
// Close handler
self.onclose = function() {
// Reset any cursor
resetCursor.call(self);
// Parent
if (typeof(self.parent.onclose) === 'function') {
self.parent.onclose(self.parent, self);
}
}
self.onopen = function() {
// Parent
if (typeof(self.parent.onopen) === 'function') {
self.parent.onopen(self.parent, self);
}
}
/**
* Close the modal
*/
self.close = function() {
// Close modals with higher level
self.parent.close(index);
}
/**
* Open submenu handler
* @param {object} s
* @param {boolean} cursor - Activate the first item
*/
self.open = function(s, cursor) {
if (s.submenu) {
// Get the modal in the container of modals
let current = self.parent.modals[index+1];
// Do not exist yet, create it.
if (! current) {
// Modal needs to be created
current = self.parent.create();
}
// Get the parent from this one
let parent = self.parent.modals[index];
// Update modal content
if (current.options !== s.submenu) {
// Close modals with higher level
current.options = s.submenu;
// Close other modals
self.parent.close(index+1);
}
// Update the selected modal
self.parent.modalIndex = index+1;
let rect = parent.modal.el.getBoundingClientRect();
// Update modal
current.modal.open();
// Calculate initial position using item's actual screen position (accounts for scroll)
let itemRect = s.el.getBoundingClientRect();
let submenuWidth = 250;
let submenuTop = itemRect.y;
let submenuLeft = rect.x + rect.width - 2; // Position to the right of parent
// Check if parent was positioned to the left (has negative margin or is on left side)
let parentOpenedLeft = parent.openedLeft || false;
// Check horizontal space
let spaceOnRight = window.innerWidth - (rect.x + rect.width);
let spaceOnLeft = rect.x;
// Determine which side to open the submenu
let openLeft = parentOpenedLeft; // Follow parent's direction by default
// If parent opened to right, check if we still have space
if (!parentOpenedLeft && spaceOnRight < submenuWidth + 10) {
// Not enough space on right, switch to left
openLeft = true;
}
// If parent opened to left, check if we still have space on left
if (parentOpenedLeft && spaceOnLeft < submenuWidth + 10) {
// Not enough space on left, switch to right if possible
if (spaceOnRight >= submenuWidth + 10) {
openLeft = false;
}
}
if (openLeft) {
// Position to the left of parent menu
submenuLeft = rect.x - submenuWidth + 2;
// Ensure it doesn't go off the left edge
if (submenuLeft < 10) {
submenuLeft = 10;
}
current.openedLeft = true;
} else {
current.openedLeft = false;
}
// Set position
current.modal.top = submenuTop;
current.modal.left = submenuLeft;
// Adjust vertical position after render
queueMicrotask(() => {
let submenuEl = current.modal.el;
let submenuRect = submenuEl.getBoundingClientRect();
// Check if submenu goes off the bottom
if (submenuRect.bottom > window.innerHeight - 10) {
let overflow = submenuRect.bottom - (window.innerHeight - 10);
let newTop = submenuTop - overflow;
// Don't go above the top of the screen
if (newTop < 10) {
newTop = 10;
}
current.modal.top = newTop;
}
});
// Keep current item for each modal
current.item = s;
s.expanded = true;
// Activate the cursor
if (cursor === true) {
// Place cursor in the first position
current.options[0].cursor = true;
// Position cursor
current.cursor = 0;
}
onopen(current, s.submenu)
} else {
// Close modals with higher level
self.parent.close(index+1);
}
}
// Mouse open
self.mouseUp = function(e, s) {
if (typeof(s.onclick) === 'function') {
s.onclick.call(s, e, s.el);
}
if (! s.submenu) {
self.close();
}
}
self.mouseEnter = function(e, s) {
if (delayTimer) {
clearTimeout(delayTimer);
}
delayTimer = setTimeout(function() {
self.open(s);
}, 200);
}
self.mouseLeave = function(e, s) {
if (delayTimer) {
clearTimeout(delayTimer);
}
}
let template = `<lm-modal :overflow="true" :closed="true" :ref="self.modal" :responsive="false" :auto-adjust="true" :focus="false" :layers="false" :onopen="self.onopen" :onclose="self.onclose">
<div class="lm-menu-submenu" role="menu" aria-orientation="vertical">
<Item :loop="self.options" />
</div>
</lm-modal>`;
return lemonade.element(template, self, { Item: Item });
}
const findNextEnabledCursor = function(startIndex, direction) {
if (!this.options || this.options.length === 0) {
return null;
}
let cursor = startIndex;
let attempts = 0;
const maxAttempts = this.options.length;
while (attempts < maxAttempts) {
if (direction) {
// Down
if (cursor >= this.options.length) {
cursor = 0;
}
} else {
// Up
if (cursor < 0) {
cursor = this.options.length - 1;
}
}
let item = this.options[cursor];
if (item && !item.disabled && item.type !== 'line') {
return cursor;
}
cursor = direction ? cursor + 1 : cursor - 1;
attempts++;
}
return null;
};
const setCursor = function(direction) {
let cursor = null;
if (typeof(this.cursor) !== 'undefined') {
if (! direction) {
// Up
cursor = findNextEnabledCursor.call(this, this.cursor - 1, false);
} else {
// Down
cursor = findNextEnabledCursor.call(this, this.cursor + 1, true);
}
}
// Remove the cursor
if (cursor === null) {
if (direction) {
cursor = findNextEnabledCursor.call(this, 0, true);
} else {
cursor = findNextEnabledCursor.call(this, this.options.length - 1, false);
}
} else if (typeof(this.cursor) !== 'undefined') {
this.options[this.cursor].cursor = false;
}
// Add the cursor if found
if (cursor !== null) {
this.options[cursor].cursor = true;
this.cursor = cursor;
return true;
}
return false;
}
/**
* Reset the cursor for a contextmenu
*/
const resetCursor = function() {
// Contextmenu modal
let item = this.options[this.cursor];
// Cursor is found so reset it
if (typeof(item) !== 'undefined') {
// Remove the cursor style
item.cursor = false;
// Delete reference index
delete this.cursor;
}
}
/**
* Go through all items of a menu
* @param s
* @param options
*/
const onopen = function(s, options) {
// Onopen
for (let i = 0; i < options.length; i++) {
if (typeof(options[i].onopen) === 'function') {
options[i].onopen(s);
}
}
}
const Contextmenu = function(children, { onload }) {
let self = this;
// Container for all modals
self.modals = [];
self.modalIndex = 0;
self.create = function() {
// Create a new self for each modal
let s = {
parent: self,
};
// Render the modal inside the main container
lemonade.render(Create, self.el, s);
// Add the reference of the modal in a container#
self.modals.push(s);
// Return self
return s;
}
self.isClosed = function() {
return self.modals[0].modal.closed === true;
}
self.openAt = function(a, b) {
let x, y;
if (a instanceof Event || (a && a.clientX !== undefined)) {
// openAt(event)
x = a.clientX;
y = a.clientY;
} else {
// openAt(x, y)
x = a;
y = b;
}
self.open(self.options, x, y, true);
};
self.open = function(options, x, y, adjust) {
// Get the main modal
let menu = self.modals[0];
// Reset cursor
resetCursor.call(menu);
// Define new position
menu.modal.top = y;
menu.modal.left = x;
// Open
menu.modal.open();
// If the modal is open and the content is different from what is shown. Close modals with higher level
self.close(1);
// Update the data
if (options && menu.options !== options) {
// Refresh content
menu.options = options;
}
onopen(self, options);
// Adjust position to respect mouse cursor after auto-adjust
// Use queueMicrotask to ensure it runs after the modal's auto-adjust
if (adjust === true) {
queueMicrotask(() => {
let modalEl = menu.modal.el;
let rect = modalEl.getBoundingClientRect();
let marginLeft = parseFloat(modalEl.style.marginLeft) || 0;
let marginTop = parseFloat(modalEl.style.marginTop) || 0;
// Check if horizontal adjustment was applied (margin is non-zero)
if (marginLeft !== 0) {
// Position modal so its right edge is at x - 1 (cursor 1px to the right of modal)
// Formula: left + margin + width = x - 1, where left = x
// Therefore: margin = -width - 1
let newMarginLeft = -rect.width - 1;
// Check if this would push modal off the left edge
let newLeft = x + newMarginLeft;
if (newLeft < 10) {
// Keep a 10px margin from the left edge
newMarginLeft = 10 - x;
}
modalEl.style.marginLeft = newMarginLeft + 'px';
}
// Check if vertical adjustment was applied (margin is non-zero)
if (marginTop !== 0) {
// Position modal so its bottom edge is at y - 1 (cursor 1px below modal)
// Formula: top + margin + height = y - 1, where top = y
// Therefore: margin = -height - 1
let newMarginTop = -rect.height - 1;
// Check if this would push modal off the top edge
let newTop = y + newMarginTop;
if (newTop < 10) {
// Keep a 10px margin from the top edge
newMarginTop = 10 - y;
}
modalEl.style.marginTop = newMarginTop + 'px';
}
});
}
// Focus
self.el.classList.add('lm-menu-focus');
// Focus on the contextmenu
self.el.focus();
}
self.close = function(level) {
// Close all modals from the level specified
self.modals.forEach(function(menu, k) {
if (k >= level) {
if (menu.item) {
menu.item.expanded = false;
menu.item = null;
}
menu.openedLeft = false;
menu.modal.close();
}
});
// Keep the index of the modal that is opened
self.modalIndex = level ? level - 1 : 0;
// Close event
if (level === 0) {
self.el.classList.remove('lm-menu-focus');
Dispatch.call(self, self.onclose, 'close', {
instance: self,
});
}
}
onload(() => {
// Create first menu
self.create();
// Create event for focus out
self.el.addEventListener("focusout", (e) => {
if (! (e.relatedTarget && (self.el.contains(e.relatedTarget) || self.root?.contains(e.relatedTarget)))) {
self.close(0);
}
});
// Keyboard event
self.el.addEventListener("keydown", function(e) {
// Menu object
let menu = self.modals[self.modalIndex];
// Modal must be opened
if (! menu.modal.closed) {
// Something happens
let ret = false;
// Control
if (e.key === 'ArrowLeft') {
if (self.modalIndex > 0) {
// Close modal
menu.close();
// Action happened
ret = true;
}
} else if (e.key === 'ArrowRight') {
// Get the selected cursor
let item = menu.options[menu.cursor];
// Open submenu
if (typeof (item) !== 'undefined') {
// Open submenu in case that exists
if (item.submenu && !item.disabled) {
// Open modal
menu.open(item, true);
// Action happened
ret = true;
}
}
} else if (e.key === 'ArrowUp') {
ret = setCursor.call(menu, 0);
} else if (e.key === 'ArrowDown') {
ret = setCursor.call(menu, 1);
} else if (e.key === 'Enter') {
// Contextmenu modal
let item = menu.options[menu.cursor];
// Cursor is found so reset it
if (typeof(item) !== 'undefined') {
// Execute action
if (typeof (item.onclick) === 'function') {
item.onclick.call(item, e, item.el);
}
// Open sub menu in case exists
if (item.submenu) {
// Open menu
menu.open(item, true);
// Action happened
ret = true;
} else {
// Close all menu
self.close(0);
// Action happened
ret = true;
}
}
} else if (e.key === 'Escape') {
self.close(0);
}
// Something important happen so block any progression
if (ret === true) {
e.preventDefault();
e.stopImmediatePropagation();
}
}
});
if (! self.root) {
if (self.tagName) {
self.root = self.el.parentNode.parentNode;
} else {
self.root = self.el.parentNode;
}
}
// Parent
self.root.addEventListener("contextmenu", function(e) {
if (Array.isArray(self.options) && self.options.length) {
let [x, y] = getCoords(e);
self.open(self.options, x, y, true);
e.preventDefault();
e.stopImmediatePropagation();
}
});
});
return `<div class="lm-menu" role="menu" aria-orientation="vertical" tabindex="0"></div>`;
}
lemonade.setComponents({ Contextmenu: Contextmenu });
lemonade.createWebComponent('contextmenu', Contextmenu);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Contextmenu, root, options)
return options;
} else {
return Contextmenu.call(this, root)
}
}
})));
/***/ }),
/***/ 692:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/**
* Implement page up and down navigation
* Implement color attribute for items
*/
if (!lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
if (!Modal && "function" === 'function') {
var Modal = __webpack_require__(72);
}
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
return this.dispatchEvent(new CustomEvents(type, options));
}
}
// Default row height
let defaultRowHeight = 24;
// Translations
const T = function(t) {
if (typeof(document) !== "undefined" && document.dictionary) {
return document.dictionary[t] || t;
} else {
return t;
}
}
const isEmpty = function(v) {
return v === '' || v === null || v === undefined || (Array.isArray(v) && v.length === 0);
}
/**
* Compare two values (arrays, strings, numbers, etc.)
* Returns true if both are equal or empty
* @param {*} a1
* @param {*} a2
*/
const compareValues = function(a1, a2) {
if (a1 === a2 || (isEmpty(a1) && isEmpty(a2))) {
return true;
}
if (!a1 || !a2) {
return false;
}
if (Array.isArray(a1) && Array.isArray(a2)) {
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
return a1 === a2;
}
const lazyLoading = function (self) {
/**
* Get the position from top of a row by its index
* @param item
* @returns {number}
*/
const getRowPosition = function (item) {
// Position from top
let top = 0;
if (item) {
let items = self.rows;
if (items && items.length) {
let index = self.rows.indexOf(item);
// Go through the items
for (let j = 0; j < index; j++) {
top += items[j].height || defaultRowHeight;
}
}
}
return top;
}
const updateScroll = function () {
let items = self.rows;
if (items) {
// Before control
let before = true;
// Total of items in the container
let numOfItems = items.length;
// Position from top
let height = 0;
// Size of the adjustment
let size = 0;
// Go through the items
for (let j = 0; j < numOfItems; j++) {
let h = items[j].height || defaultRowHeight;
// Height
height += h;
// Start tracking all items as before
if (items[j] === self.result[0]) {
before = false;
}
// Adjustment
if (before) {
size += h;
}
}
// Update height
scroll.style.height = height + 'px';
// Adjust scroll position
return size;
}
return false;
}
const getVisibleRows = function (reset) {
let items = self.rows;
if (items) {
let adjust;
// Total of items in the container
let numOfItems = items.length;
// Get the position from top
let y = el.scrollTop;
// Get the height
let h = null;
if (self.type === 'searchbar' || self.type === 'picker') {
// Priority should be the size used on the viewport
h = y + (el.offsetHeight || self.height);
} else {
// Priority is the height define during initialization
h = y + (self.height || el.offsetHeight);
}
// Go through the items
let rows = [];
// Height
let height = 0;
// Go through all items
for (let j = 0; j < numOfItems; j++) {
if (items[j].visible !== false) {
// Height
let rowHeight = items[j].height || defaultRowHeight;
// Return on partial width
if (height + rowHeight > y && height < h) {
rows.push(items[j]);
}
height += rowHeight;
}
}
// Update visible rows
if (reset || !compareValues(rows, self.result)) {
// Render the items
self.result = rows;
// Adjust scroll height
let adjustScroll = reset;
// Adjust scrolling
for (let i = 0; i < rows.length; i++) {
// Item
let item = rows[i];
// Item height
let h = item.el.offsetHeight;
// Update row height
if (!item.height || h !== item.height) {
// Keep item height
item.height = h;
// Adjust total height
adjustScroll = true;
}
}
// Update scroll if the height of one element has been changed
if (adjustScroll) {
// Adjust the scroll height
adjust = updateScroll();
}
}
// Adjust position of the first element
let position = getRowPosition(self.result[0]);
let diff = position - el.scrollTop;
if (diff > 0) {
diff = 0;
}
self.container.style.top = diff + 'px';
return adjust;
}
}
/**
* Move the position to the top and re-render based on the scroll
* @param reset
*/
const render = function (reset) {
// Move scroll to the top
el.scrollTop = 0;
// Reset scroll
updateScroll();
// Append first batch
getVisibleRows(reset);
}
/**
* Will adjust the items based on the scroll position offset
*/
self.adjustPosition = function (item) {
if (item.el) {
let h = item.el.offsetHeight;
let calc = item.el.offsetTop + h;
if (calc > el.offsetHeight) {
let size = calc - el.offsetHeight;
if (size < h) {
size = h;
}
el.scrollTop -= -1 * size;
}
}
}
// Controls
const scrollControls = function () {
getVisibleRows(false);
}
// Element for scrolling
let el = self.container.parentNode;
el.classList.add('lm-lazy');
// Div to represent the height of the content
const scroll = document.createElement('div');
scroll.classList.add('lm-lazy-scroll');
// Force the height and add scrolling
el.appendChild(scroll);
el.addEventListener('scroll', scrollControls, { passive: true });
el.addEventListener('wheel', scrollControls, { passive: true });
self.container.classList.add('lm-lazy-items');
self.goto = function (item) {
el.scrollTop = getRowPosition(item);
let adjust = getVisibleRows(false);
if (adjust) {
el.scrollTop = adjust;
// Last adjust on the visible rows
getVisibleRows(false);
}
}
return (prop) => {
if (prop === 'rows') {
render(true);
}
}
}
const getAttributeName = function(prop) {
if (prop.substring(0,1) === ':') {
prop = prop.substring(1);
} else if (prop.substring(0,3) === 'lm-') {
prop = prop.substring(3);
}
return prop.toLowerCase();
}
const extractFromHtml = function(element) {
let data = [];
// Content
for (let i = 0; i < element.children.length; i++) {
let e = element.children[i];
let item = {
text: e.textContent || e.getAttribute('title'),
value: e.getAttribute('value'),
}
if (item.value == null) {
item.value = item.text;
}
data.push(item);
}
return data;
}
const extract = function(children) {
let data = [];
if (this.tagName) {
data = extractFromHtml(this);
// Remove all elements
this.textContent = '';
} else {
// Get data
if (typeof(children) === 'string') {
// Version 4
let d = document.createElement('div');
d.innerHTML = children;
data = extractFromHtml(d);
} else if (children && children.length) {
// Version 5
children.forEach((v) => {
let item = {}
v.props.forEach((prop) => {
item[getAttributeName(prop.name)] = prop.value;
});
if (! item.text) {
item.text = v.children[0]?.props[0]?.value || '';
}
data.push(item);
});
// Block children
children.length = 0;
}
}
return data;
}
const isDOM = function(o) {
return (o instanceof Element || o instanceof HTMLDocument || o instanceof DocumentFragment);
}
const Dropdown = function (children, { onchange, onload }) {
let self = this;
// Data
let data = [];
// Internal value controllers
let value = [];
// Cursor
let cursor = null;
// Control events
let ignoreEvents = false;
// Lazy loading global instance
let lazyloading = null;
// Tracking changes
let changesDetected = false;
// Debounce timer for search
let searchTimeout = null;
// Data
if (! Array.isArray(self.data)) {
self.data = [];
}
let d = extract.call(this, children);
if (d) {
d.forEach((v) => {
self.data.push(v)
})
}
// Decide the type based on the size of the screen
let autoType = self.type === 'auto';
// Custom events defined by the user
let load = self.onload;
self.onload = null;
let change = self.onchange;
self.onchange = null;
// Compatibility
if (typeof self.newOptions !== 'undefined') {
self.insert = self.newOptions;
}
// Cursor controllers
const setCursor = function (index, force) {
let item = self.rows[index];
if (typeof (item) !== 'undefined') {
// Set the cursor number
cursor = index;
// Set visual indication
item.cursor = true;
// Go to the item on the scroll in case the item is not on the viewport
if (!(item.el && item.el.parentNode) || force === true) {
// Goto method
self.goto(item);
}
// Adjust cursor position
setTimeout(function () {
self.adjustPosition(item);
});
}
}
const removeCursor = function (reset) {
if (cursor !== null) {
if (typeof (self.rows[cursor]) !== 'undefined') {
self.rows[cursor].cursor = false;
}
if (reset) {
// Cursor is null
cursor = null;
}
}
}
const moveCursor = function (direction, jump) {
// Remove cursor
removeCursor();
// Last item
let last = self.rows.length - 1;
if (jump) {
if (direction < 0) {
cursor = 0;
} else {
cursor = last;
}
} else {
// Position
if (cursor === null) {
cursor = 0;
} else {
// Move previous
cursor = cursor + direction;
}
// Reach the boundaries
if (direction < 0) {
// Back to the last one
if (cursor < 0) {
cursor = last;
}
} else {
// Back to the first one
if (cursor > last) {
cursor = 0;
}
}
}
// Add cursor
setCursor(cursor);
}
const adjustDimensions = function(data) {
// Estimate width
let width = self.width ?? 0;
// Adjust the width
let w = getInput().offsetWidth;
if (width < w) {
width = w;
}
// Width && values
data.map(function (s) {
// Estimated width of the element
if (s.text) {
let w = Math.max(width, s.text.length * 7.5);
if (width < w) {
width = w;
}
}
});
// Min width for the container
self.container.parentNode.style.width = (width - 2) + 'px';
}
const setData = function () {
// Data
data = JSON.parse(JSON.stringify(self.data));
// Re-order to make sure groups are in sequence
if (data && data.length) {
// Adjust width and height
adjustDimensions(data);
// Groups
data.sort((a, b) => {
// Compare groups
if (a.group && b.group) {
return a.group.localeCompare(b.group);
}
return 0;
});
let group = '';
// Define group headers
data.map((v) => {
// Compare groups
if (v && v.group && v.group !== group) {
v.header = v.group;
group = v.group;
}
});
}
// Data to be listed
self.rows = data;
}
const updateLabel = function () {
if (value && value.length) {
getInput().textContent = value.filter(v => v.selected).map(i => i.text).join('; ');
} else {
getInput().textContent = '';
}
}
const setValue = function (v, ignoreEvent) {
// Values
let newValue;
if (! Array.isArray(v)) {
if (typeof(v) === 'string') {
newValue = v.split(self.divisor ?? ';');
} else {
newValue = [v];
}
} else {
newValue = v;
}
// Width && values
value = [];
if (Array.isArray(data)) {
data.map(function (s) {
s.selected = newValue.some(v => {
// Use strict equality when either value is empty string to avoid '' == 0 being true
if (v === '' || s.value === '') {
return v === s.value;
}
return v == s.value;
});
if (s.selected) {
value.push(s);
}
});
}
// Update label
if (self.isClosed()) {
updateLabel();
}
// Component onchange
if (! ignoreEvent) {
Dispatch.call(self, change, 'change', {
instance: self,
value: getValue(),
});
}
}
const getValue = function () {
if (self.multiple) {
if (value && value.length) {
return value.filter(v => v.selected).map(i => i.value);
}
} else {
if (value && value.length) {
return value[0].value;
}
}
return null;
}
const getText = function () {
if (self.multiple) {
if (value && value.length) {
return value.filter(v => v.selected).map(i => i.text);
}
} else {
if (value && value.length) {
return value[0].text;
}
}
return null;
}
const onopen = function () {
self.state = true;
// Value
let v = value[value.length - 1];
// Make sure goes back to the top of the scroll
if (self.container.parentNode.scrollTop > 0) {
self.container.parentNode.scrollTop = 0;
}
// Move to the correct position
if (v) {
// Mark the position of the cursor to the same element
setCursor(self.rows.indexOf(v), true);
}
// Prepare search field
if (self.autocomplete) {
// Get the input
let input = getInput();
// Editable
input.setAttribute('contenteditable', true);
// Clear input
input.textContent = '';
// Focus on the item
input.focus();
}
// Adjust width and height
adjustDimensions(self.data);
// Open event
Dispatch.call(self, self.onopen, 'open', {
instance: self
});
}
const onclose = function (options, origin) {
// Cursor
removeCursor(true);
// Reset search
if (self.autocomplete) {
// Go to begin of the data
self.rows = data;
// Get the input
let input = getInput();
if (input) {
// Remove editable attribute
input.removeAttribute('contenteditable');
// Clear input
input.textContent = '';
}
}
if (origin === 'escape') {
// Cancel operation and keep the same previous value
setValue(self.value, true);
} else {
// Current value
let newValue = getValue();
// If that is different from the component value
if (changesDetected === true && ! compareValues(newValue, self.value)) {
self.value = newValue;
} else {
// Update label
updateLabel();
}
}
// Identify the new state of the dropdown
self.state = false;
// Close event
Dispatch.call(self, self.onclose, 'close', {
instance: self,
...options
});
}
const normalizeData = function(result) {
if (result && result.length) {
return result.map((v) => {
if (typeof v === 'string' || typeof v === 'number') {
return { value: v, text: v };
} else if (typeof v === 'object' && v.hasOwnProperty('name')) {
return { value: v.id, text: v.name };
} else {
return v;
}
});
}
}
const loadData = function(result) {
result = normalizeData(result);
// Loading controls
lazyloading = lazyLoading(self);
// Loading new data from a remote source
if (result) {
result.forEach((v) => {
self.data.push(v);
});
}
// Process the data
setData();
// Set value
if (typeof(self.value) !== 'undefined') {
setValue(self.value, true);
}
// Onload method
Dispatch.call(self, load, 'load', {
instance: self
});
// Remove loading spin
self.input.classList.remove('lm-dropdown-loading');
}
const resetData = function(result) {
result = normalizeData(result);
// Reset cursor
removeCursor(true);
let r = data.filter(item => {
return item.selected === true;
});
// Loading new data from a remote source
if (result) {
result.forEach((v) => {
r.push(v);
});
}
self.rows = r;
// Remove loading spin
self.input.classList.remove('lm-dropdown-loading');
// Event
Dispatch.call(self, self.onsearch, 'search', {
instance: self,
result: result,
});
}
const getInput = function() {
return self.input;
}
const search = function(query) {
if (! self.isClosed() && self.autocomplete) {
// Remote or normal search
if (self.remote === true && self.url) {
// Clear existing timeout
if (searchTimeout) {
clearTimeout(searchTimeout);
}
// Loading spin
self.input.classList.add('lm-dropdown-loading');
// Headers
let http = {
headers: {
'Content-Type': 'text/json',
}
}
let ret = Dispatch.call(self, self.onbeforesearch, 'beforesearch', {
instance: self,
http: http,
query: query,
});
if (ret === false) {
return;
}
// Debounce the search with 300ms delay
searchTimeout = setTimeout(() => {
let url = self.url;
url += url.indexOf('?') === -1 ? '?' : '&';
url += `q=${query}`;
fetch(url, http).then(r => r.json()).then(resetData).catch((error) => {
resetData([]);
});
}, 300);
} else {
// Filter options
let temp;
const find = (prop) => {
if (prop) {
if (Array.isArray(prop)) {
// match if ANY element contains the query (case-insensitive)
return prop.some(v => v != null && v.toString().toLowerCase().includes(query));
}
// handle strings/numbers/others
return prop.toString().toLowerCase().includes(query);
}
return false;
};
if (! query) {
temp = data;
} else {
temp = data.filter(item => {
return item.selected === true || find(item.text) || find(item.group) || find(item.keywords) || find(item.synonym);
});
}
// Cursor
removeCursor(true);
// Update the data from the dropdown
self.rows = temp;
}
}
}
const events = {
focusout: (e) => {
if (self.modal) {
if (! (e.relatedTarget && self.el.contains(e.relatedTarget))) {
if (! self.isClosed()) {
self.close({ origin: 'focusout '});
}
}
}
},
keydown: (e) => {
if (! self.isClosed()) {
let prevent = false;
if (e.code === 'ArrowUp') {
moveCursor(-1);
prevent = true;
} else if (e.code === 'ArrowDown') {
moveCursor(1);
prevent = true;
} else if (e.code === 'Home') {
moveCursor(-1, true);
if (!self.autocomplete) {
prevent = true;
}
} else if (e.code === 'End') {
moveCursor(1, true);
if (!self.autocomplete) {
prevent = true;
}
} else if (e.code === 'Enter') {
if (e.target.tagName === 'BUTTON') {
e.target.click();
let input = getInput();
input.focus();
} else {
select(e, self.rows[cursor]);
}
prevent = true;
} else if (e.code === 'Escape') {
self.close({ origin: 'escape'});
prevent = true;
} else {
if (e.keyCode === 32 && !self.autocomplete) {
select(e, self.rows[cursor]);
}
}
if (prevent) {
e.preventDefault();
e.stopImmediatePropagation();
}
} else {
if (e.code === 'ArrowUp' || e.code === 'ArrowDown' || e.code === 'Enter') {
self.open();
e.preventDefault();
e.stopImmediatePropagation();
}
}
},
mousedown: (e) => {
if (e.target.classList.contains('lm-dropdown-input')) {
if (self.autocomplete) {
let x;
if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].clientX;
} else {
x = e.clientX;
}
if (e.target.offsetWidth - (x - e.target.offsetLeft) < 20) {
toggle();
} else {
self.open();
}
} else {
toggle();
}
}
},
paste: (e) => {
if (e.target.classList.contains('lm-dropdown-input')) {
let text;
if (e.clipboardData || e.originalEvent.clipboardData) {
text = (e.originalEvent || e).clipboardData.getData('text/plain');
} else if (window.clipboardData) {
text = window.clipboardData.getData('Text');
}
text = text.replace(/(\r\n|\n|\r)/gm, "");
document.execCommand('insertText', false, text)
e.preventDefault();
}
},
input: (e) => {
if (e.target.classList.contains('lm-dropdown-input')) {
search(e.target.textContent.toLowerCase());
}
},
}
const selectItem = function(s) {
if (self.remote === true) {
if (data.indexOf(s) === -1) {
self.data.push(s);
data.push(s);
}
}
if (self.multiple === true) {
let position = value.indexOf(s);
if (position === -1) {
value.push(s);
s.selected = true;
} else {
value.splice(position, 1);
s.selected = false;
}
} else {
if (value[0] === s) {
if (self.allowEmpty === false) {
s.selected = true;
} else {
s.selected = !s.selected;
}
} else {
if (value[0]) {
value[0].selected = false;
}
s.selected = true;
}
if (s.selected) {
value = [s];
} else {
value = [];
}
}
changesDetected = true;
}
const add = async function (e) {
let input = getInput();
let text = input.textContent;
if (! text) {
return false;
}
// New item
let s = {
text: text,
value: text,
}
self.add(s);
e.preventDefault();
}
const select = function (e, s) {
if (s && s.disabled !== true) {
selectItem(s);
// Close the modal
if (self.multiple !== true) {
self.close({ origin: 'button' });
}
}
}
const toggle = function () {
if (self.modal) {
if (self.isClosed()) {
self.open();
} else {
self.close({ origin: 'button' });
}
}
}
self.add = async function (newItem) {
// Event
if (typeof(self.onbeforeinsert) === 'function') {
self.input.classList.add('lm-dropdown-loading');
let ret = await self.onbeforeinsert(self, newItem);
self.input.classList.remove('lm-dropdown-loading');
if (ret === false) {
return;
} else if (ret) {
newItem = ret;
}
}
// Process the data
data.push(newItem);
self.data.push(newItem);
// Refresh screen
self.result.unshift(newItem);
self.rows.unshift(newItem);
self.refresh('result');
Dispatch.call(self, self.oninsert, 'insert', {
instance: self,
item: newItem,
});
}
self.open = function () {
if (self.modal && ! self.disabled) {
if (self.isClosed()) {
if (autoType) {
self.type = window.innerWidth > 640 ? self.type = 'default' : (self.autocomplete ? 'searchbar' : 'picker');
}
// Track
changesDetected = false;
// Open the modal
self.modal.open();
}
}
}
self.close = function (options) {
if (self.modal) {
if (options?.origin) {
self.modal.close(options)
} else {
self.modal.close({ origin: 'button' })
}
}
}
self.isClosed = function() {
if (self.modal) {
return self.modal.isClosed();
}
}
self.setData = function(data) {
self.data = data;
}
self.getData = function() {
return self.data;
}
self.getValue = function() {
return self.value;
}
self.getText = function() {
return getText();
}
self.setValue = function(v) {
self.value = v;
}
self.reset = function() {
self.value = null;
self.close({ origin: 'button' });
}
self.onevent = function(e) {
if (events[e.type]) {
events[e.type](e);
}
}
// Init with a
let input = self.input;
onload(() => {
if (self.type === "inline") {
// For inline dropdown
self.el.setAttribute('tabindex', 0);
// Remove search
self.input.remove();
} else {
// Create modal instance
self.modal = {
closed: true,
focus: false,
onopen: onopen,
onclose: onclose,
position: 'absolute',
'auto-adjust': true,
'auto-close': false,
};
// Generate modal
Modal(self.el.children[1], self.modal);
}
if (self.remote === 'true') {
self.remote = true;
}
if (self.autocomplete === 'true') {
self.autocomplete = true;
}
if (self.multiple === 'true') {
self.multiple = true;
}
if (self.insert === 'true') {
self.insert = true;
}
// Autocomplete will be forced to be true when insert action is active
if ((self.insert === true || self.type === 'searchbar' || self.remote === true) && ! self.autocomplete) {
self.autocomplete = true;
}
if (typeof(input) !== 'undefined') {
// Remove the native element
if (isDOM(input)) {
input.classList.add('lm-dropdown-input');
}
// Remove search
self.input.remove();
// New input
self.input = input;
} else {
self.el.children[0].style.position = 'relative';
}
// Default width
if (self.width) {
// Dropdown
self.el.style.width = self.width + 'px';
}
// Height
self.height = 400;
// Animation for mobile
if (document.documentElement.clientWidth < 800) {
self.animation = true;
}
// Events
self.el.addEventListener('focusout', events.focusout);
self.el.addEventListener('keydown', events.keydown);
self.el.addEventListener('mousedown', events.mousedown);
self.el.addEventListener('paste', events.paste);
self.el.addEventListener('input', events.input);
// Load remote data
if (self.url) {
if (self.remote === true) {
loadData();
} else {
// Loading spin
self.input.classList.add('lm-dropdown-loading');
// Load remote data
fetch(self.url, {
headers: {
'Content-Type': 'text/json',
}
}).then(r => r.json()).then(loadData).catch(() => {
loadData();
});
}
} else {
loadData();
}
});
onchange(prop => {
if (prop === 'value') {
setValue(self.value);
} else if (prop === 'data') {
// Store current value before resetting data
let currentValue = self.value;
setData();
// Only reset value if it's not in the new data
if (currentValue !== null && currentValue !== undefined && currentValue !== '') {
let valuesToCheck = Array.isArray(currentValue) ? currentValue : [currentValue];
// Filter to keep only values that exist in the new data
let validValues = valuesToCheck.filter(v => {
return self.data.some(item => {
if (v === '' || item.value === '') {
return v === item.value;
}
return v == item.value;
});
});
if (validValues.length === 0) {
// No valid values remain, reset to null
self.value = null;
} else if (self.multiple) {
// Multi-select: keep only valid values
self.value = validValues;
} else {
// Single select: re-apply the value
self.value = validValues[0];
}
}
}
if (typeof (lazyloading) === 'function') {
lazyloading(prop);
}
});
return render => render`<div class="lm-dropdown" data-state="{{self.state}}" data-insert="{{self.insert}}" data-type="{{self.type}}" data-disabled="{{self.disabled}}" :value="self.value" :data="self.data">
<div class="lm-dropdown-header">
<div class="lm-dropdown-input" placeholder="{{self.placeholder}}" :ref="self.input" tabindex="0"></div>
<button class="lm-dropdown-add" onclick="${add}" tabindex="0"></button>
<div class="lm-dropdown-header-controls">
<button onclick="self.reset" class="lm-dropdown-done">${T('Reset')}</button>
<button onclick="self.close" class="lm-dropdown-done">${T('Done')}</button>
</div>
</div>
<div class="lm-dropdown-content">
<div>
<div :loop="self.result" :ref="self.container" :rows="self.rows">
<div class="lm-dropdown-item" onclick="${select}" data-cursor="{{self.cursor}}" data-disabled="{{self.disabled}}" data-selected="{{self.selected}}" data-group="{{self.header}}">
<div><img :src="self.image" /> <div>{{self.text}}</div></div>
</div>
</div>
</div>
</div>
</div>`;
}
lemonade.setComponents({ Dropdown: Dropdown });
lemonade.createWebComponent('dropdown', Dropdown);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Dropdown, root, options)
return options;
} else {
return Dropdown.call(this, root)
}
}
})));
/***/ }),
/***/ 72:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/**
* pin the modal to the left panel
*/
if (!lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
;(function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
this.dispatchEvent(new CustomEvents(type, options));
}
}
// References
const modals = [];
// State of the resize and move modal
let state = {};
// Internal controls of the action of resize and move
let controls = {};
// Width of the border
let cornerSize = 10;
// Container with minimized modals
const minimizedModals = [];
// Default z-index for the modals
const defaultZIndex = 20;
/**
* Send the modal to the front
* @param container
*/
const sendToFront = function(container) {
let highestXIndex = defaultZIndex;
for (let i = 0; i < modals.length; i++) {
const zIndex = parseInt(modals[i].el.style.zIndex);
if (zIndex > highestXIndex) {
highestXIndex = zIndex;
}
}
container.style.zIndex = highestXIndex + 1;
}
/**
* Send modal to the back
* @param container
*/
const sendToBack = function(container) {
container.style.zIndex = defaultZIndex;
}
// Get the coordinates of the action
const getCoords = function(e) {
let x;
let y;
if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
} else {
x = e.clientX;
y = e.clientY;
}
return [x,y];
}
// Get the button status
const getButton = function(e) {
e = e || window.event;
if (e.buttons) {
return e.buttons;
} else if (e.button) {
return e.button;
} else {
return e.which;
}
}
// Finalize any potential action
const mouseUp = function(e) {
// Finalize all actions
if (typeof(controls.action) === 'function') {
controls.action();
}
setTimeout(function() {
// Remove cursor
if (controls.e) {
controls.e.style.cursor = '';
}
// Reset controls
controls = {};
// Reset state controls
state = {
x: null,
y: null,
}
}, 0)
}
const mouseMove = function(e) {
if (! getButton(e)) {
return false;
}
// Get mouse coordinates
let [x,y] = getCoords(e);
// Move modal
if (controls.type === 'move') {
if (state && state.x == null && state.y == null) {
state.x = x;
state.y = y;
}
let dx = x - state.x;
let dy = y - state.y;
let top = controls.e.offsetTop + dy;
let left = controls.e.offsetLeft + dx;
// Update position
controls.top = top;
controls.left = left;
controls.e.style.top = top + 'px';
controls.e.style.left = left + 'px';
state.x = x;
state.y = y;
state.top = top;
state.left = left;
} else if (controls.type === 'resize') {
let top = null;
let left = null;
let width = null;
let height = null;
if (controls.d === 'e-resize' || controls.d === 'ne-resize' || controls.d === 'se-resize') {
width = controls.w + (x - controls.x);
if (e.shiftKey) {
height = controls.h + (x - controls.x) * (controls.h / controls.w);
}
} else if (controls.d === 'w-resize' || controls.d === 'nw-resize'|| controls.d === 'sw-resize') {
left = controls.l + (x - controls.x);
// Do not move further
if (left >= controls.l) {
left = controls.l;
}
// Update width
width = controls.l + controls.w - left;
// Consider shift to update height
if (e.shiftKey) {
height = controls.h - (x - controls.x) * (controls.h / controls.w);
}
}
if (controls.d === 's-resize' || controls.d === 'se-resize' || controls.d === 'sw-resize') {
if (! height) {
height = controls.h + (y - controls.y);
}
} else if (controls.d === 'n-resize' || controls.d === 'ne-resize' || controls.d === 'nw-resize') {
top = controls.t + (y - controls.y);
// Do not move further
if (top >= controls.t) {
top = controls.t;
}
// Update height
height = controls.t + controls.h - top;
}
if (top) {
controls.e.style.top = top + 'px';
}
if (left) {
controls.e.style.left = left + 'px';
}
if (width) {
controls.e.style.width = width + 'px';
}
if (height) {
controls.e.style.height = height + 'px';
}
}
}
if (typeof(document) !== "undefined") {
document.addEventListener('mouseup', mouseUp);
document.addEventListener('mousemove', mouseMove);
}
const isTrue = function(e) {
return e === true || e === 1 || e === 'true';
}
const refreshMinimized = function() {
let items = minimizedModals;
let numOfItems = items.length;
let width = 10;
let height = 55;
let offsetWidth = window.innerWidth;
let offsetHeight = window.innerHeight;
for (let i = 0; i < numOfItems; i++) {
let item = items[i];
item.el.style.left = width + 'px';
item.el.style.top = offsetHeight - height + 'px';
width += 205;
if (offsetWidth - width < 205) {
width = 10;
height += 50;
}
}
}
const delayAction = function(self, action) {
// Make sure to remove the transformation before minimize to preserve the animation
if (self.el.style.marginLeft || self.el.style.marginTop) {
// Make sure no animation during this process
self.el.classList.add('action');
// Remove adjustment
removeMargin(self);
// Make sure to continue with minimize
setTimeout(function() {
// Remove class
self.el.classList.remove('action');
// Call action
action(self);
},0)
return true;
}
}
const setMini = function(self) {
if (delayAction(self, setMini)) {
return;
}
// Minimize modals
minimizedModals.push(self);
self.el.top = self.el.offsetTop;
self.el.left = self.el.offsetLeft;
if (! self.el.style.top) {
self.el.style.top = self.el.top + 'px';
}
if (! self.el.style.left) {
self.el.style.left = self.el.left + 'px';
}
self.el.translateY = 0;
self.el.translateX = 0;
// Refresh positions
setTimeout(function() {
refreshMinimized();
self.minimized = true;
},10)
}
const removeMini = function(self) {
minimizedModals.splice(minimizedModals.indexOf(self), 1);
self.minimized = false;
self.el.style.top = self.el.top + 'px';
self.el.style.left = self.el.left + 'px';
// Refresh positions
setTimeout(() => {
refreshMinimized();
}, 10);
// Refresh positions
setTimeout(() => {
if (self.top === '') {
self.el.style.top = '';
}
if (self.left === '') {
self.el.style.left = '';
}
}, 400);
}
const removeMargin = function(self) {
if (self.el.style.marginLeft) {
let y = self.el.offsetLeft;
self.el.style.marginLeft = '';
self.left = y;
}
if (self.el.style.marginTop) {
let x = self.el.offsetTop;
self.el.style.marginTop = '';
self.top = x;
}
}
const adjustHorizontal = function(self) {
if (! isTrue(self['auto-adjust'])) {
return false;
}
self.el.style.marginLeft = '';
let viewportWidth = window.innerWidth;
let margin = 10;
if (self.position) {
if (self.position === 'absolute') {
let w = document.documentElement.offsetWidth;
if (w > viewportWidth) {
//viewportWidth = w;
}
} else if (self.position !== 'center') {
margin = 0;
}
}
let el = self.el.getBoundingClientRect();
let rightEdgeDistance = viewportWidth - (el.left + el.width);
let transformX = 0;
if (self.position === 'absolute') {
if (rightEdgeDistance < 0) {
transformX = rightEdgeDistance - margin - 10; // 10 is the scroll width
}
} else {
if (rightEdgeDistance < 0) {
transformX = rightEdgeDistance - margin;
}
}
if (el.left < 0) {
transformX = margin - el.left;
}
if (transformX !== 0) {
self.el.style.marginLeft = transformX + 'px';
}
}
const adjustVertical = function(self) {
if (! isTrue(self['auto-adjust'])) {
return false;
}
self.el.style.marginTop = '';
let viewportHeight = window.innerHeight;
let margin = 10;
if (self.position) {
if (self.position === 'absolute') {
let h = document.documentElement.offsetHeight;
if (h > viewportHeight) {
//viewportHeight = h;
}
} else if (self.position !== 'center') {
margin = 0;
}
}
let el = self.el.getBoundingClientRect();
let bottomEdgeDistance = viewportHeight - (el.top + el.height);
let transformY = 0;
if (self.position === 'absolute') {
if (bottomEdgeDistance < 5) {
transformY = (-1 * el.height) - margin - 12;
if (el.top + transformY < 0) {
transformY = -el.top + 10;
}
}
} else {
if (bottomEdgeDistance < 0) {
transformY = bottomEdgeDistance - margin;
}
}
if (el.top < 0) {
transformY = margin - el.top;
}
if (transformY !== 0) {
self.el.style.marginTop = transformY + 'px';
}
}
const removeElements = function(root) {
// Keep the DOM elements
let elements = [];
if (root) {
while (root.firstChild) {
elements.push(root.firstChild);
root.firstChild.remove();
}
}
return elements;
}
const appendElements = function(root, elements) {
if (elements && elements.length) {
while (elements[0]) {
root.appendChild(elements.shift());
}
}
}
const Modal = function (template, { onchange, onload, track }) {
let self = this;
let backdrop = null;
let elements = null;
if (this.tagName) {
// Remove elements from the DOM
elements = removeElements(this);
this.addEventListener('dragstart', (e) => {
e.preventDefault();
});
}
// Make sure keep the state as boolean
self.closed = !! self.closed;
// Keep all modals references
modals.push(self);
// External onload remove from the lifecycle
let change = self.onchange;
self.onchange = null;
let load = self.onload;
self.onload = null;
let ignoreEvents = false;
const click = function(e) {
if (e.target.classList.contains('lm-modal-close')) {
self.close({ origin: 'button' });
}
if (e.target.classList.contains('lm-modal-minimize')) {
// Handles minimized modal positioning
if (self.minimized === true) {
removeMini(self);
} else {
setMini(self);
}
}
}
const mousemove = function(e) {
if (getButton(e)) {
return;
}
// Get mouse coordinates
let [x,y] = getCoords(e);
// Root element of the component
let item = self.el;
// Get the position and dimensions
let rect = item.getBoundingClientRect();
controls.type = null;
controls.d = null;
controls.e = item;
controls.w = rect.width;
controls.h = rect.height;
controls.t = rect.top;
controls.l = rect.left;
// When resizable
if (isTrue(self.resizable)) {
if (e.clientY - rect.top < cornerSize) {
if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'ne-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'nw-resize';
} else {
item.style.cursor = 'n-resize';
}
} else if (rect.height - (e.clientY - rect.top) < cornerSize) {
if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'se-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'sw-resize';
} else {
item.style.cursor = 's-resize';
}
} else if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'e-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'w-resize';
} else {
item.style.cursor = '';
}
if (item.style.cursor) {
controls.type = 'resize';
controls.d = item.style.cursor;
} else {
controls.type = null;
controls.d = null;
}
}
if (controls.type == null && isTrue(self.draggable)) {
if (y - rect.top < 40) {
item.style.cursor = 'move';
} else {
item.style.cursor = '';
}
if (item.style.cursor) {
controls.type = 'move';
controls.d = item.style.cursor;
} else {
controls.type = null;
controls.d = null;
}
}
}
const mousedown = function(e) {
if (! self.minimized) {
// Get mouse coordinates
let [x,y] = getCoords(e);
controls.x = x;
controls.y = y;
// Root element of the component
let item = self.el;
// Get the position and dimensions
let rect = item.getBoundingClientRect();
controls.e = item;
controls.w = rect.width;
controls.h = rect.height;
controls.t = rect.top;
controls.l = rect.left;
// If is not minimized
if (controls.type === 'resize') {
// Make sure the width and height is defined for the modal
if (! item.style.width) {
item.style.width = controls.w + 'px';
}
if (! item.style.height) {
item.style.height = controls.h + 'px';
}
// This will be the callback when finalize the resize
controls.action = function () {
self.width = parseInt(item.style.width);
self.height = parseInt(item.style.height);
controls.e.classList.remove('action');
// Event
Dispatch.call(self, self.onresize, 'resize', {
instance: self,
width: self.width,
height: self.height,
});
}
controls.e.classList.add('action');
} else if (isTrue(self.draggable) && y - rect.top < 40) {
// Callback
controls.action = function () {
self.top = parseInt(item.style.top);
self.left = parseInt(item.style.left);
controls.e.classList.remove('action');
// Open event
Dispatch.call(self, self.onmove, 'move', {
instance: self,
top: self.top,
left: self.left,
});
}
controls.e.classList.add('action');
// Remove transform
removeMargin(self);
}
}
}
self.back = function() {
sendToBack(self.el);
}
self.front = function() {
sendToFront(self.el);
}
self.open = function() {
if (self.closed === true) {
self.closed = false;
// Close event
Dispatch.call(self, self.onopen, 'open', {
instance: self
});
}
}
self.close = function(options) {
if (self.closed === false) {
self.closed = true;
// Close event
Dispatch.call(self, self.onclose, 'close', {
instance: self,
...options
});
}
}
self.isClosed = function() {
return self.closed;
}
if (! template || typeof(template) !== 'string') {
template = '';
}
// Custom Root Configuration
self.settings = {
getRoot: function() {
return self.root;
}
}
// Native lemonade
onload(() => {
// Dimensions
if (self.width) {
self.el.style.width = self.width + 'px';
}
if (self.height) {
self.el.style.height = self.height + 'px';
}
// Position
if (self.top) {
self.el.style.top = self.top + 'px';
}
if (self.left) {
self.el.style.left = self.left + 'px';
}
if (self.position === 'absolute' || self.position === 'right' || self.position === 'bottom' || self.position === 'left') {
} else {
if (!self.width && self.el.offsetWidth) {
self.width = self.el.offsetWidth;
}
if (!self.height && self.el.offsetHeight) {
self.height = self.el.offsetHeight;
}
// Initial centralize
if (self.position === 'center' || !self.top) {
self.top = (window.innerHeight - self.height) / 2;
}
if (self.position === 'center' || !self.left) {
self.left = (window.innerWidth - self.width) / 2;
}
// Responsive
if (document.documentElement.clientWidth < 800) {
// Full screen
if (self.height > 300) {
self.el.classList.add('fullscreen');
}
}
}
// Auto adjust
adjustHorizontal(self);
adjustVertical(self);
// Backdrop
if (self.backdrop === true) {
backdrop = document.createElement('div');
backdrop.classList.add('lm-modal-backdrop');
backdrop.addEventListener('click', () => {
self.close({ origin: 'backdrop' });
});
if (self.closed === false) {
self.el.parentNode.insertBefore(backdrop, self.el);
}
}
// Import content from DOM
if (self.content) {
if (typeof(self.content) === 'string') {
template = self.content;
} else if (typeof(self.content) === 'object' && self.content.tagName) {
self.root.appendChild(self.content);
}
}
// Focus out of the component
self.el.addEventListener('focusout', function(e) {
if (! self.el.contains(e.relatedTarget)) {
if (isTrue(self['auto-close'])) {
self.close({ origin: 'focusout' });
}
// Remove focus
self.el.classList.remove('lm-modal-focus');
}
});
// Focus out of the component
self.el.addEventListener('focusin', function(e) {
self.el.classList.add('lm-modal-focus');
});
// Close and stop propagation
self.el.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (self.closed === false) {
self.close({ origin: 'escape' });
e.preventDefault();
e.stopImmediatePropagation();
}
} else if (e.key === 'Enter') {
click(e);
}
});
// Append elements to the container
appendElements(self.el.children[1], elements);
if (self.url) {
fetch(self.url)
.then(response => response.clone().body)
.then(body => {
let reader = body.getReader();
reader.read().then(({ done, value }) => {
// Add HTML to the modal
self.root.innerHTML = new TextDecoder().decode(value.buffer);
// Call onload event
Dispatch.call(self, load, 'load', {
instance: self
});
});
});
} else {
// Call onload event
Dispatch.call(self, load, 'load', {
instance: self
});
}
});
onchange((property) => {
if (ignoreEvents) {
return false;
}
if (property === 'closed') {
if (self.closed === false) {
// Focus on the modal
if (self.focus !== false) {
self.el.focus();
}
// Show backdrop
if (backdrop) {
self.el.parentNode.insertBefore(backdrop, self.el);
}
// Auto adjust
queueMicrotask(() => {
adjustHorizontal(self);
adjustVertical(self);
});
} else {
// Hide backdrop
if (backdrop) {
backdrop.remove();
}
}
} else if (property === 'top' || property === 'left' || property === 'width' || property === 'height') {
if (self[property] !== '') {
self.el.style[property] = self[property] + 'px';
} else {
self.el.style[property] = '';
}
if (self.closed === false) {
queueMicrotask(() => {
if (property === 'top') {
adjustVertical(self);
}
if (property === 'left') {
adjustHorizontal(self);
}
});
}
} else if (property === 'position') {
if (self.position) {
if (self.position === 'center') {
self.top = (window.innerHeight - self.el.offsetHeight) / 2;
self.left = (window.innerWidth - self.el.offsetWidth) / 2;
} else {
self.top = '';
self.left = '';
}
} else {
if (! self.top) {
self.top = (window.innerHeight - self.el.offsetHeight) / 2;
}
if (! self.left) {
self.left = (window.innerWidth - self.el.offsetWidth) / 2;
}
}
}
});
track('top');
track('left');
track('width');
track('height');
return render => render`<div class="lm-modal" animation="{{self.animation}}" position="{{self.position}}" closed="{{self.closed}}" closable="{{self.closable}}" minimizable="{{self.minimizable}}" minimized="{{self.minimized}}" overflow="{{self.overflow}}" tabindex="-1" role="modal" onmousedown="${mousedown}" onmousemove="${mousemove}" onclick="${click}">
<div class="lm-modal-title" data-title="{{self.title}}" data-icon="{{self.icon}}"><div class="lm-modal-icon">{{self.icon}}</div><div>{{self.title}}</div><div class="lm-modal-icon lm-modal-minimize" tabindex="0"></div><div class="lm-modal-icon lm-modal-close" tabindex="0"></div></div>
<div :ref="self.root">${template}</div>
</div>`
}
const Component = function (root, options) {
if (typeof(root) === 'object') {
// Remove elements from the DOM
let elements = removeElements(root);
// Create the modal
let e = lemonade.render(Modal, root, options);
// Add elements to the container
appendElements(e.children[1], elements);
return options;
} else {
return Modal.call(this);
}
}
// Create LemonadeJS Component
lemonade.setComponents({ Modal: Modal });
// Create Web Component
lemonade.createWebComponent('modal', Modal)
return Component;
})));
/***/ }),
/***/ 867:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (!lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
;(function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
this.dispatchEvent(new CustomEvents(type, options));
}
}
const Rating = function(children, { onchange, onload }) {
let self = this;
// Event
let change = self.onchange;
self.onchange = null;
if (! self.number) {
self.number = 5;
}
self.stars = [];
// Current self star
let current = null;
/**
* Update the number of stars
*/
const len = function () {
// Remove stars
if (self.number < self.stars.length) {
self.stars.splice(self.number, self.stars.length);
if (self.value > self.number) {
self.value = self.number;
}
}
// Add missing stars
for (let i = 0; i < self.number; i++) {
if (! self.stars[i]) {
self.stars[i] = {
icon: 'star',
};
if (self.tooltip[i]) {
self.stars[i].title = self.tooltip[i];
}
}
}
// Refresh
self.refresh('stars');
}
const val = function (index, events) {
if (typeof(index) === 'string') {
index = Number(index);
}
// Apply value to the selected property in each star
for (let i = 0; i < self.number; i++) {
self.stars[i].selected = i <= index - 1 ? 1 : 0;
}
// Keep current value
current = index;
// Dispatch method
if (events !== false) {
Dispatch.call(self, change, 'change', {
instance: self,
value: index,
});
}
}
const getElementPosition = function(child) {
if (child.tagName === 'I') {
let root = self.el;
for (let i = 0; i < root.children.length; i++) {
let c = root.children[i];
if (c === child) {
return i;
}
}
}
return -1;
}
const click = function(e, s) {
let ret = getElementPosition(e.target);
if (ret !== -1) {
let index = ret + 1;
if (index === current) {
index = 0;
}
self.value = index;
}
}
const mouseover = function(e, s) {
let index = getElementPosition(e.target);
if (index !== -1) {
for (let i = 0; i < self.number; i++) {
if (i <= index) {
self.stars[i].hover = 1;
} else {
self.stars[i].hover = 0;
}
}
}
}
const mouseout = function(e, s) {
for (let i = 0; i < self.number; i++) {
self.stars[i].hover = 0;
}
}
onchange((prop) => {
if (prop === 'number') {
len();
} else if (prop === 'value') {
val(self.value);
} else if (prop === 'tooltip') {
if (typeof(self.tooltip) === 'string') {
self.tooltip = self.tooltip.split(',')
}
len();
}
})
onload(() => {
// Bind global method to be compatible with LemonadeJS forms
self.el.val = function (v) {
if (typeof (v) === 'undefined') {
return self.value;
} else {
self.value = v;
}
}
if (self.tooltip && typeof(self.tooltip) === 'string') {
self.tooltip = self.tooltip.split(',')
} else {
self.tooltip = '';
}
len();
// Ignore events
val(self.value, false);
self.el.addEventListener('click', click);
self.el.addEventListener('mouseout', mouseout);
self.el.addEventListener('mouseover', mouseover);
});
self.getValue = function () {
return Number(self.value);
}
self.setValue = function (index) {
self.value = index;
}
return `<div class="lm-rating" value="{{self.value}}" number="{{self.number}}" name="{{self.name}}" data-size="{{self.size}}" :loop="self.stars">
<i class="material-symbols-outlined material-icons" data-selected="{{self.selected}}" data-hover="{{self.hover}}" title="{{self.title}}">star</i>
</div>`;
}
// Register the LemonadeJS Component
lemonade.setComponents({ Rating: Rating });
// Register the web component
lemonade.createWebComponent('rating', Rating);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Rating, root, options)
return options;
} else {
return Rating.call(this, root)
}
}
})));
/***/ }),
/***/ 539:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (!lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
this.dispatchEvent(new CustomEvents(type, options));
}
}
const Switch = function (children, { onchange, onload }) {
let self = this;
// Event
let change = self.onchange;
self.onchange = null;
const state = () => {
let s = self.el.firstChild.checked;
if (s !== self.checked) {
self.checked = s;
}
}
onchange((prop, a, b, c, d) => {
if (a !== b) {
Dispatch.call(self, change, 'change', {
instance: self,
value: self.value,
});
}
state();
})
onload(state);
return render => render`<label class="lm-switch" position="{{self.position}}" data-color="{{self.color}}">
<input type="checkbox" name="{{self.name}}" disabled="{{self.disabled}}" checked="{{self.checked}}" :bind="self.value" /> <span>{{self.text}}</span>
</label>`
}
// Create LemonadeJS references
lemonade.setComponents({ Switch: Switch });
// Create web-component
lemonade.createWebComponent('switch', Switch);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Switch, root, options)
return options;
} else {
return Switch.call(this, root);
}
}
})));
/***/ }),
/***/ 560:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (! lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
// Dispatcher
const Dispatch = function(method, type, options) {
// Try calling the method directly if provided
if (typeof method === 'function') {
let a = Object.values(options);
return method(...a);
} else if (this.tagName) {
this.dispatchEvent(new CustomEvents(type, options));
}
}
const extract = function(root, self) {
if (! Array.isArray(self.data)) {
self.data = [];
}
if (root.tagName) {
for (let i = 0; i < root.children.length; i++) {
self.data.push({
el: root.children[i],
})
}
} else {
root.forEach((child) => {
self.data.push({
el: child.element,
})
});
}
}
const sorting = function(el, options) {
const obj = {};
let dragElement = null;
el.addEventListener('dragstart', function(e) {
let target = e.target;
if (target.nodeType === 3) {
if (target.parentNode.getAttribute('draggable') === 'true') {
target = target.parentNode;
} else {
e.preventDefault();
e.stopPropagation();
return;
}
}
if (target.getAttribute('draggable') === 'true') {
let position = Array.prototype.indexOf.call(target.parentNode.children, target);
dragElement = {
element: target,
o: position,
d: position
}
target.style.opacity = '0.25';
e.dataTransfer.setDragImage(target,0,0);
}
});
el.addEventListener('dragover', function(e) {
e.preventDefault();
if (dragElement && getElement(e.target) && e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
let element = e.target.clientWidth / 2 > e.offsetX ? e.target : e.target.nextSibling;
e.target.parentNode.insertBefore(dragElement.element, element);
dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
}
});
el.addEventListener('dragleave', function(e) {
e.preventDefault();
});
el.addEventListener('dragend', function(e) {
e.preventDefault();
if (dragElement) {
let element = dragElement.o < dragElement.d ? e.target.parentNode.children[dragElement.o] : e.target.parentNode.children[dragElement.o].nextSibling
e.target.parentNode.insertBefore(dragElement.element, element);
dragElement.element.style.opacity = '';
dragElement = null;
}
});
el.addEventListener('drop', function(e) {
e.preventDefault();
if (dragElement) {
if (dragElement.o !== dragElement.d) {
if (typeof(options.ondrop) == 'function') {
options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
}
}
dragElement.element.style.opacity = '';
dragElement = null;
}
});
const getElement = function(element) {
var sorting = false;
function path (element) {
if (element === el) {
sorting = true;
}
if (! sorting) {
path(element.parentNode);
}
}
path(element);
return sorting;
}
for (let i = 0; i < el.children.length; i++) {
if (! el.children[i].hasAttribute('draggable')) {
el.children[i].setAttribute('draggable', 'true');
}
}
return el;
}
const Tabs = function(children, { onchange, onload }) {
let self = this
// Event
let change = self.onchange;
self.onchange = null;
// Add new tab
let createButton;
// Get the references from the root web component
let root;
let template = '';
if (this.tagName) {
root = this;
} else {
// References from LemonadeJS
if (typeof(children) === 'string') {
// Version 4
template = children;
} else if (children && children.length) {
// Version 5
root = children;
}
}
if (root) {
extract(root, self);
}
// Process the data
if (self.data) {
for (let i = 0; i < self.data.length; i++) {
if (! self.data[i].el) {
// Create element
self.data[i].el = document.createElement('div');
// Create from content
if (self.data[i].content) {
self.data[i].el.innerHTML = self.data[i].content;
}
}
}
}
let props = ['title', 'selected', 'data-icon'];
const select = function(index) {
// Make sure the index is a number
index = parseInt(index);
// Do not select tabs that does not exist
if (index >= 0 && index < self.data.length) {
for (let i = 0; i < self.root.children.length; i++) {
self.headers.children[i].classList.remove('selected');
self.root.children[i].classList.remove('selected');
}
self.headers.children[index].classList.add('selected');
self.root.children[index].classList.add('selected');
}
}
const init = function(selected) {
let tabs = [];
for (let i = 0; i < self.data.length; i++) {
// Extract meta information from the DOM
if (props) {
props.forEach((prop) => {
let short = prop.replace('data-', '');
if (! self.data[i][short]) {
let ret = self.data[i].el.getAttribute(prop);
if (ret != null) {
self.data[i][short] = ret;
}
}
});
}
// Create tabs object
tabs[i] = {
title: self.data[i].title,
}
// Which one is selected by default
if (self.data[i].selected) {
selected = i;
}
if (self.data[i].icon) {
tabs[i].icon = self.data[i].icon;
}
self.root.appendChild(self.data[i].el);
}
// Create headers
self.tabs = tabs;
// Default selected
if (typeof(selected) !== 'undefined') {
self.selected = selected;
}
if (props) {
// Add create new tab button
if (createButton) {
self.headers.appendChild(createButton);
}
// Add sorting
sorting(self.el.firstChild.firstChild, {
ondrop: (el, fromIndex, toIndex) => {
// Remove the item from its original position
const [movedItem] = self.data.splice(fromIndex, 1);
// Insert it into the new position
self.data.splice(toIndex, 0, movedItem);
// Make sure correct order
for (let i = 0; i < self.data.length; i++) {
self.root.appendChild(self.data[i].el);
}
// Select new position
self.selected = toIndex;
// Dispatch event
Dispatch.call(self, self.onchangeposition, 'changeposition', {
instance: self,
fromIndex: fromIndex,
toIndex: toIndex,
});
}
})
}
props = null;
}
const create = function() {
// Create a new item
self.create({ title: 'Untitled' }, null, true);
}
const open = function(e) {
if (e.target.tagName === 'LI') {
// Avoid select something already selected
let index = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
if (index !== self.selected) {
self.selected = index;
}
}
}
const keydown = function(e, s) {
let index = null;
if (e.key === 'Enter') {
self.click(e, s);
} else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
index = self.selected - 1;
if (index < 0) {
index = 0;
}
} else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
index = self.selected + 1;
if (index > self.tabs.length-1) {
index = self.tabs.length-1;
}
}
// Make selection
if (index !== null) {
self.tabs[index].el.focus();
}
}
onload(() => {
if (template) {
extract(self.root, self);
}
init(self.selected || 0);
})
onchange((property) => {
if (property === 'selected') {
select(self.selected);
Dispatch.call(self, self.onopen, 'open', {
instance: self,
selected: self.selected,
});
Dispatch.call(self, change, 'change', {
instance: self,
value: self.selected,
});
}
})
self.open = function (index) {
self.selected = index;
}
self.create = function(item, position, select) {
// Create element
if (typeof(item) !== 'object') {
console.error('Item must be an object');
} else {
let ret = Dispatch.call(self, self.onbeforecreate, 'beforecreate', {
instance: self,
item: item,
position: position,
});
if (ret === false) {
return false;
}
// Create DOM
item.el = document.createElement('div');
// Create from content
if (item.content) {
item.el.innerHTML = item.content;
}
// Add the new item in the end
if (typeof(position) === 'undefined' || position === null) {
// Mew item
position = self.data.length;
// Add in the end
self.data.push(item);
} else {
self.data.splice(position, 0, item);
}
// New position
if (select) {
// Refresh
init(self.data.indexOf(item));
} else {
init(self.selected);
}
self.tabs.forEach(item => {
item.el.setAttribute('draggable', 'true');
})
Dispatch.call(self, self.oncreate, 'create', {
instance: self,
item: item,
position: position,
});
}
}
self.allowCreate = !! self.allowCreate;
return render => render`<div class="lm-tabs" data-position="{{self.position}}" data-round="{{self.round}}">
<div role="tabs" class="lm-tabs-headers">
<ul :ref="self.headers" :loop="self.tabs" :selected="self.selected" onclick="${open}" onkeydown="${keydown}" onfocusin="${open}"><li class="lm-tab" tabindex="0" role="tab" data-icon="{{self.icon}}">{{self.title}}</li></ul>
<div data-visible="{{self.allowCreate}}" class="lm-tabs-insert-button" role="insert-tab" onclick="${create}">add</div>
</div>
<div :ref="self.root" class="lm-tabs-content">${template}</div>
</div>`
}
lemonade.setComponents({ Tabs: Tabs });
lemonade.createWebComponent('tabs', Tabs);
return function (root, options) {
if (typeof (root) === 'object') {
if (typeof(options) !== 'object') {
options = {};
}
// Extract DOM references
extract(root, options);
// Create the modal
lemonade.render(Tabs, root, options);
// Return self
return options;
} else {
return Tabs.call(this);
}
};
})));
/***/ }),
/***/ 330:
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
if (!lemonade && "function" === 'function') {
var lemonade = __webpack_require__(966);
}
if (! Contextmenu && "function" === 'function') {
var Contextmenu = __webpack_require__(238);
}
; (function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
const Topmenu = function(children, { onload, onchange }) {
let self = this;
// Current selection
let currentIndex = null;
const getElementPosition = function(child) {
let root = self.el.children[0];
for (let i = 0; i < root.children.length; i++) {
let c = root.children[i];
if (c === child) {
return i;
}
}
return -1;
}
const select = function(e, s) {
if (! self.menu.isClosed()) {
let index = self.options.indexOf(s);
if (index !== currentIndex) {
open(index);
}
}
}
const deselect = function() {
if (self.options) {
self.options.forEach(v => v.selected = false);
}
}
const selectIndex = function(newIndex) {
if (self.options) {
let s = self.options[newIndex];
if (s && ! s.disabled) {
deselect();
// Make it selected
s.selected = true;
// New index
currentIndex = newIndex;
// Focus
s.el.focus();
}
}
}
const open = function(index) {
// Update cursor position
selectIndex(index);
let s = self.options[currentIndex];
if (s && s.submenu) {
let x = s.el.offsetLeft;
let y = s.el.offsetTop + s.el.offsetHeight + 2;
self.menu.open(s.submenu, x, y);
s.expanded = true;
}
}
const close = function() {
self.menu.close(0);
let s = self.options[currentIndex];
if (s) {
s.el.focus();
s.expanded = false;
}
}
const toggle = function(e, s) {
if (s.submenu && ! s.disabled) {
let index = self.options.indexOf(s);
if (index === currentIndex && ! self.menu.isClosed()) {
close();
} else {
open(index);
}
cancel(e);
}
}
const findNextEnabledIndex = function(startIndex) {
if (!self.options || self.options.length === 0) {
return null;
}
let index = startIndex;
let attempts = 0;
const maxAttempts = self.options.length;
while (attempts < maxAttempts) {
if (index >= self.options.length) {
index = 0;
}
if (!self.options[index].disabled) {
return index;
}
index++;
attempts++;
}
return null;
};
const findPreviousEnabledIndex = function(startIndex) {
if (!self.options || self.options.length === 0) {
return null;
}
let index = startIndex;
let attempts = 0;
const maxAttempts = self.options.length;
while (attempts < maxAttempts) {
if (index < 0) {
index = self.options.length - 1;
}
if (!self.options[index].disabled) {
return index;
}
index--;
attempts++;
}
return null;
};
const adjustOptionProperties = function() {
if (self.options) {
self.options.forEach(v => {
v.haspopup = !!v.submenu;
v.expanded = false;
if (v.disabled) {
v.el.removeAttribute('tabindex');
} else {
v.el.setAttribute('tabindex', '0');
}
})
}
};
/**
* Open a submenu programaticaly. Default 0
* @param {number} index
*/
self.open = function(index) {
if (typeof index === 'undefined') {
index = currentIndex;
}
if (! index) {
index = 0;
}
let s = self.options[index];
if (s) {
open(index);
}
}
onchange((prop) => {
if (prop === 'options') {
adjustOptionProperties();
}
});
// Keyboard event
onload(() => {
self.el.addEventListener("focusin", function(e) {
let index = getElementPosition(e.target);
if (index !== -1) {
if (e.relatedTarget === self.menu.el) {
close();
} else {
selectIndex(index);
}
}
});
self.el.addEventListener("focusout", function(e) {
if (! (e.relatedTarget && self.el.contains(e.relatedTarget))) {
if (self.options[currentIndex]) {
self.options[currentIndex].selected = false;
}
}
});
self.el.addEventListener("keydown", function(e) {
let o = self.options;
// Select top menu
let select = null;
if (e.key === 'Enter') {
toggle(e, o[currentIndex])
} else if (e.key === 'ArrowLeft') {
select = findPreviousEnabledIndex(currentIndex - 1);
} else if (e.key === 'ArrowRight') {
select = findNextEnabledIndex(currentIndex + 1);
}
if (select !== null) {
if (self.menu.isClosed()) {
selectIndex(select);
} else {
open(select);
}
}
});
adjustOptionProperties();
});
const cancel = function(e) {
e.preventDefault();
e.stopImmediatePropagation();
}
return render => render`<div class="lm-topmenu" role="menubar" aria-orientation="horizontal" oncontextmenu="${cancel}">
<div class="lm-topmenu-options" :loop="self.options">
<div class="lm-topmenu-title" role="menuitem" data-disabled="{{self.disabled}}" data-selected="{{self.selected}}" tabindex="0" aria-haspopup="{{self.haspopup}}" aria-expanded="{{self.expanded}}" aria-label="{{self.title}}" onmousedown="${toggle}" onmouseenter="${select}">{{self.title}}</div>
</div>
<Contextmenu :ref="self.menu" :root="self.el" />
</div>`
}
lemonade.setComponents({ Topmenu: Topmenu });
// Register the web component
lemonade.createWebComponent('topmenu', Topmenu);
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Topmenu, root, options)
return options;
} else {
return Topmenu.call(this, root)
}
}
})));
/***/ }),
/***/ 966:
/***/ (function(module) {
/**
* LemonadeJS v5
*
* Website: https://lemonadejs.com
* Description: Create amazing web based reusable components.
*
* This software is distributed under MIT License
* @Roadmap
* Accept interpolated values on properties `<div test="test: ${state}"></div>
*/
;(function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
'use strict';
/**
* Global control element
*/
let R = {
queue: [],
container: {},
tracking: new Map,
components: {},
version: 5,
};
// Global LemonadeJS controllers
if (typeof(document) !== "undefined") {
if (! document.lemonadejs) {
document.lemonadejs = R;
} else {
R = document.lemonadejs;
}
}
// Get any conflict of versions
if (! R.version) {
console.error('This project seems to be using version 4 and version 5 causing a conflict of versions.');
}
// Script expression inside LemonadeJS templates
let isScript = /{{(.*?)}}/g;
/**
* Apply value in a object based on the address
*/
const Path = function(str, val, remove) {
str = str.split('.');
if (str.length) {
let o = this;
let p = null;
while (str.length > 1) {
// Get the property
p = str.shift();
// Check if the property exists
if (o.hasOwnProperty(p)) {
o = o[p];
} else {
// Property does not exist
if (typeof(val) === 'undefined') {
return undefined;
} else {
// Create the property
o[p] = {};
// Next property
o = o[p];
}
}
}
// Get the property
p = str.shift();
// Set or get the value
if (typeof(val) !== 'undefined') {
if (remove === true) {
delete o[p];
} else {
o[p] = val;
}
// Success
return true;
} else {
// Return the value
if (o) {
return o[p];
}
}
}
// Something went wrong
return false;
}
/**
* Show a better error developers
*/
const createError = function() {
throw new Error('LemonadeJS ' + Array.from(arguments).join(' '));
}
/**
* Reference token is a string that define a token and not an expression
* @param {string} token
* @param {boolean?} topLevelOnly
* @returns {boolean}
*/
function isReferenceToken(token, topLevelOnly) {
if (topLevelOnly) {
return /^(this|self)(\.\w+)$/gm.test(token);
} else {
return /^(this|self)(\.\w+|\[\d+])*$/gm.test(token);
}
}
/**
* Extract all valid tokens from a string
* @param {string} content
* @returns {string[]}
*/
function extractTokens(content) {
// Input validation
if (typeof content !== 'string') {
throw new TypeError('Content must be a string');
}
// Single regex pattern to match both 'self.' and 'this.' prefixed identifiers Negative lookahead (?!\.\w) prevents matching nested properties
//const pattern = /(?:self|this)\.\w+\b(?!\.\w)/g;
const pattern = /(?<=(?:this|self)\.)[a-zA-Z_]\w*/gm
// If no matches found, return empty array early
const matches = content.match(pattern);
if (!matches) {
return [];
}
// Create Set directly from matches array with map transform This is more efficient than adding items one by one
return [...new Set(matches.map(match => match.slice(match.indexOf('.') + 1)))];
}
/**
* Bind a property to one action and start tracking
* @param {object} lemon
* @param {string} prop
*/
const trackProperty = function(lemon, prop) {
// Lemon handler
let s = lemon.self;
if (typeof(s) === 'object') {
// Change
let change = lemon.change;
// Events
let events = lemon.events[prop];
// Current value
let value = s[prop];
// Do not allow undefined
if (typeof(value) === 'undefined') {
value = '';
}
// Create the observer
Object.defineProperty(s, prop, {
set: function(v) {
// Old value
let oldValue = value;
// New value
value = v;
// Dispatch reactions
if (events) {
events.forEach((action) => {
action();
});
}
// Refresh bound elements
if (change && change.length) {
change.forEach((action) => {
if (typeof (action) === 'function') {
action.call(s, prop, oldValue, v);
}
})
}
},
get: function () {
// Get value
return value;
},
configurable: true,
enumerable: true,
});
}
}
/**
* Check if an element is appended to the DOM or a shadowRoot
* @param {HTMLElement} node
* @return {boolean}
*/
const isAppended = function(node) {
while (node) {
if (node === document.body) {
return true; // Node is in main document
}
if (node.parentNode === null) {
if (node.host) {
node = node.host; // Traverse up through ShadowRoot
} else {
return false; // Detached node
}
} else {
node = node.parentNode; // Traverse up through parentNode
}
}
return false;
}
const elementNotReady = new Set;
/**
* Execute all pending events from onload
* @param lemon
*/
const executeOnload = function(lemon) {
let s = lemon.self;
// Ready event
while (lemon.ready.length) {
lemon.ready.shift()();
}
// Native onload
if (typeof(s.onload) === 'function') {
s.onload.call(s, s.el);
}
// Current self
if (typeof(lemon.load) === 'function') {
lemon.load.call(s, s.el);
}
}
/**
* Process the onload methods
*/
const processOnload = function(lemon) {
let root = lemon.tree.element;
if (root.tagName === 'ROOT' && lemon.elements) {
root = lemon.elements[0];
}
// Add to the queue
elementNotReady.add(lemon);
// Check if the element is appended to the DOM
if (isAppended(root)) {
elementNotReady.forEach((item) => {
// Remove from the list
elementNotReady.delete(item);
// Run onload and ready events
executeOnload(item);
})
}
}
/**
* Return the element based on the type
* @param item
* @returns {*}
*/
const getElement = function(item) {
return typeof(item.type) === 'function' ? item.self : item.element;
}
const HTMLParser = function(html, values) {
/**
* process the scape chars
* @param char
* @returns {*|string}
*/
function escape(char) {
const escapeMap = {
'n': String.fromCharCode(0x0A),
'r': String.fromCharCode(0x0D),
't': String.fromCharCode(0x09),
'b': String.fromCharCode(0x08),
'f': String.fromCharCode(0x0C),
'v': String.fromCharCode(0x0B),
'0': String.fromCharCode(0x00)
};
return escapeMap[char] || char;
}
/**
* Check if is a self-closing tag
* @param {string|function} type - Tag name or component function
* @returns {boolean}
*/
function isSelfClosing(type) {
if (! type) {
return false;
} else {
// List of self-closing or void HTML elements
const selfClosingTags = [
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr'
];
// Convert tagName to lowercase to ensure case-insensitive comparison
return typeof(type) === 'function' || selfClosingTags.includes(type.toLowerCase());
}
}
/**
* Create a text node and add it to current node's children
* @param {Object} tag - Text node properties
*/
const createTextNode = function(tag) {
if (! this.current.children) {
this.current.children = [];
}
this.current.children.push({
type: '#text',
parent: this.current,
props: [tag],
});
}
/**
* Find the parent node by tag name
* @param {Object} node - Current node
* @param {string} type - Tag name to find
* @returns {Object|undefined}
*/
const findParentByTagName = function(node, type) {
if (node && type) {
if (node.type === type) {
return node;
} else {
return findParentByTagName(node.parent, type);
}
}
return undefined;
}
/**
* Get expression value and update tag metadata
* @param {Object} tag - Tag to update
* @returns {*} Expression value
*/
const getExpression = function(tag) {
// Get value
const v = values && values[this.index] !== undefined ? values[this.index] : '';
if (tag) {
// Keep the reference
tag.expression = this.reference;
// Keep the index
tag.index = this.index;
}
// Move the value index
this.index++;
// Delete reference
delete this.reference;
// Return value
return v;
}
/**
* Handle the text node creation
*/
const commitText = function() {
if (typeof(this.text) !== 'undefined') {
const text = this.text.replace(/\r?\n\s+/g, '');
if (text) {
createTextNode.call(this, { name: 'textContent', value: text });
}
delete this.text;
}
}
const commitComments = function() {
if (typeof(this.comments) !== 'undefined') {
let comments = this.comments;
if (comments) {
comments = comments
.replace('<!--', '')
.replace('-->', '')
if (! this.current.children) {
this.current.children = [];
}
this.current.children.push({
type: '#comments',
parent: this.current,
props: [{ name: 'text', value: comments }],
});
}
delete this.comments;
}
}
/**
* Save the attribute to the tag
*/
const commitAttribute = function() {
if (this.tag.attributeName) {
// Commit any current attribute
if (! this.tag.props) {
this.tag.props = [];
}
let k = this.tag.attributeName;
let v = this.tag.attributeValue;
if (typeof(v) === 'undefined') {
v = k;
}
let tag = {
name: k,
value: v,
};
if (typeof(this.tag.expression) !== 'undefined') {
tag.index = this.tag.index;
tag.expression = this.tag.expression;
}
this.tag.props.push(tag);
// Clean up temporary properties
delete this.tag.attributeName;
delete this.tag.attributeValue;
delete this.tag.index;
delete this.tag.expression;
if (this.tag.attributeIsReadyToClose) {
delete this.tag.attributeIsReadyToClose;
}
}
}
/**
* Actions controller
* @param {Object} control - Parser control object
* @param {string} char - Current character
*/
const actions = function(control, char) {
const method = control.action || 'text';
if (typeof actions[method] === 'function') {
actions[method].call(control, char);
}
}
/**
* Extract content between expression markers, handling quoted content
* @param {string} html - Full HTML string
* @param {number} startIndex - Starting index (position after ${})
* @returns {Object} Expression content and ending position
*/
function extractExpressionContent(html, startIndex) {
let text = '';
let i = startIndex;
let insideQuotes = null;
while (i < html.length) {
const char = html[i];
// Handle quotes
if ((char === '"' || char === "'" || char === '`')) {
if (!insideQuotes) {
insideQuotes = char;
} else if (char === insideQuotes) {
insideQuotes = null;
}
}
// Found closing brace outside quotes
if (char === '}' && !insideQuotes) {
return {
content: text,
position: i
};
}
text += char;
i++;
}
return {
content: text,
position: i
};
}
/**
* Process a new tag
* @param char
*/
actions.processTag = function(char) {
// Just to check if there are any text to commit
commitText.call(this);
// Process the tag
if (char === '<') {
// Create new tag
this.tag = {
type: '',
parent: this.current
};
} else if (char.match(/[a-zA-Z0-9-]/)) {
// Tag name
this.tag.type += char;
} else {
if (char === '$' && this.reference) {
// Custom tags
this.tag.type = getExpression.call(this);
}
// Finished with tag name, move to attribute handling
this.action = 'attributeName';
}
}
/**
* Handle tag closing
* @param char
*/
actions.closeTag = function(char) {
// Make sure to commit attribute
commitAttribute.call(this);
// Close the tag
if (char === '>') {
// Get the new parent
if (isSelfClosing(this.tag.type)) {
// Push new tag to the parent
if (! this.tag.parent.children) {
this.tag.parent.children = [];
}
this.tag.parent.children.push(this.tag);
} else if (this.tag.closingTag) {
// Need to find the parent on the chain
const parentNode = findParentByTagName(this.tag.parent, this.tag.type);
if (parentNode) {
this.current = parentNode.parent;
}
} else {
if (this.tag.closing) {
// Current is the parent
this.current = this.tag.parent;
} else {
this.current = this.tag;
}
// Push new tag to the parent
if (! this.tag.parent.children) {
this.tag.parent.children = [];
}
this.tag.parent.children.push(this.tag);
}
// Remote temporary properties
delete this.tag.insideQuote;
delete this.tag.closingTag;
delete this.tag.closing;
// Finalize tag
this.tag = null;
// New action
this.action = 'text';
} else if (! this.tag.locked) {
if (char === '/') {
if (! this.tag.type) {
// This is a closing tag
this.tag.closingTag = true;
}
// Closing character is found
this.tag.closing = true;
} else if (char.match(/[a-zA-Z0-9-]/)) {
// If is a closing tag, get the tag name
if (this.tag.closingTag) {
this.tag.type += char;
}
} else {
// Wait to the closing sign
if (this.tag.type) {
this.locked = true;
}
}
}
}
actions.attributeName = function(char) {
// There is another attribute to commit
if (this.tag.attributeIsReadyToClose) {
commitAttribute.call(this);
}
// Build attribute name
if (char.match(/[a-zA-Z0-9-:]/)) {
if (! this.tag.attributeName) {
this.tag.attributeName = '';
}
this.tag.attributeName += char;
} else if (char === '=') {
// Move to attribute value
if (this.tag.attributeName) {
this.action = 'attributeValue';
delete this.tag.attributeIsReadyToClose;
}
} else if (char.match(/\s/)) {
if (this.tag.attributeName) {
this.tag.attributeIsReadyToClose = true;
}
}
};
actions.attributeValue = function(char) {
if (! this.tag.attributeValue) {
this.tag.attributeValue = '';
}
if (char === '"' || char === "'") {
if (this.tag.insideQuote) {
if (this.tag.insideQuote === char) {
this.tag.insideQuote = false;
} else {
this.tag.attributeValue += char;
}
} else {
this.tag.insideQuote = char;
}
} else {
if (char === '$' && this.reference) {
// Custom tags
char = getExpression.call(this, this.tag);
}
// Inside quotes, keep appending to the attribute value
if (this.tag.insideQuote) {
if (this.tag.attributeValue) {
this.tag.attributeValue += char;
} else {
this.tag.attributeValue = char;
}
} else if (typeof(char) === 'string' && char.match(/\s/)) {
if (this.tag.attributeValue) {
this.action = 'attributeName';
}
this.tag.attributeIsReadyToClose = true;
} else {
if (this.tag.attributeIsReadyToClose) {
this.action = 'attributeName';
actions.attributeName.call(this, char);
} else {
if (this.tag.attributeValue) {
this.tag.attributeValue += char;
} else {
this.tag.attributeValue = char;
}
}
}
}
}
actions.text = function(char) {
if (char === '$' && this.reference) {
// Just to check if there are any text to commit
commitText.call(this);
// Custom tags
let tag = { name: 'textContent' }
tag.value = getExpression.call(this, tag);
// Add node tag
createTextNode.call(this, tag);
} else {
if (referenceControl === 1) {
// Just to check if there are any text to commit
commitText.call(this);
}
// Normal text processing
if (! this.text) {
this.text = '';
}
this.text += char; // Keep appending to text content
if (referenceControl === 2) {
// Just to check if there are any text to commit
commitText.call(this);
}
}
}
actions.comments = function(char) {
if (! this.comments) {
this.comments = '';
}
this.comments += char;
if (this.comments.endsWith('-->')) {
commitComments.call(this);
this.action = 'text';
}
}
// Control the LemonadeJS native references
let referenceControl = null;
const result = { type: 'template' };
const control = {
current: result,
action: 'text',
index: 0,
};
// Input validation
if (typeof html !== 'string') {
throw new TypeError('HTML input must be a string');
}
// Main loop to process the HTML string
for (let i = 0; i < html.length; i++) {
// Current char
let char = html[i];
if (control.action === 'text' && char === '<' && html[i+1] === '!' && html[i+2] === '-' && html[i+3] === '-') {
control.action = 'comments';
}
if (control.action !== 'comments') {
let escaped = false;
if (values !== null) {
// Handle scape
if (char === '\\') {
// This is a escaped char
escaped = true;
// Parse escape char
char = escape(html[i+1]);
// Move to the next char
i++;
}
}
// Global control logic
if (control.tag) {
if (char === '>' || char === '/') {
// End of tag, commit any attributes and go back to text parsing
if (!control.tag.insideQuote) {
control.action = 'closeTag';
}
}
} else {
if (char === '<') {
control.action = 'processTag';
}
}
// Register references for a dynamic template
if (!escaped && char === '$' && html[i + 1] === '{') {
const result = extractExpressionContent(html, i + 2);
control.reference = result.content;
i = result.position;
}
// Control node references
if (char === '{' && html[i + 1] === '{') {
referenceControl = 1;
} else if (char === '}' && html[i - 1] === '}') {
referenceControl = 2;
}
}
// Execute action
actions(control, char);
// Reference control
referenceControl = null;
}
// Handle any remaining text
commitText.call(control);
return result.children && result.children[0];
}
const generateHTML = function(lemon) {
const appendEvent = function(token, event, exec) {
if (! lemon.events[token]) {
lemon.events[token] = []
}
// Push the event
lemon.events[token].push(event);
// Execute
if (exec) {
event();
}
}
const createEventsFromExpression = function(expression, event, exec) {
// Get the tokens should be updated to populate this attribute
let tokens = extractTokens(expression);
if (tokens.length) {
// Process all the tokens
for (let i = 0; i < tokens.length; i++) {
appendEvent(tokens[i], event);
}
}
// Execute method
if (exec) {
event();
}
}
const setDynamicValue = function(item, prop, attributeName) {
// Create a reference to the DOM element
let property = prop.expression || prop.value;
if (isReferenceToken(property)) {
// Event
let event = function() {
// Reference
let value = extractFromPath.call(lemon.self, property);
// Update reference
setAttribute(getElement(item), attributeName, value);
}
// Append event only for the top level properties in the self
if (isReferenceToken(property, true)) {
let p = extractFromPath.call(lemon.self, property, true);
if (p) {
// Append event to the token change
let token = p[1]
// Append event
appendEvent(token, event);
}
}
// Execute the event in the first time
event();
} else {
setAttribute(getElement(item), attributeName, castProperty(prop.value));
}
}
const dynamicContent = function(text) {
try {
// Cast value
let cast = null;
// Replace the text
text = text.replace(isScript, function (a, b) {
let s = lemon.self;
// Try to find the property
let result = extractFromPath.call(s, b);
// Evaluation for legacy purposes
if (typeof(result) === 'undefined') {
// This is deprecated and will be dropped on LemonadeJS 6
result = run.call(s, b);
if (typeof (result) === 'undefined') {
result = '';
}
} else if (result === null) {
result = '';
}
// Parse correct type
if (typeof(result) !== 'string' && a === text) {
cast = result;
}
// Return
return result;
});
if (cast !== null) {
return cast;
}
return text;
} catch (e) {
}
}
const isHTML = function(str) {
return /<[^>]*>/.test(str);
}
const appendHTMLBeforeNode = function(item, value) {
// Remove previous elements
if (item.current) {
item.current.forEach(e => e?.remove());
}
item.current = [];
// Node container
let node = getElement(item);
// Append content
if (! isHTML(value)) {
node.textContent = value;
} else {
// TODO: improve that
queueMicrotask(() => {
// Create a temporary container
const t = document.createElement('div');
t.innerHTML = value;
// Insert elements and store references
while (t.firstChild) {
item.current.push(t.firstChild);
node.parentNode.insertBefore(t.firstChild, node);
}
});
}
}
const applyElementAttribute = function(item, prop) {
if (typeof(prop.expression) !== 'undefined') {
// Event to update the designed position
let event = function() {
// Extra value from the template
let value = lemon.view(parseTemplate)[prop.index];
// Process the NODE
if (item.type === '#text') {
appendHTMLBeforeNode(item, value);
} else {
// Set attribute
setAttribute(getElement(item), prop.name, value);
}
}
// Bind event to any tokens change
createEventsFromExpression(prop.expression, event, true);
// Register event for state changes
lemon.events[prop.index] = event;
} else {
// Get the tokens should be updated to populate this attribute
let tokens = extractTokens(prop.value);
if (tokens.length) {
// Dynamic
createEventsFromExpression(prop.value, function() {
// Dynamic text
let value = dynamicContent(prop.value);
// Get the dynamic value
setAttribute(getElement(item), prop.name, value);
}, true)
} else {
let value = prop.value;
if (value.match(isScript)) {
value = dynamicContent(value);
}
setAttribute(getElement(item), prop.name, value, true);
}
}
}
/**
* Create a LemonadeJS self bind
* @param item
* @param prop
*/
const applyBindHandler = function(item, prop) {
// Create a reference to the DOM element
let property = prop.expression || prop.value;
if (isReferenceToken(property, true)) {
let prop = property.split('.')[1];
// Event from component to the property
let event = function () {
let value = getAttribute(getElement(item), 'value');
if (lemon.self[prop] !== value) {
lemon.self[prop] = value;
}
}
if (typeof (item.type) === 'function') {
item.bind = event;
} else {
item.element.addEventListener('input', event);
}
// Event property to the element
event = () => {
setAttribute(getElement(item), 'value', lemon.self[prop]);
}
// Append event
appendEvent(prop, event, true);
} else if (prop.value instanceof state) {
// TODO: implement :bind states
}
}
/**
* Create a LemonadeJS self render
* @param item
* @param prop
*/
const applyRenderHandler = function(item, prop) {
// Create a reference to the DOM element
let property = prop.expression || prop.value;
let getValue = null;
let token = null;
if (isReferenceToken(property, true)) {
token = property.split('.')[1];
// Get value
getValue = () => {
return lemon.self[token];
}
} else {
if (typeof(prop.expression) !== 'undefined') {
getValue = () => {
// Extra value from the template
let data = lemon.view(parseTemplate)[prop.index];
if (data instanceof state) {
data = data.value;
}
return data;
}
}
}
if (getValue) {
// Create container to keep the elements
item.container = [];
// Event property to the element
let event = () => {
// Root element
let root = typeof(item.type) === 'function' ? item.self.el : item.element;
// Curren value
let value = getValue();
if (value) {
item.container.forEach(e => {
root.appendChild(e);
});
} else {
while (root.firstChild) {
item.container.push(root.firstChild);
root.firstChild.remove();
}
}
}
if (token) {
// Append event
appendEvent(token, event);
} else {
if (typeof(prop.expression) !== 'undefined') {
lemon.events[prop.index] = event;
createEventsFromExpression(prop.expression, event);
}
}
// Execute event
let root = typeof(item.type) === 'function' ? item.self.el : item.element;
if (root) {
event();
} else {
lemon.ready.push(event);
}
}
}
/**
* Create a dynamic reference
* @param item
* @param prop
*/
const createReference = function(item, prop) {
let ref = getElement(item);
if (typeof(prop.value) === 'function') {
prop.value(ref);
} else {
// Create a reference to the DOM element
let property = prop.expression || prop.value;
// Reference
if (isReferenceToken(property)) {
let p = extractFromPath.call(lemon.self, property, true);
if (p) {
lemon.self[p[1]] = ref;
}
}
}
}
/**
* Process the :ready. Call when DOM is ready
* @param item
* @param prop
*/
const whenIsReady = function(item, prop) {
let value = prop.value;
// If not a method, should be converted to a method
if (typeof(value) !== 'function') {
let t = extractFromPath.call(lemon.self, value);
if (t) {
value = t;
}
}
// Must be a function
if (typeof(value) === 'function') {
lemon.ready.push(function() {
value(getElement(item), lemon.self);
});
} else {
createError(`:ready ${value} is not a function`)
}
}
const getRoot = function(item) {
if (typeof(item.type) === 'function') {
if (item.parent.type === 'template') {
return lemon.root;
} else {
return item.parent.element;
}
}
return item.element;
}
const registerLoop = function(item, prop) {
// Create a reference to the DOM element
let property = prop.expression || prop.value;
// Append the template back to the correct position
if (lemon.self.settings?.loop) {
lemon.self.settings.loop(item)
}
// Event
let updateLoop = function(data) {
// Component
let method = typeof(item.type) === 'function' ? item.type : Basic;
// Remove all DOM
let root = getRoot(item);
if (root) {
while (root.firstChild) {
root.firstChild.remove();
}
}
// Process the data
if (data && Array.isArray(data)) {
// Process data
data.forEach(function(self) {
let el = self.el;
if (el) {
root.appendChild(el);
} else {
// Register parent
register(self, 'parent', lemon.self);
// Pass parent view for expressions in loop templates
item.parentView = lemon.view;
// Render
L.render(method, root, self, item);
}
if (root?.getAttribute('unique') === 'false') {
delete self.el;
}
})
}
}
// Event
let event;
// Type of property
if (isReferenceToken(property)) {
// Event
event = function() {
// Reference
let data = extractFromPath.call(lemon.self, property);
// Update reference
updateLoop(data);
}
// Append event only for the top level properties in the self
if (isReferenceToken(property, true)) {
let p = extractFromPath.call(lemon.self, property, true);
if (p) {
// Append event to the token change
appendEvent(p[1], event);
}
}
} else {
if (typeof(prop.expression) !== 'undefined') {
event = function() {
// Extra value from the template
let data = lemon.view(parseTemplate)[prop.index];
if (data instanceof state) {
data = data.value;
}
// Update the data
updateLoop(data);
}
// Register event for state changes
lemon.events[prop.index] = event;
}
}
// Defer event since the dom is not ready
if (event) {
if (getRoot(item)) {
event();
} else {
lemon.ready.push(event);
}
}
}
const isLoopAttribute = function(props) {
let test = false;
props.forEach(function(prop) {
if (prop.name === ':loop' || prop.name === 'lm-loop' || prop.name === '@loop') {
test = true;
}
});
return test;
}
const registerPath = function(item, prop) {
if (! lemon.path.elements) {
lemon.path.elements = [];
}
let element = getElement(item);
lemon.path.elements.push({
element: element,
path: prop.value
});
// Event from component to the property
let event = function () {
// Get the current value of my HTML form element or component
let value = getAttribute(element, 'value');
// Apply the new value on the path on the object
if (lemon.path.value) {
Path.call(lemon.path.value, prop.value, value);
// Call the callback when exist
if (typeof(lemon.path.change) === 'function') {
lemon.path.change(value, prop.value, element);
}
} else {
console.log('Use setPath to define the form container before using lm-path');
}
}
if (typeof(item.type) === 'function') {
item.path = event;
} else {
item.element.addEventListener('input', event);
}
}
const appendChildren = function(container, children) {
if (container && children) {
for (let i = 0; i < children.length; i++) {
let child = children[i];
if (typeof(child) === 'string') {
container.appendChild(document.createTextNode(child));
} else if (child.element) {
if (child.element.tagName === 'ROOT') {
while (child.element.firstChild) {
container.appendChild(child.element.firstChild);
}
} else {
container.appendChild(child.element);
}
}
}
}
}
const getAttributeName = function(prop) {
return prop[0] === ':' || prop[0] === '@' ? prop.substring(1) : prop.substring(3);
}
const getAttributeEvent = function(event) {
event = event.toLowerCase();
if (event.startsWith('on')) {
return event.toLowerCase();
} else if (event.startsWith(':on')) {
return getAttributeName(event);
}
}
/**
* CHeck if the event name is a valid DOM event name
* @param element
* @param eventName
* @returns {boolean}
*/
const isValidEventName = function(element, eventName) {
const validEventPattern = /^on[a-z]+$/;
return validEventPattern.test(eventName);
}
/**
* Create element from the string tagname
* @param tagName
* @returns {*}
*/
const createElementFromString = function(tagName) {
// List of SVG tags (you can expand this list if needed)
const svgTags = [
"svg", "path", "circle", "rect", "line", "polygon", "polyline", "text"
];
if (svgTags.includes(tagName)) {
// For SVG elements, use createElementNS with the correct namespace
return document.createElementNS("http://www.w3.org/2000/svg", tagName);
} else {
// For regular HTML elements, use createElement
return document.createElement(tagName);
}
}
const reorderProps = function(arr) {
// Define the desired order of names
const orderPriority = ['ref', 'bind', 'loop', 'ready'];
// Separate items into prioritized and non-prioritized
const prioritized = [];
const others = [];
// Sort items into respective arrays
arr.forEach(item => {
if (orderPriority.includes(item.name.substring(1)) || orderPriority.includes(item.name.substring(3))) {
prioritized.push(item);
} else {
others.push(item);
}
});
// Return combined array with prioritized items at the end
return [...others, ...prioritized];
}
const createElements = function(item) {
if (typeof(item) === 'object') {
// Create an element
if (item.type === '#comments') {
item.element = document.createComment(item.props[0].value);
} else if (item.type === '#text') {
// Text node
item.element = document.createTextNode('');
// Check for dynamic content
applyElementAttribute(item, item.props[0]);
} else {
// Apply attributes if they exist
if (item.props && ! Array.isArray(item.props)) {
let props = [];
let keys = Object.keys(item.props);
for (let i = 0; i < keys.length; i++) {
props.push({ name: keys[i], value: item.props[keys[i]] });
}
item.props = props;
} else if (! item.props) {
item.props = [];
}
// This item is a parent for a loop
if (isLoopAttribute(item.props)) {
// Mark this item as a loop
item.loop = true;
}
if (! item.type) {
item.type = 'root';
}
if (typeof(item.type) === 'string') {
if (item.type.match(/^[A-Z][a-zA-Z0-9\-]*$/g)) {
let controller = item.type.toUpperCase();
if (typeof(R.components[controller]) === 'function') {
item.type = R.components[controller];
} else if (typeof (lemon.components[controller]) === 'function') {
item.type = lemon.components[controller];
} else {
}
}
}
if (typeof(item.type) === 'string') {
item.element = createElementFromString(item.type);
} else if (typeof(item.type) === 'function') {
// Create instance without calling constructor
if (isClass(item.type)) {
item.self = new item.type;
} else {
item.self = {};
}
}
// Create all children
if (! item.loop) {
if (item.children && Array.isArray(item.children)) {
item.children.forEach(child => {
createElements(child);
});
}
if (item.element) {
appendChildren(item.element, item.children);
}
}
// Process attributes
if (item.props.length) {
// Reorder props
item.props = reorderProps(item.props);
// Order by priority
item.props.forEach(function(prop) {
try {
// If the property is an event
let event = getAttributeEvent(prop.name);
// When event for a DOM
if (event) {
// Element
let element = item.element;
// Value
let value = prop.value;
if (value) {
let handler = null; // Reset handler for each iteration
if (typeof (value) === 'function') {
handler = value;
} else {
let t = extractFromPath.call(lemon.self, value);
if (t) {
if (typeof (t) === 'function') {
prop.value = handler = t;
}
}
}
// When the element is a DOM
if (isDOM(element)) {
// Create the event handler
let eventHandler;
// Bind event
if (typeof(handler) === 'function') {
eventHandler = function(e, a, b) {
return handler.call(element, e, lemon.self, a, b);
}
} else {
// Legacy compatibility. Inline scripting is non-Compliance with Content Security Policy (CSP).
eventHandler = function (e) {
return Function('e', 'self', value).call(element, e, lemon.self); // TODO, quebra tudo se mudar
}
}
if (isValidEventName(element, prop.name)) {
element.addEventListener(event.substring(2), eventHandler);
} else {
if (element.tagName?.includes('-')) {
element[event] = handler;
} else {
element[event] = eventHandler;
}
}
} else {
item.self[event] = handler || value;
}
}
} else if (prop.name.startsWith(':') || prop.name.startsWith('@') || prop.name.startsWith('lm-')) {
// Special lemonade attribute name
let attrName = getAttributeName(prop.name);
// Special properties bound to the self
if (attrName === 'ready') {
whenIsReady(item, prop);
} else if (attrName === 'ref') {
createReference(item, prop);
} else if (attrName === 'loop') {
registerLoop(item, prop);
} else if (attrName === 'bind') {
applyBindHandler(item, prop);
} else if (attrName === 'path') {
registerPath(item, prop);
} else if (attrName === 'render') {
applyRenderHandler(item, prop);
} else {
setDynamicValue(item, prop, attrName);
}
} else {
applyElementAttribute(item, prop);
}
} catch (e) {
let propValue = prop.expression || prop.value;
console.error(`${prop.name}="${propValue}" - ${e.message}`, item);
}
})
}
// Do not create elements at this state if this is a loop
if (! item.loop) {
if (typeof(item.type) === 'function') {
// Execute component
item.element = L.render(item.type, null, item.self, item);
// Create all children
if (item.children) {
let root = item.element;
if (typeof(item.self?.settings?.getRoot) === 'function') {
root = item.self.settings.getRoot();
}
appendChildren(root, item.children);
}
}
}
}
}
}
// Create DOM elements
createElements(lemon.tree);
return lemon.tree.element;
}
/**
* Extract a property from a nested object using a string address
* @param {string} str address inside the nested object
* @param {boolean} config get the configuration obj => property
*/
const extractFromPath = function(str, config) {
try {
let t = str.toString().replace(/[\[\]]/g, '.').split('.');
if (t[0] === 'self' || t[0] === 'this') {
t.shift();
}
// Remove blanks
t = t.filter(item => item !== '');
// Object
let o = this;
let lastObject;
while (t.length) {
// Get the property
let p = t.shift();
// Process config
if (config) {
if (typeof(o) === 'object' && ! Array.isArray(o)) {
lastObject = [o,p];
}
if (t.length === 0) {
return lastObject;
}
}
// Check if the property exists
if (o.hasOwnProperty(p) || typeof(o[p]) !== 'undefined') {
o = o[p];
} else {
return undefined;
}
}
if (typeof(o) !== 'undefined') {
return o;
}
} catch (e) {}
// Something went wrong
return undefined;
}
/**
* Cast the value of an attribute
*/
const castProperty = function(attr) {
// Parse type
try {
if (typeof(attr) === 'string' && attr) {
// Remove any white spaces
attr = attr.trim();
if (attr === 'true') {
return true;
} else if (attr === 'false') {
return false;
} else if (! isNaN(attr)) {
return Number(attr);
} else {
let firstChar = attr[0];
if (firstChar === '{' || firstChar === '[') {
if (attr.slice(-1) === '}' || attr.slice(-1) === ']') {
return JSON.parse(attr);
}
} else if (attr.startsWith('self.') || attr.startsWith('this.')) {
let v = extractFromPath.call(this, attr);
if (typeof(v) !== 'undefined') {
return v;
}
}
}
}
} catch (e) {}
return attr;
}
/**
* This allows to run inline script on LEGACY system. Inline script can lead to security issues so use carefully.
* @param {string} s string to function
*/
const run = function(s) {
return Function('self', '"use strict";return (' + s + ')')(this);
}
/**
* Check if the content {o} is a valid DOM Element
* @param {HTMLElement|DocumentFragment|object} o - is this a valid dom?
* @return {boolean}
*/
const isDOM = function(o) {
return (o instanceof HTMLElement || o instanceof Element || o instanceof DocumentFragment);
}
/**
* Check if the method is a method or a class
* @param {function} f
* @return {boolean}
*/
const isClass = function(f) {
return typeof f === 'function' && /^class\s/.test(Function.prototype.toString.call(f));
}
/**
* Basic handler
* @param {string|object} t - HTML template
* @return {string|object}
*/
const Basic = function(t) {
return t;
}
/**
* Get the attribute helper
* @param {object} e Element
* @param {string} attribute
*/
const getAttribute = function(e, attribute) {
let value;
if (attribute === 'value') {
if (typeof(e.val) === 'function') {
value = e.val();
} else {
if (e.getAttribute) {
if (e.tagName === 'SELECT' && e.getAttribute('multiple') !== null) {
value = [];
for (let i = 0; i < e.options.length; i++) {
if (e.options[i].selected) {
value.push(e.options[i].value);
}
}
} else if (e.type === 'checkbox') {
value = e.checked && e.getAttribute('value') ? e.value : e.checked;
} else if (e.getAttribute('contenteditable')) {
value = e.innerHTML;
} else {
value = e.value;
}
} else {
value = e.value;
}
}
}
return value;
}
/**
* Set attribute value helper
* @param {object} e Element
* @param {string} attribute
* @param {any} value
* @param {boolean?} propertyValue
*/
const setAttribute = function(e, attribute, value, propertyValue) {
// Handle state
if (value instanceof state) {
value = value.value;
} else if (typeof(value) === 'undefined') {
value = '';
}
if (attribute === 'value' && ! propertyValue) {
// Update HTML form element
if (typeof (e.val) === 'function') {
if (e.val() != value) {
e.val(value);
}
} else if (e.tagName === 'SELECT' && e.getAttribute('multiple') !== null) {
for (let j = 0; j < e.children.length; j++) {
e.children[j].selected = value.indexOf(e.children[j].value) >= 0;
}
} else if (e.type === 'checkbox') {
e.checked = !(!value || value === '0' || value === 'false');
} else if (e.type === 'radio') {
e.checked = e.value == value;
} else if (e.getAttribute && e.getAttribute('contenteditable')) {
if (e.innerHTML != value) {
e.innerHTML = value;
}
} else {
// Make sure apply that to the value
e.value = value;
// Update attribute if exists
if (e.getAttribute && e.getAttribute('value') !== null) {
e.setAttribute('value', value);
}
}
} else if (typeof(value) === 'object' || typeof(value) === 'function') {
e[attribute] = value;
} else {
if (isDOM(e)) {
if (typeof (e[attribute]) !== 'undefined' && !(e.namespaceURI && e.namespaceURI.includes('svg'))) {
e[attribute] = value;
} else {
if (value === '') {
e.removeAttribute(attribute);
} else {
e.setAttribute(attribute, value);
}
}
} else {
e[attribute] = value;
}
}
}
/**
* Get attributes as an object
* @param {boolean} props - all attributes that are not undefined
* @return {object}
*/
const getAttributes = function(props) {
let o = {};
let k = null;
let a = this.attributes;
if (a && a.length) {
for (let i = 0; i < a.length; i++) {
k = a[i].name;
if (props && typeof(this[k]) !== 'undefined') {
o[k] = this[k];
} else {
o[k] = a[i].value;
}
}
}
return o;
}
/**
* Register a getter without setter for a self object
* @param {object} s - self object
* @param {string} p - self property
* @param {string|object|number} v - value
*/
const register = function(s, p, v) {
if (typeof(s) === 'object') {
Object.defineProperty(s, p, {
enumerable: false,
configurable: true,
get: function () {
return v;
}
});
}
}
/**
* Extract variables from the dynamic and append to the self
* @return {[string, array]} grab the literal injection
*/
const parseTemplate = function() {
let args = Array.from(arguments);
// Remove first
args.shift()
// Return the final template
return args;
}
function cloneChildren(element) {
// Base case: if an element is null/undefined or not an object, return as is
if (!element || typeof element !== 'object') {
return element;
}
// Handle arrays
if (Array.isArray(element)) {
return element.map(item => cloneChildren(item));
}
// Create a new object
const cloned = {};
// Clone each property
for (const key in element) {
if (key === 'children') {
// Handle children especially as before
cloned.children = element.children ? cloneChildren(element.children) : undefined;
} else if (key === 'props') {
// Deep clone props array
cloned.props = element.props.map(prop => ({
...prop,
value: prop.value // If value is a function, it will maintain the reference which is what we want
}));
} else {
// Clone other properties
cloned[key] = element[key];
}
}
return cloned;
}
// LemonadeJS object
const L = {};
/**
* Render a lemonade DOM element, method or class into a root DOM element
* @param {function} component - LemonadeJS component or DOM created
* @param {HTMLElement} root - root DOM element to receive the new HTML
* @param {object?} self - self to be used
* @param {object?} item - item
* @return {HTMLElement|boolean} o
*/
L.render = function(component, root, self, item) {
if (typeof(component) !== 'function') {
console.error('Component is not a function');
return false;
}
// In case the self has not initial definition by the developer
if (typeof(self) === 'undefined') {
self = {};
}
// Web component
if (self.tagName && self.tagName.includes('-')) {
let props = getAttributes.call(self, true);
// Copy all values to the object
L.setProperties.call(self, props, true);
}
// Arguments
let args = Array.from(arguments);
// Lemonade component object
let lemon = {
self: self,
ready: [],
change: [],
events: {},
components: {},
elements: [],
root: root,
path: {},
}
// Current onchange
let externalOnchange = self.onchange;
let externalOnload = self.onload;
if (! item) {
item = {};
} else if (typeof(item) === 'string') {
item = {
children: item,
}
}
let view;
let result;
// New self
if (component === Basic) {
view = cloneChildren(item.children[0]);
if (item.parentView) {
lemon.view = item.parentView;
}
} else {
R.currentLemon = lemon;
const tools = {
onload: (...args) => setOnload(lemon, ...args),
onchange: (...args) => setOnchange(lemon, ...args),
track: (...args) => setTrack(lemon, ...args),
state: (...args) => setState(lemon, ...args),
setPath: (...args) => setPath(lemon, ...args),
};
if (isClass(component)) {
if (! (self instanceof component)) {
lemon.self = self = new component(self);
}
view = self.render(item.children, tools);
} else {
// Execute component
view = component.call(self, item.children, tools);
}
// Resolve onchange scope conflict
if (typeof(self.onchange) === 'function' && externalOnchange !== self.onchange) {
// Keep onchange event in the new onchange format
lemon.change.push(self.onchange);
// Keep the external onchange
self.onchange = externalOnchange;
}
R.currentLemon = null;
}
// Values
let values = null;
// Process return
if (typeof(view) === 'function') {
values = view(parseTemplate);
// Curren values
lemon.values = values;
// A render template to be executed
lemon.view = view;
// Template from the method
result = view.toString().split('`');
// Get the original template
if (result) {
// Remove the last element
result.shift();
result.pop();
// Join everything else
result = result.join('`').trim();
}
} else {
result = view;
}
// Virtual DOM tree
if (typeof(result) === 'string') {
result = HTMLParser(result.trim(), values);
}
let element;
// Process the result
if (result) {
// Get the HTML virtual DOM representation
lemon.tree = result;
// Create real DOM and append to the root
element = generateHTML(lemon);
if (element) {
// Parents
lemon.elements = [];
// Append parents
if (element.tagName === 'ROOT') {
element.childNodes.forEach((e) => {
lemon.elements.push(e);
});
} else {
lemon.elements.push(element);
}
// Register element when is not registered inside the component
register(lemon.self, 'el', element);
const destroy = () => {
// Do not add in the same root
let div = document.createElement('div');
// Append temporary DIV to the same position
lemon.elements[0].parentNode.insertBefore(div, lemon.elements[0]);
// Remove the old elements
lemon.elements.forEach((e) => {
e.remove();
});
// Root element
args[1] = div;
// Create a new component
L.render(...args);
// Append elements in the same position in the DOM tree
while (div.firstChild) {
div.parentNode.insertBefore(div.firstChild, div);
}
// Remove DIV
div.remove();
// Object not in use
lemon = null;
}
// Refresh
register(lemon.self, 'refresh', (prop) => {
if (prop) {
if (prop === true) {
destroy();
} else {
lemon.self[prop] = lemon.self[prop];
}
} else {
if (lemon.view) {
runViewValues(lemon);
} else {
destroy();
}
}
});
// Append element to the DOM
if (root) {
lemon.elements.forEach((e) => {
root.appendChild(e);
});
}
} else {
// Refresh
register(lemon.self, 'refresh', (prop) => {
if (prop) {
if (prop === true) {
runViewValues(lemon);
} else {
lemon.self[prop] = lemon.self[prop];
}
}
});
}
}
// Bind to custom component
if (item.bind || item.path) {
let token = 'value';
if (! lemon.events[token]) {
lemon.events[token] = []
}
// Push the event
if (item.bind) {
lemon.events[token].push(item.bind);
}
if (item.path) {
lemon.events[token].push(item.path);
}
}
// In case initial exists
if (typeof(lemon.path.initial) === 'object') {
// Get the value of the draft
let newValue = lemon.path.initial;
// Delete draft
delete lemon.path.initial;
// Apply new values
lemon.path.setValue(newValue);
}
// Apply events
if (lemon.events) {
let props = Object.keys(lemon.events);
if (props.length) {
for (let i = 0; i <props.length; i++) {
let prop = props[i];
if (prop != Number(prop)) {
trackProperty(lemon, prop);
}
}
}
}
// Process the onload
if (element) {
processOnload(lemon);
}
return element;
}
const registerComponents = function(components) {
if (components && R.currentLemon) {
for (const key in components) {
R.currentLemon.components[key.toUpperCase()] = components[key];
}
}
}
/**
* Deprecated
* @param {string} template
* @param {object?} s (self)
* @param {object?} components
*/
L.element = function(template, s, components) {
if (R.currentLemon && s && typeof(s) === 'object') {
if (s !== R.currentLemon.self) {
R.currentLemon.self = s;
}
}
registerComponents(components);
return template;
}
/**
* Apply self to an existing appended DOM element
* @param {HTMLElement} el - element root
* @param {object} s - self to associate to the template
* @param {object?} components - object with component declarations
*/
L.apply = function(el, s, components) {
let template = el.innerHTML;
el.textContent = '';
let Component = function() {
registerComponents(components);
return `<>${template}</>`;
}
return L.render(Component,el,s);
}
/**
* Get all properties existing in {o} and create a new object with the values from {this};
* @param {object} o - reference object with the properties relevant to the new object
* @return {object} n - the new object with all new values
*/
L.getProperties = function(o) {
// The new object with all properties found in {o} with values from {this}
let n = {};
for (let p in o) {
n[p] = this[p];
}
return n;
}
/**
* Set the values from {o} to {this}
* @param {object} o set the values of {this} when the `this[property]` is found in {o}, or when flag force is true
* @param {boolean} f create a new property when that does not exist yet, but is found in {o}
* @return {object} this is redundant since object {this} is a reference and is already available in the caller
*/
L.setProperties = function(o, f) {
for (let p in o) {
if (this.hasOwnProperty(p) || f) {
this[p] = o[p];
}
}
return this;
}
/**
* Reset the values of any common property name between this and a given object
* @param {object} o - all properties names in the object {o} found in {this} will be reset.
*/
L.resetProperties = function(o) {
for (let p in o) {
this[p] = '';
}
}
/**
* Lemonade CC (common container) helps you share a self or function through the whole application
* @param {string} name alias for your declared object(self) or function
* @returns {Object | Function} - registered element
*/
L.get = function(name) {
return R.container[name];
}
/**
* Register something to the Lemonade CC (common container)
* @param {string} name - alias for your declared object(self) or function
* @param {object|function} e - the element to be added to the common container. Can be an object(self) or function.
* @param {boolean} persistence - optional the persistence flag. Only applicable for functions.
*/
L.set = function(name, e, persistence) {
// Applicable only when the o is a function
if (typeof(e) === 'function' && persistence === true) {
// Keep the flag
e.storage = true;
// Any existing values
let t = window.localStorage.getItem(name);
if (t) {
// Parse JSON
t = JSON.parse(t);
// Execute method with the existing values
e(t);
}
}
// Save to the sugar container
R.container[name] = e;
}
/**
* Dispatch the new values to the function
* @param {string} name - alias to the element saved on the Lemonade CC (common container)
* @param {object?} data - data to be dispatched
*/
L.dispatch = function(name, data) {
// Get from the container
let e = R.container[name];
// Confirm that the alias is a function
if (typeof(e) === 'function') {
// Save the data to the local storage
if (e.storage === true) {
window.localStorage.setItem(name, JSON.stringify(data));
}
// Dispatch the data to the function
return e(data);
}
}
/**
* Register components
* @param {object} components - register components
*/
L.setComponents = function(components) {
if (typeof(components) === 'object') {
// Component names
let k = Object.keys(components);
// Make sure they follow the standard
for (let i = 0; i < k.length; i++) {
R.components[k[i].toUpperCase()] = components[k[i]];
}
}
}
L.component = class {}
/**
* Create a Web Component
* @param {string} name - web component name
* @param {function} handler - lemonadejs component
* @param {object} options - options to create the web components
*/
L.createWebComponent = function(name, handler, options) {
if (typeof(window) === 'undefined') {
return;
}
if (typeof(handler) !== 'function') {
return 'Handler should be an function';
}
// Prefix
let prefix = options && options.prefix ? options.prefix : 'lm';
// Component name
const componentName = prefix + '-' + name;
// Check if the component is already defined
if (! window.customElements.get(componentName)) {
class Component extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
// LemonadeJS self
let self = this;
// First call
let state = typeof(this.el) === 'undefined';
// LemonadeJS is already rendered
if (state === true) {
// Render
if (options && options.applyOnly === true) {
// Merge component
handler.call(this);
// Apply
L.apply(this, self);
} else {
let root = this;
if (options && options.shadowRoot === true) {
this.attachShadow({ mode: 'open' });
root = document.createElement('div');
this.shadowRoot.appendChild(root);
}
// Give the browser time to calculate all width and heights
L.render(handler, root, self);
}
}
queueMicrotask(() => {
// Event
if (typeof(self.onconnect) === 'function') {
self.onconnect(self, state);
}
});
}
disconnectedCallback() {
if (typeof(this.ondisconnect) === 'function') {
this.ondisconnect(this);
}
}
}
if (! window.customElements.get(componentName)) {
window.customElements.define(componentName, Component);
}
}
}
L.h = function(type, props, ...children) {
return { type, props: props || {}, children };
}
L.Fragment = function(props) {
return props.children;
}
const wrongLevel = 'Hooks must be called at the top level of your component';
const setOnload = function(lemon, event) {
lemon.load = event;
}
const setOnchange = function(lemon, event) {
lemon.change.push(event);
}
const setTrack = function(lemon, prop) {
if (! lemon.events[prop]) {
lemon.events[prop] = [];
}
}
const setPath = function(lemon, initialValues, change) {
// My value object
let value = {};
// Create a method to update the state
const setValue = (newValue) => {
if (typeof(lemon.path.initial) === 'undefined') {
if (typeof (newValue) === 'object') {
// If my has been declared
let elements = lemon.path.elements;
if (elements) {
for (let i = 0; i < elements.length; i++) {
let v = Path.call(newValue, elements[i].path)
setAttribute(elements[i].element, 'value', v);
// Also sync value to the data object (use '' for undefined to match form behavior)
Path.call(lemon.path.value, elements[i].path, v !== undefined ? v : '');
}
}
}
} else {
lemon.path.initial = newValue;
}
}
const getValue = () => {
let ret = {};
// If my has been declared
let elements = lemon.path.elements;
if (elements) {
for (let i = 0; i < elements.length; i++) {
let v = getAttribute(elements[i].element, 'value');
Path.call(ret, elements[i].path, v);
}
}
return ret;
}
lemon.path = {
setValue: setValue,
value: value,
change: change,
initial: initialValues || {}
};
return [value, setValue, getValue];
}
const setState = function(lemon, value, callback) {
// Create a state container
const s = new state();
// Create a method to update the state
const setValue = (newValue) => {
let oldValue = value;
// Update original value
value = typeof newValue === 'function' ? newValue(value) : newValue;
// Values from the view
runViewValues(lemon, s);
// Call back
callback?.(value, oldValue);
}
// Make the value attribute dynamic
Object.defineProperty(s, 'value', {
set: setValue,
get: () => value
});
return s;
}
L.onload = function(event) {
if (! R.currentLemon) {
createError(wrongLevel);
}
return setOnload(R.currentLemon, event);
}
L.onchange = function(event) {
if (! R.currentLemon) {
createError(wrongLevel);
}
return setOnchange(R.currentLemon, event);
}
L.track = function(prop) {
if (! R.currentLemon) {
createError(wrongLevel);
}
return setTrack(R.currentLemon, prop);
}
L.setPath = function(initialValues, change) {
if (! R.currentLemon) {
createError(wrongLevel);
}
return setPath(R.currentLemon, initialValues, change);
}
/**
* Run view values
* @param lemon
* @param s - refresh from state
*/
const runViewValues = function(lemon, s) {
let values = lemon.view(parseTemplate);
if (values && values.length) {
values.forEach((v, k) => {
let current = lemon.values[k];
// Compare if the previous value
if (s && s === v || v !== current) {
// Update current value
lemon.values[k] = v;
// Trigger state events
if (typeof(lemon.events[k]) === 'function') {
lemon.events[k]();
}
}
});
}
}
const state = function() {}
state.prototype.toString = function() {
return this.value.toString();
}
state.prototype.valueOf = function() {
return this.value;
}
state.prototype[Symbol.toPrimitive] = function(hint) {
if (hint === 'string') {
return this.value.toString();
}
return this.value;
}
// TODO: Proxy for Objects and Arrays
L.state = function(value, callback) {
if (! R.currentLemon) {
createError(wrongLevel);
}
return setState(R.currentLemon, value, callback);
}
L.helpers = {
path: extractFromPath,
properties: {
get: L.getProperties,
set: L.setProperties,
reset: L.resetProperties,
}
}
L.events = (function() {
class CustomEvents extends Event {
constructor(type, props, options) {
super(type, {
bubbles: true,
composed: true,
...options,
});
if (props) {
for (const key in props) {
// Avoid assigning if property already exists anywhere on `this`
if (! (key in this)) {
this[key] = props[key];
}
}
}
}
}
const create = function(type, props, options) {
return new CustomEvents(type, props, options);
};
return {
create: create,
dispatch(element, event, options) {
if (typeof event === 'string') {
event = create(event, options);
}
element.dispatchEvent(event);
}
};
})();
return L;
})));
/***/ }),
/***/ 195:
/***/ (function(module) {
/**
* (c) jSuites Javascript Plugins and Web Components (v4)
*
* Website: https://jsuites.net
* Description: Create amazing web based applications.
* Plugin: Organogram
*
* MIT License
*/
;(function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
return (function(str) {
function int64(msint_32, lsint_32) {
this.highOrder = msint_32;
this.lowOrder = lsint_32;
}
var H = [new int64(0x6a09e667, 0xf3bcc908), new int64(0xbb67ae85, 0x84caa73b),
new int64(0x3c6ef372, 0xfe94f82b), new int64(0xa54ff53a, 0x5f1d36f1),
new int64(0x510e527f, 0xade682d1), new int64(0x9b05688c, 0x2b3e6c1f),
new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x5be0cd19, 0x137e2179)];
var K = [new int64(0x428a2f98, 0xd728ae22), new int64(0x71374491, 0x23ef65cd),
new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xe9b5dba5, 0x8189dbbc),
new int64(0x3956c25b, 0xf348b538), new int64(0x59f111f1, 0xb605d019),
new int64(0x923f82a4, 0xaf194f9b), new int64(0xab1c5ed5, 0xda6d8118),
new int64(0xd807aa98, 0xa3030242), new int64(0x12835b01, 0x45706fbe),
new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, 0xd5ffb4e2),
new int64(0x72be5d74, 0xf27b896f), new int64(0x80deb1fe, 0x3b1696b1),
new int64(0x9bdc06a7, 0x25c71235), new int64(0xc19bf174, 0xcf692694),
new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xefbe4786, 0x384f25e3),
new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x240ca1cc, 0x77ac9c65),
new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x76f988da, 0x831153b5),
new int64(0x983e5152, 0xee66dfab), new int64(0xa831c66d, 0x2db43210),
new int64(0xb00327c8, 0x98fb213f), new int64(0xbf597fc7, 0xbeef0ee4),
new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xd5a79147, 0x930aa725),
new int64(0x06ca6351, 0xe003826f), new int64(0x14292967, 0x0a0e6e70),
new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, 0x9d95b3df),
new int64(0x650a7354, 0x8baf63de), new int64(0x766a0abb, 0x3c77b2a8),
new int64(0x81c2c92e, 0x47edaee6), new int64(0x92722c85, 0x1482353b),
new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa81a664b, 0xbc423001),
new int64(0xc24b8b70, 0xd0f89791), new int64(0xc76c51a3, 0x0654be30),
new int64(0xd192e819, 0xd6ef5218), new int64(0xd6990624, 0x5565a910),
new int64(0xf40e3585, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x1e376c08, 0x5141ab53),
new int64(0x2748774c, 0xdf8eeb99), new int64(0x34b0bcb5, 0xe19b48a8),
new int64(0x391c0cb3, 0xc5c95a63), new int64(0x4ed8aa4a, 0xe3418acb),
new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, 0xd6b2b8a3),
new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
new int64(0x84c87814, 0xa1f0ab72), new int64(0x8cc70208, 0x1a6439ec),
new int64(0x90befffa, 0x23631e28), new int64(0xa4506ceb, 0xde82bde9),
new int64(0xbef9a3f7, 0xb2c67915), new int64(0xc67178f2, 0xe372532b),
new int64(0xca273ece, 0xea26619c), new int64(0xd186b8c7, 0x21c0c207),
new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xf57d4f7f, 0xee6ed178),
new int64(0x06f067aa, 0x72176fba), new int64(0x0a637dc5, 0xa2c898a6),
new int64(0x113f9804, 0xbef90dae), new int64(0x1b710b35, 0x131c471b),
new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, 0x9c100d4c),
new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x597f299c, 0xfc657e2a),
new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)];
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
var charsize = 8;
function utf8_encode(str) {
return unescape(encodeURIComponent(str));
}
function str2binb(str) {
var bin = [];
var mask = (1 << charsize) - 1;
var len = str.length * charsize;
for (var i = 0; i < len; i += charsize) {
bin[i >> 5] |= (str.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32));
}
return bin;
}
function binb2hex(binarray) {
var hex_tab = "0123456789abcdef";
var str = "";
var length = binarray.length * 4;
var srcByte;
for (var i = 0; i < length; i += 1) {
srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8);
str += hex_tab.charAt((srcByte >> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF);
}
return str;
}
function safe_add_2(x, y) {
var lsw, msw, lowOrder, highOrder;
lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF);
msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16);
msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
return new int64(highOrder, lowOrder);
}
function safe_add_4(a, b, c, d) {
var lsw, msw, lowOrder, highOrder;
lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF);
msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16);
msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
return new int64(highOrder, lowOrder);
}
function safe_add_5(a, b, c, d, e) {
var lsw, msw, lowOrder, highOrder;
lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF);
msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16);
msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
return new int64(highOrder, lowOrder);
}
function maj(x, y, z) {
return new int64(
(x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder),
(x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder)
);
}
function ch(x, y, z) {
return new int64(
(x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder),
(x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder)
);
}
function rotr(x, n) {
if (n <= 32) {
return new int64(
(x.highOrder >>> n) | (x.lowOrder << (32 - n)),
(x.lowOrder >>> n) | (x.highOrder << (32 - n))
);
} else {
return new int64(
(x.lowOrder >>> n) | (x.highOrder << (32 - n)),
(x.highOrder >>> n) | (x.lowOrder << (32 - n))
);
}
}
function sigma0(x) {
var rotr28 = rotr(x, 28);
var rotr34 = rotr(x, 34);
var rotr39 = rotr(x, 39);
return new int64(
rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder,
rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder
);
}
function sigma1(x) {
var rotr14 = rotr(x, 14);
var rotr18 = rotr(x, 18);
var rotr41 = rotr(x, 41);
return new int64(
rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder,
rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder
);
}
function gamma0(x) {
var rotr1 = rotr(x, 1), rotr8 = rotr(x, 8), shr7 = shr(x, 7);
return new int64(
rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder,
rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder
);
}
function gamma1(x) {
var rotr19 = rotr(x, 19);
var rotr61 = rotr(x, 61);
var shr6 = shr(x, 6);
return new int64(
rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder,
rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder
);
}
function shr(x, n) {
if (n <= 32) {
return new int64(
x.highOrder >>> n,
x.lowOrder >>> n | (x.highOrder << (32 - n))
);
} else {
return new int64(
0,
x.highOrder << (32 - n)
);
}
}
var str = utf8_encode(str);
var strlen = str.length*charsize;
str = str2binb(str);
str[strlen >> 5] |= 0x80 << (24 - strlen % 32);
str[(((strlen + 128) >> 10) << 5) + 31] = strlen;
for (var i = 0; i < str.length; i += 32) {
a = H[0];
b = H[1];
c = H[2];
d = H[3];
e = H[4];
f = H[5];
g = H[6];
h = H[7];
for (var j = 0; j < 80; j++) {
if (j < 16) {
W[j] = new int64(str[j*2 + i], str[j*2 + i + 1]);
} else {
W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]);
}
T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]);
T2 = safe_add_2(sigma0(a), maj(a, b, c));
h = g;
g = f;
f = e;
e = safe_add_2(d, T1);
d = c;
c = b;
b = a;
a = safe_add_2(T1, T2);
}
H[0] = safe_add_2(a, H[0]);
H[1] = safe_add_2(b, H[1]);
H[2] = safe_add_2(c, H[2]);
H[3] = safe_add_2(d, H[3]);
H[4] = safe_add_2(e, H[4]);
H[5] = safe_add_2(f, H[5]);
H[6] = safe_add_2(g, H[6]);
H[7] = safe_add_2(h, H[7]);
}
var binarray = [];
for (var i = 0; i < H.length; i++) {
binarray.push(H[i].highOrder);
binarray.push(H[i].lowOrder);
}
return binb2hex(binarray);
});
})));
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
!function() {
"use strict";
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": function() { return /* binding */ jsuites; }
});
;// CONCATENATED MODULE: ./src/utils/dictionary.js
// Update dictionary
const setDictionary = function(d) {
if (! document.dictionary) {
document.dictionary = {}
}
// Replace the key into the dictionary and append the new ones
let k = Object.keys(d);
for (let i = 0; i < k.length; i++) {
document.dictionary[k[i]] = d[k[i]];
}
}
// Translate
const translate = function(t) {
if (typeof(document) !== "undefined" && document.dictionary) {
return document.dictionary[t] || t;
} else {
return t;
}
}
/* harmony default export */ var dictionary = ({ setDictionary, translate });
;// CONCATENATED MODULE: ./src/utils/tracking.js
const Tracking = function(component, state) {
if (state === true) {
window['jSuitesStateControl'] = window['jSuitesStateControl'].filter(function(v) {
return v !== null;
});
// Start after all events
setTimeout(function() {
window['jSuitesStateControl'].push(component);
}, 0);
} else {
var index = window['jSuitesStateControl'].indexOf(component);
if (index >= 0) {
window['jSuitesStateControl'].splice(index, 1);
}
}
}
/* harmony default export */ var tracking = (Tracking);
;// CONCATENATED MODULE: ./src/utils/helpers.js
const Helpers = {};
// Two digits
Helpers.two = function(value) {
value = '' + value;
if (value.length == 1) {
value = '0' + value;
}
return value;
}
Helpers.focus = function(el) {
if (el.textContent.length) {
// Handle contenteditable elements
const range = document.createRange();
const sel = window.getSelection();
let node = el;
// Go as deep as possible to the last text node
while (node.lastChild) node = node.lastChild;
// Ensure it's a text node
if (node.nodeType === Node.TEXT_NODE) {
range.setStart(node, node.length);
} else {
range.setStart(node, node.childNodes.length);
}
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.scrollLeft = el.scrollWidth;
}
}
Helpers.isNumeric = (function (num) {
if (typeof(num) === 'string') {
num = num.trim();
}
return !isNaN(num) && num !== null && num !== '';
});
Helpers.guid = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
Helpers.getNode = function() {
let node = document.getSelection().anchorNode;
if (node) {
return (node.nodeType == 3 ? node.parentNode : node);
} else {
return null;
}
}
/**
* Generate hash from a string
*/
Helpers.hash = function(str) {
let hash = 0, i, chr;
if (str.length === 0) {
return hash;
} else {
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
if (chr > 32) {
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
}
}
return hash;
}
/**
* Generate a random color
*/
Helpers.randomColor = function(h) {
let lum = -0.25;
let hex = String('#' + Math.random().toString(16).slice(2, 8).toUpperCase()).replace(/[^0-9a-f]/gi, '');
if (hex.length < 6) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
let rgb = [], c;
for (let i = 0; i < 3; i++) {
c = parseInt(hex.substr(i * 2, 2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb.push(("00" + c).substr(c.length));
}
// Return hex
if (h === true) {
return '#' + Helpers.two(rgb[0].toString(16)) + Helpers.two(rgb[1].toString(16)) + Helpers.two(rgb[2].toString(16));
}
return rgb;
}
Helpers.getWindowWidth = function() {
let w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = w.innerWidth || e.clientWidth || g.clientWidth;
return x;
}
Helpers.getWindowHeight = function() {
let w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
y = w.innerHeight|| e.clientHeight|| g.clientHeight;
return y;
}
Helpers.getPosition = function(e) {
let x;
let y;
if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].pageX;
y = e.changedTouches[0].pageY;
} else {
x = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
y = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
}
return [ x, y ];
}
Helpers.click = function(el) {
if (el.click) {
el.click();
} else {
let evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
el.dispatchEvent(evt);
}
}
Helpers.findElement = function(element, condition) {
let foundElement = false;
function path (element) {
if (element && ! foundElement) {
if (typeof(condition) == 'function') {
foundElement = condition(element)
} else if (typeof(condition) == 'string') {
if (element.classList && element.classList.contains(condition)) {
foundElement = element;
}
}
}
if (element.parentNode && ! foundElement) {
path(element.parentNode);
}
}
path(element);
return foundElement;
}
/* harmony default export */ var helpers = (Helpers);
;// CONCATENATED MODULE: ./src/utils/path.js
const isValidPathObj = function(o) {
return o !== null && (typeof o === 'object' || typeof o === 'function');
}
function Path(pathString, value, remove) {
// Ensure the path is a valid, non-empty string
if (typeof pathString !== 'string' || pathString.length === 0) {
return undefined;
}
// Split the path into individual keys and filter out empty keys
const keys = pathString.split('.');
if (keys.length === 0) {
return undefined;
}
// Start with the root object
let currentObject = this;
// Read mode: retrieve a value
if (typeof value === 'undefined' && typeof remove === 'undefined') {
// Traverse all keys
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
// Check if the current object is valid and has the key
if (currentObject != null && isValidPathObj(currentObject) && key in currentObject) {
currentObject = currentObject[key];
} else {
// Return undefined if the path is invalid or currentObject is null/undefined
return undefined;
}
}
// Return the final value
return currentObject;
}
// Write mode: set or delete a value
// Traverse all keys except the last one
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
// Check if the current object is invalid (null/undefined or non-object)
if (currentObject == null || ! isValidPathObj(currentObject)) {
console.warn(`Cannot set value: path '${pathString}' blocked by invalid object at '${key}'`);
return false;
}
// If the key exists but is null/undefined or a non-object, replace it with an empty object
if (
key in currentObject &&
(currentObject[key] == null || ! isValidPathObj(currentObject[key]))
) {
currentObject[key] = {};
} else if (!(key in currentObject)) {
// If the key doesn't exist, create an empty object
currentObject[key] = {};
}
// Move to the next level
currentObject = currentObject[key];
}
// Handle the final key
const finalKey = keys[keys.length - 1];
// Check if the current object is valid for setting/deleting
if (currentObject == null || ! isValidPathObj(currentObject)) {
return false;
}
// Delete the property if remove is true
if (remove === true) {
if (finalKey in currentObject) {
delete currentObject[finalKey];
return true;
}
return false; // Nothing to delete
}
// Set the value
currentObject[finalKey] = value;
return true;
}
;// CONCATENATED MODULE: ./src/utils/sorting.js
function Sorting(el, options) {
var obj = {};
obj.options = {};
var defaults = {
pointer: null,
direction: null,
ondragstart: null,
ondragend: null,
ondrop: null,
}
var dragElement = null;
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
el.classList.add('jsorting');
el.addEventListener('dragstart', function(e) {
let target = e.target;
if (target.nodeType === 3) {
if (target.parentNode.getAttribute('draggable') === 'true') {
target = target.parentNode;
} else {
e.preventDefault();
e.stopPropagation();
return;
}
}
if (target.getAttribute('draggable') === 'true') {
let position = Array.prototype.indexOf.call(target.parentNode.children, target);
dragElement = {
element: target,
o: position,
d: position
}
target.style.opacity = '0.25';
if (typeof (obj.options.ondragstart) == 'function') {
obj.options.ondragstart(el, target, e);
}
e.dataTransfer.setDragImage(target,0,0);
}
});
el.addEventListener('dragover', function(e) {
e.preventDefault();
if (dragElement) {
if (getElement(e.target)) {
if (e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
if (!obj.options.direction) {
var condition = e.target.clientHeight / 2 > e.offsetY;
} else {
var condition = e.target.clientWidth / 2 > e.offsetX;
}
if (condition) {
e.target.parentNode.insertBefore(dragElement.element, e.target);
} else {
e.target.parentNode.insertBefore(dragElement.element, e.target.nextSibling);
}
dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
}
}
}
});
el.addEventListener('dragleave', function(e) {
e.preventDefault();
});
el.addEventListener('dragend', function(e) {
e.preventDefault();
if (dragElement) {
if (typeof(obj.options.ondragend) == 'function') {
obj.options.ondragend(el, dragElement.element, e);
}
// Cancelled put element to the original position
if (dragElement.o < dragElement.d) {
e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o]);
} else {
e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o].nextSibling);
}
dragElement.element.style.opacity = '';
dragElement = null;
}
});
el.addEventListener('drop', function(e) {
e.preventDefault();
if (dragElement) {
if (dragElement.o !== dragElement.d) {
if (typeof (obj.options.ondrop) == 'function') {
obj.options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
}
}
dragElement.element.style.opacity = '';
dragElement = null;
}
});
var getElement = function(element) {
var sorting = false;
function path (element) {
if (element.className) {
if (element.classList.contains('jsorting')) {
sorting = true;
}
}
if (! sorting) {
path(element.parentNode);
}
}
path(element);
return sorting;
}
for (var i = 0; i < el.children.length; i++) {
if (! el.children[i].hasAttribute('draggable')) {
el.children[i].setAttribute('draggable', 'true');
}
}
el.val = function() {
var id = null;
var data = [];
for (var i = 0; i < el.children.length; i++) {
if (id = el.children[i].getAttribute('data-id')) {
data.push(id);
}
}
return data;
}
return el;
}
;// CONCATENATED MODULE: ./src/utils/lazyloading.js
function LazyLoading(el, options) {
var obj = {}
// Mandatory options
if (! options.loadUp || typeof(options.loadUp) != 'function') {
options.loadUp = function() {
return false;
}
}
if (! options.loadDown || typeof(options.loadDown) != 'function') {
options.loadDown = function() {
return false;
}
}
// Timer ms
if (! options.timer) {
options.timer = 100;
}
// Timer
var timeControlLoading = null;
// Controls
var scrollControls = function(e) {
if (timeControlLoading == null) {
var event = false;
var scrollTop = el.scrollTop;
if (el.scrollTop + (el.clientHeight * 2) >= el.scrollHeight) {
if (options.loadDown()) {
if (scrollTop == el.scrollTop) {
el.scrollTop = el.scrollTop - (el.clientHeight);
}
event = true;
}
} else if (el.scrollTop <= el.clientHeight) {
if (options.loadUp()) {
if (scrollTop == el.scrollTop) {
el.scrollTop = el.scrollTop + (el.clientHeight);
}
event = true;
}
}
timeControlLoading = setTimeout(function() {
timeControlLoading = null;
}, options.timer);
if (event) {
if (typeof(options.onupdate) == 'function') {
options.onupdate();
}
}
}
}
// Onscroll
el.onscroll = function(e) {
scrollControls(e);
}
el.onwheel = function(e) {
scrollControls(e);
}
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/ajax.js
function Ajax() {
var Component = (function(options, complete) {
if (Array.isArray(options)) {
// Create multiple request controller
var multiple = {
instance: [],
complete: complete,
}
if (options.length > 0) {
for (var i = 0; i < options.length; i++) {
options[i].multiple = multiple;
multiple.instance.push(Component(options[i]));
}
}
return multiple;
}
if (! options.data) {
options.data = {};
}
if (options.type) {
options.method = options.type;
}
// Default method
if (! options.method) {
options.method = 'GET';
}
// Default type
if (! options.dataType) {
options.dataType = 'json';
}
if (options.data) {
// Parse object to variables format
var parseData = function (value, key) {
var vars = [];
if (value) {
var keys = Object.keys(value);
if (keys.length) {
for (var i = 0; i < keys.length; i++) {
if (key) {
var k = key + '[' + keys[i] + ']';
} else {
var k = keys[i];
}
if (value[k] instanceof FileList) {
vars[k] = value[keys[i]];
} else if (value[keys[i]] === null || value[keys[i]] === undefined) {
vars[k] = '';
} else if (typeof(value[keys[i]]) == 'object') {
var r = parseData(value[keys[i]], k);
var o = Object.keys(r);
for (var j = 0; j < o.length; j++) {
vars[o[j]] = r[o[j]];
}
} else {
vars[k] = value[keys[i]];
}
}
}
}
return vars;
}
var d = parseData(options.data);
var k = Object.keys(d);
// Data form
if (options.method == 'GET') {
if (k.length) {
var data = [];
for (var i = 0; i < k.length; i++) {
data.push(k[i] + '=' + encodeURIComponent(d[k[i]]));
}
if (options.url.indexOf('?') < 0) {
options.url += '?';
}
options.url += data.join('&');
}
} else {
var data = new FormData();
for (var i = 0; i < k.length; i++) {
if (d[k[i]] instanceof FileList) {
if (d[k[i]].length) {
for (var j = 0; j < d[k[i]].length; j++) {
data.append(k[i], d[k[i]][j], d[k[i]][j].name);
}
}
} else {
data.append(k[i], d[k[i]]);
}
}
}
}
var httpRequest = new XMLHttpRequest();
httpRequest.open(options.method, options.url, true);
if (options.requestedWith) {
httpRequest.setRequestHeader('X-Requested-With', options.requestedWith);
} else {
if (options.requestedWith !== false) {
httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
}
}
// Content type
if (options.contentType) {
httpRequest.setRequestHeader('Content-Type', options.contentType);
}
// Headers
if (options.method === 'POST') {
httpRequest.setRequestHeader('Accept', 'application/json');
} else {
if (options.dataType === 'blob') {
httpRequest.responseType = "blob";
} else {
if (! options.contentType) {
if (options.dataType === 'json') {
httpRequest.setRequestHeader('Content-Type', 'text/json');
} else if (options.dataType === 'html') {
httpRequest.setRequestHeader('Content-Type', 'text/html');
}
}
}
}
// No cache
if (options.cache !== true) {
httpRequest.setRequestHeader('pragma', 'no-cache');
httpRequest.setRequestHeader('cache-control', 'no-cache');
}
// Authentication
if (options.withCredentials === true) {
httpRequest.withCredentials = true
}
// Before send
if (typeof(options.beforeSend) == 'function') {
options.beforeSend(httpRequest);
}
// Before send
if (typeof(Component.beforeSend) == 'function') {
Component.beforeSend(httpRequest);
}
if (document.ajax && typeof(document.ajax.beforeSend) == 'function') {
document.ajax.beforeSend(httpRequest);
}
httpRequest.onerror = function() {
if (options.error && typeof(options.error) == 'function') {
options.error({
message: 'Network error: Unable to reach the server.',
status: 0
});
}
}
httpRequest.ontimeout = function() {
if (options.error && typeof(options.error) == 'function') {
options.error({
message: 'Request timed out after ' + httpRequest.timeout + 'ms.',
status: 0
});
}
}
httpRequest.onload = function() {
if (httpRequest.status >= 200 && httpRequest.status < 300) {
if (options.dataType === 'json') {
try {
var result = JSON.parse(httpRequest.responseText);
if (options.success && typeof(options.success) == 'function') {
options.success(result);
}
} catch(err) {
if (options.error && typeof(options.error) == 'function') {
options.error(err, result);
}
}
} else {
if (options.dataType === 'blob') {
var result = httpRequest.response;
} else {
var result = httpRequest.responseText;
}
if (options.success && typeof(options.success) == 'function') {
options.success(result);
}
}
} else {
if (options.error && typeof(options.error) == 'function') {
options.error(httpRequest.responseText, httpRequest.status);
}
}
// Global queue
if (Component.queue && Component.queue.length > 0) {
Component.send(Component.queue.shift());
}
// Global complete method
if (Component.requests && Component.requests.length) {
// Get index of this request in the container
var index = Component.requests.indexOf(httpRequest);
// Remove from the ajax requests container
Component.requests.splice(index, 1);
// Deprecated: Last one?
if (! Component.requests.length) {
// Object event
if (options.complete && typeof(options.complete) == 'function') {
options.complete(result);
}
}
// Group requests
if (options.group) {
if (Component.oncomplete && typeof(Component.oncomplete[options.group]) == 'function') {
if (! Component.pending(options.group)) {
Component.oncomplete[options.group]();
Component.oncomplete[options.group] = null;
}
}
}
// Multiple requests controller
if (options.multiple && options.multiple.instance) {
// Get index of this request in the container
var index = options.multiple.instance.indexOf(httpRequest);
// Remove from the ajax requests container
options.multiple.instance.splice(index, 1);
// If this is the last one call method complete
if (! options.multiple.instance.length) {
if (options.multiple.complete && typeof(options.multiple.complete) == 'function') {
options.multiple.complete(result);
}
}
}
}
}
// Keep the options
httpRequest.options = options;
// Data
httpRequest.data = data;
// Queue
if (options.queue === true && Component.requests.length > 0) {
Component.queue.push(httpRequest);
} else {
Component.send(httpRequest)
}
return httpRequest;
});
Component.send = function(httpRequest) {
if (httpRequest.data) {
if (Array.isArray(httpRequest.data)) {
httpRequest.send(httpRequest.data.join('&'));
} else {
httpRequest.send(httpRequest.data);
}
} else {
httpRequest.send();
}
Component.requests.push(httpRequest);
}
Component.exists = function(url, __callback) {
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
if (http.status) {
__callback(http.status);
}
}
Component.pending = function(group) {
var n = 0;
var o = Component.requests;
if (o && o.length) {
for (var i = 0; i < o.length; i++) {
if (! group || group == o[i].options.group) {
n++
}
}
}
return n;
}
Component.oncomplete = {};
Component.requests = [];
Component.queue = [];
return Component
}
/* harmony default export */ var ajax = (Ajax());
;// CONCATENATED MODULE: ./src/plugins/animation.js
function Animation() {
const Component = {
loading: {}
}
Component.loading.show = function(timeout) {
if (! Component.loading.element) {
Component.loading.element = document.createElement('div');
Component.loading.element.className = 'jloading';
}
document.body.appendChild(Component.loading.element);
// Max timeout in seconds
if (timeout > 0) {
setTimeout(function() {
Component.loading.hide();
}, timeout * 1000)
}
}
Component.loading.hide = function() {
if (Component.loading.element && Component.loading.element.parentNode) {
document.body.removeChild(Component.loading.element);
}
}
Component.slideLeft = function (element, direction, done) {
if (direction == true) {
element.classList.add('jslide-left-in');
setTimeout(function () {
element.classList.remove('jslide-left-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-left-out');
setTimeout(function () {
element.classList.remove('jslide-left-out');
if (typeof (done) == 'function') {
done();
}
}, 400);
}
}
Component.slideRight = function (element, direction, done) {
if (direction === true) {
element.classList.add('jslide-right-in');
setTimeout(function () {
element.classList.remove('jslide-right-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-right-out');
setTimeout(function () {
element.classList.remove('jslide-right-out');
if (typeof (done) == 'function') {
done();
}
}, 400);
}
}
Component.slideTop = function (element, direction, done) {
if (direction === true) {
element.classList.add('jslide-top-in');
setTimeout(function () {
element.classList.remove('jslide-top-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-top-out');
setTimeout(function () {
element.classList.remove('jslide-top-out');
if (typeof (done) == 'function') {
done();
}
}, 400);
}
}
Component.slideBottom = function (element, direction, done) {
if (direction === true) {
element.classList.add('jslide-bottom-in');
setTimeout(function () {
element.classList.remove('jslide-bottom-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-bottom-out');
setTimeout(function () {
element.classList.remove('jslide-bottom-out');
if (typeof (done) == 'function') {
done();
}
}, 100);
}
}
Component.fadeIn = function (element, done) {
element.style.display = '';
element.classList.add('jfade-in');
setTimeout(function () {
element.classList.remove('jfade-in');
if (typeof (done) == 'function') {
done();
}
}, 2000);
}
Component.fadeOut = function (element, done) {
element.classList.add('jfade-out');
setTimeout(function () {
element.style.display = 'none';
element.classList.remove('jfade-out');
if (typeof (done) == 'function') {
done();
}
}, 1000);
}
return Component;
}
/* harmony default export */ var animation = (Animation());
// EXTERNAL MODULE: ./node_modules/@jsuites/utils/dist/index.js
var dist = __webpack_require__(791);
var dist_default = /*#__PURE__*/__webpack_require__.n(dist);
;// CONCATENATED MODULE: ./src/plugins/calendar.js
const Mask = (dist_default()).Mask;
const HelpersDate = (dist_default()).Helpers;
function Calendar() {
var Component = (function (el, options) {
// Already created, update options
if (el.calendar) {
return el.calendar.setOptions(options, true);
}
// New instance
var obj = {type: 'calendar'};
obj.options = {};
// Date
obj.date = null;
/**
* Update options
*/
obj.setOptions = function (options, reset) {
// Default configuration
var defaults = {
// Render type: [ default | year-month-picker ]
type: 'default',
// Restrictions
validRange: null,
// Starting weekday - 0 for sunday, 6 for saturday
startingDay: null,
// Date format
format: 'DD/MM/YYYY',
// Allow keyboard date entry
readonly: true,
// Today is default
today: false,
// Show timepicker
time: false,
// Show the reset button
resetButton: true,
// Placeholder
placeholder: '',
// Translations can be done here
months: HelpersDate.monthsShort,
monthsFull: HelpersDate.months,
weekdays: HelpersDate.weekdays,
textDone: dictionary.translate('Done'),
textReset: dictionary.translate('Reset'),
textUpdate: dictionary.translate('Update'),
// Value
value: null,
// Fullscreen (this is automatic set for screensize < 800)
fullscreen: false,
// Create the calendar closed as default
opened: false,
// Events
onopen: null,
onclose: null,
onchange: null,
onupdate: null,
// Internal mode controller
mode: null,
position: null,
// Data type
dataType: null,
// Controls
controls: true,
// Auto select
autoSelect: true,
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof (obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Register custom months and weekdays with the dictionary for translation
if (obj.options.monthsFull && obj.options.monthsFull !== defaults.monthsFull) {
const englishMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const translations = {};
for (let i = 0; i < 12; i++) {
if (obj.options.monthsFull[i]) {
translations[englishMonths[i]] = obj.options.monthsFull[i];
}
}
dictionary.setDictionary(translations);
}
if (obj.options.weekdays && obj.options.weekdays !== defaults.weekdays) {
const englishWeekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const translations = {};
for (let i = 0; i < 7; i++) {
if (obj.options.weekdays[i]) {
translations[englishWeekdays[i]] = obj.options.weekdays[i];
}
}
dictionary.setDictionary(translations);
}
// Reset button
if (obj.options.resetButton == false) {
calendarReset.style.display = 'none';
} else {
calendarReset.style.display = '';
}
// Readonly
if (obj.options.readonly) {
el.setAttribute('readonly', 'readonly');
} else {
el.removeAttribute('readonly');
}
// Placeholder
if (obj.options.placeholder) {
el.setAttribute('placeholder', obj.options.placeholder);
} else {
el.removeAttribute('placeholder');
}
if (helpers.isNumeric(obj.options.value) && obj.options.value > 0) {
obj.options.value = Component.numToDate(obj.options.value);
// Data type numeric
obj.options.dataType = 'numeric';
}
// Texts
calendarReset.innerHTML = obj.options.textReset;
calendarConfirm.innerHTML = obj.options.textDone;
calendarControlsUpdateButton.innerHTML = obj.options.textUpdate;
// Define mask
if (obj.options.format) {
el.setAttribute('data-mask', obj.options.format.toLowerCase());
}
// Value
if (!obj.options.value && obj.options.today) {
var value = Component.now();
} else {
var value = obj.options.value;
}
// Set internal date
if (value) {
// Force the update
obj.options.value = null;
// New value
obj.setValue(value);
}
return obj;
}
/**
* Open the calendar
*/
obj.open = function (value) {
if (!calendar.classList.contains('jcalendar-focus')) {
if (!calendar.classList.contains('jcalendar-inline')) {
// Current
Component.current = obj;
// Start tracking
tracking(obj, true);
// Create the days
obj.getDays();
// Render months
if (obj.options.type == 'year-month-picker') {
obj.getMonths();
}
// Get time
if (obj.options.time) {
calendarSelectHour.value = obj.date[3];
calendarSelectMin.value = obj.date[4];
}
// Show calendar
calendar.classList.add('jcalendar-focus');
// Get the position of the corner helper
if (helpers.getWindowWidth() < 800 || obj.options.fullscreen) {
calendar.classList.add('jcalendar-fullsize');
// Animation
animation.slideBottom(calendarContent, 1);
} else {
calendar.classList.remove('jcalendar-fullsize');
var rect = el.getBoundingClientRect();
var rectContent = calendarContent.getBoundingClientRect();
if (obj.options.position) {
calendarContainer.style.position = 'fixed';
if (window.innerHeight < rect.bottom + rectContent.height) {
calendarContainer.style.top = (rect.top - (rectContent.height + 2)) + 'px';
} else {
calendarContainer.style.top = (rect.top + rect.height + 2) + 'px';
}
calendarContainer.style.left = rect.left + 'px';
} else {
if (window.innerHeight < rect.bottom + rectContent.height) {
var d = -1 * (rect.height + rectContent.height + 2);
if (d + rect.top < 0) {
d = -1 * (rect.top + rect.height);
}
calendarContainer.style.top = d + 'px';
} else {
calendarContainer.style.top = 2 + 'px';
}
if (window.innerWidth < rect.left + rectContent.width) {
var d = window.innerWidth - (rect.left + rectContent.width + 20);
calendarContainer.style.left = d + 'px';
} else {
calendarContainer.style.left = '0px';
}
}
}
// Events
if (typeof (obj.options.onopen) == 'function') {
obj.options.onopen(el);
}
}
}
}
obj.close = function (ignoreEvents, update) {
if (obj.options.autoSelect !== true && typeof(update) === 'undefined') {
update = false;
}
if (calendar.classList.contains('jcalendar-focus')) {
if (update !== false) {
var element = calendar.querySelector('.jcalendar-selected');
if (typeof (update) == 'string') {
var value = update;
} else if (!element || element.classList.contains('jcalendar-disabled')) {
var value = obj.options.value
} else {
var value = obj.getValue();
}
obj.setValue(value);
} else {
let value = obj.options.value || '';
obj.options.value = null;
obj.setValue(value)
}
// Events
if (!ignoreEvents && typeof (obj.options.onclose) == 'function') {
obj.options.onclose(el);
}
// Hide
calendar.classList.remove('jcalendar-focus');
// Stop tracking
tracking(obj, false);
// Current
Component.current = null;
}
return obj.options.value;
}
obj.prev = function () {
// Check if the visualization is the days picker or years picker
if (obj.options.mode == 'years') {
obj.date[0] = obj.date[0] - 12;
// Update picker table of days
obj.getYears();
} else if (obj.options.mode == 'months') {
obj.date[0] = parseInt(obj.date[0]) - 1;
// Update picker table of months
obj.getMonths();
} else {
// Go to the previous month
if (obj.date[1] < 2) {
obj.date[0] = obj.date[0] - 1;
obj.date[1] = 12;
} else {
obj.date[1] = obj.date[1] - 1;
}
// Update picker table of days
obj.getDays();
}
}
obj.next = function () {
// Check if the visualization is the days picker or years picker
if (obj.options.mode == 'years') {
obj.date[0] = parseInt(obj.date[0]) + 12;
// Update picker table of days
obj.getYears();
} else if (obj.options.mode == 'months') {
obj.date[0] = parseInt(obj.date[0]) + 1;
// Update picker table of months
obj.getMonths();
} else {
// Go to the previous month
if (obj.date[1] > 11) {
obj.date[0] = parseInt(obj.date[0]) + 1;
obj.date[1] = 1;
} else {
obj.date[1] = parseInt(obj.date[1]) + 1;
}
// Update picker table of days
obj.getDays();
}
}
/**
* Set today
*/
obj.setToday = function () {
// Today
var value = new Date().toISOString().substr(0, 10);
// Change value
obj.setValue(value);
// Value
return value;
}
obj.setValue = function (val) {
if (!val) {
val = '' + val;
}
// Values
var newValue = val;
var oldValue = obj.options.value;
if (oldValue != newValue) {
// Set label
if (!newValue) {
obj.date = null;
var val = '';
el.classList.remove('jcalendar_warning');
el.title = '';
} else {
var value = obj.setLabel(newValue, obj.options);
var date = newValue.split(' ');
if (!date[1]) {
date[1] = '00:00:00';
}
var time = date[1].split(':')
var date = date[0].split('-');
var y = parseInt(date[0]);
var m = parseInt(date[1]);
var d = parseInt(date[2]);
var h = parseInt(time[0]);
var i = parseInt(time[1]);
obj.date = [y, m, d, h, i, 0];
var val = obj.setLabel(newValue, obj.options);
// Current selection day
var current = Component.now(new Date(y, m - 1, d), true);
// Available ranges
if (obj.options.validRange) {
if (!obj.options.validRange[0] || current >= obj.options.validRange[0]) {
var test1 = true;
} else {
var test1 = false;
}
if (!obj.options.validRange[1] || current <= obj.options.validRange[1]) {
var test2 = true;
} else {
var test2 = false;
}
if (!(test1 && test2)) {
el.classList.add('jcalendar_warning');
el.title = dictionary.translate('Date outside the valid range');
} else {
el.classList.remove('jcalendar_warning');
el.title = '';
}
} else {
el.classList.remove('jcalendar_warning');
el.title = '';
}
}
// New value
obj.options.value = newValue;
if (typeof (obj.options.onchange) == 'function') {
obj.options.onchange(el, newValue, oldValue);
}
// Lemonade JS
if (el.value != val) {
el.value = val;
if (typeof (el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
if (obj.date) {
obj.getDays();
// Render months
if (obj.options.type == 'year-month-picker') {
obj.getMonths();
}
}
}
obj.getValue = function () {
if (obj.date) {
if (obj.options.time) {
return helpers.two(obj.date[0]) + '-' + helpers.two(obj.date[1]) + '-' + helpers.two(obj.date[2]) + ' ' + helpers.two(obj.date[3]) + ':' + helpers.two(obj.date[4]) + ':' + helpers.two(0);
} else {
return helpers.two(obj.date[0]) + '-' + helpers.two(obj.date[1]) + '-' + helpers.two(obj.date[2]) + ' ' + helpers.two(0) + ':' + helpers.two(0) + ':' + helpers.two(0);
}
} else {
return "";
}
}
/**
* Calendar
*/
obj.update = function (element, v) {
if (element.classList.contains('jcalendar-disabled')) {
// Do nothing
} else {
var elements = calendar.querySelector('.jcalendar-selected');
if (elements) {
elements.classList.remove('jcalendar-selected');
}
element.classList.add('jcalendar-selected');
if (element.classList.contains('jcalendar-set-month')) {
obj.date[1] = v;
obj.date[2] = 1; // first day of the month
} else {
obj.date[2] = element.innerText;
}
if (!obj.options.time) {
obj.close(null, true);
} else {
obj.date[3] = calendarSelectHour.value;
obj.date[4] = calendarSelectMin.value;
}
}
// Update
updateActions();
}
/**
* Set to blank
*/
obj.reset = function () {
// Close calendar
obj.setValue('');
obj.date = null;
obj.close(false, false);
}
/**
* Get calendar days
*/
obj.getDays = function () {
// Mode
obj.options.mode = 'days';
// Setting current values in case of NULLs
var date = new Date();
// Current selection
var year = obj.date && helpers.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
var month = obj.date && helpers.isNumeric(obj.date[1]) ? obj.date[1] : parseInt(date.getMonth()) + 1;
var day = obj.date && helpers.isNumeric(obj.date[2]) ? obj.date[2] : parseInt(date.getDate());
var hour = obj.date && helpers.isNumeric(obj.date[3]) ? obj.date[3] : parseInt(date.getHours());
var min = obj.date && helpers.isNumeric(obj.date[4]) ? obj.date[4] : parseInt(date.getMinutes());
// Selection container
obj.date = [year, month, day, hour, min, 0];
// Update title
calendarLabelYear.innerHTML = year;
calendarLabelMonth.innerHTML = obj.options.months[month - 1];
// Current month and Year
var isCurrentMonthAndYear = (date.getMonth() == month - 1) && (date.getFullYear() == year) ? true : false;
var currentDay = date.getDate();
// Number of days in the month
var date = new Date(year, month, 0, 0, 0);
var numberOfDays = date.getDate();
// First day
var date = new Date(year, month - 1, 0, 0, 0);
var firstDay = date.getDay() + 1;
// Index value
var index = obj.options.startingDay || 0;
// First of day relative to the starting calendar weekday
firstDay = firstDay - index;
// Reset table
calendarBody.innerHTML = '';
// Weekdays Row
var row = document.createElement('tr');
row.setAttribute('align', 'center');
calendarBody.appendChild(row);
// Create weekdays row
for (var i = 0; i < 7; i++) {
var cell = document.createElement('td');
cell.classList.add('jcalendar-weekday')
cell.innerHTML = obj.options.weekdays[index].substr(0, 1);
row.appendChild(cell);
// Next week day
index++;
// Restart index
if (index > 6) {
index = 0;
}
}
// Index of days
var index = 0;
var d = 0;
// Calendar table
for (var j = 0; j < 6; j++) {
// Reset cells container
var row = document.createElement('tr');
row.setAttribute('align', 'center');
row.style.height = '34px';
// Create cells
for (var i = 0; i < 7; i++) {
// Create cell
var cell = document.createElement('td');
cell.classList.add('jcalendar-set-day');
if (index >= firstDay && index < (firstDay + numberOfDays)) {
// Day cell
d++;
cell.innerHTML = d;
// Selected
if (d == day) {
cell.classList.add('jcalendar-selected');
}
// Current selection day is today
if (isCurrentMonthAndYear && currentDay == d) {
cell.style.fontWeight = 'bold';
}
// Current selection day
var current = Component.now(new Date(year, month - 1, d), true);
// Available ranges
if (obj.options.validRange) {
if (!obj.options.validRange[0] || current >= obj.options.validRange[0]) {
var test1 = true;
} else {
var test1 = false;
}
if (!obj.options.validRange[1] || current <= obj.options.validRange[1]) {
var test2 = true;
} else {
var test2 = false;
}
if (!(test1 && test2)) {
cell.classList.add('jcalendar-disabled');
}
}
}
// Day cell
row.appendChild(cell);
// Index
index++;
}
// Add cell to the calendar body
calendarBody.appendChild(row);
}
// Show time controls
if (obj.options.time) {
calendarControlsTime.style.display = '';
} else {
calendarControlsTime.style.display = 'none';
}
// Update
updateActions();
}
obj.getMonths = function () {
// Mode
obj.options.mode = 'months';
// Loading month labels
var months = obj.options.months;
// Value
var value = obj.options.value;
// Current date
var date = new Date();
var currentYear = parseInt(date.getFullYear());
var currentMonth = parseInt(date.getMonth()) + 1;
var selectedYear = obj.date && helpers.isNumeric(obj.date[0]) ? obj.date[0] : currentYear;
var selectedMonth = obj.date && helpers.isNumeric(obj.date[1]) ? obj.date[1] : currentMonth;
// Update title
calendarLabelYear.innerHTML = obj.date[0];
calendarLabelMonth.innerHTML = months[selectedMonth - 1];
// Table
var table = document.createElement('table');
table.setAttribute('width', '100%');
// Row
var row = null;
// Calendar table
for (var i = 0; i < 12; i++) {
if (!(i % 4)) {
// Reset cells container
var row = document.createElement('tr');
row.setAttribute('align', 'center');
table.appendChild(row);
}
// Create cell
var cell = document.createElement('td');
cell.classList.add('jcalendar-set-month');
cell.setAttribute('data-value', i + 1);
cell.innerText = months[i];
if (obj.options.validRange) {
var current = selectedYear + '-' + helpers.two(i + 1);
if (!obj.options.validRange[0] || current >= obj.options.validRange[0].substr(0, 7)) {
var test1 = true;
} else {
var test1 = false;
}
if (!obj.options.validRange[1] || current <= obj.options.validRange[1].substr(0, 7)) {
var test2 = true;
} else {
var test2 = false;
}
if (!(test1 && test2)) {
cell.classList.add('jcalendar-disabled');
}
}
if (i + 1 == selectedMonth) {
cell.classList.add('jcalendar-selected');
}
if (currentYear == selectedYear && i + 1 == currentMonth) {
cell.style.fontWeight = 'bold';
}
row.appendChild(cell);
}
calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
calendarBody.children[0].children[0].appendChild(table);
// Update
updateActions();
}
obj.getYears = function () {
// Mode
obj.options.mode = 'years';
// Current date
var date = new Date();
var currentYear = date.getFullYear();
var selectedYear = obj.date && helpers.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
// Array of years
var y = [];
for (var i = 0; i < 25; i++) {
y[i] = parseInt(obj.date[0]) + (i - 12);
}
// Assembling the year tables
var table = document.createElement('table');
table.setAttribute('width', '100%');
for (var i = 0; i < 25; i++) {
if (!(i % 5)) {
// Reset cells container
var row = document.createElement('tr');
row.setAttribute('align', 'center');
table.appendChild(row);
}
// Create cell
var cell = document.createElement('td');
cell.classList.add('jcalendar-set-year');
cell.innerText = y[i];
if (selectedYear == y[i]) {
cell.classList.add('jcalendar-selected');
}
if (currentYear == y[i]) {
cell.style.fontWeight = 'bold';
}
row.appendChild(cell);
}
calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
calendarBody.firstChild.firstChild.appendChild(table);
// Update
updateActions();
}
obj.setLabel = function (value, mixed) {
return Component.getDateString(value, mixed);
}
obj.fromFormatted = function (value, format) {
return Component.extractDateFromString(value, format);
}
var mouseUpControls = function (e) {
var element = helpers.findElement(e.target, 'jcalendar-container');
if (element) {
var action = e.target.className;
// Object id
if (action == 'jcalendar-prev') {
obj.prev();
} else if (action == 'jcalendar-next') {
obj.next();
} else if (action == 'jcalendar-month') {
obj.getMonths();
} else if (action == 'jcalendar-year') {
obj.getYears();
} else if (action == 'jcalendar-set-year') {
obj.date[0] = e.target.innerText;
if (obj.options.type == 'year-month-picker') {
obj.getMonths();
} else {
obj.getDays();
}
} else if (e.target.classList.contains('jcalendar-set-month')) {
var month = parseInt(e.target.getAttribute('data-value'));
if (obj.options.type == 'year-month-picker') {
obj.update(e.target, month);
} else {
obj.date[1] = month;
obj.getDays();
}
} else if (action == 'jcalendar-confirm' || action == 'jcalendar-update' || action == 'jcalendar-close') {
obj.close(null, true);
} else if (action == 'jcalendar-backdrop') {
obj.close(false, false);
} else if (action == 'jcalendar-reset') {
obj.reset();
} else if (e.target.classList.contains('jcalendar-set-day') && e.target.innerText) {
obj.update(e.target);
}
} else {
obj.close(false, false);
}
}
var keyUpControls = function (e) {
if (e.target.value && e.target.value.length > 3) {
var test = Component.extractDateFromString(e.target.value, obj.options.format);
if (test) {
obj.setValue(test);
}
}
}
// Update actions button
var updateActions = function () {
var currentDay = calendar.querySelector('.jcalendar-selected');
if (currentDay && currentDay.classList.contains('jcalendar-disabled')) {
calendarControlsUpdateButton.setAttribute('disabled', 'disabled');
calendarSelectHour.setAttribute('disabled', 'disabled');
calendarSelectMin.setAttribute('disabled', 'disabled');
} else {
calendarControlsUpdateButton.removeAttribute('disabled');
calendarSelectHour.removeAttribute('disabled');
calendarSelectMin.removeAttribute('disabled');
}
// Event
if (typeof (obj.options.onupdate) == 'function') {
obj.options.onupdate(el, obj.getValue());
}
}
var calendar = null;
var calendarReset = null;
var calendarConfirm = null;
var calendarContainer = null;
var calendarContent = null;
var calendarLabelYear = null;
var calendarLabelMonth = null;
var calendarTable = null;
var calendarBody = null;
var calendarControls = null;
var calendarControlsTime = null;
var calendarControlsUpdate = null;
var calendarControlsUpdateButton = null;
var calendarSelectHour = null;
var calendarSelectMin = null;
var init = function () {
// Get value from initial element if that is an input
if (el.tagName == 'INPUT' && el.value) {
options.value = el.value;
}
// Calendar DOM elements
calendarReset = document.createElement('div');
calendarReset.className = 'jcalendar-reset';
calendarConfirm = document.createElement('div');
calendarConfirm.className = 'jcalendar-confirm';
calendarControls = document.createElement('div');
calendarControls.className = 'jcalendar-controls'
calendarControls.style.borderBottom = '1px solid #ddd';
calendarControls.appendChild(calendarReset);
calendarControls.appendChild(calendarConfirm);
calendarContainer = document.createElement('div');
calendarContainer.className = 'jcalendar-container';
calendarContent = document.createElement('div');
calendarContent.className = 'jcalendar-content';
calendarContainer.appendChild(calendarContent);
// Main element
if (el.tagName == 'DIV') {
calendar = el;
calendar.classList.add('jcalendar-inline');
} else {
// Add controls to the screen
calendarContent.appendChild(calendarControls);
calendar = document.createElement('div');
calendar.className = 'jcalendar';
}
calendar.classList.add('jcalendar-container');
calendar.appendChild(calendarContainer);
// Table container
var calendarTableContainer = document.createElement('div');
calendarTableContainer.className = 'jcalendar-table';
calendarContent.appendChild(calendarTableContainer);
// Previous button
var calendarHeaderPrev = document.createElement('td');
calendarHeaderPrev.setAttribute('colspan', '2');
calendarHeaderPrev.className = 'jcalendar-prev';
// Header with year and month
calendarLabelYear = document.createElement('span');
calendarLabelYear.className = 'jcalendar-year';
calendarLabelMonth = document.createElement('span');
calendarLabelMonth.className = 'jcalendar-month';
var calendarHeaderTitle = document.createElement('td');
calendarHeaderTitle.className = 'jcalendar-header';
calendarHeaderTitle.setAttribute('colspan', '3');
calendarHeaderTitle.appendChild(calendarLabelMonth);
calendarHeaderTitle.appendChild(calendarLabelYear);
var calendarHeaderNext = document.createElement('td');
calendarHeaderNext.setAttribute('colspan', '2');
calendarHeaderNext.className = 'jcalendar-next';
var calendarHeader = document.createElement('thead');
var calendarHeaderRow = document.createElement('tr');
calendarHeaderRow.appendChild(calendarHeaderPrev);
calendarHeaderRow.appendChild(calendarHeaderTitle);
calendarHeaderRow.appendChild(calendarHeaderNext);
calendarHeader.appendChild(calendarHeaderRow);
calendarTable = document.createElement('table');
calendarBody = document.createElement('tbody');
calendarTable.setAttribute('cellpadding', '0');
calendarTable.setAttribute('cellspacing', '0');
calendarTable.appendChild(calendarHeader);
calendarTable.appendChild(calendarBody);
calendarTableContainer.appendChild(calendarTable);
calendarSelectHour = document.createElement('select');
calendarSelectHour.className = 'jcalendar-select';
calendarSelectHour.onchange = function () {
obj.date[3] = this.value;
// Event
if (typeof (obj.options.onupdate) == 'function') {
obj.options.onupdate(el, obj.getValue());
}
}
for (var i = 0; i < 24; i++) {
var element = document.createElement('option');
element.value = i;
element.innerHTML = helpers.two(i);
calendarSelectHour.appendChild(element);
}
calendarSelectMin = document.createElement('select');
calendarSelectMin.className = 'jcalendar-select';
calendarSelectMin.onchange = function () {
obj.date[4] = this.value;
// Event
if (typeof (obj.options.onupdate) == 'function') {
obj.options.onupdate(el, obj.getValue());
}
}
for (var i = 0; i < 60; i++) {
var element = document.createElement('option');
element.value = i;
element.innerHTML = helpers.two(i);
calendarSelectMin.appendChild(element);
}
// Footer controls
var calendarControlsFooter = document.createElement('div');
calendarControlsFooter.className = 'jcalendar-controls';
calendarControlsTime = document.createElement('div');
calendarControlsTime.className = 'jcalendar-time';
calendarControlsTime.style.maxWidth = '140px';
calendarControlsTime.appendChild(calendarSelectHour);
calendarControlsTime.appendChild(calendarSelectMin);
calendarControlsUpdateButton = document.createElement('button');
calendarControlsUpdateButton.setAttribute('type', 'button');
calendarControlsUpdateButton.className = 'jcalendar-update';
calendarControlsUpdate = document.createElement('div');
calendarControlsUpdate.style.flexGrow = '10';
calendarControlsUpdate.appendChild(calendarControlsUpdateButton);
calendarControlsFooter.appendChild(calendarControlsTime);
// Only show the update button for input elements
if (el.tagName == 'INPUT') {
calendarControlsFooter.appendChild(calendarControlsUpdate);
}
calendarContent.appendChild(calendarControlsFooter);
var calendarBackdrop = document.createElement('div');
calendarBackdrop.className = 'jcalendar-backdrop';
calendar.appendChild(calendarBackdrop);
// Handle events
el.addEventListener("keyup", keyUpControls);
// Add global events
calendar.addEventListener("swipeleft", function (e) {
animation.slideLeft(calendarTable, 0, function () {
obj.next();
animation.slideRight(calendarTable, 1);
});
e.preventDefault();
e.stopPropagation();
});
calendar.addEventListener("swiperight", function (e) {
animation.slideRight(calendarTable, 0, function () {
obj.prev();
animation.slideLeft(calendarTable, 1);
});
e.preventDefault();
e.stopPropagation();
});
if ('ontouchend' in document.documentElement === true) {
calendar.addEventListener("touchend", mouseUpControls);
el.addEventListener("touchend", obj.open);
} else {
calendar.addEventListener("mouseup", mouseUpControls);
el.addEventListener("mouseup", obj.open);
}
// Global controls
if (!Component.hasEvents) {
// Execute only one time
Component.hasEvents = true;
// Enter and Esc
document.addEventListener("keydown", Component.keydown);
}
// Set configuration
obj.setOptions(options);
// Append element to the DOM
if (el.tagName == 'INPUT') {
el.parentNode.insertBefore(calendar, el.nextSibling);
// Add properties
el.setAttribute('autocomplete', 'off');
// Element
el.classList.add('jcalendar-input');
// Value
el.value = obj.setLabel(obj.getValue(), obj.options);
} else {
// Get days
obj.getDays();
// Hour
if (obj.options.time) {
calendarSelectHour.value = obj.date[3];
calendarSelectMin.value = obj.date[4];
}
}
// Controls - must be set before open() for correct height calculations
if (obj.options.controls == false) {
calendarContainer.classList.add('jcalendar-hide-controls');
}
// Default opened
if (obj.options.opened == true) {
obj.open();
}
// Change method
el.change = obj.setValue;
// Global generic value handler
el.val = function (val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Keep object available from the node
el.calendar = calendar.calendar = obj;
}
init();
return obj;
});
Component.keydown = function (e) {
var calendar = null;
if (calendar = Component.current) {
if (e.which == 13) {
// ENTER
calendar.close(false, true);
} else if (e.which == 27) {
// ESC
calendar.close(false, false);
}
}
}
Component.prettify = function (d, texts) {
if (!texts) {
var texts = {
justNow: 'Just now',
xMinutesAgo: '{0}m ago',
xHoursAgo: '{0}h ago',
xDaysAgo: '{0}d ago',
xWeeksAgo: '{0}w ago',
xMonthsAgo: '{0} mon ago',
xYearsAgo: '{0}y ago',
}
}
if (d.indexOf('GMT') === -1 && d.indexOf('Z') === -1) {
d += ' GMT';
}
var d1 = new Date();
var d2 = new Date(d);
var total = parseInt((d1 - d2) / 1000 / 60);
String.prototype.format = function (o) {
return this.replace('{0}', o);
}
if (total == 0) {
var text = texts.justNow;
} else if (total < 90) {
var text = texts.xMinutesAgo.format(total);
} else if (total < 1440) { // One day
var text = texts.xHoursAgo.format(Math.round(total / 60));
} else if (total < 20160) { // 14 days
var text = texts.xDaysAgo.format(Math.round(total / 1440));
} else if (total < 43200) { // 30 days
var text = texts.xWeeksAgo.format(Math.round(total / 10080));
} else if (total < 1036800) { // 24 months
var text = texts.xMonthsAgo.format(Math.round(total / 43200));
} else { // 24 months+
var text = texts.xYearsAgo.format(Math.round(total / 525600));
}
return text;
}
Component.prettifyAll = function () {
var elements = document.querySelectorAll('.prettydate');
for (var i = 0; i < elements.length; i++) {
if (elements[i].getAttribute('data-date')) {
elements[i].innerHTML = Component.prettify(elements[i].getAttribute('data-date'));
} else {
if (elements[i].innerHTML) {
elements[i].setAttribute('title', elements[i].innerHTML);
elements[i].setAttribute('data-date', elements[i].innerHTML);
elements[i].innerHTML = Component.prettify(elements[i].innerHTML);
}
}
}
}
Component.now = HelpersDate.now;
Component.toArray = HelpersDate.toArray;
Component.dateToNum = HelpersDate.dateToNum
Component.numToDate = HelpersDate.numToDate;
Component.weekdays = HelpersDate.weekdays;
Component.months = HelpersDate.months;
Component.weekdaysShort = HelpersDate.weekdaysShort;
Component.monthsShort = HelpersDate.monthsShort;
// Legacy shortcut
Component.extractDateFromString = Mask.extractDateFromString;
Component.getDateString = Mask.getDateString;
return Component;
}
/* harmony default export */ var calendar = (Calendar());
;// CONCATENATED MODULE: ./src/plugins/palette.js
// More palettes https://coolors.co/ or https://gka.github.io/palettes/#/10|s|003790,005647,ffffe0|ffffe0,ff005e,93003a|1|1
function Palette() {
var palette = {
material: [
["#ffebee", "#fce4ec", "#f3e5f5", "#e8eaf6", "#e3f2fd", "#e0f7fa", "#e0f2f1", "#e8f5e9", "#f1f8e9", "#f9fbe7", "#fffde7", "#fff8e1", "#fff3e0", "#fbe9e7", "#efebe9", "#fafafa", "#eceff1"],
["#ffcdd2", "#f8bbd0", "#e1bee7", "#c5cae9", "#bbdefb", "#b2ebf2", "#b2dfdb", "#c8e6c9", "#dcedc8", "#f0f4c3", "#fff9c4", "#ffecb3", "#ffe0b2", "#ffccbc", "#d7ccc8", "#f5f5f5", "#cfd8dc"],
["#ef9a9a", "#f48fb1", "#ce93d8", "#9fa8da", "#90caf9", "#80deea", "#80cbc4", "#a5d6a7", "#c5e1a5", "#e6ee9c", "#fff59d", "#ffe082", "#ffcc80", "#ffab91", "#bcaaa4", "#eeeeee", "#b0bec5"],
["#e57373", "#f06292", "#ba68c8", "#7986cb", "#64b5f6", "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f", "#ffb74d", "#ff8a65", "#a1887f", "#e0e0e0", "#90a4ae"],
["#ef5350", "#ec407a", "#ab47bc", "#5c6bc0", "#42a5f5", "#26c6da", "#26a69a", "#66bb6a", "#9ccc65", "#d4e157", "#ffee58", "#ffca28", "#ffa726", "#ff7043", "#8d6e63", "#bdbdbd", "#78909c"],
["#f44336", "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b"],
["#e53935", "#d81b60", "#8e24aa", "#3949ab", "#1e88e5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e", "#6d4c41", "#757575", "#546e7a"],
["#d32f2f", "#c2185b", "#7b1fa2", "#303f9f", "#1976d2", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#5d4037", "#616161", "#455a64"],
["#c62828", "#ad1457", "#6a1b9a", "#283593", "#1565c0", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#4e342e", "#424242", "#37474f"],
["#b71c1c", "#880e4f", "#4a148c", "#1a237e", "#0d47a1", "#006064", "#004d40", "#1b5e20", "#33691e", "#827717", "#f57f17", "#ff6f00", "#e65100", "#bf360c", "#3e2723", "#212121", "#263238"],
],
fire: [
["0b1a6d", "840f38", "b60718", "de030b", "ff0c0c", "fd491c", "fc7521", "faa331", "fbb535", "ffc73a"],
["071147", "5f0b28", "930513", "be0309", "ef0000", "fa3403", "fb670b", "f9991b", "faad1e", "ffc123"],
["03071e", "370617", "6a040f", "9d0208", "d00000", "dc2f02", "e85d04", "f48c06", "faa307", "ffba08"],
["020619", "320615", "61040d", "8c0207", "bc0000", "c82a02", "d05203", "db7f06", "e19405", "efab00"],
["020515", "2d0513", "58040c", "7f0206", "aa0000", "b62602", "b94903", "c57205", "ca8504", "d89b00"],
],
baby: [
["eddcd2", "fff1e6", "fde2e4", "fad2e1", "c5dedd", "dbe7e4", "f0efeb", "d6e2e9", "bcd4e6", "99c1de"],
["e1c4b3", "ffd5b5", "fab6ba", "f5a8c4", "aacecd", "bfd5cf", "dbd9d0", "baceda", "9dc0db", "7eb1d5"],
["daa990", "ffb787", "f88e95", "f282a9", "8fc4c3", "a3c8be", "cec9b3", "9dbcce", "82acd2", "649dcb"],
["d69070", "ff9c5e", "f66770", "f05f8f", "74bbb9", "87bfae", "c5b993", "83aac3", "699bca", "4d89c2"],
["c97d5d", "f58443", "eb4d57", "e54a7b", "66a9a7", "78ae9c", "b5a67e", "7599b1", "5c88b7", "4978aa"],
],
chart: [
['#C1D37F', '#4C5454', '#FFD275', '#66586F', '#D05D5B', '#C96480', '#95BF8F', '#6EA240', '#0F0F0E', '#EB8258', '#95A3B3', '#995D81'],
],
}
var Component = function (o) {
// Otherwise get palette value
if (palette[o]) {
return palette[o];
} else {
return palette.material;
}
}
Component.get = function (o) {
// Otherwise get palette value
if (palette[o]) {
return palette[o];
} else {
return palette;
}
}
Component.set = function (o, v) {
palette[o] = v;
}
return Component;
}
/* harmony default export */ var palette = (Palette());
;// CONCATENATED MODULE: ./src/plugins/tabs.js
function Tabs(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
data: [],
position: null,
allowCreate: false,
allowChangePosition: false,
onclick: null,
onload: null,
onchange: null,
oncreate: null,
ondelete: null,
onbeforecreate: null,
onchangeposition: null,
animation: false,
hideHeaders: false,
padding: null,
palette: null,
maxWidth: null,
}
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Class
el.classList.add('jtabs');
var prev = null;
var next = null;
var border = null;
var header = null;
var controls = null;
var add = null;
// Event handler references for cleanup
var headersClickHandler = null;
var headersContextMenuHandler = null;
// Helpers
const setBorder = function(index) {
if (obj.options.animation) {
setTimeout(function() {
// Guard against destroyed state
if (!obj.headers || !obj.headers.children[index]) {
return;
}
let rect = obj.headers.children[index].getBoundingClientRect();
if (obj.options.palette === 'modern') {
border.style.width = rect.width - 4 + 'px';
border.style.left = obj.headers.children[index].offsetLeft + 2 + 'px';
} else {
border.style.width = rect.width + 'px';
border.style.left = obj.headers.children[index].offsetLeft + 'px';
}
if (obj.options.position === 'bottom') {
border.style.top = '0px';
} else {
border.style.bottom = '0px';
}
}, 50);
}
}
var updateControls = function(x) {
// Guard against destroyed state
if (!obj.headers) {
return;
}
if (typeof(obj.headers.scrollTo) == 'function') {
obj.headers.scrollTo({
left: x,
behavior: 'smooth',
});
} else {
obj.headers.scrollLeft = x;
}
if (x <= 1) {
prev.classList.add('disabled');
} else {
prev.classList.remove('disabled');
}
if (x >= obj.headers.scrollWidth - obj.headers.offsetWidth) {
next.classList.add('disabled');
} else {
next.classList.remove('disabled');
}
if (obj.headers.scrollWidth <= obj.headers.offsetWidth) {
prev.style.display = 'none';
next.style.display = 'none';
} else {
prev.style.display = '';
next.style.display = '';
}
}
obj.setBorder = setBorder;
// Set value
obj.open = function(index) {
// This is to force safari to update the children
const items = Array.from(obj.content.children);
if (! obj.content.children[index]) {
return;
}
var previous = null;
for (var i = 0; i < obj.headers.children.length; i++) {
if (obj.headers.children[i].classList.contains('jtabs-selected')) {
// Current one
previous = i;
}
// Remote selected
obj.headers.children[i].classList.remove('jtabs-selected');
obj.headers.children[i].removeAttribute('aria-selected')
if (obj.content.children[i]) {
obj.content.children[i].classList.remove('jtabs-selected');
}
}
obj.headers.children[index].classList.add('jtabs-selected');
obj.headers.children[index].setAttribute('aria-selected', 'true')
if (obj.content.children[index]) {
obj.content.children[index].classList.add('jtabs-selected');
}
if (previous != index && typeof(obj.options.onchange) == 'function') {
if (obj.content.children[index]) {
obj.options.onchange(el, obj, index, obj.headers.children[index], obj.content.children[index]);
}
}
// Hide
if (obj.options.hideHeaders == true && (obj.headers.children.length < 3 && obj.options.allowCreate == false)) {
obj.headers.parentNode.style.display = 'none';
} else {
obj.headers.parentNode.style.display = '';
var x1 = obj.headers.children[index].offsetLeft;
var x2 = x1 + obj.headers.children[index].offsetWidth;
var r1 = obj.headers.scrollLeft;
var r2 = r1 + obj.headers.offsetWidth;
if (! (r1 <= x1 && r2 >= x2)) {
// Out of the viewport
updateControls(x1 - 1);
}
// Set border
setBorder(index);
}
}
obj.selectIndex = function(a) {
var index = Array.prototype.indexOf.call(obj.headers.children, a);
if (index >= 0) {
obj.open(index);
}
return index;
}
obj.rename = function(i, title) {
if (! title) {
title = prompt('New title', obj.headers.children[i].innerText);
}
obj.headers.children[i].innerText = title;
setBorder(obj.getActive());
}
obj.create = function(title, url) {
if (typeof(obj.options.onbeforecreate) == 'function') {
var ret = obj.options.onbeforecreate(el, title);
if (ret === false) {
return false;
} else {
title = ret;
}
}
var div = obj.appendElement(title);
if (typeof(obj.options.oncreate) == 'function') {
obj.options.oncreate(el, div)
}
setBorder(obj.getActive());
return div;
}
obj.remove = function(index) {
return obj.deleteElement(index);
}
obj.nextNumber = function() {
var num = 0;
for (var i = 0; i < obj.headers.children.length; i++) {
var tmp = obj.headers.children[i].innerText.match(/[0-9].*/);
if (tmp > num) {
num = parseInt(tmp);
}
}
if (! num) {
num = 1;
} else {
num++;
}
return num;
}
obj.deleteElement = function(index) {
let current = obj.getActive();
if (! obj.headers.children[index]) {
return false;
} else {
obj.headers.removeChild(obj.headers.children[index]);
obj.content.removeChild(obj.content.children[index]);
}
if (current === index) {
obj.open(0);
} else {
let current = obj.getActive() || 0;
setBorder(current);
}
if (typeof(obj.options.ondelete) == 'function') {
obj.options.ondelete(el, index)
}
}
obj.appendElement = function(title, cb, openTab, position) {
if (! title) {
var title = prompt('Title?', '');
}
if (title) {
let headerId = helpers.guid();
let contentId = helpers.guid();
// Add content
var div = document.createElement('div');
div.setAttribute('id', contentId);
div.setAttribute('role', 'tabpanel');
div.setAttribute('aria-labelledby', headerId);
// Add headers
var h = document.createElement('div');
h.setAttribute('id', headerId);
h.setAttribute('role', 'tab');
h.setAttribute('aria-controls', contentId);
h.textContent = title;
h.content = div;
if (typeof(position) === 'undefined') {
obj.content.appendChild(div);
obj.headers.insertBefore(h, obj.headers.lastChild);
} else {
let r = obj.content.children[position];
if (r) {
obj.content.insertBefore(div, r);
} else {
obj.content.appendChild(div);
}
r = obj.headers.children[position] || obj.headers.lastChild;
obj.headers.insertBefore(h, r);
}
// Sortable
if (obj.options.allowChangePosition) {
h.setAttribute('draggable', 'true');
}
// Open new tab
if (openTab !== false) {
// Open new tab
obj.selectIndex(h);
}
// Callback
if (typeof(cb) == 'function') {
cb(div, h);
}
// Return element
return div;
}
}
obj.getActive = function() {
for (var i = 0; i < obj.headers.children.length; i++) {
if (obj.headers.children[i].classList.contains('jtabs-selected')) {
return i;
}
}
return false;
}
obj.updateContent = function(position, newContent) {
if (typeof newContent !== 'string') {
var contentItem = newContent;
} else {
var contentItem = document.createElement('div');
contentItem.innerHTML = newContent;
}
if (obj.content.children[position].classList.contains('jtabs-selected')) {
newContent.classList.add('jtabs-selected');
}
obj.content.replaceChild(newContent, obj.content.children[position]);
setBorder();
}
obj.updatePosition = function(f, t, ignoreEvents, openTab) {
// Ondrop update position of content
if (f > t) {
obj.content.insertBefore(obj.content.children[f], obj.content.children[t]);
} else {
obj.content.insertBefore(obj.content.children[f], obj.content.children[t].nextSibling);
}
// Open destination tab
if (openTab !== false) {
obj.open(t);
} else {
const activeIndex = obj.getActive();
if (t < activeIndex) {
obj.setBorder(activeIndex);
}
}
// Call event
if (! ignoreEvents && typeof(obj.options.onchangeposition) == 'function') {
obj.options.onchangeposition(obj.headers, f, t);
}
}
obj.move = function(f, t, ignoreEvents, openTab) {
if (f > t) {
obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t]);
} else {
obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t].nextSibling);
}
obj.updatePosition(f, t, ignoreEvents, openTab);
}
obj.setBorder = setBorder;
obj.init = function() {
el.textContent = '';
// Make sure the component is blank
obj.headers = document.createElement('div');
obj.content = document.createElement('div');
obj.headers.classList.add('jtabs-headers');
obj.headers.setAttribute('role', 'tablist');
obj.content.classList.add('jtabs-content');
obj.content.setAttribute('role', 'region');
obj.content.setAttribute('aria-label', 'Tab Panels');
if (obj.options.palette) {
el.classList.add('jtabs-modern');
} else {
el.classList.remove('jtabs-modern');
}
// Padding
if (obj.options.padding) {
obj.content.style.padding = parseInt(obj.options.padding) + 'px';
}
// Header
header = document.createElement('div');
header.className = 'jtabs-headers-container';
header.appendChild(obj.headers);
if (obj.options.maxWidth) {
header.style.maxWidth = parseInt(obj.options.maxWidth) + 'px';
}
// Controls
controls = document.createElement('div');
controls.className = 'jtabs-controls';
controls.setAttribute('draggable', 'false');
header.appendChild(controls);
// Append DOM elements
if (obj.options.position == 'bottom') {
el.appendChild(obj.content);
el.appendChild(header);
} else {
el.appendChild(header);
el.appendChild(obj.content);
}
// New button
if (obj.options.allowCreate == true) {
add = document.createElement('div');
add.className = 'jtabs-add';
add.onclick = function() {
obj.create();
}
controls.appendChild(add);
}
prev = document.createElement('div');
prev.className = 'jtabs-prev';
prev.onclick = function() {
updateControls(obj.headers.scrollLeft - obj.headers.offsetWidth);
}
controls.appendChild(prev);
next = document.createElement('div');
next.className = 'jtabs-next';
next.onclick = function() {
updateControls(obj.headers.scrollLeft + obj.headers.offsetWidth);
}
controls.appendChild(next);
// Data
for (var i = 0; i < obj.options.data.length; i++) {
// Title
if (obj.options.data[i].titleElement) {
var headerItem = obj.options.data[i].titleElement;
} else {
var headerItem = document.createElement('div');
}
// Icon
if (obj.options.data[i].icon) {
var iconContainer = document.createElement('div');
var icon = document.createElement('i');
icon.classList.add('material-icons');
icon.textContent = obj.options.data[i].icon;
iconContainer.appendChild(icon);
headerItem.appendChild(iconContainer);
}
// Title
if (obj.options.data[i].title) {
var title = document.createTextNode(obj.options.data[i].title);
headerItem.appendChild(title);
}
// Width
if (obj.options.data[i].width) {
headerItem.style.width = obj.options.data[i].width;
}
// Content
if (obj.options.data[i].contentElement) {
var contentItem = obj.options.data[i].contentElement;
} else {
var contentItem = document.createElement('div');
contentItem.innerHTML = obj.options.data[i].content;
}
obj.headers.appendChild(headerItem);
obj.content.appendChild(contentItem);
}
// Animation
border = document.createElement('div');
border.className = 'jtabs-border';
obj.headers.appendChild(border);
if (obj.options.animation) {
el.classList.add('jtabs-animation');
}
// Events
headersClickHandler = function(e) {
if (e.target.parentNode.classList.contains('jtabs-headers')) {
var target = e.target;
} else {
if (e.target.tagName == 'I') {
var target = e.target.parentNode.parentNode;
} else {
var target = e.target.parentNode;
}
}
var index = obj.selectIndex(target);
if (typeof(obj.options.onclick) == 'function') {
obj.options.onclick(el, obj, index, obj.headers.children[index], obj.content.children[index]);
}
};
obj.headers.addEventListener("click", headersClickHandler);
headersContextMenuHandler = function(e) {
obj.selectIndex(e.target);
};
obj.headers.addEventListener("contextmenu", headersContextMenuHandler);
if (obj.headers.children.length) {
// Open first tab
obj.open(0);
}
// Update controls
setTimeout(() => {
updateControls(0);
})
if (obj.options.allowChangePosition == true) {
Sorting(obj.headers, {
direction: 1,
ondrop: function(a,b,c) {
obj.updatePosition(b,c);
},
});
}
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj);
}
}
// Loading existing nodes as the data
if (el.children[0] && el.children[0].children.length) {
// Create from existing elements
for (var i = 0; i < el.children[0].children.length; i++) {
var item = obj.options.data && obj.options.data[i] ? obj.options.data[i] : {};
if (el.children[1] && el.children[1].children[i]) {
item.titleElement = el.children[0].children[i];
item.contentElement = el.children[1].children[i];
} else {
item.contentElement = el.children[0].children[i];
}
obj.options.data[i] = item;
}
}
// Remote controller flag
var loadingRemoteData = false;
// Create from data
if (obj.options.data) {
// Append children
for (var i = 0; i < obj.options.data.length; i++) {
if (obj.options.data[i].url) {
ajax({
url: obj.options.data[i].url,
type: 'GET',
dataType: 'text/html',
index: i,
success: function(result) {
obj.options.data[this.index].content = result;
},
complete: function() {
obj.init();
}
});
// Flag loading
loadingRemoteData = true;
}
}
}
if (! loadingRemoteData) {
obj.init();
}
/**
* Destroy the tabs instance and release all resources
*/
obj.destroy = function() {
// Remove event listeners from headers
if (obj.headers) {
if (headersClickHandler) {
obj.headers.removeEventListener('click', headersClickHandler);
}
if (headersContextMenuHandler) {
obj.headers.removeEventListener('contextmenu', headersContextMenuHandler);
}
}
// Clear onclick handlers
if (prev) {
prev.onclick = null;
}
if (next) {
next.onclick = null;
}
if (add) {
add.onclick = null;
}
// Remove DOM elements
if (header && header.parentNode) {
header.parentNode.removeChild(header);
}
if (obj.content && obj.content.parentNode) {
obj.content.parentNode.removeChild(obj.content);
}
// Remove class from element
el.classList.remove('jtabs');
el.classList.remove('jtabs-animation');
el.classList.remove('jtabs-modern');
// Remove instance reference
delete el.tabs;
// Clear options callbacks to release closures
if (obj.options) {
obj.options.onclick = null;
obj.options.onload = null;
obj.options.onchange = null;
obj.options.oncreate = null;
obj.options.ondelete = null;
obj.options.onbeforecreate = null;
obj.options.onchangeposition = null;
}
// Clear references
obj.headers = null;
obj.content = null;
header = null;
controls = null;
prev = null;
next = null;
border = null;
add = null;
headersClickHandler = null;
headersContextMenuHandler = null;
}
el.tabs = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/color.js
function Color(el, options) {
// Already created, update options
if (el.color) {
return el.color.setOptions(options, true);
}
// New instance
var obj = { type: 'color' };
obj.options = {};
var container = null;
var backdrop = null;
var content = null;
var resetButton = null;
var closeButton = null;
var tabs = null;
var jsuitesTabs = null;
/**
* Update options
*/
obj.setOptions = function(options, reset) {
/**
* @typedef {Object} defaults
* @property {(string|Array)} value - Initial value of the compontent
* @property {string} placeholder - The default instruction text on the element
* @property {requestCallback} onchange - Method to be execute after any changes on the element
* @property {requestCallback} onclose - Method to be execute when the element is closed
* @property {string} doneLabel - Label for button done
* @property {string} resetLabel - Label for button reset
* @property {string} resetValue - Value for button reset
* @property {Bool} showResetButton - Active or note for button reset - default false
*/
var defaults = {
placeholder: '',
value: null,
onopen: null,
onclose: null,
onchange: null,
closeOnChange: true,
palette: null,
position: null,
doneLabel: 'Done',
resetLabel: 'Reset',
fullscreen: false,
opened: false,
}
if (! options) {
options = {};
}
if (options && ! options.palette) {
// Default palette
options.palette = palette();
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Update the text of the controls, if they have already been created
if (resetButton) {
resetButton.innerHTML = obj.options.resetLabel;
}
if (closeButton) {
closeButton.innerHTML = obj.options.doneLabel;
}
// Update the pallete
if (obj.options.palette && jsuitesTabs) {
jsuitesTabs.updateContent(0, table());
}
// Value
if (typeof obj.options.value === 'string') {
el.value = obj.options.value;
if (el.tagName === 'INPUT') {
el.style.color = el.value;
el.style.backgroundColor = el.value;
}
}
// Placeholder
if (obj.options.placeholder) {
el.setAttribute('placeholder', obj.options.placeholder);
} else {
if (el.getAttribute('placeholder')) {
el.removeAttribute('placeholder');
}
}
return obj;
}
obj.select = function(color) {
// Remove current selected mark
var selected = container.querySelector('.jcolor-selected');
if (selected) {
selected.classList.remove('jcolor-selected');
}
// Mark cell as selected
if (obj.values[color]) {
obj.values[color].classList.add('jcolor-selected');
}
obj.options.value = color;
}
/**
* Open color pallete
*/
obj.open = function() {
if (! container.classList.contains('jcolor-focus')) {
// Start tracking
tracking(obj, true);
// Show color picker
container.classList.add('jcolor-focus');
// Select current color
if (obj.options.value) {
obj.select(obj.options.value);
}
// Reset margin
content.style.marginTop = '';
content.style.marginLeft = '';
var rectContent = content.getBoundingClientRect();
var availableWidth = helpers.getWindowWidth();
var availableHeight = helpers.getWindowHeight();
if (availableWidth < 800 || obj.options.fullscreen == true) {
content.classList.add('jcolor-fullscreen');
animation.slideBottom(content, 1);
backdrop.style.display = 'block';
} else {
if (content.classList.contains('jcolor-fullscreen')) {
content.classList.remove('jcolor-fullscreen');
backdrop.style.display = '';
}
if (obj.options.position) {
content.style.position = 'fixed';
} else {
content.style.position = '';
}
if (rectContent.left + rectContent.width > availableWidth) {
content.style.marginLeft = -1 * (rectContent.left + rectContent.width - (availableWidth - 20)) + 'px';
}
if (rectContent.top + rectContent.height > availableHeight) {
content.style.marginTop = -1 * (rectContent.top + rectContent.height - (availableHeight - 20)) + 'px';
}
}
if (typeof(obj.options.onopen) == 'function') {
obj.options.onopen(el, obj);
}
jsuitesTabs.setBorder(jsuitesTabs.getActive());
// Update sliders
if (obj.options.value) {
var rgb = HexToRgb(obj.options.value);
rgbInputs.forEach(function(rgbInput, index) {
rgbInput.value = rgb[index];
rgbInput.dispatchEvent(new Event('input'));
});
}
}
}
/**
* Close color pallete
*/
obj.close = function(ignoreEvents) {
if (container && container.classList.contains('jcolor-focus')) {
// Remove focus
container.classList.remove('jcolor-focus');
// Make sure backdrop is hidden
if (backdrop) {
backdrop.style.display = '';
}
// Call related events
if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
obj.options.onclose(el, obj);
}
// Stop the object
tracking(obj, false);
}
return obj.options.value ? obj.options.value : null;
}
/**
* Set value
*/
obj.setValue = function(color) {
if (! color) {
color = '';
}
if (color != obj.options.value) {
obj.options.value = color;
slidersResult = color;
// Remove current selecded mark
obj.select(color);
// Onchange
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, color, obj);
}
// Changes
if (el.value != obj.options.value) {
// Set input value
el.value = obj.options.value;
if (el.tagName === 'INPUT') {
el.style.color = el.value;
el.style.backgroundColor = el.value;
}
// Element onchange native
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
if (obj.options.closeOnChange == true) {
obj.close();
}
}
}
/**
* Get value
*/
obj.getValue = function() {
return obj.options.value;
}
var backdropClickControl = false;
// Converts a number in decimal to hexadecimal
var decToHex = function(num) {
var hex = num.toString(16);
return hex.length === 1 ? "0" + hex : hex;
}
// Converts a color in rgb to hexadecimal
var rgbToHex = function(r, g, b) {
return "#" + decToHex(r) + decToHex(g) + decToHex(b);
}
// Converts a number in hexadecimal to decimal
var hexToDec = function(hex) {
return parseInt('0x' + hex);
}
// Converts a color in hexadecimal to rgb
var HexToRgb = function(hex) {
return [hexToDec(hex.substr(1, 2)), hexToDec(hex.substr(3, 2)), hexToDec(hex.substr(5, 2))]
}
var table = function() {
// Content of the first tab
var tableContainer = document.createElement('div');
tableContainer.className = 'jcolor-grid';
// Cells
obj.values = [];
// Table pallete
var t = document.createElement('table');
t.setAttribute('cellpadding', '7');
t.setAttribute('cellspacing', '0');
for (var j = 0; j < obj.options.palette.length; j++) {
var tr = document.createElement('tr');
for (var i = 0; i < obj.options.palette[j].length; i++) {
var td = document.createElement('td');
var color = obj.options.palette[j][i];
if (color.length < 7 && color.substr(0,1) !== '#') {
color = '#' + color;
}
td.style.backgroundColor = color;
td.setAttribute('data-value', color);
td.innerHTML = '';
tr.appendChild(td);
// Selected color
if (obj.options.value == color) {
td.classList.add('jcolor-selected');
}
// Possible values
obj.values[color] = td;
}
t.appendChild(tr);
}
// Append to the table
tableContainer.appendChild(t);
return tableContainer;
}
// Canvas where the image will be rendered
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 160;
var context = canvas.getContext("2d");
var resizeCanvas = function() {
// Specifications necessary to correctly obtain colors later in certain positions
var m = tabs.firstChild.getBoundingClientRect();
canvas.width = m.width - 14;
gradient()
}
var gradient = function() {
var g = context.createLinearGradient(0, 0, canvas.width, 0);
// Create color gradient
g.addColorStop(0, "rgb(255,0,0)");
g.addColorStop(0.15, "rgb(255,0,255)");
g.addColorStop(0.33, "rgb(0,0,255)");
g.addColorStop(0.49, "rgb(0,255,255)");
g.addColorStop(0.67, "rgb(0,255,0)");
g.addColorStop(0.84, "rgb(255,255,0)");
g.addColorStop(1, "rgb(255,0,0)");
context.fillStyle = g;
context.fillRect(0, 0, canvas.width, canvas.height);
g = context.createLinearGradient(0, 0, 0, canvas.height);
g.addColorStop(0, "rgba(255,255,255,1)");
g.addColorStop(0.5, "rgba(255,255,255,0)");
g.addColorStop(0.5, "rgba(0,0,0,0)");
g.addColorStop(1, "rgba(0,0,0,1)");
context.fillStyle = g;
context.fillRect(0, 0, canvas.width, canvas.height);
}
var hsl = function() {
var element = document.createElement('div');
element.className = "jcolor-hsl";
var point = document.createElement('div');
point.className = 'jcolor-point';
var div = document.createElement('div');
div.appendChild(canvas);
div.appendChild(point);
element.appendChild(div);
// Moves the marquee point to the specified position
var update = function(buttons, x, y) {
if (buttons === 1) {
var rect = element.getBoundingClientRect();
var left = x - rect.left;
var top = y - rect.top;
if (left < 0) {
left = 0;
}
if (top < 0) {
top = 0;
}
if (left > rect.width) {
left = rect.width;
}
if (top > rect.height) {
top = rect.height;
}
point.style.left = left + 'px';
point.style.top = top + 'px';
var pixel = context.getImageData(left, top, 1, 1).data;
slidersResult = rgbToHex(pixel[0], pixel[1], pixel[2]);
}
}
// Applies the point's motion function to the div that contains it
element.addEventListener('mousedown', function(e) {
update(e.buttons, e.clientX, e.clientY);
});
element.addEventListener('mousemove', function(e) {
update(e.buttons, e.clientX, e.clientY);
});
element.addEventListener('touchmove', function(e) {
update(1, e.changedTouches[0].clientX, e.changedTouches[0].clientY);
});
return element;
}
var slidersResult = '';
var rgbInputs = [];
var changeInputColors = function() {
if (slidersResult !== '') {
for (var j = 0; j < rgbInputs.length; j++) {
var currentColor = HexToRgb(slidersResult);
currentColor[j] = 0;
var newGradient = 'linear-gradient(90deg, rgb(';
newGradient += currentColor.join(', ');
newGradient += '), rgb(';
currentColor[j] = 255;
newGradient += currentColor.join(', ');
newGradient += '))';
rgbInputs[j].style.backgroundImage = newGradient;
}
}
}
var sliders = function() {
// Content of the third tab
var slidersElement = document.createElement('div');
slidersElement.className = 'jcolor-sliders';
var slidersBody = document.createElement('div');
// Creates a range-type input with the specified name
var createSliderInput = function(name) {
var inputContainer = document.createElement('div');
inputContainer.className = 'jcolor-sliders-input-container';
var label = document.createElement('label');
label.innerText = name;
var subContainer = document.createElement('div');
subContainer.className = 'jcolor-sliders-input-subcontainer';
var input = document.createElement('input');
input.type = 'range';
input.min = 0;
input.max = 255;
input.value = 0;
input.setAttribute('aria-label', "Color value");
input.setAttribute('aria-valuemin', "0");
input.setAttribute('aria-valuemax', "255");
input.setAttribute('aria-valuenow', "0");
inputContainer.appendChild(label);
subContainer.appendChild(input);
var value = document.createElement('div');
value.innerText = input.value;
input.addEventListener('input', function() {
value.innerText = input.value;
});
subContainer.appendChild(value);
inputContainer.appendChild(subContainer);
slidersBody.appendChild(inputContainer);
return input;
}
// Creates red, green and blue inputs
rgbInputs = [
createSliderInput('Red'),
createSliderInput('Green'),
createSliderInput('Blue'),
];
slidersElement.appendChild(slidersBody);
// Element that prints the current color
var slidersResultColor = document.createElement('div');
slidersResultColor.className = 'jcolor-sliders-final-color';
var resultElement = document.createElement('div');
resultElement.style.visibility = 'hidden';
resultElement.innerText = 'a';
slidersResultColor.appendChild(resultElement)
// Update the element that prints the current color
var updateResult = function() {
var resultColor = rgbToHex(parseInt(rgbInputs[0].value), parseInt(rgbInputs[1].value), parseInt(rgbInputs[2].value));
resultElement.innerText = resultColor;
resultElement.style.color = resultColor;
resultElement.style.removeProperty('visibility');
slidersResult = resultColor;
}
// Apply the update function to color inputs
rgbInputs.forEach(function(rgbInput) {
rgbInput.addEventListener('input', function() {
updateResult();
changeInputColors();
});
});
slidersElement.appendChild(slidersResultColor);
return slidersElement;
}
var init = function() {
// Initial options
obj.setOptions(options);
// Add a proper input tag when the element is an input
if (el.tagName == 'INPUT') {
el.classList.add('jcolor-input');
el.readOnly = true;
}
// Table container
container = document.createElement('div');
container.className = 'jcolor';
// Table container
backdrop = document.createElement('div');
backdrop.className = 'jcolor-backdrop';
container.appendChild(backdrop);
// Content
content = document.createElement('div');
content.className = 'jcolor-content';
// Controls
var controls = document.createElement('div');
controls.className = 'jcolor-controls';
content.appendChild(controls);
// Reset button
resetButton = document.createElement('div');
resetButton.className = 'jcolor-reset';
resetButton.innerHTML = obj.options.resetLabel;
controls.appendChild(resetButton);
// Close button
closeButton = document.createElement('div');
closeButton.className = 'jcolor-close';
closeButton.innerHTML = obj.options.doneLabel;
controls.appendChild(closeButton);
// Element that will be used to create the tabs
tabs = document.createElement('div');
content.appendChild(tabs);
// Starts the jSuites tabs component
jsuitesTabs = Tabs(tabs, {
animation: true,
data: [
{
title: 'Grid',
contentElement: table(),
},
{
title: 'Spectrum',
contentElement: hsl(),
},
{
title: 'Sliders',
contentElement: sliders(),
}
],
onchange: function(element, instance, index) {
if (index === 1) {
resizeCanvas();
} else {
var color = slidersResult !== '' ? slidersResult : obj.getValue();
if (index === 2 && color) {
var rgb = HexToRgb(color);
rgbInputs.forEach(function(rgbInput, index) {
rgbInput.value = rgb[index];
rgbInput.dispatchEvent(new Event('input'));
});
}
}
},
palette: 'modern',
});
container.appendChild(content);
// Insert picker after the element
if (el.tagName == 'INPUT') {
el.parentNode.insertBefore(container, el.nextSibling);
} else {
el.appendChild(container);
}
containerClickHandler = function(e) {
if (e.target.tagName == 'TD') {
var value = e.target.getAttribute('data-value');
if (value) {
obj.setValue(value);
}
} else if (e.target.classList.contains('jcolor-reset')) {
obj.setValue('');
obj.close();
} else if (e.target.classList.contains('jcolor-close')) {
if (jsuitesTabs.getActive() > 0) {
obj.setValue(slidersResult);
}
obj.close();
} else if (e.target.classList.contains('jcolor-backdrop')) {
obj.close();
} else {
obj.open();
}
};
container.addEventListener("click", containerClickHandler);
/**
* If element is focus open the picker
*/
elMouseupHandler = function(e) {
obj.open();
};
el.addEventListener("mouseup", elMouseupHandler);
// If the picker is open on the spectrum tab, it changes the canvas size when the window size is changed
windowResizeHandler = function() {
if (container.classList.contains('jcolor-focus') && jsuitesTabs.getActive() == 1) {
resizeCanvas();
}
};
window.addEventListener('resize', windowResizeHandler);
// Default opened
if (obj.options.opened == true) {
obj.open();
}
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Keep object available from the node
el.color = obj;
// Container shortcut
container.color = obj;
}
obj.toHex = function(rgb) {
var hex = function(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
if (rgb) {
if (/^#[0-9A-F]{6}$/i.test(rgb)) {
return rgb;
} else {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
if (rgb && rgb.length) {
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
} else {
return "";
}
}
}
}
// Store event handler references for cleanup
var containerClickHandler = null;
var elMouseupHandler = null;
var windowResizeHandler = null;
/**
* Destroy the color picker instance and release all resources
*/
obj.destroy = function() {
// Close if open (removes from tracking)
obj.close(true);
// Remove event listeners
if (container && containerClickHandler) {
container.removeEventListener('click', containerClickHandler);
}
if (el && elMouseupHandler) {
el.removeEventListener('mouseup', elMouseupHandler);
}
if (windowResizeHandler) {
window.removeEventListener('resize', windowResizeHandler);
}
// Destroy the tabs component if it has destroy method
if (jsuitesTabs && typeof jsuitesTabs.destroy === 'function') {
jsuitesTabs.destroy();
}
// Clear rgbInputs references
rgbInputs = [];
// Remove container from DOM
if (container && container.parentNode) {
container.parentNode.removeChild(container);
}
// Clean up element
if (el.tagName === 'INPUT') {
el.classList.remove('jcolor-input');
el.readOnly = false;
el.style.color = '';
el.style.backgroundColor = '';
}
// Remove instance properties from el
delete el.color;
delete el.change;
delete el.val;
// Clear options callbacks to release closures
if (obj.options) {
obj.options.onchange = null;
obj.options.onclose = null;
obj.options.onopen = null;
obj.options.onload = null;
}
// Clear references
container = null;
backdrop = null;
content = null;
resetButton = null;
closeButton = null;
tabs = null;
jsuitesTabs = null;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/contextmenu.js
function Contextmenu() {
var Component = function(el, options) {
// New instance
var obj = {type: 'contextmenu'};
obj.options = {};
// Default configuration
var defaults = {
items: null,
onclick: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Class definition
el.classList.add('jcontextmenu');
/**
* Open contextmenu
*/
obj.open = function (e, items) {
if (items) {
// Update content
obj.options.items = items;
// Create items
obj.create(items);
}
// Close current contextmenu
if (Component.current) {
Component.current.close();
}
// Add to the opened components monitor
tracking(obj, true);
// Show context menu
el.classList.add('jcontextmenu-focus');
// Current
Component.current = obj;
// Coordinates
if ((obj.options.items && obj.options.items.length > 0) || el.children.length) {
if (e.target) {
if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
} else {
var x = e.x;
var y = e.y;
}
var rect = el.getBoundingClientRect();
if (window.innerHeight < y + rect.height) {
var h = y - rect.height;
if (h < 0) {
h = 0;
}
el.style.top = h + 'px';
} else {
el.style.top = y + 'px';
}
if (window.innerWidth < x + rect.width) {
if (x - rect.width > 0) {
el.style.left = (x - rect.width) + 'px';
} else {
el.style.left = '10px';
}
} else {
el.style.left = x + 'px';
}
}
}
obj.isOpened = function () {
return el.classList.contains('jcontextmenu-focus') ? true : false;
}
/**
* Close menu
*/
obj.close = function () {
if (el.classList.contains('jcontextmenu-focus')) {
el.classList.remove('jcontextmenu-focus');
}
tracking(obj, false);
}
/**
* Create items based on the declared objectd
* @param {object} items - List of object
*/
obj.create = function (items) {
// Update content
el.innerHTML = '';
// Add header contextmenu
var itemHeader = createHeader();
el.appendChild(itemHeader);
// Append items
for (var i = 0; i < items.length; i++) {
var itemContainer = createItemElement(items[i]);
el.appendChild(itemContainer);
}
}
/**
* createHeader for context menu
* @private
* @returns {HTMLElement}
*/
function createHeader() {
var header = document.createElement('div');
header.classList.add("header");
header.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
});
var title = document.createElement('a');
title.classList.add("title");
title.innerHTML = dictionary.translate("Menu");
header.appendChild(title);
var closeButton = document.createElement('a');
closeButton.classList.add("close");
closeButton.innerHTML = dictionary.translate("close");
closeButton.addEventListener("click", function (e) {
obj.close();
});
header.appendChild(closeButton);
return header;
}
/**
* Private function for create a new Item element
* @param {type} item
* @returns {jsuitesL#15.jSuites.contextmenu.createItemElement.itemContainer}
*/
function createItemElement(item) {
if (item.type && (item.type == 'line' || item.type == 'divisor')) {
var itemContainer = document.createElement('hr');
} else {
var itemContainer = document.createElement('div');
var itemText = document.createElement('a');
itemText.innerHTML = item.title;
if (item.tooltip) {
itemContainer.setAttribute('title', item.tooltip);
}
if (item.icon) {
itemContainer.setAttribute('data-icon', item.icon);
}
if (item.id) {
itemContainer.id = item.id;
}
if (item.disabled) {
itemContainer.className = 'jcontextmenu-disabled';
} else if (item.onclick) {
let method = item.onclick;
itemContainer.addEventListener("mousedown", function (e) {
e.preventDefault();
});
itemContainer.addEventListener("mouseup", function (e) {
method(this, e);
});
}
itemContainer.appendChild(itemText);
if (item.submenu) {
var itemIconSubmenu = document.createElement('span');
itemIconSubmenu.innerHTML = "►";
itemContainer.appendChild(itemIconSubmenu);
itemContainer.classList.add('jcontexthassubmenu');
var el_submenu = document.createElement('div');
// Class definition
el_submenu.classList.add('jcontextmenu');
// Focusable
el_submenu.setAttribute('tabindex', '900');
// Append items
var submenu = item.submenu;
for (var i = 0; i < submenu.length; i++) {
var itemContainerSubMenu = createItemElement(submenu[i]);
el_submenu.appendChild(itemContainerSubMenu);
}
itemContainer.appendChild(el_submenu);
// Submenu positioning logic:
// Case 1: Default (enough space to the right) - submenu opens to the right of the parent menu item.
// Case 2: Not enough space to the right, but enough to the left - submenu opens to the left of the parent menu item.
// Case 3: Not enough space on either side (e.g., very narrow viewport) - submenu opens below the parent menu item.
itemContainer.addEventListener('mouseenter', function () {
// Reset to default
el_submenu.style.left = '';
el_submenu.style.right = '';
el_submenu.style.minWidth = itemContainer.offsetWidth + 'px';
// Temporarily show submenu to measure
el_submenu.style.display = 'block';
el_submenu.style.opacity = '0';
el_submenu.style.pointerEvents = 'none';
// Use getBoundingClientRect to determine position
var parentRect = itemContainer.getBoundingClientRect();
var submenuRect = el_submenu.getBoundingClientRect();
var viewportWidth = window.innerWidth || document.documentElement.clientWidth;
// Calculate the right edge if rendered to the right
var rightEdge = parentRect.right + submenuRect.width;
var leftEdge = parentRect.left - submenuRect.width;
// If rendering to the right would overflow, render to the left
if (rightEdge > viewportWidth && leftEdge >= 0) {
el_submenu.style.left = 'auto';
el_submenu.style.right = '99%';
}
// If both right and left would overflow, render to the right of the left border (worst case)
else if (rightEdge > viewportWidth && leftEdge < 0) {
el_submenu.style.left = '32px';
el_submenu.style.right = 'auto';
el_submenu.style.top = '100%';
}
// Default: render to the right
else {
el_submenu.style.left = '99%';
el_submenu.style.right = 'auto';
}
// Restore visibility
el_submenu.style.opacity = '';
el_submenu.style.pointerEvents = '';
el_submenu.style.display = '';
});
// Also reset submenu position on mouseleave to avoid stale styles
itemContainer.addEventListener('mouseleave', function () {
el_submenu.style.left = '';
el_submenu.style.right = '';
el_submenu.style.minWidth = '';
});
} else if (item.shortcut) {
var itemShortCut = document.createElement('span');
itemShortCut.innerHTML = item.shortcut;
itemContainer.appendChild(itemShortCut);
}
}
return itemContainer;
}
if (typeof (obj.options.onclick) == 'function') {
el.addEventListener('click', function (e) {
obj.options.onclick(obj, e);
});
}
// Create items
if (obj.options.items) {
obj.create(obj.options.items);
}
window.addEventListener("mousewheel", function () {
obj.close();
});
el.contextmenu = obj;
return obj;
}
return Component;
}
/* harmony default export */ var contextmenu = (Contextmenu());
;// CONCATENATED MODULE: ./src/plugins/dropdown.js
function Dropdown() {
var Component = (function (el, options) {
// Already created, update options
if (el.dropdown) {
return el.dropdown.setOptions(options, true);
}
// New instance
var obj = {type: 'dropdown'};
obj.options = {};
// Success
var success = function (data, val) {
// Set data
if (data && data.length) {
// Sort
if (obj.options.sortResults !== false) {
if (typeof obj.options.sortResults == "function") {
data.sort(obj.options.sortResults);
} else {
data.sort(sortData);
}
}
obj.setData(data);
}
// Onload method
if (typeof (obj.options.onload) == 'function') {
obj.options.onload(el, obj, data, val);
}
// Set value
if (val) {
applyValue(val);
}
// Component value
if (val === undefined || val === null) {
obj.options.value = '';
}
el.value = obj.options.value;
// Open dropdown
if (obj.options.opened == true) {
obj.open();
}
}
// Default sort
var sortData = function (itemA, itemB) {
var testA, testB;
if (typeof itemA == "string") {
testA = itemA;
} else {
if (itemA.text) {
testA = itemA.text;
} else if (itemA.name) {
testA = itemA.name;
}
}
if (typeof itemB == "string") {
testB = itemB;
} else {
if (itemB.text) {
testB = itemB.text;
} else if (itemB.name) {
testB = itemB.name;
}
}
if (typeof testA == "string" || typeof testB == "string") {
if (typeof testA != "string") {
testA = "" + testA;
}
if (typeof testB != "string") {
testB = "" + testB;
}
return testA.localeCompare(testB);
} else {
return testA - testB;
}
}
/**
* Reset the options for the dropdown
*/
var resetValue = function () {
// Reset value container
obj.value = {};
// Remove selected
for (var i = 0; i < obj.items.length; i++) {
if (obj.items[i].selected == true) {
if (obj.items[i].element) {
obj.items[i].element.classList.remove('jdropdown-selected')
}
obj.items[i].selected = null;
}
}
// Reset options
obj.options.value = '';
// Reset value
el.value = '';
}
/**
* Apply values to the dropdown
*/
var applyValue = function (values) {
// Reset the current values
resetValue();
// Read values
if (values !== null) {
if (!values) {
if (typeof (obj.value['']) !== 'undefined') {
obj.value[''] = '';
}
} else {
if (!Array.isArray(values)) {
values = ('' + values).split(';');
}
for (var i = 0; i < values.length; i++) {
obj.value[values[i]] = '';
}
}
}
// Update the DOM
for (var i = 0; i < obj.items.length; i++) {
if (typeof (obj.value[Value(i)]) !== 'undefined') {
if (obj.items[i].element) {
obj.items[i].element.classList.add('jdropdown-selected')
}
obj.items[i].selected = true;
// Keep label
obj.value[Value(i)] = Text(i);
}
}
// Global value
obj.options.value = Object.keys(obj.value).join(';');
// Update labels
obj.header.value = obj.getText();
}
// Get the value of one item
var Value = function (k, v) {
// Legacy purposes
if (!obj.options.format) {
var property = 'value';
} else {
var property = 'id';
}
if (obj.items[k]) {
if (v !== undefined) {
return obj.items[k].data[property] = v;
} else {
return obj.items[k].data[property];
}
}
return '';
}
// Get the label of one item
var Text = function (k, v) {
// Legacy purposes
if (!obj.options.format) {
var property = 'text';
} else {
var property = 'name';
}
if (obj.items[k]) {
if (v !== undefined) {
return obj.items[k].data[property] = v;
} else {
return obj.items[k].data[property];
}
}
return '';
}
var getValue = function () {
return Object.keys(obj.value);
}
var getText = function () {
var data = [];
var k = Object.keys(obj.value);
for (var i = 0; i < k.length; i++) {
data.push(obj.value[k[i]]);
}
return data;
}
obj.setOptions = function (options, reset) {
if (!options) {
options = {};
}
// Default configuration
var defaults = {
url: null,
data: [],
format: 0,
multiple: false,
autocomplete: false,
remoteSearch: false,
lazyLoading: false,
type: null,
width: null,
maxWidth: null,
opened: false,
value: null,
placeholder: '',
newOptions: false,
position: false,
onchange: null,
onload: null,
onopen: null,
onclose: null,
onfocus: null,
onblur: null,
oninsert: null,
onbeforeinsert: null,
onsearch: null,
onbeforesearch: null,
sortResults: false,
autofocus: false,
prompt: null,
allowEmpty: true,
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof (obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Force autocomplete search
if (obj.options.remoteSearch == true || obj.options.type === 'searchbar') {
obj.options.autocomplete = true;
}
// New options
if (obj.options.newOptions == true) {
obj.header.classList.add('jdropdown-add');
} else {
obj.header.classList.remove('jdropdown-add');
}
// Autocomplete
if (obj.options.autocomplete == true) {
obj.header.removeAttribute('readonly');
} else {
obj.header.setAttribute('readonly', 'readonly');
}
// Place holder
if (obj.options.placeholder) {
obj.header.setAttribute('placeholder', obj.options.placeholder);
} else {
obj.header.removeAttribute('placeholder');
}
// Remove specific dropdown typing to add again
el.classList.remove('jdropdown-searchbar');
el.classList.remove('jdropdown-picker');
el.classList.remove('jdropdown-list');
if (obj.options.type == 'searchbar') {
el.classList.add('jdropdown-searchbar');
} else if (obj.options.type == 'list') {
el.classList.add('jdropdown-list');
} else if (obj.options.type == 'picker') {
el.classList.add('jdropdown-picker');
} else {
if (helpers.getWindowWidth() < 800) {
if (obj.options.autocomplete) {
el.classList.add('jdropdown-searchbar');
obj.options.type = 'searchbar';
} else {
el.classList.add('jdropdown-picker');
obj.options.type = 'picker';
}
} else {
if (obj.options.width) {
el.style.width = obj.options.width;
el.style.minWidth = obj.options.width;
} else {
el.style.removeProperty('width');
el.style.removeProperty('min-width');
}
el.classList.add('jdropdown-default');
obj.options.type = 'default';
}
}
// Close button
if (obj.options.type == 'searchbar') {
containerHeader.appendChild(closeButton);
} else {
container.insertBefore(closeButton, container.firstChild);
}
// Load the content
if (obj.options.url && !options.data) {
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
success: function (data) {
if (data) {
success(data, obj.options.value);
}
}
});
} else {
success(obj.options.data, obj.options.value);
}
// Return the instance
return obj;
}
// Helpers
var containerHeader = null;
var container = null;
var content = null;
var closeButton = null;
var resetButton = null;
var backdrop = null;
var keyTimer = null;
/**
* Init dropdown
*/
var init = function () {
// Do not accept null
if (!options) {
options = {};
}
// If the element is a SELECT tag, create a configuration object
if (el.tagName == 'SELECT') {
var ret = Component.extractFromDom(el, options);
el = ret.el;
options = ret.options;
}
// Place holder
if (!options.placeholder && el.getAttribute('placeholder')) {
options.placeholder = el.getAttribute('placeholder');
}
// Value container
obj.value = {};
// Containers
obj.items = [];
obj.groups = [];
// Search options
obj.search = '';
obj.results = null;
// Create dropdown
el.classList.add('jdropdown');
// Header container
containerHeader = document.createElement('div');
containerHeader.className = 'jdropdown-container-header';
// Header
obj.header = document.createElement('input');
obj.header.className = 'jdropdown-header jss_object';
obj.header.type = 'text';
obj.header.setAttribute('autocomplete', 'off');
obj.header.onfocus = function () {
if (typeof (obj.options.onfocus) == 'function') {
obj.options.onfocus(el);
}
}
obj.header.onblur = function () {
if (typeof (obj.options.onblur) == 'function') {
obj.options.onblur(el);
}
}
obj.header.onkeyup = function (e) {
if (obj.options.autocomplete == true && !keyTimer) {
if (obj.search != obj.header.value.trim()) {
keyTimer = setTimeout(function () {
obj.find(obj.header.value.trim());
keyTimer = null;
}, 400);
}
if (!el.classList.contains('jdropdown-focus')) {
obj.open();
}
} else {
if (!obj.options.autocomplete) {
obj.next(e.key);
}
}
}
// Global controls
if (!Component.hasEvents) {
// Execute only one time
Component.hasEvents = true;
// Enter and Esc
document.addEventListener("keydown", Component.keydown);
}
// Container
container = document.createElement('div');
container.className = 'jdropdown-container';
// Dropdown content
content = document.createElement('div');
content.className = 'jdropdown-content';
// Close button
closeButton = document.createElement('div');
closeButton.className = 'jdropdown-close';
closeButton.textContent = 'Done';
// Reset button
resetButton = document.createElement('div');
resetButton.className = 'jdropdown-reset';
resetButton.textContent = 'x';
resetButton.onclick = function () {
obj.reset();
obj.close();
}
// Create backdrop
backdrop = document.createElement('div');
backdrop.className = 'jdropdown-backdrop';
// Append elements
containerHeader.appendChild(obj.header);
container.appendChild(content);
el.appendChild(containerHeader);
el.appendChild(container);
el.appendChild(backdrop);
// Set the otiptions
obj.setOptions(options);
if ('ontouchsend' in document.documentElement === true) {
el.addEventListener('touchsend', Component.mouseup);
} else {
el.addEventListener('mouseup', Component.mouseup);
}
// Lazyloading
if (obj.options.lazyLoading == true) {
LazyLoading(content, {
loadUp: obj.loadUp,
loadDown: obj.loadDown,
});
}
content.onwheel = function (e) {
e.stopPropagation();
}
// Change method
el.change = obj.setValue;
// Global generic value handler
el.val = function (val) {
if (val === undefined) {
return obj.getValue(obj.options.multiple ? true : false);
} else {
obj.setValue(val);
}
}
// Keep object available from the node
el.dropdown = obj;
}
/**
* Get the current remote source of data URL
*/
obj.getUrl = function () {
return obj.options.url;
}
/**
* Set the new data from a remote source
* @param {string} url - url from the remote source
* @param {function} callback - callback when the data is loaded
*/
obj.setUrl = function (url, callback) {
obj.options.url = url;
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
success: function (data) {
obj.setData(data);
// Callback
if (typeof (callback) == 'function') {
callback(obj);
}
}
});
}
/**
* Set ID for one item
*/
obj.setId = function (item, v) {
// Legacy purposes
if (!obj.options.format) {
var property = 'value';
} else {
var property = 'id';
}
if (typeof (item) == 'object') {
item[property] = v;
} else {
obj.items[item].data[property] = v;
}
}
const add = function(title, id) {
if (! title) {
let current = obj.options.autocomplete == true ? obj.header.value : '';
title = prompt(dictionary.translate('Add A New Option'), current);
if (! title) {
return false;
}
}
// Id
if (! id) {
id = helpers.guid();
}
// Create new item
if (!obj.options.format) {
var item = {
value: id,
text: title,
}
} else {
var item = {
id: id,
name: title,
}
}
// Callback
if (typeof (obj.options.onbeforeinsert) == 'function') {
let ret = obj.options.onbeforeinsert(obj, item);
if (ret === false) {
return false;
} else if (ret) {
item = ret;
}
}
// Add item to the main list
obj.options.data.push(item);
// Create DOM
var newItem = obj.createItem(item);
// Append DOM to the list
content.appendChild(newItem.element);
// Callback
if (typeof (obj.options.oninsert) == 'function') {
obj.options.oninsert(obj, item, newItem);
}
// Show content
if (content.style.display == 'none') {
content.style.display = '';
}
// Search?
if (obj.results) {
obj.results.push(newItem);
}
return item;
}
/**
* Add a new item
* @param {string} title - title of the new item
* @param {string} id - value/id of the new item
*/
obj.add = function (title, id) {
if (typeof (obj.options.prompt) == 'function') {
return obj.options.prompt.call(obj, add);
}
return add(title, id);
}
/**
* Create a new item
*/
obj.createItem = function (data, group, groupName) {
// Keep the correct source of data
if (!obj.options.format) {
if (!data.value && data.id !== undefined) {
data.value = data.id;
//delete data.id;
}
if (!data.text && data.name !== undefined) {
data.text = data.name;
//delete data.name;
}
} else {
if (!data.id && data.value !== undefined) {
data.id = data.value;
//delete data.value;
}
if (!data.name && data.text !== undefined) {
data.name = data.text
//delete data.text;
}
}
// Create item
var item = {};
item.element = document.createElement('div');
item.element.className = 'jdropdown-item';
item.element.indexValue = obj.items.length;
item.data = data;
// Groupd DOM
if (group) {
item.group = group;
}
// Id
if (data.id) {
item.element.setAttribute('id', data.id);
}
// Disabled
if (data.disabled == true) {
item.element.setAttribute('data-disabled', true);
}
// Tooltip
if (data.tooltip) {
item.element.setAttribute('title', data.tooltip);
}
// Image
if (data.image) {
var image = document.createElement('img');
image.className = 'jdropdown-image';
image.src = data.image;
if (!data.title) {
image.classList.add('jdropdown-image-small');
}
item.element.appendChild(image);
} else if (data.icon) {
var icon = document.createElement('span');
icon.className = "jdropdown-icon material-icons";
icon.innerText = data.icon;
if (!data.title) {
icon.classList.add('jdropdown-icon-small');
}
if (data.color) {
icon.style.color = data.color;
}
item.element.appendChild(icon);
} else if (data.color) {
var color = document.createElement('div');
color.className = 'jdropdown-color';
color.style.backgroundColor = data.color;
item.element.appendChild(color);
}
// Set content
if (!obj.options.format) {
var text = data.text;
} else {
var text = data.name;
}
var node = document.createElement('div');
node.className = 'jdropdown-description';
node.textContent = text || ' ';
// Title
if (data.title) {
var title = document.createElement('div');
title.className = 'jdropdown-title';
title.innerText = data.title;
node.appendChild(title);
}
// Set content
if (!obj.options.format) {
var val = data.value;
} else {
var val = data.id;
}
// Value
if (obj.value[val]) {
item.element.classList.add('jdropdown-selected');
item.selected = true;
}
// Keep DOM accessible
obj.items.push(item);
// Add node to item
item.element.appendChild(node);
return item;
}
obj.appendData = function (data) {
// Create elements
if (data.length) {
// Helpers
var items = [];
var groups = [];
// Prepare data
for (var i = 0; i < data.length; i++) {
// Process groups
if (data[i].group) {
if (!groups[data[i].group]) {
groups[data[i].group] = [];
}
groups[data[i].group].push(i);
} else {
items.push(i);
}
}
// Number of items counter
var counter = 0;
// Groups
var groupNames = Object.keys(groups);
// Append groups in case exists
if (groupNames.length > 0) {
for (var i = 0; i < groupNames.length; i++) {
// Group container
var group = document.createElement('div');
group.className = 'jdropdown-group';
// Group name
var groupName = document.createElement('div');
groupName.className = 'jdropdown-group-name';
groupName.textContent = groupNames[i];
// Group arrow
var groupArrow = document.createElement('i');
groupArrow.className = 'jdropdown-group-arrow jdropdown-group-arrow-down';
groupName.appendChild(groupArrow);
// Group items
var groupContent = document.createElement('div');
groupContent.className = 'jdropdown-group-items';
for (var j = 0; j < groups[groupNames[i]].length; j++) {
var item = obj.createItem(data[groups[groupNames[i]][j]], group, groupNames[i]);
if (obj.options.lazyLoading == false || counter < 200) {
groupContent.appendChild(item.element);
counter++;
}
}
// Group itens
group.appendChild(groupName);
group.appendChild(groupContent);
// Keep group DOM
obj.groups.push(group);
// Only add to the screen if children on the group
if (groupContent.children.length > 0) {
// Add DOM to the content
content.appendChild(group);
}
}
}
if (items.length) {
for (var i = 0; i < items.length; i++) {
var item = obj.createItem(data[items[i]]);
if (obj.options.lazyLoading == false || counter < 200) {
content.appendChild(item.element);
counter++;
}
}
}
}
}
obj.setData = function (data) {
// Reset current value
resetValue();
// Make sure the content container is blank
content.textContent = '';
// Reset
obj.header.value = '';
// Reset items and values
obj.items = [];
// Prepare data
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
// Compatibility
if (typeof (data[i]) != 'object') {
// Correct format
if (!obj.options.format) {
data[i] = {
value: data[i],
text: data[i]
}
} else {
data[i] = {
id: data[i],
name: data[i]
}
}
}
}
// Append data
obj.appendData(data);
// Update data
obj.options.data = data;
} else {
// Update data
obj.options.data = [];
}
obj.close();
}
obj.getData = function () {
return obj.options.data;
}
/**
* Get position of the item
*/
obj.getPosition = function (val) {
for (var i = 0; i < obj.items.length; i++) {
if (Value(i) == val) {
return i;
}
}
return false;
}
/**
* Get dropdown current text
*/
obj.getText = function (asArray) {
// Get value
var v = getText();
// Return value
if (asArray) {
return v;
} else {
return v.join('; ');
}
}
/**
* Get dropdown current value
*/
obj.getValue = function (asArray) {
// Get value
var v = getValue();
// Return value
if (asArray) {
return v;
} else {
return v.join(';');
}
}
/**
* Change event
*/
var change = function (oldValue) {
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof (el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
// Events
if (typeof (obj.options.onchange) == 'function') {
obj.options.onchange(el, obj, oldValue, obj.options.value);
}
}
/**
* Set value
*/
obj.setValue = function (newValue) {
// Current value
var oldValue = obj.getValue();
// New value
if (Array.isArray(newValue)) {
newValue = newValue.join(';')
}
if (oldValue !== newValue) {
// Set value
applyValue(newValue);
// Change
change(oldValue);
}
}
obj.resetSelected = function () {
obj.setValue(null);
}
obj.selectIndex = function (index, force) {
// Make sure is a number
var index = parseInt(index);
// Only select those existing elements
if (obj.items && obj.items[index] && (force === true || obj.items[index].data.disabled !== true)) {
// Reset cursor to a new position
obj.setCursor(index, false);
// Behaviour
if (!obj.options.multiple) {
// Update value
if (obj.items[index].selected) {
if (obj.options.allowEmpty !== false) {
obj.setValue(null);
}
} else {
obj.setValue(Value(index));
}
// Close component
obj.close();
} else {
// Old value
var oldValue = obj.options.value;
// Toggle option
if (obj.items[index].selected) {
obj.items[index].element.classList.remove('jdropdown-selected');
obj.items[index].selected = false;
delete obj.value[Value(index)];
} else {
// Select element
obj.items[index].element.classList.add('jdropdown-selected');
obj.items[index].selected = true;
// Set value
obj.value[Value(index)] = Text(index);
}
// Global value
obj.options.value = Object.keys(obj.value).join(';');
// Update labels for multiple dropdown
if (obj.options.autocomplete == false) {
obj.header.value = getText().join('; ');
}
// Events
change(oldValue);
}
}
}
obj.selectItem = function (item) {
obj.selectIndex(item.indexValue);
}
var exists = function (k, result) {
for (var j = 0; j < result.length; j++) {
if (!obj.options.format) {
if (result[j].value == k) {
return true;
}
} else {
if (result[j].id == k) {
return true;
}
}
}
return false;
}
obj.find = function (str) {
if (obj.search == str.trim()) {
return false;
}
// Search term
obj.search = str;
// Reset index
obj.setCursor();
// Remove nodes from all groups
if (obj.groups.length) {
for (var i = 0; i < obj.groups.length; i++) {
obj.groups[i].lastChild.textContent = '';
}
}
// Remove all nodes
content.textContent = '';
// Remove current items in the remote search
if (obj.options.remoteSearch == true) {
// Reset results
obj.results = null;
// URL
var url = obj.options.url;
// Ajax call
let o = {
url: url,
method: 'GET',
data: { q: str },
dataType: 'json',
success: function (result) {
// Reset items
obj.items = [];
// Add the current selected items to the results in case they are not there
var current = Object.keys(obj.value);
if (current.length) {
for (var i = 0; i < current.length; i++) {
if (!exists(current[i], result)) {
if (!obj.options.format) {
result.unshift({value: current[i], text: obj.value[current[i]]});
} else {
result.unshift({id: current[i], name: obj.value[current[i]]});
}
}
}
}
// Append data
obj.appendData(result);
// Show or hide results
if (!result.length) {
content.style.display = 'none';
} else {
content.style.display = '';
}
if (typeof(obj.options.onsearch) === 'function') {
obj.options.onsearch(obj, result);
}
}
}
if (typeof(obj.options.onbeforesearch) === 'function') {
let ret = obj.options.onbeforesearch(obj, o);
if (ret === false) {
return;
} else if (typeof(ret) === 'object') {
o = ret;
}
}
// Remote search
ajax(o);
} else {
// Search terms
str = new RegExp(str, 'gi');
// Reset search
var results = [];
// Append options
for (var i = 0; i < obj.items.length; i++) {
// Item label
var label = Text(i);
// Item title
var title = obj.items[i].data.title || '';
// Group name
var groupName = obj.items[i].data.group || '';
// Synonym
var synonym = obj.items[i].data.synonym || '';
if (synonym) {
synonym = synonym.join(' ');
}
if (str == null || obj.items[i].selected == true || label.toString().match(str) || title.match(str) || groupName.match(str) || synonym.match(str)) {
results.push(obj.items[i]);
}
}
if (!results.length) {
content.style.display = 'none';
// Results
obj.results = null;
} else {
content.style.display = '';
// Results
obj.results = results;
// Show 200 items at once
var number = results.length || 0;
// Lazyloading
if (obj.options.lazyLoading == true && number > 200) {
number = 200;
}
for (var i = 0; i < number; i++) {
if (obj.results[i].group) {
if (!obj.results[i].group.parentNode) {
content.appendChild(obj.results[i].group);
}
obj.results[i].group.lastChild.appendChild(obj.results[i].element);
} else {
content.appendChild(obj.results[i].element);
}
}
}
}
// Auto focus
if (obj.options.autofocus == true) {
obj.first();
}
}
obj.open = function () {
// Focus
if (!el.classList.contains('jdropdown-focus')) {
// Current dropdown
Component.current = obj;
// Start tracking
tracking(obj, true);
// Add focus
el.classList.add('jdropdown-focus');
// Animation
if (helpers.getWindowWidth() < 800) {
if (obj.options.type == null || obj.options.type == 'picker') {
animation.slideBottom(container, 1);
}
}
// Filter
if (obj.options.autocomplete == true) {
obj.header.value = obj.search;
obj.header.focus();
}
// Set cursor for the first or first selected element
var k = getValue();
if (k[0]) {
var cursor = obj.getPosition(k[0]);
if (cursor !== false) {
obj.setCursor(cursor);
}
}
// Container Size
if (!obj.options.type || obj.options.type == 'default') {
var rect = el.getBoundingClientRect();
var rectContainer = container.getBoundingClientRect();
if (obj.options.position) {
container.style.position = 'fixed';
if (window.innerHeight < rect.bottom + rectContainer.height) {
container.style.top = '';
container.style.bottom = (window.innerHeight - rect.top) + 1 + 'px';
} else {
container.style.top = rect.bottom + 'px';
container.style.bottom = '';
}
container.style.left = rect.left + 'px';
} else {
if (window.innerHeight < rect.bottom + rectContainer.height) {
container.style.top = '';
container.style.bottom = rect.height + 1 + 'px';
} else {
container.style.top = '';
container.style.bottom = '';
}
}
container.style.minWidth = rect.width + 'px';
if (obj.options.maxWidth) {
container.style.maxWidth = obj.options.maxWidth;
}
if (!obj.items.length && obj.options.autocomplete == true) {
content.style.display = 'none';
} else {
content.style.display = '';
}
}
}
// Events
if (typeof (obj.options.onopen) == 'function') {
obj.options.onopen(el);
}
}
obj.close = function (ignoreEvents) {
if (el.classList.contains('jdropdown-focus')) {
// Update labels
obj.header.value = obj.getText();
// Remove cursor
obj.setCursor();
// Events
if (!ignoreEvents && typeof (obj.options.onclose) == 'function') {
obj.options.onclose(el);
}
// Blur
if (obj.header.blur) {
obj.header.blur();
}
// Remove focus
el.classList.remove('jdropdown-focus');
// Start tracking
tracking(obj, false);
// Current dropdown
Component.current = null;
}
return obj.getValue();
}
/**
* Set cursor
*/
obj.setCursor = function (index, setPosition) {
// Remove current cursor
if (obj.currentIndex != null) {
// Remove visual cursor
if (obj.items && obj.items[obj.currentIndex]) {
obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor');
}
}
if (index == undefined) {
obj.currentIndex = null;
} else {
index = parseInt(index);
// Cursor only for visible items
if (obj.items[index].element.parentNode) {
obj.items[index].element.classList.add('jdropdown-cursor');
obj.currentIndex = index;
// Update scroll to the cursor element
if (setPosition !== false && obj.items[obj.currentIndex].element) {
var container = content.scrollTop;
var element = obj.items[obj.currentIndex].element;
content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95;
}
}
}
}
// Compatibility
obj.resetCursor = obj.setCursor;
obj.updateCursor = obj.setCursor;
/**
* Reset cursor and selected items
*/
obj.reset = function () {
// Reset cursor
obj.setCursor();
// Reset selected
obj.setValue(null);
}
/**
* First available item
*/
obj.first = function () {
if (obj.options.lazyLoading === true) {
obj.loadFirst();
}
var items = content.querySelectorAll('.jdropdown-item');
if (items.length) {
var newIndex = items[0].indexValue;
obj.setCursor(newIndex);
}
}
/**
* Last available item
*/
obj.last = function () {
if (obj.options.lazyLoading === true) {
obj.loadLast();
}
var items = content.querySelectorAll('.jdropdown-item');
if (items.length) {
var newIndex = items[items.length - 1].indexValue;
obj.setCursor(newIndex);
}
}
obj.next = function (letter) {
var newIndex = null;
if (letter) {
if (letter.length == 1) {
// Current index
var current = obj.currentIndex || -1;
// Letter
letter = letter.toLowerCase();
var e = null;
var l = null;
var items = content.querySelectorAll('.jdropdown-item');
if (items.length) {
for (var i = 0; i < items.length; i++) {
if (items[i].indexValue > current) {
if (e = obj.items[items[i].indexValue]) {
if (l = e.element.innerText[0]) {
l = l.toLowerCase();
if (letter == l) {
newIndex = items[i].indexValue;
break;
}
}
}
}
}
obj.setCursor(newIndex);
}
}
} else {
if (obj.currentIndex == undefined || obj.currentIndex == null) {
obj.first();
} else {
var element = obj.items[obj.currentIndex].element;
var next = element.nextElementSibling;
if (next) {
if (next.classList.contains('jdropdown-group')) {
next = next.lastChild.firstChild;
}
newIndex = next.indexValue;
} else {
if (element.parentNode.classList.contains('jdropdown-group-items')) {
if (next = element.parentNode.parentNode.nextElementSibling) {
if (next.classList.contains('jdropdown-group')) {
next = next.lastChild.firstChild;
} else if (next.classList.contains('jdropdown-item')) {
newIndex = next.indexValue;
} else {
next = null;
}
}
if (next) {
newIndex = next.indexValue;
}
}
}
if (newIndex !== null) {
obj.setCursor(newIndex);
}
}
}
}
obj.prev = function () {
var newIndex = null;
if (obj.currentIndex === null) {
obj.first();
} else {
var element = obj.items[obj.currentIndex].element;
var prev = element.previousElementSibling;
if (prev) {
if (prev.classList.contains('jdropdown-group')) {
prev = prev.lastChild.lastChild;
}
newIndex = prev.indexValue;
} else {
if (element.parentNode.classList.contains('jdropdown-group-items')) {
if (prev = element.parentNode.parentNode.previousElementSibling) {
if (prev.classList.contains('jdropdown-group')) {
prev = prev.lastChild.lastChild;
} else if (prev.classList.contains('jdropdown-item')) {
newIndex = prev.indexValue;
} else {
prev = null
}
}
if (prev) {
newIndex = prev.indexValue;
}
}
}
}
if (newIndex !== null) {
obj.setCursor(newIndex);
}
}
obj.loadFirst = function () {
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
// Show 200 items at once
var number = results.length || 0;
// Lazyloading
if (obj.options.lazyLoading == true && number > 200) {
number = 200;
}
// Reset container
content.textContent = '';
// First 200 items
for (var i = 0; i < number; i++) {
if (results[i].group) {
if (!results[i].group.parentNode) {
content.appendChild(results[i].group);
}
results[i].group.lastChild.appendChild(results[i].element);
} else {
content.appendChild(results[i].element);
}
}
// Scroll go to the begin
content.scrollTop = 0;
}
obj.loadLast = function () {
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
// Show first page
var number = results.length;
// Max 200 items
if (number > 200) {
number = number - 200;
// Reset container
content.textContent = '';
// First 200 items
for (var i = number; i < results.length; i++) {
if (results[i].group) {
if (!results[i].group.parentNode) {
content.appendChild(results[i].group);
}
results[i].group.lastChild.appendChild(results[i].element);
} else {
content.appendChild(results[i].element);
}
}
// Scroll go to the begin
content.scrollTop = content.scrollHeight;
}
}
obj.loadUp = function () {
var test = false;
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
var items = content.querySelectorAll('.jdropdown-item');
var fistItem = items[0].indexValue;
fistItem = obj.items[fistItem];
var index = results.indexOf(fistItem) - 1;
if (index > 0) {
var number = 0;
while (index > 0 && results[index] && number < 200) {
if (results[index].group) {
if (!results[index].group.parentNode) {
content.insertBefore(results[index].group, content.firstChild);
}
results[index].group.lastChild.insertBefore(results[index].element, results[index].group.lastChild.firstChild);
} else {
content.insertBefore(results[index].element, content.firstChild);
}
index--;
number++;
}
// New item added
test = true;
}
return test;
}
obj.loadDown = function () {
var test = false;
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
var items = content.querySelectorAll('.jdropdown-item');
var lastItem = items[items.length - 1].indexValue;
lastItem = obj.items[lastItem];
var index = results.indexOf(lastItem) + 1;
if (index < results.length) {
var number = 0;
while (index < results.length && results[index] && number < 200) {
if (results[index].group) {
if (!results[index].group.parentNode) {
content.appendChild(results[index].group);
}
results[index].group.lastChild.appendChild(results[index].element);
} else {
content.appendChild(results[index].element);
}
index++;
number++;
}
// New item added
test = true;
}
return test;
}
init();
return obj;
});
Component.keydown = function (e) {
var dropdown = null;
if (dropdown = Component.current) {
if (e.which == 13 || e.which == 9) { // enter or tab
if (dropdown.header.value && dropdown.currentIndex == null && dropdown.options.newOptions) {
// if they typed something in, but it matched nothing, and newOptions are allowed, start that flow
dropdown.add();
} else {
// Quick Select/Filter
if (dropdown.currentIndex == null && dropdown.options.autocomplete == true && dropdown.header.value != "") {
dropdown.find(dropdown.header.value);
}
dropdown.selectIndex(dropdown.currentIndex);
}
} else if (e.which == 38) { // up arrow
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex > 0) {
dropdown.prev();
}
e.preventDefault();
} else if (e.which == 40) { // down arrow
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex + 1 < dropdown.items.length) {
dropdown.next();
}
e.preventDefault();
} else if (e.which == 36) {
dropdown.first();
if (!e.target.classList.contains('jdropdown-header')) {
e.preventDefault();
}
} else if (e.which == 35) {
dropdown.last();
if (!e.target.classList.contains('jdropdown-header')) {
e.preventDefault();
}
} else if (e.which == 27) {
dropdown.close();
} else if (e.which == 33) { // page up
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex > 0) {
for (var i = 0; i < 7; i++) {
dropdown.prev()
}
}
e.preventDefault();
} else if (e.which == 34) { // page down
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex + 1 < dropdown.items.length) {
for (var i = 0; i < 7; i++) {
dropdown.next()
}
}
e.preventDefault();
}
}
}
Component.mouseup = function (e) {
var element = helpers.findElement(e.target, 'jdropdown');
if (element) {
var dropdown = element.dropdown;
if (e.target.classList.contains('jdropdown-header')) {
if (element.classList.contains('jdropdown-focus') && element.classList.contains('jdropdown-default')) {
var rect = element.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 30) {
if (e.target.classList.contains('jdropdown-add')) {
dropdown.add();
} else {
dropdown.close();
}
} else {
if (dropdown.options.autocomplete == false) {
dropdown.close();
}
}
} else {
dropdown.open();
}
} else if (e.target.classList.contains('jdropdown-group-name')) {
var items = e.target.nextSibling.children;
if (e.target.nextSibling.style.display != 'none') {
for (var i = 0; i < items.length; i++) {
if (items[i].style.display != 'none') {
dropdown.selectItem(items[i]);
}
}
}
} else if (e.target.classList.contains('jdropdown-group-arrow')) {
if (e.target.classList.contains('jdropdown-group-arrow-down')) {
e.target.classList.remove('jdropdown-group-arrow-down');
e.target.classList.add('jdropdown-group-arrow-up');
e.target.parentNode.nextSibling.style.display = 'none';
} else {
e.target.classList.remove('jdropdown-group-arrow-up');
e.target.classList.add('jdropdown-group-arrow-down');
e.target.parentNode.nextSibling.style.display = '';
}
} else if (e.target.classList.contains('jdropdown-item')) {
dropdown.selectItem(e.target);
} else if (e.target.classList.contains('jdropdown-image')) {
dropdown.selectItem(e.target.parentNode);
} else if (e.target.classList.contains('jdropdown-description')) {
dropdown.selectItem(e.target.parentNode);
} else if (e.target.classList.contains('jdropdown-title')) {
dropdown.selectItem(e.target.parentNode.parentNode);
} else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) {
dropdown.close();
}
}
}
Component.extractFromDom = function (el, options) {
// Keep reference
var select = el;
if (!options) {
options = {};
}
// Prepare configuration
if (el.getAttribute('multiple') && (!options || options.multiple == undefined)) {
options.multiple = true;
}
if (el.getAttribute('placeholder') && (!options || options.placeholder == undefined)) {
options.placeholder = el.getAttribute('placeholder');
}
if (el.getAttribute('data-autocomplete') && (!options || options.autocomplete == undefined)) {
options.autocomplete = true;
}
if (!options || options.width == undefined) {
options.width = el.offsetWidth;
}
if (el.value && (!options || options.value == undefined)) {
options.value = el.value;
}
if (!options || options.data == undefined) {
options.data = [];
for (var j = 0; j < el.children.length; j++) {
if (el.children[j].tagName == 'OPTGROUP') {
for (var i = 0; i < el.children[j].children.length; i++) {
options.data.push({
value: el.children[j].children[i].value,
text: el.children[j].children[i].textContent,
group: el.children[j].getAttribute('label'),
});
}
} else {
options.data.push({
value: el.children[j].value,
text: el.children[j].textContent,
});
}
}
}
if (!options || options.onchange == undefined) {
options.onchange = function (a, b, c, d) {
if (options.multiple == true) {
if (obj.items[b].classList.contains('jdropdown-selected')) {
select.options[b].setAttribute('selected', 'selected');
} else {
select.options[b].removeAttribute('selected');
}
} else {
select.value = d;
}
}
}
// Create DIV
var div = document.createElement('div');
el.parentNode.insertBefore(div, el);
el.style.display = 'none';
el = div;
return {el: el, options: options};
}
return Component;
}
/* harmony default export */ var dropdown = (Dropdown());
;// CONCATENATED MODULE: ./src/plugins/picker.js
function Picker(el, options) {
// Already created, update options
if (el.picker) {
return el.picker.setOptions(options, true);
}
// New instance
var obj = { type: 'picker' };
obj.options = {};
var dropdownHeader = null;
var dropdownContent = null;
/**
* The element passed is a DOM element
*/
var isDOM = function(o) {
return (o instanceof Element || o instanceof HTMLDocument);
}
/**
* Create the content options
*/
var createContent = function() {
dropdownContent.innerHTML = '';
// Create items
var keys = Object.keys(obj.options.data);
// Go though all options
for (var i = 0; i < keys.length; i++) {
// Item
var dropdownItem = document.createElement('div');
dropdownItem.classList.add('jpicker-item');
dropdownItem.setAttribute('role', 'option');
dropdownItem.k = keys[i];
dropdownItem.v = obj.options.data[keys[i]];
// Label
var item = obj.getLabel(keys[i], dropdownItem);
if (isDOM(item)) {
dropdownItem.appendChild(item);
} else {
dropdownItem.innerHTML = item;
}
// Append
dropdownContent.appendChild(dropdownItem);
}
}
/**
* Set or reset the options for the picker
*/
obj.setOptions = function(options, reset) {
// Default configuration
var defaults = {
value: 0,
data: null,
render: null,
onchange: null,
onmouseover: null,
onselect: null,
onopen: null,
onclose: null,
onload: null,
width: null,
header: true,
right: false,
bottom: false,
content: false,
columns: null,
grid: null,
height: null,
}
// Legacy purpose only
if (options && options.options) {
options.data = options.options;
}
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Start using the options
if (obj.options.header === false) {
dropdownHeader.style.display = 'none';
} else {
dropdownHeader.style.display = '';
}
// Width
if (obj.options.width) {
dropdownHeader.style.width = parseInt(obj.options.width) + 'px';
} else {
dropdownHeader.style.width = '';
}
// Height
if (obj.options.height) {
dropdownContent.style.maxHeight = obj.options.height + 'px';
dropdownContent.style.overflow = 'scroll';
} else {
dropdownContent.style.overflow = '';
}
if (obj.options.columns > 0) {
if (! obj.options.grid) {
dropdownContent.classList.add('jpicker-columns');
dropdownContent.style.width = obj.options.width ? obj.options.width : 36 * obj.options.columns + 'px';
} else {
dropdownContent.classList.add('jpicker-grid');
dropdownContent.style.gridTemplateColumns = 'repeat(' + obj.options.grid + ', 1fr)';
}
}
if (isNaN(parseInt(obj.options.value))) {
obj.options.value = 0;
}
// Create list from data
createContent();
// Set value
obj.setValue(obj.options.value);
// Set options all returns the own instance
return obj;
}
obj.getValue = function() {
return obj.options.value;
}
obj.setValue = function(k, e) {
// Set label
obj.setLabel(k);
// Update value
obj.options.value = String(k);
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
if (dropdownContent.children[k] && dropdownContent.children[k].getAttribute('type') !== 'generic') {
obj.close();
}
// Call method
if (e) {
if (typeof (obj.options.onchange) == 'function') {
var v = obj.options.data[k];
obj.options.onchange(el, obj, v, v, k, e);
}
}
}
obj.getLabel = function(v, item) {
var label = obj.options.data[v] || null;
if (typeof(obj.options.render) == 'function') {
label = obj.options.render(label, item);
}
return label;
}
obj.setLabel = function(v) {
var item;
if (obj.options.content) {
item = document.createElement('i');
item.textContent = obj.options.content;
item.classList.add('material-icons');
} else {
item = obj.getLabel(v, null);
}
// Label
if (isDOM(item)) {
dropdownHeader.textContent = '';
dropdownHeader.appendChild(item);
} else {
dropdownHeader.innerHTML = item;
}
}
obj.open = function() {
if (! el.classList.contains('jpicker-focus')) {
// Start tracking the element
tracking(obj, true);
// Open picker
el.classList.add('jpicker-focus');
el.focus();
var top = 0;
var left = 0;
dropdownContent.style.marginLeft = '';
var rectHeader = dropdownHeader.getBoundingClientRect();
var rectContent = dropdownContent.getBoundingClientRect();
if (window.innerHeight < rectHeader.bottom + rectContent.height || obj.options.bottom) {
top = -1 * (rectContent.height + 4);
} else {
top = rectHeader.height + 4;
}
if (obj.options.right === true) {
left = -1 * rectContent.width + rectHeader.width;
}
if (rectContent.left + left < 0) {
left = left + rectContent.left + 10;
}
if (rectContent.left + rectContent.width > window.innerWidth) {
left = -1 * (10 + rectContent.left + rectContent.width - window.innerWidth);
}
dropdownContent.style.marginTop = parseInt(top) + 'px';
dropdownContent.style.marginLeft = parseInt(left) + 'px';
//dropdownContent.style.marginTop
if (typeof obj.options.onopen == 'function') {
obj.options.onopen(el, obj);
}
}
}
obj.close = function() {
if (el.classList.contains('jpicker-focus')) {
el.classList.remove('jpicker-focus');
// Start tracking the element
tracking(obj, false);
if (typeof obj.options.onclose == 'function') {
obj.options.onclose(el, obj);
}
}
}
/**
* Destroy the picker instance and release all resources
*/
obj.destroy = function() {
// Close if open (removes from tracking)
obj.close();
// Remove event listeners
el.onmousedown = null;
if (dropdownContent) {
dropdownContent.onclick = null;
}
// Remove created DOM elements
if (dropdownHeader && dropdownHeader.parentNode) {
dropdownHeader.parentNode.removeChild(dropdownHeader);
}
if (dropdownContent && dropdownContent.parentNode) {
dropdownContent.parentNode.removeChild(dropdownContent);
}
// Remove classes and attributes from el
el.classList.remove('jpicker');
el.classList.remove('jpicker-focus');
el.removeAttribute('role');
el.removeAttribute('aria-haspopup');
el.removeAttribute('aria-expanded');
el.removeAttribute('aria-controls');
el.removeAttribute('tabindex');
// Remove instance properties from el
delete el.picker;
delete el.value;
delete el.change;
delete el.val;
// Clear options callbacks to release closures
if (obj.options) {
obj.options.onchange = null;
obj.options.onclose = null;
obj.options.onopen = null;
obj.options.onload = null;
obj.options.onselect = null;
obj.options.onmouseover = null;
obj.options.render = null;
}
// Clear references
dropdownHeader = null;
dropdownContent = null;
}
/**
* Create floating picker
*/
var init = function() {
let id = helpers.guid();
// Class
el.classList.add('jpicker');
el.setAttribute('role', 'combobox');
el.setAttribute('aria-haspopup', 'listbox');
el.setAttribute('aria-expanded', 'false');
el.setAttribute('aria-controls', id);
el.setAttribute('tabindex', '0');
el.onmousedown = function(e) {
if (! el.classList.contains('jpicker-focus')) {
obj.open();
}
}
// Dropdown Header
dropdownHeader = document.createElement('div');
dropdownHeader.classList.add('jpicker-header');
// Dropdown content
dropdownContent = document.createElement('div');
dropdownContent.setAttribute('id', id);
dropdownContent.setAttribute('role', 'listbox');
dropdownContent.classList.add('jpicker-content');
dropdownContent.onclick = function(e) {
var item = helpers.findElement(e.target, 'jpicker-item');
if (item) {
if (item.parentNode === dropdownContent) {
// Update label
obj.setValue(item.k, e);
}
}
}
// Append content and header
el.appendChild(dropdownHeader);
el.appendChild(dropdownContent);
// Default value
el.value = options.value || 0;
// Set options
obj.setOptions(options);
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj);
}
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Reference
el.picker = obj;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/toolbar.js
function Toolbar(el, options) {
// New instance
var obj = { type:'toolbar' };
obj.options = {};
// Track internal components for cleanup
var internalComponents = [];
// Event handler references for cleanup
var elClickHandler = null;
var windowResizeHandler = null;
// Default configuration
var defaults = {
app: null,
container: false,
badge: false,
title: false,
responsive: false,
maxWidth: null,
bottom: true,
items: [],
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
if (! el && options.app && options.app.el) {
el = document.createElement('div');
options.app.el.appendChild(el);
}
// Arrow
var toolbarArrow = document.createElement('div');
toolbarArrow.classList.add('jtoolbar-item');
toolbarArrow.classList.add('jtoolbar-arrow');
let arrow = document.createElement('i');
arrow.classList.add('material-icons');
arrow.textContent = 'more_vert';
toolbarArrow.appendChild(arrow);
var toolbarFloating = document.createElement('div');
toolbarFloating.classList.add('jtoolbar-floating');
toolbarArrow.appendChild(toolbarFloating);
obj.selectItem = function(element) {
var elements = toolbarContent.children;
for (var i = 0; i < elements.length; i++) {
if (element != elements[i]) {
elements[i].classList.remove('jtoolbar-selected');
}
}
element.classList.add('jtoolbar-selected');
}
obj.hide = function() {
animation.slideBottom(el, 0, function() {
el.style.display = 'none';
});
}
obj.show = function() {
el.style.display = '';
animation.slideBottom(el, 1);
}
obj.get = function() {
return el;
}
obj.setBadge = function(index, value) {
toolbarContent.children[index].children[1].firstChild.innerHTML = value;
}
obj.destroy = function() {
// Close if open (removes from tracking)
obj.close();
// Destroy all internal components (pickers, etc.)
for (var i = 0; i < internalComponents.length; i++) {
var comp = internalComponents[i];
if (comp) {
// First destroy any nested components created in onload (stored on the picker instance)
if (comp.components && comp.components.length) {
for (var j = 0; j < comp.components.length; j++) {
if (comp.components[j] && typeof comp.components[j].destroy === 'function') {
comp.components[j].destroy();
}
}
comp.components = null;
}
// Then destroy the picker/component itself
if (typeof comp.destroy === 'function') {
comp.destroy();
}
}
}
internalComponents = [];
// Clear onclick handlers from DOM elements (releases bound function references)
if (toolbarContent) {
var items = toolbarContent.querySelectorAll('.jtoolbar-item');
for (var i = 0; i < items.length; i++) {
items[i].onclick = null;
items[i].updateState = null;
}
}
if (toolbarFloating) {
var items = toolbarFloating.querySelectorAll('.jtoolbar-item');
for (var i = 0; i < items.length; i++) {
items[i].onclick = null;
items[i].updateState = null;
}
}
// Clear options items callbacks to release closures
if (obj.options && obj.options.items) {
for (var i = 0; i < obj.options.items.length; i++) {
var item = obj.options.items[i];
// Clear all callback references
item.onclick = null;
item.onchange = null;
item.onload = null;
item.render = null;
item.updateState = null;
item.onopen = null;
item.onclose = null;
}
obj.options.items = null;
}
// Remove event listeners
if (elClickHandler) {
el.removeEventListener('click', elClickHandler);
elClickHandler = null;
}
if (windowResizeHandler) {
window.removeEventListener('resize', windowResizeHandler);
windowResizeHandler = null;
}
// Clear DOM
el.innerHTML = '';
// Clear references
toolbarContent = null;
toolbarFloating = null;
toolbarArrow = null;
// Remove classes
el.classList.remove('jtoolbar');
el.classList.remove('jtoolbar-container');
el.classList.remove('jtoolbar-mobile');
el.classList.remove('jtoolbar-disabled');
// Remove instance reference
delete el.toolbar;
}
obj.update = function(a, b) {
for (var i = 0; i < toolbarContent.children.length; i++) {
// Toolbar element
var toolbarItem = toolbarContent.children[i];
// State management
if (typeof(toolbarItem.updateState) == 'function') {
toolbarItem.updateState(el, obj, toolbarItem, a, b);
}
}
for (var i = 0; i < toolbarFloating.children.length; i++) {
// Toolbar element
var toolbarItem = toolbarFloating.children[i];
// State management
if (typeof(toolbarItem.updateState) == 'function') {
toolbarItem.updateState(el, obj, toolbarItem, a, b);
}
}
}
obj.create = function(items) {
// Reset anything in the toolbar
toolbarContent.innerHTML = '';
// Create elements in the toolbar
for (var i = 0; i < items.length; i++) {
var toolbarItem = document.createElement('div');
toolbarItem.classList.add('jtoolbar-item');
if (items[i].width) {
toolbarItem.style.width = parseInt(items[i].width) + 'px';
}
if (items[i].k) {
toolbarItem.k = items[i].k;
}
if (items[i].tooltip) {
toolbarItem.setAttribute('title', items[i].tooltip);
toolbarItem.setAttribute('aria-label', items[i].tooltip);
}
// Id
if (items[i].id) {
toolbarItem.setAttribute('id', items[i].id);
}
// Selected
if (items[i].updateState) {
toolbarItem.updateState = items[i].updateState;
}
if (items[i].active) {
toolbarItem.classList.add('jtoolbar-active');
}
if (items[i].disabled) {
toolbarItem.classList.add('jtoolbar-disabled');
}
if (items[i].type == 'select' || items[i].type == 'dropdown') {
var picker = Picker(toolbarItem, items[i]);
internalComponents.push(picker);
} else if (items[i].type == 'divisor') {
toolbarItem.classList.add('jtoolbar-divisor');
} else if (items[i].type == 'label') {
toolbarItem.classList.add('jtoolbar-label');
toolbarItem.innerHTML = items[i].content;
} else {
// Material icons
var toolbarIcon = document.createElement('i');
if (typeof(items[i].class) === 'undefined') {
toolbarIcon.classList.add('material-icons');
} else {
var c = items[i].class.split(' ');
for (var j = 0; j < c.length; j++) {
toolbarIcon.classList.add(c[j]);
}
}
toolbarIcon.innerHTML = items[i].content ? items[i].content : '';
toolbarItem.setAttribute('role', 'button');
toolbarItem.appendChild(toolbarIcon);
// Badge options
if (obj.options.badge == true) {
var toolbarBadge = document.createElement('div');
toolbarBadge.classList.add('jbadge');
var toolbarBadgeContent = document.createElement('div');
toolbarBadgeContent.innerHTML = items[i].badge ? items[i].badge : '';
toolbarBadge.appendChild(toolbarBadgeContent);
toolbarItem.appendChild(toolbarBadge);
}
// Title
if (items[i].title) {
if (obj.options.title == true) {
var toolbarTitle = document.createElement('span');
toolbarTitle.innerHTML = items[i].title;
toolbarItem.appendChild(toolbarTitle);
} else {
toolbarItem.setAttribute('title', items[i].title);
}
}
if (obj.options.app && items[i].route) {
// Route
toolbarItem.route = items[i].route;
// Onclick for route
toolbarItem.onclick = function() {
obj.options.app.pages(this.route);
}
// Create pages
obj.options.app.pages(items[i].route, {
toolbarItem: toolbarItem,
closed: true
});
}
// Render
if (typeof(items[i].render) === 'function') {
items[i].render(toolbarItem, items[i]);
}
}
if (items[i].onclick) {
toolbarItem.onclick = items[i].onclick.bind(items[i], el, obj, toolbarItem);
}
toolbarContent.appendChild(toolbarItem);
}
// Fits to the page
setTimeout(function() {
obj.refresh();
}, 0);
}
obj.open = function() {
toolbarArrow.classList.add('jtoolbar-arrow-selected');
var rectElement = el.getBoundingClientRect();
var rect = toolbarFloating.getBoundingClientRect();
if (rect.bottom > window.innerHeight || obj.options.bottom) {
toolbarFloating.style.bottom = '0';
} else {
toolbarFloating.style.removeProperty('bottom');
}
toolbarFloating.style.right = '0';
toolbarArrow.children[1].focus();
// Start tracking
tracking(obj, true);
}
obj.close = function() {
if (toolbarArrow) {
toolbarArrow.classList.remove('jtoolbar-arrow-selected')
}
// End tracking
tracking(obj, false);
}
obj.refresh = function() {
if (obj.options.responsive == true) {
// Width of the c
var rect = el.parentNode.getBoundingClientRect();
if (! obj.options.maxWidth) {
obj.options.maxWidth = rect.width;
}
// Available parent space
var available = parseInt(obj.options.maxWidth);
// Remove arrow
if (toolbarArrow.parentNode) {
toolbarArrow.parentNode.removeChild(toolbarArrow);
}
// Move all items to the toolbar
while (toolbarFloating.firstChild) {
toolbarContent.appendChild(toolbarFloating.firstChild);
}
// Toolbar is larger than the parent, move elements to the floating element
if (available < toolbarContent.offsetWidth) {
// Give space to the floating element
available -= 50;
// Move to the floating option
while (toolbarContent.lastChild && available < toolbarContent.offsetWidth) {
toolbarFloating.insertBefore(toolbarContent.lastChild, toolbarFloating.firstChild);
}
}
// Show arrow
if (toolbarFloating.children.length > 0) {
toolbarContent.appendChild(toolbarArrow);
}
}
}
obj.setReadonly = function(state) {
state = state ? 'add' : 'remove';
el.classList[state]('jtoolbar-disabled');
}
elClickHandler = function(e) {
var element = helpers.findElement(e.target, 'jtoolbar-item');
if (element) {
obj.selectItem(element);
}
if (e.target.classList.contains('jtoolbar-arrow') || e.target.parentNode.classList.contains('jtoolbar-arrow')) {
obj.open();
}
};
el.addEventListener('click', elClickHandler);
windowResizeHandler = function() {
obj.refresh();
};
window.addEventListener('resize', windowResizeHandler);
// Toolbar
el.classList.add('jtoolbar');
// Reset content
el.innerHTML = '';
// Container
if (obj.options.container == true) {
el.classList.add('jtoolbar-container');
}
// Content
var toolbarContent = document.createElement('div');
el.appendChild(toolbarContent);
// Special toolbar for mobile applications
if (obj.options.app) {
el.classList.add('jtoolbar-mobile');
}
// Create toolbar
obj.create(obj.options.items);
// Shortcut
el.toolbar = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/utils/filter.js
// Valid tags (removed iframe for security)
const validTags = [
'html','body','address','span', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'b', 'i', 'blockquote',
'strong', 'em', 'ul', 'ol', 'li', 'a', 'code', 'pre', 'hr', 'br', 'img',
'figure', 'picture', 'figcaption', 'table', 'thead', 'tbody', 'tfoot', 'tr',
'th', 'td', 'caption', 'u', 'del', 'ins', 'sub', 'sup', 'small', 'mark',
'input', 'textarea', 'select', 'option', 'button', 'label', 'fieldset',
'legend', 'audio', 'video', 'abbr', 'cite', 'kbd', 'section', 'article',
'nav', 'aside', 'header', 'footer', 'main', 'details', 'summary', 'svg', 'line', 'source'
];
// Dangerous tags that should be completely removed
const dangerousTags = ['script', 'object', 'embed', 'applet', 'meta', 'base', 'link', 'iframe'];
// Valid properties (added id, class, title, alt for better editor support)
const validProperty = ['width', 'height', 'align', 'border', 'src', 'href', 'tabindex', 'id', 'class', 'title', 'alt'];
// Tags that are allowed to have src attribute
const tagsAllowedSrc = ['img', 'audio', 'video', 'source'];
// Tags that are allowed to have href attribute
const tagsAllowedHref = ['a'];
// Valid CSS attributes
const validStyle = ['color', 'font-weight', 'font-size', 'background', 'background-color', 'margin', 'padding', 'text-align', 'text-decoration'];
// Function to decode HTML entities (prevents bypassing with javascript:)
function decodeHTMLEntities(text) {
if (!text) return '';
const textArea = document.createElement('textarea');
textArea.innerHTML = text;
return textArea.value;
}
// Function to check if a URL scheme is dangerous
function isDangerousURL(url) {
if (!url) return false;
const decoded = decodeHTMLEntities(url).toLowerCase().trim();
// Remove whitespace and null bytes that can be used to bypass filters
const cleaned = decoded.replace(/[\s\0]/g, '');
return cleaned.startsWith('javascript:') ||
cleaned.startsWith('data:text/') ||
cleaned.startsWith('data:application/') ||
cleaned.startsWith('vbscript:') ||
cleaned.includes('<script');
}
// Function to sanitize CSS value
function sanitizeStyleValue(value) {
if (!value) return '';
const decoded = decodeHTMLEntities(value).toLowerCase();
// Block dangerous CSS content
if (decoded.includes('javascript:') ||
decoded.includes('expression(') ||
decoded.includes('behavior:') ||
decoded.includes('-moz-binding') ||
decoded.includes('binding:') ||
decoded.includes('@import') ||
decoded.includes('url(') && (decoded.includes('javascript:') || decoded.includes('data:'))) {
return '';
}
return value;
}
const parse = function(element, img) {
if (!element || !element.tagName) {
return;
}
const tagName = element.tagName.toLowerCase();
// Remove dangerous tags completely
if (dangerousTags.indexOf(tagName) !== -1) {
if (element.parentNode) {
element.parentNode.removeChild(element);
}
return;
}
// Remove elements that are not white-listed
if (validTags.indexOf(tagName) === -1) {
if (element.innerText) {
element.innerHTML = element.innerText;
}
}
// Remove attributes
if (element.attributes && element.attributes.length) {
let style = null;
// Process style attribute
let elementStyle = element.getAttribute('style');
if (elementStyle) {
style = [];
let t = elementStyle.split(';');
for (let j = 0; j < t.length; j++) {
let v = t[j].trim().split(':');
const property = v[0].trim();
if (validStyle.indexOf(property) >= 0) {
let k = v.shift();
v = v.join(':');
// Sanitize the CSS value
const sanitizedValue = sanitizeStyleValue(v);
if (sanitizedValue) {
style.push(k + ':' + sanitizedValue);
}
}
}
}
// Process image
if (tagName === 'img') {
const src = element.getAttribute('src');
if (!src || isDangerousURL(src)) {
if (element.parentNode) {
element.parentNode.removeChild(element);
}
return;
} else {
element.setAttribute('tabindex', '900');
// Check attributes for persistence
img.push(src);
}
}
// Collect all attribute names first
let attr = [];
for (let i = 0; i < element.attributes.length; i++) {
attr.push(element.attributes[i].name);
}
// Process attributes
if (attr.length) {
attr.forEach(function (v) {
const attrName = v.toLowerCase();
// Remove all event handlers (onclick, onerror, onload, etc.)
if (attrName.startsWith('on')) {
element.removeAttribute(v);
return;
}
// Check if attribute is in whitelist
if (validProperty.indexOf(attrName) === -1) {
element.removeAttribute(v);
} else {
// Validate whitelisted attributes
let attrValue = element.getAttribute(v);
// Special handling for src attribute
if (attrName === 'src') {
if (tagsAllowedSrc.indexOf(tagName) === -1) {
// src not allowed on this tag
element.removeAttribute(v);
return;
}
if (isDangerousURL(attrValue)) {
element.removeAttribute(v);
return;
}
}
// Special handling for href attribute
if (attrName === 'href') {
if (tagsAllowedHref.indexOf(tagName) === -1) {
// href not allowed on this tag
element.removeAttribute(v);
return;
}
if (isDangerousURL(attrValue)) {
element.removeAttribute(v);
return;
}
}
// Protection XSS - check for dangerous characters
if (attrValue && attrValue.indexOf('<') !== -1) {
element.setAttribute(v, attrValue.replace(/</g, '<'));
}
}
});
}
element.style = '';
// Add valid style
if (style && style.length) {
element.setAttribute('style', style.join(';'));
}
}
// Parse children recursively
if (element.children.length) {
for (let i = element.children.length; i > 0; i--) {
parse(element.children[i - 1], img);
}
}
}
const filter = function(data, img) {
if (data) {
data = data.replace(new RegExp('<!--(.*?)-->', 'gsi'), '');
}
let parser = new DOMParser();
let d = parser.parseFromString(data, "text/html");
parse(d.body, img);
let div = document.createElement('div');
div.innerHTML = d.body.innerHTML;
return div;
}
/* harmony default export */ var utils_filter = (filter);
;// CONCATENATED MODULE: ./src/plugins/editor.js
function Editor() {
var Component = (function(el, options) {
// New instance
var obj = { type:'editor' };
obj.options = {};
// Default configuration
var defaults = {
// Load data from a remove location
url: null,
// Initial HTML content
value: '',
// Initial snippet
snippet: null,
// Add toolbar
toolbar: true,
toolbarOnTop: false,
// Website parser is to read websites and images from cross domain
remoteParser: null,
// Placeholder
placeholder: null,
// Parse URL
filterPaste: true,
// Accept drop files
dropZone: true,
dropAsSnippet: false,
acceptImages: true,
acceptFiles: false,
maxFileSize: 5000000,
allowImageResize: true,
// Style
maxHeight: null,
height: null,
focus: false,
// Events
onclick: null,
onfocus: null,
onblur: null,
onload: null,
onkeyup: null,
onkeydown: null,
onchange: null,
extensions: null,
type: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Private controllers
var editorTimer = null;
var editorAction = null;
var files = [];
// Keep the reference for the container
obj.el = el;
if (typeof(obj.options.onclick) == 'function') {
el.onclick = function(e) {
obj.options.onclick(el, obj, e);
}
}
// Prepare container
el.classList.add('jeditor-container');
// Snippet
var snippet = document.createElement('div');
snippet.className = 'jsnippet';
snippet.setAttribute('contenteditable', false);
// Toolbar
var toolbar = document.createElement('div');
toolbar.className = 'jeditor-toolbar';
obj.editor = document.createElement('div');
obj.editor.setAttribute('contenteditable', true);
obj.editor.setAttribute('spellcheck', false);
obj.editor.classList.add('jeditor');
// Placeholder
if (obj.options.placeholder) {
obj.editor.setAttribute('data-placeholder', obj.options.placeholder);
}
// Max height
if (obj.options.maxHeight || obj.options.height) {
obj.editor.style.overflowY = 'auto';
if (obj.options.maxHeight) {
obj.editor.style.maxHeight = obj.options.maxHeight;
}
if (obj.options.height) {
obj.editor.style.height = obj.options.height;
}
}
// Set editor initial value
if (obj.options.url) {
ajax({
url: obj.options.url,
dataType: 'html',
success: function(result) {
obj.editor.innerHTML = result;
Component.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
}
})
} else {
if (obj.options.value) {
obj.editor.innerHTML = obj.options.value;
} else {
// Create from existing elements
for (var i = 0; i < el.children.length; i++) {
obj.editor.appendChild(el.children[i]);
}
}
}
// Make sure element is empty
el.innerHTML = '';
/**
* Onchange event controllers
*/
var change = function(e) {
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, obj, e);
}
// Update value
obj.options.value = obj.getData();
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
/**
* Extract images from a HTML string
*/
var extractImageFromHtml = function(html) {
let img = [];
// Create temp element
var div = document.createElement('div');
utils_filter(html, img);
if (img.length) {
for (var i = 0; i < img.length; i++) {
obj.addImage(img[i]);
}
}
}
/**
* Insert node at caret
*/
var insertNodeAtCaret = function(newNode) {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
var selectedText = range.toString();
range.deleteContents();
range.insertNode(newNode);
// move the cursor after element
range.setStartAfter(newNode);
range.setEndAfter(newNode);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
var updateTotalImages = function() {
var o = null;
if (o = snippet.children[0]) {
// Make sure is a grid
if (! o.classList.contains('jslider-grid')) {
o.classList.add('jslider-grid');
}
// Quantify of images
var number = o.children.length;
// Set the configuration of the grid
o.setAttribute('data-number', number > 4 ? 4 : number);
// Total of images inside the grid
if (number > 4) {
o.setAttribute('data-total', number - 4);
} else {
o.removeAttribute('data-total');
}
}
}
/**
* Append image to the snippet
*/
var appendImage = function(image) {
if (! snippet.innerHTML) {
obj.appendSnippet({});
}
snippet.children[0].appendChild(image);
updateTotalImages();
}
/**
* Append snippet
* @Param object data
*/
obj.appendSnippet = function(data) {
// Reset snippet
snippet.innerHTML = '';
// Attributes
var a = [ 'image', 'title', 'description', 'host', 'url' ];
for (var i = 0; i < a.length; i++) {
var div = document.createElement('div');
div.className = 'jsnippet-' + a[i];
div.setAttribute('data-k', a[i]);
snippet.appendChild(div);
if (data[a[i]]) {
if (a[i] == 'image') {
if (! Array.isArray(data.image)) {
data.image = [ data.image ];
}
for (var j = 0; j < data.image.length; j++) {
var img = document.createElement('img');
img.src = data.image[j];
div.appendChild(img);
}
} else {
div.innerHTML = data[a[i]];
}
}
}
obj.editor.appendChild(document.createElement('br'));
obj.editor.appendChild(snippet);
}
/**
* Set editor value
*/
obj.setData = function(o) {
if (typeof(o) == 'object') {
obj.editor.innerHTML = o.content;
} else {
obj.editor.innerHTML = o;
}
if (obj.options.focus) {
Component.setCursor(obj.editor, true);
}
// Reset files container
files = [];
}
obj.getFiles = function() {
var f = obj.editor.querySelectorAll('.jfile');
var d = [];
for (var i = 0; i < f.length; i++) {
if (files[f[i].src]) {
d.push(files[f[i].src]);
}
}
return d;
}
obj.getText = function() {
return obj.editor.innerText;
}
/**
* Get editor data
*/
obj.getData = function(json) {
if (! json) {
var data = obj.editor.innerHTML;
} else {
var data = {
content : '',
}
// Get snippet
if (snippet.innerHTML) {
var index = 0;
data.snippet = {};
for (var i = 0; i < snippet.children.length; i++) {
// Get key from element
var key = snippet.children[i].getAttribute('data-k');
if (key) {
if (key == 'image') {
if (! data.snippet.image) {
data.snippet.image = [];
}
// Get all images
for (var j = 0; j < snippet.children[i].children.length; j++) {
data.snippet.image.push(snippet.children[i].children[j].getAttribute('src'))
}
} else {
data.snippet[key] = snippet.children[i].innerHTML;
}
}
}
}
// Get files
var f = Object.keys(files);
if (f.length) {
data.files = [];
for (var i = 0; i < f.length; i++) {
data.files.push(files[f[i]]);
}
}
// Get content
var d = document.createElement('div');
d.innerHTML = obj.editor.innerHTML;
var s = d.querySelector('.jsnippet');
if (s) {
s.remove();
}
var text = d.innerHTML;
text = text.replace(/<br>/g, "\n");
text = text.replace(/<\/div>/g, "<\/div>\n");
text = text.replace(/<(?:.|\n)*?>/gm, "");
data.content = text.trim();
// Process extensions
processExtensions('getData', data);
}
return data;
}
// Reset
obj.reset = function() {
obj.editor.innerHTML = '';
snippet.innerHTML = '';
files = [];
}
obj.addPdf = function(data) {
if (data.result.substr(0,4) != 'data') {
console.error('Invalid source');
} else {
var canvas = document.createElement('canvas');
canvas.width = 60;
canvas.height = 60;
var img = new Image();
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(function(blob) {
var newImage = document.createElement('img');
newImage.src = window.URL.createObjectURL(blob);
newImage.title = data.name;
newImage.className = 'jfile pdf';
files[newImage.src] = {
file: newImage.src,
extension: 'pdf',
content: data.result,
}
//insertNodeAtCaret(newImage);
document.execCommand('insertHtml', false, newImage.outerHTML);
});
}
}
obj.addImage = function(src, asSnippet) {
if (! obj.options.acceptImages) {
return;
}
if (! src) {
src = '';
}
if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
console.error('remoteParser not defined in your initialization');
} else {
// This is to process cross domain images
if (src.substr(0,4) == 'data') {
var extension = src.split(';')
extension = extension[0].split('/');
extension = extension[1];
} else {
var extension = src.substr(src.lastIndexOf('.') + 1);
// Work for cross browsers
src = obj.options.remoteParser + src;
}
var img = new Image();
img.onload = function onload() {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(function(blob) {
var newImage = document.createElement('img');
newImage.src = window.URL.createObjectURL(blob);
newImage.classList.add('jfile');
newImage.setAttribute('tabindex', '900');
newImage.setAttribute('width', img.width);
newImage.setAttribute('height', img.height);
files[newImage.src] = {
file: newImage.src,
extension: extension,
content: canvas.toDataURL(),
}
if (obj.options.dropAsSnippet || asSnippet) {
appendImage(newImage);
// Just to understand the attachment is part of a snippet
files[newImage.src].snippet = true;
} else {
//insertNodeAtCaret(newImage);
document.execCommand('insertHtml', false, newImage.outerHTML);
}
change();
});
};
img.src = src;
}
}
obj.addFile = function(files) {
var reader = [];
for (var i = 0; i < files.length; i++) {
if (files[i].size > obj.options.maxFileSize) {
alert('The file is too big');
} else {
// Only PDF or Images
var type = files[i].type.split('/');
if (type[0] == 'image') {
type = 1;
} else if (type[1] == 'pdf') {
type = 2;
} else {
type = 0;
}
if (type) {
// Create file
reader[i] = new FileReader();
reader[i].index = i;
reader[i].type = type;
reader[i].name = files[i].name;
reader[i].date = files[i].lastModified;
reader[i].size = files[i].size;
reader[i].addEventListener("load", function (data) {
// Get result
if (data.target.type == 2) {
if (obj.options.acceptFiles == true) {
obj.addPdf(data.target);
}
} else {
obj.addImage(data.target.result);
}
}, false);
reader[i].readAsDataURL(files[i])
} else {
alert('The extension is not allowed');
}
}
}
}
// Destroy
obj.destroy = function() {
obj.editor.removeEventListener('mouseup', editorMouseUp);
obj.editor.removeEventListener('mousedown', editorMouseDown);
obj.editor.removeEventListener('mousemove', editorMouseMove);
obj.editor.removeEventListener('keyup', editorKeyUp);
obj.editor.removeEventListener('keydown', editorKeyDown);
obj.editor.removeEventListener('dragstart', editorDragStart);
obj.editor.removeEventListener('dragenter', editorDragEnter);
obj.editor.removeEventListener('dragover', editorDragOver);
obj.editor.removeEventListener('drop', editorDrop);
obj.editor.removeEventListener('paste', editorPaste);
obj.editor.removeEventListener('blur', editorBlur);
obj.editor.removeEventListener('focus', editorFocus);
el.editor = null;
el.classList.remove('jeditor-container');
toolbar.remove();
snippet.remove();
obj.editor.remove();
}
obj.upload = function() {
helpers.click(obj.file);
}
var select = function(e) {
var s = window.getSelection()
var r = document.createRange();
r.selectNode(e);
s.addRange(r)
}
var editorPaste = function(e) {
if (obj.options.filterPaste == true) {
if (e.clipboardData || e.originalEvent.clipboardData) {
var html = (e.originalEvent || e).clipboardData.getData('text/html');
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
var file = (e.originalEvent || e).clipboardData.files
} else if (window.clipboardData) {
var html = window.clipboardData.getData('Html');
var text = window.clipboardData.getData('Text');
var file = window.clipboardData.files
}
if (file.length) {
// Paste a image from the clipboard
obj.addFile(file);
} else {
if (! html) {
html = text.split('\r\n');
if (! e.target.innerText) {
html.map(function(v) {
var d = document.createElement('div');
d.innerText = v;
obj.editor.appendChild(d);
});
} else {
html = html.map(function(v) {
return '<div>' + v + '</div>';
});
document.execCommand('insertText', false, html.join(''));
}
} else {
let img = [];
var d = utils_filter(html, img);
if (img.length) {
for (var i = 0; i < img.length; i++) {
obj.addImage(img[i]);
}
}
// Paste to the editor
//insertNodeAtCaret(d);
document.execCommand('insertHtml', false, d.innerHTML);
}
}
e.preventDefault();
}
}
var editorDragStart = function(e) {
if (editorAction && editorAction.e) {
e.preventDefault();
}
}
var editorDragEnter = function(e) {
if (editorAction || obj.options.dropZone == false) {
// Do nothing
} else {
el.classList.add('jeditor-dragging');
e.preventDefault();
}
}
var editorDragOver = function(e) {
if (editorAction || obj.options.dropZone == false) {
// Do nothing
} else {
if (editorTimer) {
clearTimeout(editorTimer);
}
editorTimer = setTimeout(function() {
el.classList.remove('jeditor-dragging');
}, 100);
e.preventDefault();
}
}
var editorDrop = function(e) {
if (editorAction || obj.options.dropZone === false) {
// Do nothing
} else {
// Position caret on the drop
let range = null;
if (document.caretRangeFromPoint) {
range=document.caretRangeFromPoint(e.clientX, e.clientY);
} else if (e.rangeParent) {
range=document.createRange();
range.setStart(e.rangeParent,e.rangeOffset);
}
let sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
sel.anchorNode.parentNode.focus();
let html = (e.originalEvent || e).dataTransfer.getData('text/html');
let text = (e.originalEvent || e).dataTransfer.getData('text/plain');
let file = (e.originalEvent || e).dataTransfer.files;
if (file.length) {
obj.addFile(file);
} else if (text) {
extractImageFromHtml(html);
}
el.classList.remove('jeditor-dragging');
e.preventDefault();
}
}
var editorBlur = function(e) {
// Process extensions
processExtensions('onevent', e);
// Apply changes
change(e);
// Blur
if (typeof(obj.options.onblur) == 'function') {
obj.options.onblur(el, obj, e);
}
}
var editorFocus = function(e) {
// Focus
if (typeof(obj.options.onfocus) == 'function') {
obj.options.onfocus(el, obj, e);
}
}
var editorKeyUp = function(e) {
if (! obj.editor.innerHTML) {
obj.editor.innerHTML = '<div><br></div>';
}
if (typeof(obj.options.onkeyup) == 'function') {
obj.options.onkeyup(el, obj, e);
}
}
var editorKeyDown = function(e) {
// Process extensions
processExtensions('onevent', e);
if (e.key == 'Delete') {
if (e.target.tagName == 'IMG') {
var parent = e.target.parentNode;
select(e.target);
if (parent.classList.contains('jsnippet-image')) {
updateTotalImages();
}
}
}
if (typeof(obj.options.onkeydown) == 'function') {
obj.options.onkeydown(el, obj, e);
}
}
var editorMouseUp = function(e) {
if (editorAction && editorAction.e) {
editorAction.e.classList.remove('resizing');
if (editorAction.e.changed == true) {
var image = editorAction.e.cloneNode()
image.width = parseInt(editorAction.e.style.width) || editorAction.e.getAttribute('width');
image.height = parseInt(editorAction.e.style.height) || editorAction.e.getAttribute('height');
editorAction.e.style.width = '';
editorAction.e.style.height = '';
select(editorAction.e);
document.execCommand('insertHtml', false, image.outerHTML);
}
}
editorAction = false;
}
var editorMouseDown = function(e) {
var close = function(snippet) {
var rect = snippet.getBoundingClientRect();
if (rect.width - (e.clientX - rect.left) < 40 && e.clientY - rect.top < 40) {
snippet.innerHTML = '';
snippet.remove();
}
}
if (e.target.tagName == 'IMG') {
if (e.target.style.cursor) {
var rect = e.target.getBoundingClientRect();
editorAction = {
e: e.target,
x: e.clientX,
y: e.clientY,
w: rect.width,
h: rect.height,
d: e.target.style.cursor,
}
if (! e.target.getAttribute('width')) {
e.target.setAttribute('width', rect.width)
}
if (! e.target.getAttribute('height')) {
e.target.setAttribute('height', rect.height)
}
var s = window.getSelection();
if (s.rangeCount) {
for (var i = 0; i < s.rangeCount; i++) {
s.removeRange(s.getRangeAt(i));
}
}
e.target.classList.add('resizing');
} else {
editorAction = true;
}
} else {
if (e.target.classList.contains('jsnippet')) {
close(e.target);
} else if (e.target.parentNode.classList.contains('jsnippet')) {
close(e.target.parentNode);
}
editorAction = true;
}
}
var editorMouseMove = function(e) {
if (e.target.tagName == 'IMG' && ! e.target.parentNode.classList.contains('jsnippet-image') && obj.options.allowImageResize == true) {
if (e.target.getAttribute('tabindex')) {
var rect = e.target.getBoundingClientRect();
if (e.clientY - rect.top < 5) {
if (rect.width - (e.clientX - rect.left) < 5) {
e.target.style.cursor = 'ne-resize';
} else if (e.clientX - rect.left < 5) {
e.target.style.cursor = 'nw-resize';
} else {
e.target.style.cursor = 'n-resize';
}
} else if (rect.height - (e.clientY - rect.top) < 5) {
if (rect.width - (e.clientX - rect.left) < 5) {
e.target.style.cursor = 'se-resize';
} else if (e.clientX - rect.left < 5) {
e.target.style.cursor = 'sw-resize';
} else {
e.target.style.cursor = 's-resize';
}
} else if (rect.width - (e.clientX - rect.left) < 5) {
e.target.style.cursor = 'e-resize';
} else if (e.clientX - rect.left < 5) {
e.target.style.cursor = 'w-resize';
} else {
e.target.style.cursor = '';
}
}
}
// Move
if (e.which == 1 && editorAction && editorAction.d) {
if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
editorAction.e.style.width = (editorAction.w + (e.clientX - editorAction.x));
if (e.shiftKey) {
var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w);
editorAction.e.style.height = editorAction.h + newHeight;
} else {
var newHeight = null;
}
}
if (! newHeight) {
if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
if (! e.shiftKey) {
editorAction.e.style.height = editorAction.h + (e.clientY - editorAction.y);
}
}
}
editorAction.e.changed = true;
}
}
var processExtensions = function(method, data) {
if (obj.options.extensions) {
var ext = Object.keys(obj.options.extensions);
if (ext.length) {
for (var i = 0; i < ext.length; i++)
if (obj.options.extensions[ext[i]] && typeof(obj.options.extensions[ext[i]][method]) == 'function') {
obj.options.extensions[ext[i]][method].call(obj, data);
}
}
}
}
var loadExtensions = function() {
if (obj.options.extensions) {
var ext = Object.keys(obj.options.extensions);
if (ext.length) {
for (var i = 0; i < ext.length; i++) {
if (obj.options.extensions[ext[i]] && typeof (obj.options.extensions[ext[i]]) == 'function') {
obj.options.extensions[ext[i]] = obj.options.extensions[ext[i]](el, obj);
}
}
}
}
}
document.addEventListener('mouseup', editorMouseUp);
document.addEventListener('mousemove', editorMouseMove);
obj.editor.addEventListener('mousedown', editorMouseDown);
obj.editor.addEventListener('keyup', editorKeyUp);
obj.editor.addEventListener('keydown', editorKeyDown);
obj.editor.addEventListener('dragstart', editorDragStart);
obj.editor.addEventListener('dragenter', editorDragEnter);
obj.editor.addEventListener('dragover', editorDragOver);
obj.editor.addEventListener('drop', editorDrop);
obj.editor.addEventListener('paste', editorPaste);
obj.editor.addEventListener('focus', editorFocus);
obj.editor.addEventListener('blur', editorBlur);
// Append editor to the container
el.appendChild(obj.editor);
// Snippet
if (obj.options.snippet) {
obj.appendSnippet(obj.options.snippet);
}
// Add toolbar
if (obj.options.toolbar) {
// Default toolbar configuration
if (Array.isArray(obj.options.toolbar)) {
var toolbarOptions = {
container: true,
responsive: true,
items: obj.options.toolbar
}
} else if (obj.options.toolbar === true) {
var toolbarOptions = {
container: true,
responsive: true,
items: [],
}
} else {
var toolbarOptions = obj.options.toolbar;
}
// Default items
if (! (toolbarOptions.items && toolbarOptions.items.length)) {
toolbarOptions.items = Component.getDefaultToolbar(obj);
}
if (obj.options.toolbarOnTop) {
// Add class
el.classList.add('toolbar-on-top');
// Append to the DOM
el.insertBefore(toolbar, el.firstChild);
} else {
// Add padding to the editor
obj.editor.style.padding = '15px';
// Append to the DOM
el.appendChild(toolbar);
}
// Create toolbar
Toolbar(toolbar, toolbarOptions);
toolbar.addEventListener('click', function() {
obj.editor.focus();
})
}
// Upload file
obj.file = document.createElement('input');
obj.file.style.display = 'none';
obj.file.type = 'file';
obj.file.setAttribute('accept', 'image/*');
obj.file.onchange = function() {
obj.addFile(this.files);
}
el.appendChild(obj.file);
// Focus to the editor
if (obj.options.focus) {
Component.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
}
// Change method
el.change = obj.setData;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
// Data type
var o = el.getAttribute('data-html') === 'true' ? false : true;
return obj.getData(o);
} else {
obj.setData(val);
}
}
loadExtensions();
el.editor = obj;
// Onload
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj, obj.editor);
}
return obj;
});
Component.setCursor = function(element, first) {
element.focus();
document.execCommand('selectAll');
var sel = window.getSelection();
var range = sel.getRangeAt(0);
if (first == true) {
var node = range.startContainer;
var size = 0;
} else {
var node = range.endContainer;
var size = node.length;
}
range.setStart(node, size);
range.setEnd(node, size);
sel.removeAllRanges();
sel.addRange(range);
}
Component.getDefaultToolbar = function(obj) {
var color = function(a,b,c) {
if (! c.color) {
var t = null;
var colorPicker = Color(c, {
onchange: function(o, v) {
if (c.k === 'color') {
document.execCommand('foreColor', false, v);
} else {
document.execCommand('backColor', false, v);
}
}
});
c.color.open();
}
}
var items = [];
items.push({
content: 'undo',
onclick: function() {
document.execCommand('undo');
}
});
items.push({
content: 'redo',
onclick: function() {
document.execCommand('redo');
}
});
items.push({
type: 'divisor'
});
if (obj.options.toolbarOnTop) {
items.push({
type: 'select',
width: '140px',
options: ['Default', 'Verdana', 'Arial', 'Courier New'],
render: function (e) {
return '<span style="font-family:' + e + '">' + e + '</span>';
},
onchange: function (a,b,c,d,e) {
document.execCommand("fontName", false, d);
}
});
items.push({
type: 'select',
content: 'format_size',
options: ['x-small', 'small', 'medium', 'large', 'x-large'],
render: function (e) {
return '<span style="font-size:' + e + '">' + e + '</span>';
},
onchange: function (a,b,c,d,e) {
//var html = `<span style="font-size: ${c}">${text}</span>`;
//document.execCommand('insertHtml', false, html);
document.execCommand("fontSize", false, parseInt(e)+1);
//var f = window.getSelection().anchorNode.parentNode
//f.removeAttribute("size");
//f.style.fontSize = d;
}
});
items.push({
type: 'select',
options: ['format_align_left', 'format_align_center', 'format_align_right', 'format_align_justify'],
render: function (e) {
return '<i class="material-icons">' + e + '</i>';
},
onchange: function (a,b,c,d,e) {
var options = ['JustifyLeft','justifyCenter','justifyRight','justifyFull'];
document.execCommand(options[e]);
}
});
items.push({
type: 'divisor'
});
items.push({
content: 'format_color_text',
k: 'color',
onclick: color,
});
items.push({
content: 'format_color_fill',
k: 'background-color',
onclick: color,
});
}
items.push({
content: 'format_bold',
onclick: function(a,b,c) {
document.execCommand('bold');
if (document.queryCommandState("bold")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_italic',
onclick: function(a,b,c) {
document.execCommand('italic');
if (document.queryCommandState("italic")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_underline',
onclick: function(a,b,c) {
document.execCommand('underline');
if (document.queryCommandState("underline")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
type:'divisor'
});
items.push({
content: 'format_list_bulleted',
onclick: function(a,b,c) {
document.execCommand('insertUnorderedList');
if (document.queryCommandState("insertUnorderedList")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_list_numbered',
onclick: function(a,b,c) {
document.execCommand('insertOrderedList');
if (document.queryCommandState("insertOrderedList")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_indent_increase',
onclick: function(a,b,c) {
document.execCommand('indent', true, null);
if (document.queryCommandState("indent")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_indent_decrease',
onclick: function(a,b,c) {
document.execCommand('outdent');
if (document.queryCommandState("outdent")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
if (obj.options.toolbarOnTop) {
items.push({
type: 'divisor'
});
items.push({
content: 'photo',
onclick: function () {
obj.upload();
}
});
items.push({
type: 'select',
content: 'table_view',
columns: 8,
grid: 8,
right: true,
options: [
'0x0', '1x0', '2x0', '3x0', '4x0', '5x0', '6x0', '7x0',
'0x1', '1x1', '2x1', '3x1', '4x1', '5x1', '6x1', '7x1',
'0x2', '1x2', '2x2', '3x2', '4x2', '5x2', '6x2', '7x2',
'0x3', '1x3', '2x3', '3x3', '4x3', '5x3', '6x3', '7x3',
'0x4', '1x4', '2x4', '3x4', '4x4', '5x4', '6x4', '7x4',
'0x5', '1x5', '2x5', '3x5', '4x5', '5x5', '6x5', '7x5',
'0x6', '1x6', '2x6', '3x6', '4x6', '5x6', '6x6', '7x6',
'0x7', '1x7', '2x7', '3x7', '4x7', '5x7', '6x7', '7x7',
],
render: function (e, item) {
if (item) {
item.onmouseover = this.onmouseover;
e = e.split('x');
item.setAttribute('data-x', e[0]);
item.setAttribute('data-y', e[1]);
}
var element = document.createElement('div');
item.style.margin = '1px';
item.style.border = '1px solid #ddd';
return element;
},
onmouseover: function (e) {
var x = parseInt(e.target.getAttribute('data-x'));
var y = parseInt(e.target.getAttribute('data-y'));
for (var i = 0; i < e.target.parentNode.children.length; i++) {
var element = e.target.parentNode.children[i];
var ex = parseInt(element.getAttribute('data-x'));
var ey = parseInt(element.getAttribute('data-y'));
if (ex <= x && ey <= y) {
element.style.backgroundColor = '#cae1fc';
element.style.borderColor = '#2977ff';
} else {
element.style.backgroundColor = '';
element.style.borderColor = '#ddd';
}
}
},
onchange: function (a, b, c) {
c = c.split('x');
var table = document.createElement('table');
var tbody = document.createElement('tbody');
for (var y = 0; y <= c[1]; y++) {
var tr = document.createElement('tr');
for (var x = 0; x <= c[0]; x++) {
var td = document.createElement('td');
td.innerHTML = '';
tr.appendChild(td);
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
table.setAttribute('width', '100%');
table.setAttribute('cellpadding', '6');
table.setAttribute('cellspacing', '0');
document.execCommand('insertHTML', false, table.outerHTML);
}
});
}
return items;
}
return Component;
}
/* harmony default export */ var editor = (Editor());
;// CONCATENATED MODULE: ./src/plugins/floating.js
function Floating() {
var Component = (function (el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
type: 'big',
title: 'Untitled',
width: 510,
height: 472,
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Private methods
var setContent = function () {
var temp = document.createElement('div');
while (el.children[0]) {
temp.appendChild(el.children[0]);
}
obj.content = document.createElement('div');
obj.content.className = 'jfloating_content';
obj.content.innerHTML = el.innerHTML;
while (temp.children[0]) {
obj.content.appendChild(temp.children[0]);
}
obj.container = document.createElement('div');
obj.container.className = 'jfloating';
obj.container.appendChild(obj.content);
if (obj.options.title) {
obj.container.setAttribute('title', obj.options.title);
} else {
obj.container.classList.add('no-title');
}
// validate element dimensions
if (obj.options.width) {
obj.container.style.width = parseInt(obj.options.width) + 'px';
}
if (obj.options.height) {
obj.container.style.height = parseInt(obj.options.height) + 'px';
}
el.innerHTML = '';
el.appendChild(obj.container);
}
var setEvents = function () {
if (obj.container) {
obj.container.addEventListener('click', function (e) {
var rect = e.target.getBoundingClientRect();
if (e.target.classList.contains('jfloating')) {
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
setTimeout(function () {
obj.close();
}, 100);
} else {
obj.setState();
}
}
});
}
}
var setType = function () {
obj.container.classList.add('jfloating-' + obj.options.type);
}
obj.state = {
isMinized: false,
}
obj.setState = function () {
if (obj.state.isMinized) {
obj.container.classList.remove('jfloating-minimized');
} else {
obj.container.classList.add('jfloating-minimized');
}
obj.state.isMinized = !obj.state.isMinized;
}
obj.close = function () {
Components.elements.splice(Component.elements.indexOf(obj.container), 1);
obj.updatePosition();
el.remove();
}
obj.updatePosition = function () {
for (var i = 0; i < Component.elements.length; i++) {
var floating = Component.elements[i];
var prevFloating = Component.elements[i - 1];
floating.style.right = i * (prevFloating ? prevFloating.offsetWidth : floating.offsetWidth) * 1.01 + 'px';
}
}
obj.init = function () {
// Set content into root
setContent();
// Set dialog events
setEvents();
// Set dialog type
setType();
// Update floating position
Component.elements.push(obj.container);
obj.updatePosition();
el.floating = obj;
}
obj.init();
return obj;
});
Component.elements = [];
return Component;
}
/* harmony default export */ var floating = (Floating());
;// CONCATENATED MODULE: ./src/plugins/validations.js
const validations_HelpersDate = (dist_default()).Helpers;
function Validations() {
/**
* Options: Object,
* Properties:
* Constraint,
* Reference,
* Value
*/
const isNumeric = function(num) {
return !isNaN(num) && num !== null && (typeof num !== 'string' || num.trim() !== '');
}
const numberCriterias = {
'between': function(value, range) {
return value >= range[0] && value <= range[1];
},
'not between': function(value, range) {
return value < range[0] || value > range[1];
},
'<': function(value, range) {
return value < range[0];
},
'<=': function(value, range) {
return value <= range[0];
},
'>': function(value, range) {
return value > range[0];
},
'>=': function(value, range) {
return value >= range[0];
},
'=': function(value, range) {
return value == range[0];
},
'!=': function(value, range) {
return value != range[0];
},
}
const dateCriterias = {
'valid date': function() {
return true;
},
'=': function(value, range) {
return value === range[0];
},
'!=': function(value, range) {
return value !== range[0];
},
'<': function(value, range) {
return value < range[0];
},
'<=': function(value, range) {
return value <= range[0];
},
'>': function(value, range) {
return value > range[0];
},
'>=': function(value, range) {
return value >= range[0];
},
'between': function(value, range) {
return value >= range[0] && value <= range[1];
},
'not between': function(value, range) {
return value < range[0] || value > range[1];
},
}
const textCriterias = {
'contains': function(value, range) {
return value.includes(range[0]);
},
'not contains': function(value, range) {
return !value.includes(range[0]);
},
'begins with': function(value, range) {
return value.startsWith(range[0]);
},
'ends with': function(value, range) {
return value.endsWith(range[0]);
},
'=': function(value, range) {
return value === range[0];
},
'!=': function(value, range) {
return value !== range[0];
},
'valid email': function(value) {
var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
return pattern.test(value);
},
'valid url': function(value) {
var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
return pattern.test(value);
},
}
// Component router
const component = function(value, options) {
if (typeof(component[options.type]) === 'function') {
if (options.allowBlank && (typeof value === 'undefined' || value === '' || value === null)) {
return true;
}
return component[options.type].call(this, value, options);
}
return null;
}
component.url = function(data) {
var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
return pattern.test(data) ? true : false;
}
component.email = function(data) {
var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
return data && pattern.test(data) ? true : false;
}
component.required = function(data) {
return data && data.trim() ? true : false;
}
component.empty = function(data) {
return typeof data === 'undefined' || data === null || (typeof data === 'string' && !data.toString().trim());
}
component['not exist'] = component.empty;
component.notEmpty = function(data) {
return !component.empty(data);
}
component.exist = component.notEmpty;
component.number = function(data, options) {
if (! isNumeric(data)) {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!numberCriterias[options.criteria]) {
return false;
}
let values = options.value.map(function(num) {
return parseFloat(num);
})
return numberCriterias[options.criteria](data, values);
};
component.login = function(data) {
let pattern = new RegExp(/^[a-zA-Z0-9._-]+$/);
return data && pattern.test(data) ? true : false;
}
component.list = function(data, options) {
let dataType = typeof data;
if (dataType !== 'string' && dataType !== 'number') {
return false;
}
let list;
if (typeof(options.value[0]) === 'string') {
if (options.source) {
list = options.source;
} else {
list = options.value[0].split(',');
}
} else {
list = options.value[0];
}
if (! Array.isArray(list)) {
return false;
} else {
let validOption = list.findIndex(function (item) {
return item == data;
});
return validOption > -1;
}
}
const getCurrentDateWithoutTime = function() {
let date = new Date();
date.setHours(0, 0, 0, 0);
return date;
}
const relativeDates = {
'one year ago': function() {
let date = getCurrentDateWithoutTime();
date.setFullYear(date.getFullYear() - 1);
return date;
},
'one month ago': function() {
let date = getCurrentDateWithoutTime();
date.setMonth(date.getMonth() - 1);
return date;
},
'one week ago': function() {
let date = getCurrentDateWithoutTime();
date.setDate(date.getDate() - 7);
return date;
},
yesterday: function() {
let date = getCurrentDateWithoutTime();
date.setDate(date.getDate() - 1);
return date;
},
today: getCurrentDateWithoutTime,
tomorrow: function() {
let date = getCurrentDateWithoutTime();
date.setDate(date.getDate() + 1);
return date;
},
};
component.date = function(data, options) {
if (isNumeric(data) && data > 0 && data < 1000000) {
data = validations_HelpersDate.numToDate(data);
}
if (new Date(data) == 'Invalid Date') {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!dateCriterias[options.criteria]) {
return false;
}
let values = options.value.map(function(date) {
if (typeof date === 'string' && relativeDates[date]) {
return relativeDates[date]().getTime();
}
return new Date(date).getTime();
});
return dateCriterias[options.criteria](new Date(data).getTime(), values);
}
component.text = function(data, options) {
if (typeof data === 'undefined' || data === null) {
data = '';
} else if (typeof data !== 'string') {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!textCriterias[options.criteria]) {
return false;
}
return textCriterias[options.criteria](data, options.value);
}
component.textLength = function(data, options) {
let textLength;
if (typeof data === 'string') {
textLength = data.length;
} else if (typeof data !== 'undefined' && data !== null && typeof data.toString === 'function') {
textLength = data.toString().length;
} else {
textLength = 0;
}
return component.number(textLength, options);
}
component.time = function(data, options) {
if (! isNumeric(data)) {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!numberCriterias[options.criteria]) {
return false;
}
let values = options.value.map(function(num) {
return parseInt(parseFloat(num) * 10**17) / 10**17;
})
return numberCriterias[options.criteria](parseInt(parseFloat(data) * 10**17) / 10**17, values);
};
return component;
}
/* harmony default export */ var validations = (Validations());
;// CONCATENATED MODULE: ./src/plugins/form.js
function Form() {
var Component = (function(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
url: null,
message: 'Are you sure? There are unsaved information in your form',
ignore: false,
currentHash: null,
submitButton:null,
validations: null,
onbeforeload: null,
onload: null,
onbeforesave: null,
onsave: null,
onbeforeremove: null,
onremove: null,
onerror: function(el, message) {
alert(message);
}
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Validations
if (! obj.options.validations) {
obj.options.validations = {};
}
// Submit Button
if (! obj.options.submitButton) {
obj.options.submitButton = el.querySelector('input[type=submit]');
}
if (obj.options.submitButton && obj.options.url) {
obj.options.submitButton.onclick = function() {
obj.save();
}
}
if (! obj.options.validations.email) {
obj.options.validations.email = validations.email;
}
if (! obj.options.validations.length) {
obj.options.validations.length = validations.length;
}
if (! obj.options.validations.required) {
obj.options.validations.required = validations.required;
}
obj.setUrl = function(url) {
obj.options.url = url;
}
obj.load = function() {
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
queue: true,
success: function(data) {
// Overwrite values from the backend
if (typeof(obj.options.onbeforeload) == 'function') {
var ret = obj.options.onbeforeload(el, data);
if (ret) {
data = ret;
}
}
// Apply values to the form
Component.setElements(el, data);
// Onload methods
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, data);
}
}
});
}
obj.save = function() {
var test = obj.validate();
if (test) {
obj.options.onerror(el, test);
} else {
var data = Component.getElements(el, true);
if (typeof(obj.options.onbeforesave) == 'function') {
var data = obj.options.onbeforesave(el, data);
if (data === false) {
return;
}
}
ajax({
url: obj.options.url,
method: 'POST',
dataType: 'json',
data: data,
success: function(result) {
if (typeof(obj.options.onsave) == 'function') {
obj.options.onsave(el, data, result);
}
}
});
}
}
obj.remove = function() {
if (typeof(obj.options.onbeforeremove) == 'function') {
var ret = obj.options.onbeforeremove(el, obj);
if (ret === false) {
return false;
}
}
ajax({
url: obj.options.url,
method: 'DELETE',
dataType: 'json',
success: function(result) {
if (typeof(obj.options.onremove) == 'function') {
obj.options.onremove(el, obj, result);
}
obj.reset();
}
});
}
var addError = function(element) {
// Add error in the element
element.classList.add('error');
// Submit button
if (obj.options.submitButton) {
obj.options.submitButton.setAttribute('disabled', true);
}
// Return error message
var error = element.getAttribute('data-error') || 'There is an error in the form';
element.setAttribute('title', error);
return error;
}
var delError = function(element) {
var error = false;
// Remove class from this element
element.classList.remove('error');
element.removeAttribute('title');
// Get elements in the form
var elements = el.querySelectorAll("input, select, textarea, div[name]");
// Run all elements
for (var i = 0; i < elements.length; i++) {
if (elements[i].getAttribute('data-validation')) {
if (elements[i].classList.contains('error')) {
error = true;
}
}
}
if (obj.options.submitButton) {
if (error) {
obj.options.submitButton.setAttribute('disabled', true);
} else {
obj.options.submitButton.removeAttribute('disabled');
}
}
}
obj.validateElement = function(element) {
// Test results
var test = false;
// Value
var value = Component.getValue(element);
// Validation
var validation = element.getAttribute('data-validation');
// Parse
if (typeof(obj.options.validations[validation]) == 'function' && ! obj.options.validations[validation](value, element)) {
// Not passed in the test
test = addError(element);
} else {
if (element.classList.contains('error')) {
delError(element);
}
}
return test;
}
obj.reset = function() {
// Get elements in the form
var name = null;
var elements = el.querySelectorAll("input, select, textarea, div[name]");
// Run all elements
for (var i = 0; i < elements.length; i++) {
if (name = elements[i].getAttribute('name')) {
if (elements[i].type == 'checkbox' || elements[i].type == 'radio') {
elements[i].checked = false;
} else {
if (typeof(elements[i].val) == 'function') {
elements[i].val('');
} else {
elements[i].value = '';
}
}
}
}
}
// Run form validation
obj.validate = function() {
var test = [];
// Get elements in the form
var elements = el.querySelectorAll("input, select, textarea, div[name]");
// Run all elements
for (var i = 0; i < elements.length; i++) {
// Required
if (elements[i].getAttribute('data-validation')) {
var res = obj.validateElement(elements[i]);
if (res) {
test.push(res);
}
}
}
if (test.length > 0) {
return test.join('<br>');
} else {
return false;
}
}
// Check the form
obj.getError = function() {
// Validation
return obj.validation() ? true : false;
}
// Return the form hash
obj.setHash = function() {
return obj.getHash(Component.getElements(el));
}
// Get the form hash
obj.getHash = function(str) {
var hash = 0, i, chr;
if (str.length === 0) {
return hash;
} else {
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
}
return hash;
}
// Is there any change in the form since start tracking?
obj.isChanged = function() {
var hash = obj.setHash();
return (obj.options.currentHash != hash);
}
// Restart tracking
obj.resetTracker = function() {
obj.options.currentHash = obj.setHash();
obj.options.ignore = false;
}
// Ignore flag
obj.setIgnore = function(ignoreFlag) {
obj.options.ignore = ignoreFlag ? true : false;
}
// Start tracking in one second
setTimeout(function() {
obj.options.currentHash = obj.setHash();
}, 1000);
// Validations
el.addEventListener("keyup", function(e) {
if (e.target.getAttribute('data-validation')) {
obj.validateElement(e.target);
}
});
// Alert
if (! Component.hasEvents) {
window.addEventListener("beforeunload", function (e) {
if (obj.isChanged() && obj.options.ignore == false) {
var confirmationMessage = obj.options.message? obj.options.message : "\o/";
if (confirmationMessage) {
if (typeof e == 'undefined') {
e = window.event;
}
if (e) {
e.returnValue = confirmationMessage;
}
return confirmationMessage;
} else {
return void(0);
}
}
});
Component.hasEvents = true;
}
el.form = obj;
return obj;
});
// Get value from one element
Component.getValue = function(element) {
var value = null;
if (element.type == 'checkbox') {
if (element.checked == true) {
value = element.value || true;
}
} else if (element.type == 'radio') {
if (element.checked == true) {
value = element.value;
}
} else if (element.type == 'file') {
value = element.files;
} else if (element.tagName == 'select' && element.multiple == true) {
value = [];
var options = element.querySelectorAll("options[selected]");
for (var j = 0; j < options.length; j++) {
value.push(options[j].value);
}
} else if (typeof(element.val) == 'function') {
value = element.val();
} else {
value = element.value || '';
}
return value;
}
// Get form elements
Component.getElements = function(el, asArray) {
var data = {};
var name = null;
var elements = el.querySelectorAll("input, select, textarea, div[name]");
for (var i = 0; i < elements.length; i++) {
if (name = elements[i].getAttribute('name')) {
data[name] = Component.getValue(elements[i]) || '';
}
}
return asArray == true ? data : JSON.stringify(data);
}
//Get form elements
Component.setElements = function(el, data) {
var name = null;
var value = null;
var elements = el.querySelectorAll("input, select, textarea, div[name]");
for (var i = 0; i < elements.length; i++) {
// Attributes
var type = elements[i].getAttribute('type');
if (name = elements[i].getAttribute('name')) {
// Transform variable names in pathname
name = name.replace(new RegExp(/\[(.*?)\]/ig), '.$1');
value = null;
// Seach for the data in the path
if (name.match(/\./)) {
var tmp = Path.call(data, name) || '';
if (typeof(tmp) !== 'undefined') {
value = tmp;
}
} else {
if (typeof(data[name]) !== 'undefined') {
value = data[name];
}
}
// Set the values
if (value !== null) {
if (type == 'checkbox' || type == 'radio') {
elements[i].checked = value ? true : false;
} else if (type == 'file') {
// Do nothing
} else {
if (typeof (elements[i].val) == 'function') {
elements[i].val(value);
} else {
elements[i].value = value;
}
}
}
}
}
}
return Component;
}
/* harmony default export */ var plugins_form = (Form());
;// CONCATENATED MODULE: ./src/plugins/mask.js
/*
Add '*' as a valid symbol
Formats such as 'DD"th of "MMMM", "YYYY'
Conditional masking
(000) 00000-00
$ (#,##0.00);$ (-#,##0.00)
$ (-#,##0.00)
j.mask.render(0, { mask: 'mm:ss.0' }
j.mask.render(0, { mask: '[h]:mm:ss' }, true)
*/
/* harmony default export */ var mask = (dist.Mask);
;// CONCATENATED MODULE: ./src/plugins/modal.js
function Modal() {
var Events = function() {
// Position
var tracker = null;
var keyDown = function (e) {
if (e.which == 27) {
var modals = document.querySelectorAll('.jmodal');
for (var i = 0; i < modals.length; i++) {
modals[i].parentNode.modal.close();
}
}
}
var mouseUp = function (e) {
let element = e.composedPath();
var item = helpers.findElement(element[0], 'jmodal');
if (item) {
// Get target info
var rect = item.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
item.parentNode.modal.close();
}
}
if (tracker) {
tracker.element.style.cursor = 'auto';
tracker = null;
}
}
var mouseDown = function (e) {
let element = e.composedPath();
var item = helpers.findElement(element[0], 'jmodal');
if (item) {
// Get target info
var rect = item.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
// Do nothing
} else {
if (y - rect.top < 50) {
if (document.selection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
tracker = {
left: rect.left,
top: rect.top,
x: e.clientX,
y: e.clientY,
width: rect.width,
height: rect.height,
element: item,
}
}
}
}
}
var mouseMove = function (e) {
if (tracker) {
e = e || window.event;
if (e.buttons) {
var mouseButton = e.buttons;
} else if (e.button) {
var mouseButton = e.button;
} else {
var mouseButton = e.which;
}
if (mouseButton) {
tracker.element.style.top = (tracker.top + (e.clientY - tracker.y) + (tracker.height / 2)) + 'px';
tracker.element.style.left = (tracker.left + (e.clientX - tracker.x) + (tracker.width / 2)) + 'px';
tracker.element.style.cursor = 'move';
} else {
tracker.element.style.cursor = 'auto';
}
}
}
document.addEventListener('keydown', keyDown);
document.addEventListener('mouseup', mouseUp);
document.addEventListener('mousedown', mouseDown);
document.addEventListener('mousemove', mouseMove);
}
var Component = (function (el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
url: null,
onopen: null,
onclose: null,
onload: null,
closed: false,
width: null,
height: null,
title: null,
padding: null,
backdrop: true,
icon: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Title
if (!obj.options.title && el.getAttribute('title')) {
obj.options.title = el.getAttribute('title');
}
var temp = document.createElement('div');
while (el.children[0]) {
temp.appendChild(el.children[0]);
}
obj.title = document.createElement('div');
obj.title.className = 'jmodal_title';
if (obj.options.icon) {
obj.title.setAttribute('data-icon', obj.options.icon);
}
obj.content = document.createElement('div');
obj.content.className = 'jmodal_content';
obj.content.innerHTML = el.innerHTML;
while (temp.children[0]) {
obj.content.appendChild(temp.children[0]);
}
obj.container = document.createElement('div');
obj.container.className = 'jmodal';
obj.container.appendChild(obj.title);
obj.container.appendChild(obj.content);
if (obj.options.padding) {
obj.content.style.padding = obj.options.padding;
}
if (obj.options.width) {
obj.container.style.width = obj.options.width;
}
if (obj.options.height) {
obj.container.style.height = obj.options.height;
}
if (obj.options.title) {
var title = document.createElement('h4');
title.innerText = obj.options.title;
obj.title.appendChild(title);
}
el.innerHTML = '';
el.style.display = 'none';
el.appendChild(obj.container);
// Backdrop
if (obj.options.backdrop) {
var backdrop = document.createElement('div');
backdrop.className = 'jmodal_backdrop';
backdrop.onclick = function () {
obj.close();
}
el.appendChild(backdrop);
}
obj.open = function () {
el.style.display = 'block';
// Fullscreen
var rect = obj.container.getBoundingClientRect();
if (helpers.getWindowWidth() < rect.width) {
obj.container.style.top = '';
obj.container.style.left = '';
obj.container.classList.add('jmodal_fullscreen');
animation.slideBottom(obj.container, 1);
} else {
if (obj.options.backdrop) {
backdrop.style.display = 'block';
}
}
// Event
if (typeof (obj.options.onopen) == 'function') {
obj.options.onopen(el, obj);
}
}
obj.resetPosition = function () {
obj.container.style.top = '';
obj.container.style.left = '';
}
obj.isOpen = function () {
return el.style.display != 'none' ? true : false;
}
obj.close = function () {
if (obj.isOpen()) {
el.style.display = 'none';
if (obj.options.backdrop) {
// Backdrop
backdrop.style.display = '';
}
// Remove fullscreen class
obj.container.classList.remove('jmodal_fullscreen');
// Event
if (typeof (obj.options.onclose) == 'function') {
obj.options.onclose(el, obj);
}
}
}
if (obj.options.url) {
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'text/html',
success: function (data) {
obj.content.innerHTML = data;
if (!obj.options.closed) {
obj.open();
}
if (typeof (obj.options.onload) === 'function') {
obj.options.onload(obj);
}
}
});
} else {
if (!obj.options.closed) {
obj.open();
}
if (typeof (obj.options.onload) === 'function') {
obj.options.onload(obj);
}
}
// Keep object available from the node
el.modal = obj;
// Create events when the first modal is create only
Events();
// Execute the events only once
Events = function() {};
return obj;
});
return Component;
}
/* harmony default export */ var modal = (Modal());
;// CONCATENATED MODULE: ./src/plugins/notification.js
function Notification() {
var Component = function (options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
icon: null,
name: 'Notification',
date: null,
error: null,
title: null,
message: null,
timeout: 4000,
autoHide: true,
closeable: true,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
var notification = document.createElement('div');
notification.className = 'jnotification';
if (obj.options.error) {
notification.classList.add('jnotification-error');
}
var notificationContainer = document.createElement('div');
notificationContainer.className = 'jnotification-container';
notification.appendChild(notificationContainer);
var notificationHeader = document.createElement('div');
notificationHeader.className = 'jnotification-header';
notificationContainer.appendChild(notificationHeader);
var notificationImage = document.createElement('div');
notificationImage.className = 'jnotification-image';
notificationHeader.appendChild(notificationImage);
if (obj.options.icon) {
var notificationIcon = document.createElement('img');
notificationIcon.src = obj.options.icon;
notificationImage.appendChild(notificationIcon);
}
var notificationName = document.createElement('div');
notificationName.className = 'jnotification-name';
notificationName.innerHTML = obj.options.name;
notificationHeader.appendChild(notificationName);
if (obj.options.closeable == true) {
var notificationClose = document.createElement('div');
notificationClose.className = 'jnotification-close';
notificationClose.onclick = function () {
obj.hide();
}
notificationHeader.appendChild(notificationClose);
}
var notificationDate = document.createElement('div');
notificationDate.className = 'jnotification-date';
notificationHeader.appendChild(notificationDate);
var notificationContent = document.createElement('div');
notificationContent.className = 'jnotification-content';
notificationContainer.appendChild(notificationContent);
if (obj.options.title) {
var notificationTitle = document.createElement('div');
notificationTitle.className = 'jnotification-title';
notificationTitle.innerHTML = obj.options.title;
notificationContent.appendChild(notificationTitle);
}
var notificationMessage = document.createElement('div');
notificationMessage.className = 'jnotification-message';
notificationMessage.innerHTML = obj.options.message;
notificationContent.appendChild(notificationMessage);
obj.show = function () {
document.body.appendChild(notification);
if (helpers.getWindowWidth() > 800) {
animation.fadeIn(notification);
} else {
animation.slideTop(notification, 1);
}
}
obj.hide = function () {
if (helpers.getWindowWidth() > 800) {
animation.fadeOut(notification, function () {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
if (notificationTimeout) {
clearTimeout(notificationTimeout);
}
}
});
} else {
animation.slideTop(notification, 0, function () {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
if (notificationTimeout) {
clearTimeout(notificationTimeout);
}
}
});
}
};
obj.show();
if (obj.options.autoHide == true) {
var notificationTimeout = setTimeout(function () {
obj.hide();
}, obj.options.timeout);
}
if (helpers.getWindowWidth() < 800) {
notification.addEventListener("swipeup", function (e) {
obj.hide();
e.preventDefault();
e.stopPropagation();
});
}
return obj;
}
Component.isVisible = function () {
var j = document.querySelector('.jnotification');
return j && j.parentNode ? true : false;
}
return Component;
}
/* harmony default export */ var notification = (Notification());
;// CONCATENATED MODULE: ./src/plugins/progressbar.js
function Progressbar(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
value: 0,
onchange: null,
width: null,
};
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Class
el.classList.add('jprogressbar');
el.setAttribute('tabindex', 1);
el.setAttribute('data-value', obj.options.value);
var bar = document.createElement('div');
bar.style.width = obj.options.value + '%';
bar.style.color = '#fff';
el.appendChild(bar);
if (obj.options.width) {
el.style.width = obj.options.width;
}
// Set value
obj.setValue = function(value) {
value = parseInt(value);
obj.options.value = value;
bar.style.width = value + '%';
el.setAttribute('data-value', value + '%');
if (value < 6) {
el.style.color = '#000';
} else {
el.style.color = '#fff';
}
// Update value
obj.options.value = value;
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, value);
}
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
obj.getValue = function() {
return obj.options.value;
}
var action = function(e) {
if (e.which) {
// Get target info
var rect = el.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
obj.setValue(Math.round((x - rect.left) / rect.width * 100));
}
}
// Events
if ('touchstart' in document.documentElement === true) {
el.addEventListener('touchstart', action);
el.addEventListener('touchend', action);
} else {
el.addEventListener('mousedown', action);
el.addEventListener("mousemove", action);
}
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Reference
el.progressbar = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/rating.js
function Rating(el, options) {
// Already created, update options
if (el.rating) {
return el.rating.setOptions(options, true);
}
// New instance
var obj = {};
obj.options = {};
obj.setOptions = function(options, reset) {
// Default configuration
var defaults = {
number: 5,
value: 0,
tooltip: [ 'Very bad', 'Bad', 'Average', 'Good', 'Very good' ],
onchange: null,
};
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Make sure the container is empty
el.innerHTML = '';
// Add elements
for (var i = 0; i < obj.options.number; i++) {
var div = document.createElement('div');
div.setAttribute('data-index', (i + 1))
div.setAttribute('title', obj.options.tooltip[i])
el.appendChild(div);
}
// Selected option
if (obj.options.value) {
for (var i = 0; i < obj.options.number; i++) {
if (i < obj.options.value) {
el.children[i].classList.add('jrating-selected');
}
}
}
return obj;
}
// Set value
obj.setValue = function(index) {
for (var i = 0; i < obj.options.number; i++) {
if (i < index) {
el.children[i].classList.add('jrating-selected');
} else {
el.children[i].classList.remove('jrating-over');
el.children[i].classList.remove('jrating-selected');
}
}
obj.options.value = index;
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, index);
}
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
obj.getValue = function() {
return obj.options.value;
}
var init = function() {
// Start plugin
obj.setOptions(options);
// Class
el.classList.add('jrating');
// Events
el.addEventListener("click", function(e) {
var index = e.target.getAttribute('data-index');
if (index != undefined) {
if (index == obj.options.value) {
obj.setValue(0);
} else {
obj.setValue(index);
}
}
});
el.addEventListener("mouseover", function(e) {
var index = e.target.getAttribute('data-index');
for (var i = 0; i < obj.options.number; i++) {
if (i < index) {
el.children[i].classList.add('jrating-over');
} else {
el.children[i].classList.remove('jrating-over');
}
}
});
el.addEventListener("mouseout", function(e) {
for (var i = 0; i < obj.options.number; i++) {
el.children[i].classList.remove('jrating-over');
}
});
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Reference
el.rating = obj;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/search.js
function Search(el, options) {
if (el.search) {
return el.search;
}
var index = null;
var select = function(e) {
if (e.target.classList.contains('jsearch_item')) {
var element = e.target;
} else {
var element = e.target.parentNode;
}
obj.selectIndex(element);
e.preventDefault();
}
var createList = function(data) {
if (typeof(obj.options.onsearch) == 'function') {
var ret = obj.options.onsearch(obj, data);
if (ret) {
data = ret;
}
}
// Reset container
container.innerHTML = '';
// Print results
if (! data.length) {
// Show container
el.style.display = '';
} else {
// Show container
el.style.display = 'block';
// Show items (only 10)
var len = data.length < 11 ? data.length : 10;
for (var i = 0; i < len; i++) {
if (typeof(data[i]) == 'string') {
var text = data[i];
var value = data[i];
} else {
// Legacy
var text = data[i].text;
if (! text && data[i].name) {
text = data[i].name;
}
var value = data[i].value;
if (! value && data[i].id) {
value = data[i].id;
}
}
var div = document.createElement('div');
div.setAttribute('data-value', value);
div.setAttribute('data-text', text);
div.className = 'jsearch_item';
if (data[i].id) {
div.setAttribute('id', data[i].id)
}
if (obj.options.forceSelect && i == 0) {
div.classList.add('selected');
}
var img = document.createElement('img');
if (data[i].image) {
img.src = data[i].image;
} else {
img.style.display = 'none';
}
div.appendChild(img);
var item = document.createElement('div');
item.innerHTML = text;
div.appendChild(item);
// Append item to the container
container.appendChild(div);
}
}
}
var execute = function(str) {
if (str != obj.terms) {
// New terms
obj.terms = str;
// New index
if (obj.options.forceSelect) {
index = 0;
} else {
index = null;
}
// Array or remote search
if (Array.isArray(obj.options.data)) {
var test = function(o) {
if (typeof(o) == 'string') {
if ((''+o).toLowerCase().search(str.toLowerCase()) >= 0) {
return true;
}
} else {
for (var key in o) {
var value = o[key];
if ((''+value).toLowerCase().search(str.toLowerCase()) >= 0) {
return true;
}
}
}
return false;
}
var results = obj.options.data.filter(function(item) {
return test(item);
});
// Show items
createList(results);
} else {
// Get remove results
ajax({
url: obj.options.data + str,
method: 'GET',
dataType: 'json',
success: function(data) {
// Show items
createList(data);
}
});
}
}
}
// Search timer
var timer = null;
// Search methods
var obj = function(str) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
execute(str);
}, 500);
}
if(options.forceSelect === null) {
options.forceSelect = true;
}
obj.options = {
data: options.data || null,
input: options.input || null,
searchByNode: options.searchByNode || null,
onselect: options.onselect || null,
forceSelect: options.forceSelect,
onsearch: options.onsearch || null,
onbeforesearch: options.onbeforesearch || null,
};
obj.selectIndex = function(item) {
var id = item.getAttribute('id');
var text = item.getAttribute('data-text');
var value = item.getAttribute('data-value');
var image = item.children[0].src || '';
// Onselect
if (typeof(obj.options.onselect) == 'function') {
obj.options.onselect(obj, text, value, id, image);
}
// Close container
obj.close();
}
obj.open = function() {
el.style.display = 'block';
}
obj.close = function() {
if (timer) {
clearTimeout(timer);
}
// Current terms
obj.terms = '';
// Remove results
container.innerHTML = '';
// Hide
el.style.display = '';
}
obj.isOpened = function() {
return el.style.display ? true : false;
}
obj.keydown = function(e) {
if (obj.isOpened()) {
if (e.key == 'Enter') {
// Enter
if (index!==null && container.children[index]) {
obj.selectIndex(container.children[index]);
e.preventDefault();
} else {
obj.close();
}
} else if (e.key === 'ArrowUp') {
// Up
if (index!==null && container.children[0]) {
container.children[index].classList.remove('selected');
if(!obj.options.forceSelect && index === 0) {
index = null;
} else {
index = Math.max(0, index-1);
container.children[index].classList.add('selected');
}
}
e.preventDefault();
} else if (e.key === 'ArrowDown') {
// Down
if(index == null) {
index = -1;
} else {
container.children[index].classList.remove('selected');
}
if (index < 9 && container.children[index+1]) {
index++;
}
container.children[index].classList.add('selected');
e.preventDefault();
}
}
}
obj.keyup = function(e) {
if (! obj.options.searchByNode && obj.options.input) {
if (obj.options.input.tagName === 'DIV') {
var terms = obj.options.input.innerText;
} else {
var terms = obj.options.input.value;
}
} else {
// Current node
var node = helpers.getNode();
if (node) {
var terms = node.innerText;
}
}
if (typeof(obj.options.onbeforesearch) == 'function') {
var ret = obj.options.onbeforesearch(obj, terms);
if (ret) {
terms = ret;
} else {
if (ret === false) {
// Ignore event
return;
}
}
}
obj(terms);
}
obj.blur = function(e) {
obj.close();
}
// Add events
if (obj.options.input) {
obj.options.input.addEventListener("keyup", obj.keyup);
obj.options.input.addEventListener("keydown", obj.keydown);
obj.options.input.addEventListener("blur", obj.blur);
}
// Append element
var container = document.createElement('div');
container.classList.add('jsearch_container');
container.onmousedown = select;
el.appendChild(container);
el.classList.add('jsearch');
el.search = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/slider.js
function Slider(el, options) {
var obj = {};
obj.options = {};
obj.currentImage = null;
if (options) {
obj.options = options;
}
// Focus
el.setAttribute('tabindex', '900')
// Items
obj.options.items = [];
if (! el.classList.contains('jslider')) {
el.classList.add('jslider');
el.classList.add('unselectable');
if (obj.options.height) {
el.style.minHeight = parseInt(obj.options.height) + 'px';
}
if (obj.options.width) {
el.style.width = parseInt(obj.options.width) + 'px';
}
if (obj.options.grid) {
el.classList.add('jslider-grid');
var number = el.children.length;
if (number > 4) {
el.setAttribute('data-total', number - 4);
}
el.setAttribute('data-number', (number > 4 ? 4 : number));
}
// Add slider counter
var counter = document.createElement('div');
counter.classList.add('jslider-counter');
// Move children inside
if (el.children.length > 0) {
// Keep children items
for (var i = 0; i < el.children.length; i++) {
obj.options.items.push(el.children[i]);
// counter click event
var item = document.createElement('div');
item.onclick = function() {
var index = Array.prototype.slice.call(counter.children).indexOf(this);
obj.show(obj.currentImage = obj.options.items[index]);
}
counter.appendChild(item);
}
}
// Add caption
var caption = document.createElement('div');
caption.className = 'jslider-caption';
// Add close buttom
var controls = document.createElement('div');
var close = document.createElement('div');
close.className = 'jslider-close';
close.innerHTML = '';
close.onclick = function() {
obj.close();
}
controls.appendChild(caption);
controls.appendChild(close);
}
obj.updateCounter = function(index) {
for (var i = 0; i < counter.children.length; i ++) {
if (counter.children[i].classList.contains('jslider-counter-focus')) {
counter.children[i].classList.remove('jslider-counter-focus');
break;
}
}
counter.children[index].classList.add('jslider-counter-focus');
}
obj.show = function(target) {
if (! target) {
var target = el.children[0];
}
// Focus element
el.classList.add('jslider-focus');
el.classList.remove('jslider-grid');
el.appendChild(controls);
el.appendChild(counter);
// Update counter
var index = obj.options.items.indexOf(target);
obj.updateCounter(index);
// Remove display
for (var i = 0; i < el.children.length; i++) {
el.children[i].style.display = '';
}
target.style.display = 'block';
// Is there any previous
if (target.previousElementSibling) {
el.classList.add('jslider-left');
} else {
el.classList.remove('jslider-left');
}
// Is there any next
if (target.nextElementSibling && target.nextElementSibling.tagName == 'IMG') {
el.classList.add('jslider-right');
} else {
el.classList.remove('jslider-right');
}
obj.currentImage = target;
// Vertical image
if (obj.currentImage.offsetHeight > obj.currentImage.offsetWidth) {
obj.currentImage.classList.add('jslider-vertical');
}
controls.children[0].innerText = obj.currentImage.getAttribute('title');
}
obj.open = function() {
obj.show();
// Event
if (typeof(obj.options.onopen) == 'function') {
obj.options.onopen(el);
}
}
obj.close = function() {
// Remove control classes
el.classList.remove('jslider-focus');
el.classList.remove('jslider-left');
el.classList.remove('jslider-right');
// Show as a grid depending on the configuration
if (obj.options.grid) {
el.classList.add('jslider-grid');
}
// Remove display
for (var i = 0; i < el.children.length; i++) {
el.children[i].style.display = '';
}
// Remove controls from the component
counter.remove();
controls.remove();
// Current image
obj.currentImage = null;
// Event
if (typeof(obj.options.onclose) == 'function') {
obj.options.onclose(el);
}
}
obj.reset = function() {
el.innerHTML = '';
}
obj.next = function() {
var nextImage = obj.currentImage.nextElementSibling;
if (nextImage && nextImage.tagName === 'IMG') {
obj.show(obj.currentImage.nextElementSibling);
}
}
obj.prev = function() {
if (obj.currentImage.previousElementSibling) {
obj.show(obj.currentImage.previousElementSibling);
}
}
var mouseUp = function(e) {
// Open slider
if (e.target.tagName == 'IMG') {
obj.show(e.target);
} else if (! e.target.classList.contains('jslider-close') && ! (e.target.parentNode.classList.contains('jslider-counter') || e.target.classList.contains('jslider-counter'))){
// Arrow controls
var offsetX = e.offsetX || e.changedTouches[0].clientX;
if (e.target.clientWidth - offsetX < 40) {
// Show next image
obj.next();
} else if (offsetX < 40) {
// Show previous image
obj.prev();
}
}
}
if ('ontouchend' in document.documentElement === true) {
el.addEventListener('touchend', mouseUp);
} else {
el.addEventListener('mouseup', mouseUp);
}
// Add global events
el.addEventListener("swipeleft", function(e) {
obj.next();
e.preventDefault();
e.stopPropagation();
});
el.addEventListener("swiperight", function(e) {
obj.prev();
e.preventDefault();
e.stopPropagation();
});
el.addEventListener('keydown', function(e) {
if (e.which == 27) {
obj.close();
}
});
el.slider = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/tags.js
function Tags(el, options) {
// Redefine configuration
if (el.tags) {
return el.tags.setOptions(options, true);
}
var obj = { type:'tags' };
obj.options = {};
// Limit
var limit = function() {
return obj.options.limit && el.children.length >= obj.options.limit ? true : false;
}
// Search helpers
var search = null;
var searchContainer = null;
obj.setOptions = function(options, reset) {
/**
* @typedef {Object} defaults
* @property {(string|Array)} value - Initial value of the compontent
* @property {number} limit - Max number of tags inside the element
* @property {string} search - The URL for suggestions
* @property {string} placeholder - The default instruction text on the element
* @property {validation} validation - Method to validate the tags
* @property {requestCallback} onbeforechange - Method to be execute before any changes on the element
* @property {requestCallback} onchange - Method to be execute after any changes on the element
* @property {requestCallback} onfocus - Method to be execute when on focus
* @property {requestCallback} onblur - Method to be execute when on blur
* @property {requestCallback} onload - Method to be execute when the element is loaded
*/
var defaults = {
value: '',
limit: null,
search: null,
placeholder: null,
validation: null,
onbeforepaste: null,
onbeforechange: null,
onremoveitem: null,
onlimit: null,
onchange: null,
onfocus: null,
onblur: null,
onload: null,
}
// Loop through though the default configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Placeholder
if (obj.options.placeholder) {
el.setAttribute('data-placeholder', obj.options.placeholder);
} else {
el.removeAttribute('data-placeholder');
}
el.placeholder = obj.options.placeholder;
// Update value
obj.setValue(obj.options.value);
// Validate items
filter();
// Create search box
if (obj.options.search) {
if (! searchContainer) {
searchContainer = document.createElement('div');
el.parentNode.insertBefore(searchContainer, el.nextSibling);
// Create container
search = Search(searchContainer, {
data: obj.options.search,
onselect: function(a,b,c) {
obj.selectIndex(b,c);
}
});
}
} else {
if (searchContainer) {
search = null;
searchContainer.remove();
searchContainer = null;
}
}
return obj;
}
/**
* Add a new tag to the element
* @param {(?string|Array)} value - The value of the new element
*/
obj.add = function(value, focus) {
if (typeof(obj.options.onbeforechange) == 'function') {
var ret = obj.options.onbeforechange(el, obj, obj.options.value, value);
if (ret === false) {
return false;
} else {
if (ret != null) {
value = ret;
}
}
}
// Make sure search is closed
if (search) {
search.close();
}
if (limit()) {
if (typeof(obj.options.onlimit) == 'function') {
obj.options.onlimit(obj, obj.options.limit);
} else {
alert(dictionary.translate('You reach the limit number of entries') + ' ' + obj.options.limit);
}
} else {
// Get node
var node = helpers.getNode();
if (node && node.parentNode && node.parentNode.classList.contains('jtags') &&
node.nextSibling && (! (node.nextSibling.innerText && node.nextSibling.innerText.trim()))) {
div = node.nextSibling;
} else {
// Remove not used last item
if (el.lastChild) {
if (! el.lastChild.innerText.trim()) {
el.removeChild(el.lastChild);
}
}
// Mix argument string or array
if (! value || typeof(value) == 'string') {
var div = createElement(value, value, node);
} else {
for (var i = 0; i <= value.length; i++) {
if (! limit()) {
if (! value[i] || typeof(value[i]) == 'string') {
var t = value[i] || '';
var v = null;
} else {
var t = value[i].text;
var v = value[i].value;
}
// Add element
var div = createElement(t, v);
}
}
}
// Change
change();
}
// Place caret
if (focus) {
setFocus(div);
}
}
}
obj.setLimit = function(limit) {
obj.options.limit = limit;
var n = el.children.length - limit;
while (el.children.length > limit) {
el.removeChild(el.lastChild);
}
}
// Remove a item node
obj.remove = function(node) {
// Remove node
node.parentNode.removeChild(node);
// Make sure element is not blank
if (! el.children.length) {
obj.add('', true);
} else {
change();
}
if (typeof(obj.options.onremoveitem) == 'function') {
obj.options.onremoveitem(el, obj, node);
}
}
/**
* Get all tags in the element
* @return {Array} data - All tags as an array
*/
obj.getData = function() {
var data = [];
for (var i = 0; i < el.children.length; i++) {
// Get value
var text = el.children[i].innerText.replace("\n", "");
// Get id
var value = el.children[i].getAttribute('data-value');
if (! value) {
value = text;
}
// Item
if (text || value) {
data.push({ text: text, value: value });
}
}
return data;
}
/**
* Get the value of one tag. Null for all tags
* @param {?number} index - Tag index number. Null for all tags.
* @return {string} value - All tags separated by comma
*/
obj.getValue = function(index) {
var value = null;
if (index != null) {
// Get one individual value
value = el.children[index].getAttribute('data-value');
if (! value) {
value = el.children[index].innerText.replace("\n", "");
}
} else {
// Get all
var data = [];
for (var i = 0; i < el.children.length; i++) {
value = el.children[i].innerText.replace("\n", "");
if (value) {
data.push(obj.getValue(i));
}
}
value = data.join(',');
}
return value;
}
/**
* Set the value of the element based on a string separeted by (,|;|\r\n)
* @param {mixed} value - A string or array object with values
*/
obj.setValue = function(mixed) {
if (! mixed) {
obj.reset();
} else {
if (el.value != mixed) {
if (Array.isArray(mixed)) {
obj.add(mixed);
} else {
// Remove whitespaces
var text = (''+mixed).trim();
// Tags
var data = extractTags(text);
// Reset
el.innerHTML = '';
// Add tags to the element
obj.add(data);
}
}
}
}
/**
* Reset the data from the element
*/
obj.reset = function() {
// Empty class
el.classList.add('jtags-empty');
// Empty element
el.innerHTML = '<div></div>';
// Execute changes
change();
}
/**
* Verify if all tags in the element are valid
* @return {boolean}
*/
obj.isValid = function() {
var test = 0;
for (var i = 0; i < el.children.length; i++) {
if (el.children[i].classList.contains('jtags_error')) {
test++;
}
}
return test == 0 ? true : false;
}
/**
* Add one element from the suggestions to the element
* @param {object} item - Node element in the suggestions container
*/
obj.selectIndex = function(text, value) {
var node = helpers.getNode();
if (node) {
// Append text to the caret
node.innerText = text;
// Set node id
if (value) {
node.setAttribute('data-value', value);
}
// Remove any error
node.classList.remove('jtags_error');
if (! limit()) {
// Add new item
obj.add('', true);
}
}
}
/**
* Search for suggestions
* @param {object} node - Target node for any suggestions
*/
obj.search = function(node) {
// Search for
var terms = node.innerText;
}
// Destroy tags element
obj.destroy = function() {
// Bind events
el.removeEventListener('mouseup', tagsMouseUp);
el.removeEventListener('keydown', tagsKeyDown);
el.removeEventListener('keyup', tagsKeyUp);
el.removeEventListener('paste', tagsPaste);
el.removeEventListener('focus', tagsFocus);
el.removeEventListener('blur', tagsBlur);
// Remove element
el.parentNode.removeChild(el);
}
var setFocus = function(node) {
if (el.children.length) {
var range = document.createRange();
var sel = window.getSelection();
if (! node) {
var node = el.childNodes[el.childNodes.length-1];
}
range.setStart(node, node.length)
range.collapse(true)
sel.removeAllRanges()
sel.addRange(range)
el.scrollLeft = el.scrollWidth;
}
}
var createElement = function(label, value, node) {
var div = document.createElement('div');
div.textContent = label ? label : '';
if (value) {
div.setAttribute('data-value', value);
}
if (node && node.parentNode.classList.contains('jtags')) {
el.insertBefore(div, node.nextSibling);
} else {
el.appendChild(div);
}
return div;
}
var change = function() {
// Value
var value = obj.getValue();
if (value != obj.options.value) {
obj.options.value = value;
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, obj, obj.options.value);
}
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
filter();
}
/**
* Filter tags
*/
var filter = function() {
for (var i = 0; i < el.children.length; i++) {
if (el.children[i].tagName === 'DIV') {
// Create label design
if (!obj.getValue(i)) {
el.children[i].classList.remove('jtags_label');
} else {
el.children[i].classList.add('jtags_label');
// Validation in place
if (typeof (obj.options.validation) == 'function') {
if (obj.getValue(i)) {
if (!obj.options.validation(el.children[i], el.children[i].innerText, el.children[i].getAttribute('data-value'))) {
el.children[i].classList.add('jtags_error');
} else {
el.children[i].classList.remove('jtags_error');
}
} else {
el.children[i].classList.remove('jtags_error');
}
} else {
el.children[i].classList.remove('jtags_error');
}
}
}
}
isEmpty();
}
var isEmpty = function() {
// Can't be empty
if (! el.innerText.trim()) {
if (! el.children.length || el.children[0].tagName === 'BR') {
el.innerHTML = '';
setFocus(createElement());
}
} else {
el.classList.remove('jtags-empty');
}
}
/**
* Extract tags from a string
* @param {string} text - Raw string
* @return {Array} data - Array with extracted tags
*/
var extractTags = function(text) {
/** @type {Array} */
var data = [];
/** @type {string} */
var word = '';
// Remove whitespaces
text = text.trim();
if (text) {
for (var i = 0; i < text.length; i++) {
if (text[i] == ',' || text[i] == ';' || text[i] == '\n') {
if (word) {
data.push(word.trim());
word = '';
}
} else {
word += text[i];
}
}
if (word) {
data.push(word);
}
}
return data;
}
/** @type {number} */
var anchorOffset = 0;
/**
* Processing event keydown on the element
* @param e {object}
*/
var tagsKeyDown = function(e) {
// Anchoroffset
anchorOffset = window.getSelection().anchorOffset;
// Verify if is empty
isEmpty();
// Comma
if (e.key === 'Tab' || e.key === ';' || e.key === ',') {
var n = window.getSelection().anchorOffset;
if (n > 1) {
if (limit()) {
if (typeof(obj.options.onlimit) == 'function') {
obj.options.onlimit(obj, obj.options.limit)
}
} else {
obj.add('', true);
}
}
e.preventDefault();
} else if (e.key == 'Enter') {
if (! search || ! search.isOpened()) {
var n = window.getSelection().anchorOffset;
if (n > 1) {
if (! limit()) {
obj.add('', true);
}
}
e.preventDefault();
}
} else if (e.key == 'Backspace') {
// Back space - do not let last item to be removed
if (el.children.length == 1 && window.getSelection().anchorOffset < 1) {
e.preventDefault();
}
}
// Search events
if (search) {
search.keydown(e);
}
// Verify if is empty
isEmpty();
}
/**
* Processing event keyup on the element
* @param e {object}
*/
var tagsKeyUp = function(e) {
if (e.which == 39) {
// Right arrow
var n = window.getSelection().anchorOffset;
if (n > 1 && n == anchorOffset) {
obj.add('', true);
}
} else if (e.which == 13 || e.which == 38 || e.which == 40) {
e.preventDefault();
} else {
if (search) {
search.keyup(e);
}
}
filter();
}
/**
* Processing event paste on the element
* @param e {object}
*/
var tagsPaste = function(e) {
if (e.clipboardData || e.originalEvent.clipboardData) {
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
} else if (window.clipboardData) {
var text = window.clipboardData.getData('Text');
}
var data = extractTags(text);
if (typeof(obj.options.onbeforepaste) == 'function') {
var ret = obj.options.onbeforepaste(el, obj, data);
if (ret === false) {
e.preventDefault();
return false;
} else {
if (ret) {
data = ret;
}
}
}
if (data.length > 1) {
obj.add(data, true);
e.preventDefault();
} else if (data[0]) {
document.execCommand('insertText', false, data[0])
e.preventDefault();
}
}
/**
* Processing event mouseup on the element
* @param e {object}
*/
var tagsMouseUp = function(e) {
if (e.target.parentNode && e.target.parentNode.classList.contains('jtags')) {
if (e.target.classList.contains('jtags_label') || e.target.classList.contains('jtags_error')) {
var rect = e.target.getBoundingClientRect();
if (rect.width - (e.clientX - rect.left) < 16) {
obj.remove(e.target);
}
}
}
// Set focus in the last item
if (e.target == el) {
setFocus();
}
}
var tagsFocus = function() {
if (! el.classList.contains('jtags-focus')) {
if (! el.children.length || obj.getValue(el.children.length - 1)) {
if (! limit()) {
createElement('');
}
}
if (typeof(obj.options.onfocus) == 'function') {
obj.options.onfocus(el, obj, obj.getValue());
}
el.classList.add('jtags-focus');
}
}
var tagsBlur = function() {
if (el.classList.contains('jtags-focus')) {
if (search) {
search.close();
}
for (var i = 0; i < el.children.length - 1; i++) {
// Create label design
if (! obj.getValue(i)) {
el.removeChild(el.children[i]);
}
}
change();
el.classList.remove('jtags-focus');
if (typeof(obj.options.onblur) == 'function') {
obj.options.onblur(el, obj, obj.getValue());
}
}
}
var init = function() {
// Bind events
if ('touchend' in document.documentElement === true) {
el.addEventListener('touchend', tagsMouseUp);
} else {
el.addEventListener('mouseup', tagsMouseUp);
}
el.addEventListener('keydown', tagsKeyDown);
el.addEventListener('keyup', tagsKeyUp);
el.addEventListener('paste', tagsPaste);
el.addEventListener('focus', tagsFocus);
el.addEventListener('blur', tagsBlur);
// Editable
el.setAttribute('contenteditable', true);
// Prepare container
el.classList.add('jtags');
// Initial options
obj.setOptions(options);
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj);
}
// Change methods
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
el.tags = obj;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/upload.js
function Upload(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
type: 'image',
extension: '*',
input: false,
minWidth: false,
maxWidth: null,
maxHeight: null,
maxJpegSizeBytes: null, // For example, 350Kb would be 350000
onchange: null,
multiple: false,
remoteParser: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Multiple
if (obj.options.multiple == true) {
el.setAttribute('data-multiple', true);
}
// Container
el.content = [];
// Upload icon
el.classList.add('jupload');
if (obj.options.input == true) {
el.classList.add('input');
}
obj.add = function(data) {
// Reset container for single files
if (obj.options.multiple == false) {
el.content = [];
el.innerText = '';
}
// Append to the element
if (obj.options.type == 'image') {
var img = document.createElement('img');
img.setAttribute('src', data.file);
img.setAttribute('tabindex', -1);
if (! el.getAttribute('name')) {
img.className = 'jfile';
img.content = data;
}
el.appendChild(img);
} else {
if (data.name) {
var name = data.name;
} else {
var name = data.file;
}
var div = document.createElement('div');
div.innerText = name || obj.options.type;
div.classList.add('jupload-item');
div.setAttribute('tabindex', -1);
el.appendChild(div);
}
if (data.content) {
data.file = helpers.guid();
}
// Push content
el.content.push(data);
// Onchange
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, data);
}
}
obj.addFromFile = function(file) {
var type = file.type.split('/');
if (type[0] == obj.options.type) {
var readFile = new FileReader();
readFile.addEventListener("load", function (v) {
var data = {
file: v.srcElement.result,
extension: file.name.substr(file.name.lastIndexOf('.') + 1),
name: file.name,
size: file.size,
lastmodified: file.lastModified,
content: v.srcElement.result,
}
obj.add(data);
});
readFile.readAsDataURL(file);
} else {
alert(dictionary.translate('This extension is not allowed'));
}
}
obj.addFromUrl = function(src) {
if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
console.error('remoteParser not defined in your initialization');
} else {
// This is to process cross domain images
if (src.substr(0,4) == 'data') {
var extension = src.split(';')
extension = extension[0].split('/');
var type = extension[0].replace('data:','');
if (type == obj.options.type) {
var data = {
file: src,
name: '',
extension: extension[1],
content: src,
}
obj.add(data);
} else {
alert(obj.options.text.extensionNotAllowed);
}
} else {
var extension = src.substr(src.lastIndexOf('.') + 1);
// Work for cross browsers
src = obj.options.remoteParser + src;
// Get remove content
ajax({
url: src,
type: 'GET',
dataType: 'blob',
success: function(data) {
//add(extension[0].replace('data:',''), data);
}
})
}
}
}
var getDataURL = function(canvas, type) {
var compression = 0.92;
var lastContentLength = null;
var content = canvas.toDataURL(type, compression);
while (obj.options.maxJpegSizeBytes && type === 'image/jpeg' &&
content.length > obj.options.maxJpegSizeBytes && content.length !== lastContentLength) {
// Apply the compression
compression *= 0.9;
lastContentLength = content.length;
content = canvas.toDataURL(type, compression);
}
return content;
}
var mime = obj.options.type + '/' + obj.options.extension;
var input = document.createElement('input');
input.type = 'file';
input.setAttribute('accept', mime);
input.onchange = function() {
for (var i = 0; i < this.files.length; i++) {
obj.addFromFile(this.files[i]);
}
}
// Allow multiple files
if (obj.options.multiple == true) {
input.setAttribute('multiple', true);
}
var current = null;
el.addEventListener("click", function(e) {
current = null;
if (! el.children.length || e.target === el) {
helpers.click(input);
} else {
if (e.target.parentNode == el) {
current = e.target;
}
}
});
el.addEventListener("dblclick", function(e) {
helpers.click(input);
});
el.addEventListener('dragenter', function(e) {
el.style.border = '1px dashed #000';
});
el.addEventListener('dragleave', function(e) {
el.style.border = '1px solid #eee';
});
el.addEventListener('dragstop', function(e) {
el.style.border = '1px solid #eee';
});
el.addEventListener('dragover', function(e) {
e.preventDefault();
});
el.addEventListener('keydown', function(e) {
if (current && e.which == 46) {
var index = Array.prototype.indexOf.call(el.children, current);
if (index >= 0) {
el.content.splice(index, 1);
current.remove();
current = null;
}
}
});
el.addEventListener('drop', function(e) {
e.preventDefault();
e.stopPropagation();
var html = (e.originalEvent || e).dataTransfer.getData('text/html');
var file = (e.originalEvent || e).dataTransfer.files;
if (file.length) {
for (var i = 0; i < e.dataTransfer.files.length; i++) {
obj.addFromFile(e.dataTransfer.files[i]);
}
} else if (html) {
if (obj.options.multiple == false) {
el.innerText = '';
}
// Create temp element
let img = [];
utils_filter(html, img);
if (img.length) {
for (var i = 0; i < img.length; i++) {
obj.addFromUrl(img[i]);
}
}
}
el.style.border = '1px solid #eee';
return false;
});
el.val = function(val) {
if (val === undefined) {
return el.content && el.content.length ? el.content : null;
} else {
// Reset
el.innerText = '';
el.content = [];
if (val) {
if (Array.isArray(val)) {
for (var i = 0; i < val.length; i++) {
if (typeof(val[i]) == 'string') {
obj.add({ file: val[i] });
} else {
obj.add(val[i]);
}
}
} else if (typeof(val) == 'string') {
obj.add({ file: val });
}
}
}
}
el.upload = el.image = obj;
return obj;
}
// EXTERNAL MODULE: ./packages/sha512/sha512.js
var sha512 = __webpack_require__(195);
var sha512_default = /*#__PURE__*/__webpack_require__.n(sha512);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/calendar/dist/index.js
var calendar_dist = __webpack_require__(763);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/color/dist/index.js
var color_dist = __webpack_require__(541);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/contextmenu/dist/index.js
var contextmenu_dist = __webpack_require__(238);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/dropdown/dist/index.js
var dropdown_dist = __webpack_require__(692);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/modal/dist/index.js
var modal_dist = __webpack_require__(72);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/switch/dist/index.js
var switch_dist = __webpack_require__(539);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/tabs/dist/index.js
var tabs_dist = __webpack_require__(560);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/topmenu/dist/index.js
var topmenu_dist = __webpack_require__(330);
// EXTERNAL MODULE: ./node_modules/@lemonadejs/rating/dist/index.js
var rating_dist = __webpack_require__(867);
;// CONCATENATED MODULE: ./src/jsuites.js
var jSuites = {
// Helpers
...dictionary,
...helpers,
/** Current version */
version: '6.3.1',
/** Bind new extensions to Jsuites */
setExtensions: function(o) {
if (typeof(o) == 'object') {
var k = Object.keys(o);
for (var i = 0; i < k.length; i++) {
jSuites[k[i]] = o[k[i]];
}
}
},
tracking: tracking,
path: Path,
sorting: Sorting,
lazyLoading: LazyLoading,
// Plugins
ajax: ajax,
animation: animation,
calendar: calendar,
color: Color,
contextmenu: contextmenu,
dropdown: dropdown,
editor: editor,
floating: floating,
form: plugins_form,
mask: mask,
modal: modal,
notification: notification,
palette: palette,
picker: Picker,
progressbar: Progressbar,
rating: Rating,
search: Search,
slider: Slider,
tabs: Tabs,
tags: Tags,
toolbar: Toolbar,
upload: Upload,
validations: validations,
}
// Legacy
jSuites.image = Upload;
jSuites.image.create = function(data) {
var img = document.createElement('img');
img.setAttribute('src', data.file);
img.className = 'jfile';
img.setAttribute('tabindex', -1);
img.content = data;
return img;
}
jSuites.tracker = plugins_form;
jSuites.loading = animation.loading;
jSuites.sha512 = (sha512_default());
/** Core events */
const Events = function() {
if (typeof(window['jSuitesStateControl']) === 'undefined') {
window['jSuitesStateControl'] = [];
} else {
// Do nothing
return;
}
const find = function(DOMElement, component) {
if (DOMElement[component.type] && DOMElement[component.type] == component) {
return true;
}
if (DOMElement.component && DOMElement.component == component) {
return true;
}
if (DOMElement.parentNode) {
return find(DOMElement.parentNode, component);
}
return false;
}
const isOpened = function(e) {
let state = window['jSuitesStateControl'];
if (state && state.length > 0) {
for (let i = 0; i < state.length; i++) {
if (state[i] && ! find(e, state[i])) {
state[i].close();
}
}
}
}
// Width of the border
let cornerSize = 15;
// Current element
let element = null;
// Controllers
let editorAction = false;
// Event state
let state = {
x: null,
y: null,
}
// Tooltip element
let tooltip = document.createElement('div')
tooltip.classList.add('jtooltip');
const isWebcomponent = function(e) {
return e && (e.shadowRoot || (e.tagName && e.tagName.includes('-')));
}
const getElement = function(e) {
let d;
let element;
// Which component I am clicking
let path = e.path || (e.composedPath && e.composedPath());
// If path available get the first element in the chain
if (path) {
element = path[0];
// Adjustment sales force
if (element && isWebcomponent(element) && ! element.shadowRoot && e.toElement) {
element = e.toElement;
}
} else {
// Try to guess using the coordinates
if (e.target && isWebcomponent(e.target)) {
d = e.target.shadowRoot;
} else {
d = document;
}
// Get the first target element
element = d.elementFromPoint(x, y);
}
return element;
}
// Events
const mouseDown = function(e) {
// Verify current components tracking
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
let element = getElement(e);
// Editable
let editable = element && element.tagName === 'DIV' && element.getAttribute('contentEditable');
// Check if this is the floating
let item = jSuites.findElement(element, 'jpanel');
// Jfloating found
if (item && ! item.classList.contains("readonly") && ! editable) {
// Keep the tracking information
let rect = item.getBoundingClientRect();
let angle = 0;
if (item.style.rotate) {
// Extract the angle value from the match and convert it to a number
angle = parseFloat(item.style.rotate);
}
let action = 'move';
if (element.getAttribute('data-action')) {
action = element.getAttribute('data-action');
} else {
if (item.style.cursor) {
action = 'resize';
} else {
item.style.cursor = 'move';
}
}
// Action
editorAction = {
action: action,
a: angle,
e: item,
x: x,
y: y,
l: rect.left,
t: rect.top,
b: rect.bottom,
r: rect.right,
w: rect.width,
h: rect.height,
d: item.style.cursor,
actioned: false,
}
// Make sure width and height styling is OK
if (! item.style.width) {
item.style.width = rect.width + 'px';
}
if (! item.style.height) {
item.style.height = rect.height + 'px';
}
} else {
// No floating action found
editorAction = false;
}
isOpened(element);
focus(e);
}
const calculateAngle = function(x1, y1, x2, y2, x3, y3) {
// Calculate dx and dy for the first line
const dx1 = x2 - x1;
const dy1 = y2 - y1;
// Calculate dx and dy for the second line
const dx2 = x3 - x1;
const dy2 = y3 - y1;
// Calculate the angle for the first line
let angle1 = Math.atan2(dy1, dx1);
// Calculate the angle for the second line
let angle2 = Math.atan2(dy2, dx2);
// Calculate the angle difference in radians
let angleDifference = angle2 - angle1;
// Convert the angle difference to degrees
angleDifference = angleDifference * (180 / Math.PI);
// Normalize the angle difference to be within [0, 360) degrees
if (angleDifference < 0) {
angleDifference += 360;
}
return angleDifference;
}
const mouseUp = function(e) {
if (editorAction && editorAction.e) {
if (typeof(editorAction.e.refresh) == 'function' && state.actioned) {
editorAction.e.refresh();
}
editorAction.e.style.cursor = '';
}
// Reset
state = {
x: null,
y: null,
}
editorAction = false;
}
const mouseMove = function(e) {
if (editorAction) {
let x = e.clientX || e.pageX;
let y = e.clientY || e.pageY;
if (state.x == null && state.y == null) {
state.x = x;
state.y = y;
}
// Action on going
if (editorAction.action === 'move') {
var dx = x - state.x;
var dy = y - state.y;
var top = editorAction.e.offsetTop + dy;
var left = editorAction.e.offsetLeft + dx;
// Update position
editorAction.e.style.top = top + 'px';
editorAction.e.style.left = left + 'px';
// Update element
if (typeof (editorAction.e.refresh) == 'function') {
state.actioned = true;
editorAction.e.refresh('position', top, left);
}
} else if (editorAction.action === 'rotate') {
let ox = editorAction.l+editorAction.w/2;
let oy = editorAction.t+editorAction.h/2;
let angle = calculateAngle(ox, oy, editorAction.x, editorAction.y, x, y);
angle = angle + editorAction.a % 360;
angle = Math.round(angle / 2) * 2;
editorAction.e.style.rotate = `${angle}deg`;
// Update element
if (typeof (editorAction.e.refresh) == 'function') {
state.actioned = true;
editorAction.e.refresh('rotate', angle);
}
} else if (editorAction.action === 'resize') {
let top = null;
let left = null;
let width = null;
let height = null;
if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
width = editorAction.e.offsetWidth + (x - state.x);
if (e.shiftKey) {
height = editorAction.e.offsetHeight + (x - state.x) * (editorAction.e.offsetHeight / editorAction.e.offsetWidth);
}
} else if (editorAction.d === 'w-resize' || editorAction.d == 'nw-resize'|| editorAction.d == 'sw-resize') {
left = editorAction.e.offsetLeft + (x - state.x);
width = editorAction.e.offsetLeft + editorAction.e.offsetWidth - left;
if (e.shiftKey) {
height = editorAction.e.offsetHeight - (x - state.x) * (editorAction.e.offsetHeight / editorAction.e.offsetWidth);
}
}
if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
if (! height) {
height = editorAction.e.offsetHeight + (y - state.y);
}
} else if (editorAction.d === 'n-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'nw-resize') {
top = editorAction.e.offsetTop + (y - state.y);
height = editorAction.e.offsetTop + editorAction.e.offsetHeight - top;
}
if (top) {
editorAction.e.style.top = top + 'px';
}
if (left) {
editorAction.e.style.left = left + 'px';
}
if (width) {
editorAction.e.style.width = width + 'px';
}
if (height) {
editorAction.e.style.height = height + 'px';
}
// Update element
if (typeof(editorAction.e.refresh) == 'function') {
state.actioned = true;
editorAction.e.refresh('dimensions', width, height);
}
}
state.x = x;
state.y = y;
} else {
let element = getElement(e);
// Resize action
let item = jSuites.findElement(element, 'jpanel');
// Found eligible component
if (item) {
// Resizing action
let controls = item.classList.contains('jpanel-controls');
if (controls) {
let position = element.getAttribute('data-position');
if (position) {
item.style.cursor = position;
} else {
item.style.cursor = '';
}
} else if (item.getAttribute('tabindex')) {
let rect = item.getBoundingClientRect();
//console.log(e.clientY - rect.top, rect.width - (e.clientX - rect.left), cornerSize)
if (e.clientY - rect.top < cornerSize) {
if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'ne-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'nw-resize';
} else {
item.style.cursor = 'n-resize';
}
} else if (rect.height - (e.clientY - rect.top) < cornerSize) {
if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'se-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'sw-resize';
} else {
item.style.cursor = 's-resize';
}
} else if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'e-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'w-resize';
} else {
item.style.cursor = '';
}
}
}
}
}
let position = ['n','ne','e','se','s','sw','w','nw','rotate'];
position.forEach(function(v, k) {
position[k] = document.createElement('div');
position[k].classList.add('jpanel-action');
if (v === 'rotate') {
position[k].setAttribute('data-action', 'rotate');
} else {
position[k].setAttribute('data-action', 'resize');
position[k].setAttribute('data-position', v + '-resize');
}
});
let currentElement;
const focus = function(e) {
let element = getElement(e);
// Check if this is the floating
let item = jSuites.findElement(element, 'jpanel');
if (item && ! item.classList.contains("readonly") && item.classList.contains('jpanel-controls')) {
item.append(...position);
if (! item.classList.contains('jpanel-rotate')) {
position[position.length-1].remove();
}
currentElement = item;
} else {
blur(e);
}
}
const blur = function(e) {
if (currentElement) {
position.forEach(function(v) {
v.remove();
});
currentElement = null;
}
}
const mouseOver = function(e) {
let element = getElement(e);
var message = element.getAttribute('data-tooltip');
if (message) {
// Instructions
tooltip.innerText = message;
// Position
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
tooltip.style.top = y + 'px';
tooltip.style.left = x + 'px';
document.body.appendChild(tooltip);
} else if (tooltip.innerText) {
tooltip.innerText = '';
document.body.removeChild(tooltip);
}
}
const contextMenu = function(e) {
var item = document.activeElement;
if (item && typeof(item.contextmenu) == 'function') {
// Create edition
item.contextmenu(e);
e.preventDefault();
e.stopImmediatePropagation();
} else {
// Search for possible context menus
item = jSuites.findElement(e.target, function(o) {
return o.tagName && o.getAttribute('aria-contextmenu-id');
});
if (item) {
var o = document.querySelector('#' + item);
if (! o) {
console.error('JSUITES: contextmenu id not found: ' + item);
} else {
o.contextmenu.open(e);
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
}
const keyDown = function(e) {
let item = document.activeElement;
if (item) {
if (e.key === "Delete" && typeof(item.delete) == 'function') {
item.delete();
e.preventDefault();
e.stopImmediatePropagation();
}
}
let state = window['jSuitesStateControl'];
if (state && state.length > 0) {
item = state[state.length - 1];
if (item) {
if (e.key === "Escape" && typeof(item.isOpened) == 'function' && typeof(item.close) == 'function') {
if (item.isOpened()) {
item.close();
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
}
}
const input = function(e) {
if (e.target.getAttribute('data-mask') || e.target.mask) {
jSuites.mask.oninput(e);
}
}
document.addEventListener('focusin', focus);
document.addEventListener('mouseup', mouseUp);
document.addEventListener("mousedown", mouseDown);
document.addEventListener('mousemove', mouseMove);
document.addEventListener('mouseover', mouseOver);
document.addEventListener('keydown', keyDown);
document.addEventListener('contextmenu', contextMenu);
document.addEventListener('input', input);
}
if (typeof(document) !== "undefined") {
Events();
}
/* harmony default export */ var jsuites = (jSuites);
}();
jSuites = __webpack_exports__["default"];
/******/ })()
;
return jSuites;
})));