From 78f04ebce6b01724702735b19d49faf258596c4e Mon Sep 17 00:00:00 2001 From: dirk4000 Date: Sun, 9 Nov 2025 17:08:43 +0100 Subject: [PATCH 1/2] Update w2ui.js Add regex search operator to w2ui Extends w2ui with full regex support for searches in grids. Includes intelligent DOM traversal for highlighting and graceful fallback for invalid patterns. --- dist/w2ui.js | 206 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 176 insertions(+), 30 deletions(-) diff --git a/dist/w2ui.js b/dist/w2ui.js index 12629fe8f..068c72e7b 100644 --- a/dist/w2ui.js +++ b/dist/w2ui.js @@ -393,6 +393,8 @@ const w2locale = { 'record': '---', 'records': '---', 'Refreshing...': '---', + 'RegEx': '---', + 'regex': '---', 'Reload data in the list': '---', 'Remove': '---', 'Remove This Field': '---', @@ -2569,10 +2571,12 @@ class Utils { } return str.replace(/\${([^}]+)?}/g, function($1, $2) { return replace_obj[$2]||$2 }) } - marker(el, items, options = { onlyFirst: false, wholeWord: false }) { + marker(el, items, options = { onlyFirst: false, wholeWord: false, isRegex: false}) { options.tag ??= 'span' options.class ??= 'w2ui-marker' options.raplace = (matched) => `<${options.tag} class="${options.class}">${matched}` + + const isRegexSearch = options.isRegex || false; if (!Array.isArray(items)) { if (items != null && items !== '') { items = [items] @@ -2583,14 +2587,125 @@ class Utils { if (typeof el == 'string') { _clearMerkers(el) items.forEach(item => { - el = _replace(el, item, options.raplace) + if (isRegexSearch) { + // For regex searches with string elements + try { + let flags = 'i' + (!options.onlyFirst ? 'g' : '') + let regex = new RegExp(item, flags) + el = el.replace(regex, options.raplace) + } catch (e) { + console.error('Invalid regular expression:', e) + // Fallback to standard replace + el = _replace(el, item, options.raplace) + } + } else { + // Standard string replace + el = _replace(el, item, options.raplace) + } }) } else { query(el).each(el => { _clearMerkers(el) - items.forEach(item => { - el.innerHTML = _replace(el.innerHTML, item, options.raplace) - }) + if (isRegexSearch) { + // For regex searches, use DOM traversal approach + items.forEach(pattern => { + try { + let flags = 'i' // Always case-insensitive + if (!options.onlyFirst) { + flags += 'g' // Add 'g' for global unless onlyFirst is true + } + if (options.wholeWord) { + // If wholeWord is true, wrap the pattern with word boundary markers + pattern = '\b' + pattern + '\b' + } + + let regex = new RegExp(pattern, flags) + + // Get all text nodes + let textNodes = [] + function getTextNodes(node) { + if (node.nodeType === 3) { // Text node + textNodes.push(node) + } else if (node.nodeType === 1) { // Element node + // Skip script and style tags + if (node.tagName !== 'SCRIPT' && node.tagName !== 'STYLE') { + for (let i = 0; i < node.childNodes.length; i++) { + getTextNodes(node.childNodes[i]) + } + } + } + } + + getTextNodes(el) + + // Process each text node + textNodes.forEach(textNode => { + let text = textNode.nodeValue + let matches = [] + let match + + // Find all matches + if (options.onlyFirst) { + match = regex.exec(text) + if (match) matches.push({ + index: match.index, + text: match[0] + }) + } else { + while ((match = regex.exec(text)) !== null) { + matches.push({ + index: match.index, + text: match[0] + }) + } + } + + // Apply highlighting + if (matches.length > 0) { + let parent = textNode.parentNode + let fragment = document.createDocumentFragment() + let lastIndex = 0 + + matches.forEach(match => { + // Add text before match + if (match.index > lastIndex) { + fragment.appendChild(document.createTextNode( + text.substring(lastIndex, match.index) + )) + } + + // Add highlighted match + let span = document.createElement(options.tag) + span.className = options.class + span.appendChild(document.createTextNode(match.text)) + fragment.appendChild(span) + + lastIndex = match.index + match.text.length + }) + + // Add remaining text + if (lastIndex < text.length) { + fragment.appendChild(document.createTextNode( + text.substring(lastIndex) + )) + } + + // Replace the text node with our fragment + parent.replaceChild(fragment, textNode) + } + }) + } catch (e) { + console.error('Invalid regular expression:', e) + // Fallback to standard innerHTML replace + el.innerHTML = _replace(el.innerHTML, pattern, options.raplace) + } + }) + } else { + // Standard innerHTML replace for non-regex + items.forEach(item => { + el.innerHTML = _replace(el.innerHTML, item, options.raplace) + }) + } }) } return el @@ -5579,7 +5694,7 @@ class MenuTooltip extends Tooltip { menuStyle : '', search : false, // search input inside tooltip filter : false, // will apply filter, if anchor is INPUT or TEXTAREA - match : 'contains', // is, begins, ends, contains + match : 'contains', // is, begins, ends, contains, regexp markSearch : false, prefilter : false, altRows : false, @@ -6252,18 +6367,29 @@ class MenuTooltip extends Tooltip { return prom } items.forEach(item => { - let prefix = '' - let suffix = '' - if (['is', 'begins', 'begins with'].indexOf(options.match) !== -1) prefix = '^' - if (['is', 'ends', 'ends with'].indexOf(options.match) !== -1) suffix = '$' - try { - let re = new RegExp(prefix + search + suffix, 'i') - if (re.test(item.text) || item.text === '...') { - item.hidden = false - } else { - item.hidden = true - } - } catch (e) {} + if (options.match == 'regex') { + try { + let re = new RegExp(search, 'i') + if (re.test(item.text) || item.text === '...') { + item.hidden = false + } else { + item.hidden = true + } + } catch (e) {} + } else { + let prefix = '' + let suffix = '' + if (['is', 'begins', 'begins with'].indexOf(options.match) !== -1) prefix = '^' + if (['is', 'ends', 'ends with'].indexOf(options.match) !== -1) suffix = '$' + try { + let re = new RegExp(prefix + search + suffix, 'i') + if (re.test(item.text) || item.text === '...') { + item.hidden = false + } else { + item.hidden = true + } + } catch (e) {} + } // do not show selected items if (options.hideSelected && selectedIds.includes(item.id)) { item.hidden = true @@ -12087,12 +12213,12 @@ class w2grid extends w2base { 'save' : { type: 'button', id: 'w2ui-save', text: 'Save', tooltip: w2utils.lang('Save changed records'), icon: 'w2ui-icon-check' } } this.operators = { // for search fields - 'text' : ['is', 'begins', 'contains', 'ends'], // could have "in" and "not in" - 'number' : ['=', 'between', '>', '<', '>=', '<='], + 'text' : ['is', 'begins', 'contains', 'ends', 'regex'], // could have "in" and "not in" + 'number' : ['=', 'between', '>', '<', '>=', '<=', 'regex'], 'date' : ['is', { oper: 'less', text: 'before'}, { oper: 'more', text: 'since' }, 'between'], 'list' : ['is'], 'hex' : ['is', 'between'], - 'color' : ['is', 'begins', 'contains', 'ends'], + 'color' : ['is', 'begins', 'contains', 'ends', 'regex'], 'enum' : ['in', 'not in'] // -- all possible // "text" : ['is', 'begins', 'contains', 'ends'], @@ -13001,6 +13127,16 @@ class w2grid extends w2base { let lastIndex = val1.lastIndexOf(val2) if (lastIndex !== -1 && lastIndex == val1.length - val2.length) fl++ // do not hide record break + case 'regex': + try { + const re = new RegExp(val2, 'i') // Case-insensitive regex + if (re.test(val1)) fl++ // do not hide record + } catch (e) { + console.error('Invalid regular expression:', e) + // use fallback + if (val1.indexOf(val2) >= 0) fl++ + } + break; } } if ((obj.last.logic == 'OR' && fl !== 0) || (obj.last.logic == 'AND' && fl == obj.searchData.length)) { @@ -16921,12 +17057,17 @@ class w2grid extends w2base { let fld = this.getSearch(sdata.field) if (!fld || fld.hidden) continue let ind = this.getColumn(sdata.field, true) - search.push({ field: sdata.field, search: sdata.value, col: ind }) + if (sdata.operator == 'regex') { + const defaultOptions = { onlyFirst: false, wholeWord: false, isRegex: true } + search.push({ field: sdata.field, search: sdata.value, col: ind, options: defaultOptions }) + } else { + search.push({ field: sdata.field, search: sdata.value, col: ind }) + } } if (search.length > 0) { search.forEach((item) => { let el = query(this.box).find('td[col="'+ item.col +'"]:not(.w2ui-head)') - w2utils.marker(el, item.search) + w2utils.marker(el, item.search, item.options) }) } }, 50) @@ -19568,12 +19709,17 @@ class w2grid extends w2base { let fld = obj.getSearch(sdata.field) if (!fld || fld.hidden) continue let ind = obj.getColumn(sdata.field, true) - search.push({ field: sdata.field, search: sdata.value, col: ind }) + if (sdata.operator == 'regex') { + const defaultOptions = { onlyFirst: false, wholeWord: false, isRegex: true } + search.push({ field: sdata.field, search: sdata.value, col: ind, options: defaultOptions }) + } else { + search.push({ field: sdata.field, search: sdata.value, col: ind }) + } } if (search.length > 0) { search.forEach((item) => { let el = query(obj.box).find('td[col="'+ item.col +'"]:not(.w2ui-head)') - w2utils.marker(el, item.search) + w2utils.marker(el, item.search, item.options) }) } }, 50) @@ -22978,7 +23124,7 @@ class w2field extends w2base { items : [], // array of items, can be a function selected : {}, // selected item itemMap : null, // can be { id: 'id', text: 'text' } to specify field mapping for an item - match : 'begins', // ['contains', 'is', 'begins', 'ends'] + match : 'begins', // ['contains', 'is', 'begins', 'ends', 'regex'] filter : true, // weather to filter at all compare : null, // compare function for filtering prefix : '', // prefix for input @@ -23032,7 +23178,7 @@ class w2field extends w2base { } options = w2utils.extend({}, defaults, options) // validate match - let valid = ['is', 'begins', 'contains', 'ends'] + let valid = ['is', 'begins', 'contains', 'ends', 'regex'] if (!valid.includes(options.match)) { console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(', ')}.`) } @@ -23055,7 +23201,7 @@ class w2field extends w2base { selected : [], itemMap : null, // can be { id: 'id', text: 'text' } to specify field mapping for an item max : 0, // max number of selected items, 0 - unlimited - match : 'begins', // ['contains', 'is', 'begins', 'ends'] + match : 'begins', // ['contains', 'is', 'begins', 'ends', 'regex'] filter : true, // if true, will apply filtering compare : null, // compare function for filtering // -- remote items -- @@ -23100,7 +23246,7 @@ class w2field extends w2base { options._items_fun = options.items } // validate match - let valid = ['is', 'begins', 'contains', 'ends'] + let valid = ['is', 'begins', 'contains', 'ends', 'regex'] if (!valid.includes(options.match)) { console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(', ')}.`) } @@ -24769,4 +24915,4 @@ if (global) { w2popup, w2alert, w2confirm, w2prompt, Dialog, w2tooltip, w2menu, w2color, w2date, Tooltip, w2toolbar, w2sidebar, w2tabs, w2layout, w2grid, w2form, w2field -}); \ No newline at end of file +}); From 42748432f3f6050f3fbc1c5ea8992e77838d7961 Mon Sep 17 00:00:00 2001 From: Dirk Heinrich Date: Sun, 23 Nov 2025 22:17:26 +0100 Subject: [PATCH 2/2] feat: add initial regex search support and marker highlighting to W2UI demo - Implement regex-based search for all grid columns - Add marker highlighting for matched substrings - Introduce validation and safe handling for invalid regex patterns - Integrate search+highlight logic into existing grid reload lifecycle --- demos/regex-demo.html | 413 +++++++++++++++++++++++++++++++++++++ demos/regex-grid-demo.html | 306 +++++++++++++++++++++++++++ dist/w2ui.es6.js | 152 ++++++++++++-- dist/w2ui.es6.min.js | 8 +- dist/w2ui.js | 58 ++---- gulpfile.js | 7 +- package.json | 8 +- patch.diff | Bin 0 -> 35330 bytes src/w2locale.js | 2 + src/w2tooltip.js | 37 ++-- src/w2utils.js | 123 ++++++++++- 11 files changed, 1023 insertions(+), 91 deletions(-) create mode 100644 demos/regex-demo.html create mode 100644 demos/regex-grid-demo.html create mode 100644 patch.diff diff --git a/demos/regex-demo.html b/demos/regex-demo.html new file mode 100644 index 000000000..0d02d4427 --- /dev/null +++ b/demos/regex-demo.html @@ -0,0 +1,413 @@ + + + + + w2ui - Regex Feature Demo + + + + + + +
+

w2ui Regex Feature Demo

+

This demo showcases the new regex support in the w2ui library, featuring enhanced text marking and menu filtering with regular expressions.

+ +

1. Marker Function with Regex Support

+
+

The marker() function now supports regex patterns for advanced text highlighting.

+ +
+ + + + +
+ +
// Example usage:
+w2utils.marker(element, ['pattern'], {
+  isRegex: true,
+  onlyFirst: false,
+  wholeWord: false
+}) +
+ +
+ The quick brown fox jumps over the lazy dog.
+ Email: user123@example.com, admin456@test.org
+ Phone: 555-1234, 555-5678
+ Product codes: ABC-001, DEF-002, GHI-003
+ Dates: 2025-01-15, 2025-02-20, 2025-03-10
+ Numbers: 42, 123, 9999
+ Special text: Test123, Demo456, Sample789 +
+
+
+ +

2. Menu Tooltip with Regex Search

+
+

The MenuTooltip now supports regex patterns for flexible filtering of menu items.

+ +
+ + + + +
+ +
// Example usage:
+w2menu.search = true;
+w2menu.match = 'regex'; // or 'contains', 'begins', 'ends', 'is'
+w2menu.filter = true; +
+ + + +
+ +

3. Practical Examples

+
+

Example 1: Highlight Email Addresses

+

Click "Try It" to highlight all email addresses in the text below:

+
+w2utils.marker(element, ['[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}'], {
+  isRegex: true,
+  class: 'w2ui-marker'
+}) +
+ +
+ Contact our support team at support@example.com or sales@company.org. You can also reach us at info@test.net for general inquiries. +
+ +

Example 2: Highlight Phone Numbers

+

Click "Try It" to highlight all phone numbers in the text below:

+
+w2utils.marker(element, ['\\d{3}-\\d{4}'], {
+  isRegex: true,
+  class: 'w2ui-marker'
+}) +
+ +
+ Call our main office at 555-1234 or customer service at 555-5678. Emergency line: 555-9999. +
+ +

Example 3: Case-Insensitive Word Search

+

Click "Try It" to highlight keywords (test, demo, sample) in the text below:

+
+w2utils.marker(element, ['test|demo|sample'], {
+  isRegex: true,
+  onlyFirst: false
+}) +
+ +
+ This is a test application. Check out the demo version or the sample code. We use testing frameworks for quality assurance. The demo shows all features. +
+
+ +

4. Interactive Menu Testing

+
+

Test different regex patterns against the menu items below. Try patterns like:

+
    +
  • ^Java - starts with "Java"
  • +
  • Test - contains "Test"
  • +
  • Script|Python - contains "Script" or "Python"
  • +
  • ^[A-Z].*Basic - starts with capital letter and ends with "Basic"
  • +
  • .*-.* - contains a hyphen
  • +
+
+ + + + +
+ +
+ +

5. Features

+
+
    +
  • Regex Pattern Matching: Support for complex pattern matching with full regex syntax
  • +
  • Case-Insensitive Search: All regex searches are case-insensitive by default
  • +
  • Global or First Match Only: Control whether to highlight all matches or just the first one
  • +
  • Word Boundary Support: Optional word boundary matching (when enabled)
  • +
  • Error Handling: Invalid regex patterns fall back to standard matching
  • +
  • DOM-Aware: Properly handles highlighting within HTML elements and skips script/style tags
  • +
  • Menu Filtering: Regex patterns work seamlessly in menu tooltip searches
  • +
+
+
+ + + + diff --git a/demos/regex-grid-demo.html b/demos/regex-grid-demo.html new file mode 100644 index 000000000..b829b0d56 --- /dev/null +++ b/demos/regex-grid-demo.html @@ -0,0 +1,306 @@ + + + + + w2ui - Grid with Regex Support Demo + + + + +
+

w2ui Grid with Regex Support Demo

+

This demo showcases how to use regex patterns to search and highlight content in w2ui grids.

+ +

1. Interactive Grid Search with Regex & Marker

+
+

Use regex patterns to search and apply text highlighting within grid cells.

+

Live Search: The grid updates automatically as you type in the search field - similar to Google's interactive search. Type any regex pattern to instantly filter results.

+ +
+ Search & Filter: +
+ + + +
+
+ +
+ Apply Marker Highlighting: +
+ + + +
+
+ +
+// Example regex patterns for grid search:
+^John // first names starting with "John"
+@example\.com // emails from example.com domain
+Sales|Market // Sales or Marketing departments
+[A-Z][a-z]{2,} // capitalized words (3+ chars)
+
+// Marker function highlights matching text with background color:
+w2utils.marker(text, pattern, { isRegex: true }) +
+ +
+
+
+
+ +

2. Features

+
+
    +
  • Regex Search: Filter grid records using powerful regex patterns
  • +
  • Text Marker: Highlight matching text within cells with background color
  • +
  • Column-Specific: Apply operations to specific columns or all columns
  • +
  • Case-Insensitive: All searches are case-insensitive by default
  • +
+
+
+ + + + + + diff --git a/dist/w2ui.es6.js b/dist/w2ui.es6.js index 119f05abd..700e99efb 100644 --- a/dist/w2ui.es6.js +++ b/dist/w2ui.es6.js @@ -1,4 +1,4 @@ -/* w2ui 2.0.x (nightly) (10/31/2025, 8:43:03 AM) (c) http://w2ui.com, vitmalina@gmail.com */ +/* w2ui 2.0.x (nightly) (11/23/2025, 8:17:07 PM) (c) http://w2ui.com, vitmalina@gmail.com */ /** * Part of w2ui 2.0 library * - Dependencies: w2utils @@ -393,6 +393,8 @@ const w2locale = { 'record': '---', 'records': '---', 'Refreshing...': '---', + 'RegEx': '---', + 'regex': '---', 'Reload data in the list': '---', 'Remove': '---', 'Remove This Field': '---', @@ -2569,10 +2571,11 @@ class Utils { } return str.replace(/\${([^}]+)?}/g, function($1, $2) { return replace_obj[$2]||$2 }) } - marker(el, items, options = { onlyFirst: false, wholeWord: false }) { + marker(el, items, options = { onlyFirst: false, wholeWord: false, isRegex: false}) { options.tag ??= 'span' options.class ??= 'w2ui-marker' options.raplace = (matched) => `<${options.tag} class="${options.class}">${matched}` + const isRegexSearch = options.isRegex || false; if (!Array.isArray(items)) { if (items != null && items !== '') { items = [items] @@ -2583,14 +2586,114 @@ class Utils { if (typeof el == 'string') { _clearMerkers(el) items.forEach(item => { - el = _replace(el, item, options.raplace) + if (isRegexSearch) { + // For regex searches with string elements + try { + let flags = 'i' + (!options.onlyFirst ? 'g' : '') + let regex = new RegExp(item, flags) + el = el.replace(regex, options.raplace) + } catch (e) { + console.error('Invalid regular expression:', e) + // Fallback to standard replace + el = _replace(el, item, options.raplace) + } + } else { + // Standard string replace + el = _replace(el, item, options.raplace) + } }) } else { query(el).each(el => { _clearMerkers(el) - items.forEach(item => { - el.innerHTML = _replace(el.innerHTML, item, options.raplace) - }) + if (isRegexSearch) { + // For regex searches, use DOM traversal approach + items.forEach(pattern => { + try { + let flags = 'i' // Always case-insensitive + if (!options.onlyFirst) { + flags += 'g' // Add 'g' for global unless onlyFirst is true + } + if (options.wholeWord) { + // If wholeWord is true, wrap the pattern with word boundary markers + pattern = '\b' + pattern + '\b' + } + let regex = new RegExp(pattern, flags) + // Get all text nodes + let textNodes = [] + function getTextNodes(node) { + if (node.nodeType === 3) { // Text node + textNodes.push(node) + } else if (node.nodeType === 1) { // Element node + // Skip script and style tags + if (node.tagName !== 'SCRIPT' && node.tagName !== 'STYLE') { + for (let i = 0; i < node.childNodes.length; i++) { + getTextNodes(node.childNodes[i]) + } + } + } + } + getTextNodes(el) + // Process each text node + textNodes.forEach(textNode => { + let text = textNode.nodeValue + let matches = [] + let match + // Find all matches + if (options.onlyFirst) { + match = regex.exec(text) + if (match) matches.push({ + index: match.index, + text: match[0] + }) + } else { + while ((match = regex.exec(text)) !== null) { + matches.push({ + index: match.index, + text: match[0] + }) + } + } + // Apply highlighting + if (matches.length > 0) { + let parent = textNode.parentNode + let fragment = document.createDocumentFragment() + let lastIndex = 0 + matches.forEach(match => { + // Add text before match + if (match.index > lastIndex) { + fragment.appendChild(document.createTextNode( + text.substring(lastIndex, match.index) + )) + } + // Add highlighted match + let span = document.createElement(options.tag) + span.className = options.class + span.appendChild(document.createTextNode(match.text)) + fragment.appendChild(span) + lastIndex = match.index + match.text.length + }) + // Add remaining text + if (lastIndex < text.length) { + fragment.appendChild(document.createTextNode( + text.substring(lastIndex) + )) + } + // Replace the text node with our fragment + parent.replaceChild(fragment, textNode) + } + }) + } catch (e) { + console.error('Invalid regular expression:', e) + // Fallback to standard innerHTML replace + el.innerHTML = _replace(el.innerHTML, pattern, options.raplace) + } + }) + } else { + // Standard innerHTML replace for non-regex + items.forEach(item => { + el.innerHTML = _replace(el.innerHTML, item, options.raplace) + }) + } }) } return el @@ -5579,7 +5682,7 @@ class MenuTooltip extends Tooltip { menuStyle : '', search : false, // search input inside tooltip filter : false, // will apply filter, if anchor is INPUT or TEXTAREA - match : 'contains', // is, begins, ends, contains + match : 'contains', // is, begins, ends, contains, regexp markSearch : false, prefilter : false, altRows : false, @@ -6252,18 +6355,29 @@ class MenuTooltip extends Tooltip { return prom } items.forEach(item => { - let prefix = '' - let suffix = '' - if (['is', 'begins', 'begins with'].indexOf(options.match) !== -1) prefix = '^' - if (['is', 'ends', 'ends with'].indexOf(options.match) !== -1) suffix = '$' - try { - let re = new RegExp(prefix + search + suffix, 'i') - if (re.test(item.text) || item.text === '...') { - item.hidden = false - } else { - item.hidden = true - } - } catch (e) {} + if (options.match == 'regex') { + try { + let re = new RegExp(search, 'i') + if (re.test(item.text) || item.text === '...') { + item.hidden = false + } else { + item.hidden = true + } + } catch (e) {} + } else { + let prefix = '' + let suffix = '' + if (['is', 'begins', 'begins with'].indexOf(options.match) !== -1) prefix = '^' + if (['is', 'ends', 'ends with'].indexOf(options.match) !== -1) suffix = '$' + try { + let re = new RegExp(prefix + search + suffix, 'i') + if (re.test(item.text) || item.text === '...') { + item.hidden = false + } else { + item.hidden = true + } + } catch (e) {} + } // do not show selected items if (options.hideSelected && selectedIds.includes(item.id)) { item.hidden = true diff --git a/dist/w2ui.es6.min.js b/dist/w2ui.es6.min.js index d1e16bad4..dcddf5a98 100644 --- a/dist/w2ui.es6.min.js +++ b/dist/w2ui.es6.min.js @@ -1,5 +1,5 @@ -/* w2ui 2.0.x (nightly) (10/31/2025, 8:43:03 AM) (c) http://w2ui.com, vitmalina@gmail.com */ -class w2event{constructor(e,t){Object.assign(this,{type:t.type??null,detail:t,owner:e,target:t.target??null,phase:t.phase??"before",object:t.object??null,execute:null,isStopped:!1,isCancelled:!1,onComplete:null,listeners:[]}),delete t.type,delete t.target,delete t.object,this.complete=new Promise((e,t)=>{this._resolve=e,this._reject=t}),this.complete.catch(()=>{})}finish(e){e&&w2utils.extend(this.detail,e),this.phase="after",this.owner.trigger.call(this.owner,this)}done(e){this.listeners.push(e)}preventDefault(){this._reject(),this.isCancelled=!0}stopPropagation(){this.isStopped=!0}}class w2base{constructor(e){if(this.activeEvents=[],this.listeners=[],void 0!==e){if(!w2utils.checkName(e))return;w2ui[e]=this}this.debug=!1}on(e,r){return(e="string"==typeof e?e.split(/[,\s]+/):[e]).forEach(e=>{var t,i,s,l="string"==typeof e?e:e.type+":"+e.execute+"."+e.scope;"string"==typeof e&&([i,t]=e.split("."),[i,s]=i.replace(":complete",":after").replace(":done",":after").split(":"),e={type:i,execute:s??"before",scope:t}),(e=w2utils.extend({type:null,execute:"before",onComplete:null},e)).type?r?(Array.isArray(this.listeners)||(this.listeners=[]),this.listeners.push({name:l,edata:e,handler:r}),this.debug&&console.log("w2base: add event",{name:l,edata:e,handler:r})):console.log("ERROR: You must specify event handler function when calling .on() method of "+this.name):console.log("ERROR: You must specify event type when calling .on() method of "+this.name)}),this}off(e,r){return(e="string"==typeof e?e.split(/[,\s]+/):[e]).forEach(i=>{var e,t,s,l="string"==typeof i?i:i.type+":"+i.execute+"."+i.scope;if("string"==typeof i&&([t,e]=i.split("."),[t,s]=t.replace(":complete",":after").replace(":done",":after").split(":"),i={type:t||"*",execute:s||"",scope:e||""}),(i=w2utils.extend({type:null,execute:null,onComplete:null},i)).type||i.scope){r=r||null;let t=0;this.listeners=this.listeners.filter(e=>"*"!==i.type&&i.type!==e.edata.type||""!==i.execute&&i.execute!==e.edata.execute||""!==i.scope&&i.scope!==e.edata.scope||null!=i.handler&&i.handler!==e.edata.handler||(t++,!1)),this.debug&&console.log(`w2base: remove event (${t})`,{name:l,edata:i,handler:r})}else console.log("ERROR: You must specify event type when calling .off() method of "+this.name)}),this}trigger(e,i){if(1==arguments.length?i="string"==typeof e?{type:e,target:this}:e:(i.type=e,i.target=i.target??this),w2utils.isPlainObject(i)&&"after"==i.phase){if(!(i=this.activeEvents.find(e=>e.type==i.type&&e.target==i.target)))return void console.log(`ERROR: Cannot find even handler for "${i.type}" on "${i.target}".`);console.log("NOTICE: This syntax \"edata.trigger({ phase: 'after' })\" is outdated. Use edata.finish() instead.")}else i instanceof w2event||(i=new w2event(this,i),this.activeEvents.push(i));let s,t,l;Array.isArray(this.listeners)||(this.listeners=[]),this.debug&&console.log(`w2base: trigger "${i.type}:${i.phase}"`,i);for(let e=this.listeners.length-1;0<=e;e--){let t=this.listeners[e];if(!(null==t||t.edata.type!==i.type&&"*"!==t.edata.type||t.edata.target!==i.target&&null!=t.edata.target||t.edata.execute!==i.phase&&"*"!==t.edata.execute&&"*"!==t.edata.phase)&&(Object.keys(t.edata).forEach(e=>{null==i[e]&&null!=t.edata[e]&&(i[e]=t.edata[e])}),s=[],l=new RegExp(/\((.*?)\)/).exec(String(t.handler).split("=>")[0]),2===(s=l?l[1].split(/\s*,\s*/):s).length?(t.handler.call(this,i.target,i),this.debug&&console.log(" - call (old)",t.handler)):(t.handler.call(this,i),this.debug&&console.log(" - call",t.handler)),!0===i.isStopped||!0===i.stop))return i}e="on"+i.type.substr(0,1).toUpperCase()+i.type.substr(1);if(!("before"===i.phase&&"function"==typeof this[e]&&(t=this[e],s=[],l=new RegExp(/\((.*?)\)/).exec(String(t).split("=>")[0]),2===(s=l?l[1].split(/\s*,\s*/):s).length?(t.call(this,i.target,i),this.debug&&console.log(" - call: on[Event] (old)",t)):(t.call(this,i),this.debug&&console.log(" - call: on[Event]",t)),!0===i.isStopped||!0===i.stop)||null!=i.object&&"before"===i.phase&&"function"==typeof i.object[e]&&(t=i.object[e],s=[],l=new RegExp(/\((.*?)\)/).exec(String(t).split("=>")[0]),2===(s=l?l[1].split(/\s*,\s*/):s).length?(t.call(this,i.target,i),this.debug&&console.log(" - call: edata.object (old)",t)):(t.call(this,i),this.debug&&console.log(" - call: edata.object",t)),!0===i.isStopped||!0===i.stop)||"after"!==i.phase)){"function"==typeof i.onComplete&&i.onComplete.call(this,i);for(let e=0;e{e.startsWith("w2ui-")&&t.push(e)}),query(this.box).off().removeClass(t).removeAttr("name").html(""),this.box=null,e.finish()}}}let w2locale={locale:"en-US",dateFormat:"m/d/yyyy",timeFormat:"hh:mi pm",datetimeFormat:"m/d/yyyy|hh:mi pm",currencyPrefix:"$",currencySuffix:"",currencyPrecision:2,groupSymbol:",",decimalSymbol:".",shortmonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],fullmonths:["January","February","March","April","May","June","July","August","September","October","November","December"],shortdays:["M","T","W","T","F","S","S"],fulldays:["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],weekStarts:"S",phrases:{"${count} letters or more...":"---","Add new record":"---","Add New":"---","Advanced Search":"---",after:"---","AJAX error. See console for more details.":"---","All Fields":"---",All:"---",Any:"---","Are you sure you want to delete ${count} ${records}?":"---","Attach files by dragging and dropping or Click to Select":"---",before:"---","begins with":"---",begins:"---",between:"---",buffered:"---",Cancel:"---",Close:"---",Column:"---",Confirmation:"---",contains:"---",Copied:"---","Copy to clipboard":"---","Current Date & Time":"---","Delete selected records":"---",Delete:"---",'Do you want to delete search item "${item}"?':"---","Edit selected record":"---",Edit:"---","Empty list":"---","ends with":"---",ends:"---","Field should be at least ${count} characters.":"---",Hide:"---",in:"---","is not":"---",is:"---","less than":"---","Line #":"---","Load ${count} more...":"---","Loading...":"---","Maximum number of files is ${count}":"---","Maximum total size is ${count}":"---",Modified:"---","more than":"---","Multiple Fields":"---",Name:"---","No items found":"---","No matches":"---",No:"---",none:"---","Not a float":"---","Not a hex number":"---","Not a valid date":"---","Not a valid email":"---","Not alpha-numeric":"---","Not an integer":"---","Not in money format":"---","not in":"---",Notification:"---",of:"---",Ok:"---",Opacity:"---","Record ID":"---",record:"---",records:"---","Refreshing...":"---","Reload data in the list":"---",Remove:"---","Remove This Field":"---","Request aborted.":"---","Required field":"---",Reset:"---","Restore Default State":"---","Returned data is not in valid JSON format.":"---","Save changed records":"---","Save Grid State":"---",Save:"---","Saved Searches":"---","Saving...":"---","Search took ${count} seconds":"---",Search:"---","Select Hour":"---","Select Minute":"---",selected:"---","Server Response ${count} seconds":"---","Show/hide columns":"---",Show:"---",Size:"---",Skip:"---","Sorting took ${count} seconds":"---","Type to search...":"---",Type:"---",Yes:"---",Yesterday:"---","Your remote data source record count has changed, reloading from the first record.":"---"}};class Query{static version=.8;constructor(e,t){this.context=t??document;let i=[];if(Array.isArray(e))i=e;else if(e instanceof Node||e instanceof Window)i=[e];else if(e instanceof Query)i=e.nodes;else if("string"==typeof e){if("function"!=typeof this.context.querySelector)throw new Error("Invalid context");i=Array.from(this.context.querySelectorAll(e))}else if(null==e)i=[];else{t=Array.from(e??[]);if("object"!=typeof e||!Array.isArray(t))throw new Error(`Invalid selector "${e}"`);i=t}this.nodes=i,this.length=i.length,this.each((e,t)=>{this[t]=e})}static _fragment(e){let i=document.createElement("template");return i.innerHTML=e,i.content.childNodes.forEach(e=>{var t=Query._scriptConvert(e);t!=e&&i.content.replaceChild(t,e)}),i.content}static _scriptConvert(e){let t=e=>{var t=e.ownerDocument.createElement("script"),i=(t.text=e.text,e.attributes);for(let e=0;e{e.parentNode.replaceChild(t(e),e)}),e}static _fixProp(e){var t={cellpadding:"cellPadding",cellspacing:"cellSpacing",class:"className",colspan:"colSpan",contenteditable:"contentEditable",for:"htmlFor",frameborder:"frameBorder",maxlength:"maxLength",readonly:"readOnly",rowspan:"rowSpan",tabindex:"tabIndex",usemap:"useMap"};return t[e]||e}_insert(l,i){let r=[],n=this.length;if(!(n<1)){let e=this;if("string"==typeof i)this.each(e=>{var t=Query._fragment(i);r.push(...t.childNodes),e[l](t)});else if(i instanceof Query){let s=1==n;i.each(i=>{this.each(e=>{var t=s?i:i.cloneNode(!0);r.push(t),e[l](t),Query._scriptConvert(t)})}),s||i.remove()}else{if(!(i instanceof Node))throw new Error(`Incorrect argument for "${l}(html)". It expects one string argument.`);this.each(e=>{var t=1===n?i:Query._fragment(i.outerHTML);r.push(...1===n?[i]:t.childNodes),e[l](t)}),1{e=Array.from(e.querySelectorAll(t));0{(e===t||"string"==typeof t&&e.matches&&e.matches(t)||"function"==typeof t&&t(e))&&i.push(e)}),new Query(i,this.context)}next(){let t=[];return this.each(e=>{e=e.nextElementSibling;e&&t.push(e)}),new Query(t,this.context)}prev(){let t=[];return this.each(e=>{e=e.previousElementSibling;e&&t.push(e)}),new Query(t,this.context)}shadow(e){let t=[];this.each(e=>{e.shadowRoot&&t.push(e.shadowRoot)});var i=new Query(t,this.context);return e?i.find(e):i}closest(t){let i=[];return this.each(e=>{e=e.closest(t);e&&i.push(e)}),new Query(i,this.context)}host(t){let i=[],s=e=>e.parentNode?s(e.parentNode):e,l=e=>{e=s(e);i.push(e.host||e),e.host&&t&&l(e.host)};return this.each(e=>{l(e)}),new Query(i,this.context)}parent(e){return this.parents(e,!0)}parents(e,t){let i=[],s=e=>{if(-1==i.indexOf(e)&&i.push(e),!t&&e.parentNode)return s(e.parentNode)};this.each(e=>{e.parentNode&&s(e.parentNode)});var l=new Query(i,this.context);return e?l.filter(e):l}add(e){e=e instanceof Query?e.nodes:Array.isArray(e)?e:[e];return new Query(this.nodes.concat(e),this.context)}each(i){return this.nodes.forEach((e,t)=>{i(e,t,this)}),this}append(e){return this._insert("append",e)}prepend(e){return this._insert("prepend",e)}after(e){return this._insert("after",e)}before(e){return this._insert("before",e)}replace(e){return this._insert("replaceWith",e)}remove(){return this.each(e=>{e.remove()}),this}css(e,t){let s=e;var i,l=arguments.length;return 0===l||1===l&&"string"==typeof e?this[0]?(l=this[0].style,"string"==typeof e?(i=l.getPropertyPriority(e),l.getPropertyValue(e)+(i?"!"+i:"")):Object.fromEntries(this[0].style.cssText.split(";").filter(e=>!!e).map(e=>e.split(":").map(e=>e.trim())))):void 0:("object"!=typeof e&&((s={})[e]=t),this.each((i,e)=>{Object.keys(s).forEach(e=>{var t=String(s[e]).toLowerCase().includes("!important")?"important":"";i.style.setProperty(e,String(s[e]).replace(/\!important/i,""),t)})}),this)}addClass(e){return this.toggleClass(e,!0),this}removeClass(e){return this.toggleClass(e,!1),this}toggleClass(t,s){return"string"==typeof t&&(t=t.split(/[,\s]+/)),this.each(i=>{let e=t;(e=null==e&&!1===s?Array.from(i.classList):e).forEach(t=>{if(""!==t){let e=null!=s?s?"add":"remove":"toggle";i.classList[e](t)}})}),this}hasClass(e){if(null==(e="string"==typeof e?e.split(/[,\s]+/):e)&&0{i=i||e.every(e=>Array.from(t.classList??[]).includes(e))}),i}on(e,s,l){"function"==typeof s&&(l=s,s=void 0);let r;return s?.delegate&&(r=s.delegate,delete s.delegate),(e=e.split(/[,\s]+/)).forEach(e=>{let[t,i]=String(e).toLowerCase().split(".");if(r){let i=l;l=e=>{var t=query(e.target).parents(r);0{this._save(e,"events",[{event:t,scope:i,callback:l,options:s}]),e.addEventListener(t,l,s)})}),this}off(e,t,r){return"function"==typeof t&&(r=t,t=void 0),(e=(e??"").split(/[,\s]+/)).forEach(e=>{let[s,l]=String(e).toLowerCase().split(".");this.each(t=>{if(Array.isArray(t._mQuery?.events))for(let e=t._mQuery.events.length-1;0<=e;e--){var i=t._mQuery.events[e];null==l||""===l?i.event!=s&&""!==s||i.callback!=r&&null!=r||(t.removeEventListener(i.event,i.callback,i.options),t._mQuery.events.splice(e,1)):i.event!=s&&""!==s||i.scope!=l||(t.removeEventListener(i.event,i.callback,i.options),t._mQuery.events.splice(e,1))}})}),this}trigger(e,t){let i;return i=e instanceof Event||e instanceof CustomEvent?e:new(["click","dblclick","mousedown","mouseup","mousemove"].includes(e)?MouseEvent:["keydown","keyup","keypress"].includes(e)?KeyboardEvent:Event)(e,t),this.each(e=>{e.dispatchEvent(i)}),this}attr(t,i){if(void 0===i&&"string"==typeof t)return this[0]?this[0].getAttribute(t):void 0;{let e={};return"object"==typeof t?e=t:e[t]=i,this.each(i=>{Object.entries(e).forEach(([e,t])=>{i.setAttribute(e,t)})}),this}}removeAttr(){return this.each(t=>{Array.from(arguments).forEach(e=>{t.removeAttribute(e)})}),this}prop(t,i){if(void 0===i&&"string"==typeof t)return this[0]?this[0][t]:void 0;{let e={};return"object"==typeof t?e=t:e[t]=i,this.each(i=>{Object.entries(e).forEach(([e,t])=>{e=Query._fixProp(e);i[e]=t,"innerHTML"==e&&Query._scriptConvert(i)})}),this}}removeProp(){return this.each(t=>{Array.from(arguments).forEach(e=>{delete t[Query._fixProp(e)]})}),this}data(i,t){if(i instanceof Object)Object.entries(i).forEach(e=>{this.data(e[0],e[1])});else{if(i&&-1!=i.indexOf("-")&&console.error(`Key "${i}" contains "-" (dash). Dashes are not allowed in property names. Use camelCase instead.`),!(arguments.length<2))return this.each(e=>{null!=t?e.dataset[i]=t instanceof Object?JSON.stringify(t):t:delete e.dataset[i]}),this;if(this[0]){let t=Object.assign({},this[0].dataset);return Object.keys(t).forEach(e=>{if(t[e].startsWith("[")||t[e].startsWith("{"))try{t[e]=JSON.parse(t[e])}catch(e){}}),i?t[i]:t}}}removeData(e){return"string"==typeof e&&(e=e.split(/[,\s]+/)),this.each(t=>{e.forEach(e=>{delete t.dataset[e]})}),this}show(){return this.toggle(!0)}hide(){return this.toggle(!1)}toggle(r){return this.each(e=>{var t,i=e.style.display,s=getComputedStyle(e).display,l="none"==i||"none"==s;!l||null!=r&&!0!==r||(t=e instanceof HTMLTableRowElement?"table-row":e instanceof HTMLTableCellElement?"table-cell":"block",e.style.display=e._mQuery?.prevDisplay??(i==s&&"none"!=s?"":t),this._save(e,"prevDisplay",null)),l||null!=r&&!1!==r||("none"!=s&&this._save(e,"prevDisplay",s),e.style.setProperty("display","none"))})}empty(){return this.html("")}html(e){return e instanceof HTMLElement?this.empty().append(e):this.prop("innerHTML",e)}text(e){return this.prop("textContent",e)}val(e){return this.prop("value",e)}change(){return this.trigger("change")}click(){return this.trigger("click")}}let query=function(e,t){if("function"!=typeof e)return new Query(e,t);"complete"==document.readyState?e():window.addEventListener("load",e)},w2ui=(query.html=e=>{e=Query._fragment(e);return query(e.children,e)},query.version=Query.version,{});class Utils{constructor(){this.version="2.0.x",this.tmp={},this.settings=this.extend({},{dataType:"JSON",dateStartYear:1950,dateEndYear:2030,macButtonOrder:!1,warnNoPhrase:!1},w2locale,{phrases:null}),this.i18nCompare=Intl.Collator().compare,this.hasLocalStorage=(()=>{var e="w2ui_test";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(e){return!1}})(),this.isMac=/Mac/i.test(navigator.platform),this.isMobile=/(iphone|ipod|mobile|android)/i.test(navigator.userAgent),this.isIOS=/(iphone|ipod|ipad)/i.test(navigator.platform),this.isAndroid=/(android)/i.test(navigator.userAgent),this.isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),this.isFirefox=/(Firefox)/i.test(navigator.userAgent),this.formatters={number(e,t){let{value:i,params:s}=t=null==t?e:t;return 20'+w2utils.formatDate(l,s)+""},datetime(e,t){let{value:i,params:s}=t=null==t?e:t;if(""===s&&(s=w2utils.settings.datetimeFormat),null==i||0===i||""===i)return"";let l=w2utils.isDateTime(i,s,!0);return''+w2utils.formatDateTime(l,s)+""},time(e,t){let{value:i,params:s}=t=null==t?e:t;if("h24"===(s="h12"===(s=""===s?w2utils.settings.timeFormat:s)?"hh:mi pm":s)&&(s="h24:mi"),null==i||0===i||""===i)return"";let l=w2utils.isDateTime(i,s,!0);return''+w2utils.formatTime(i,s)+""},timestamp(e,t){let{value:i,params:s}=t=null==t?e:t;if(""===s&&(s=w2utils.settings.datetimeFormat),null==i||0===i||""===i)return"";let l=w2utils.isDateTime(i,s,!0);return(l=!1===l?w2utils.isDate(i,s,!0):l).toString?l.toString():""},gmt(e,t){let{value:i,params:s}=t=null==t?e:t;if(""===s&&(s=w2utils.settings.datetimeFormat),null==i||0===i||""===i)return"";let l=w2utils.isDateTime(i,s,!0);return(l=!1===l?w2utils.isDate(i,s,!0):l).toUTCString?l.toUTCString():""},age(e,t){var{value:e,params:t}=t=null==t?e:t;if(null==e||0===e||""===e)return"";let i=w2utils.isDateTime(e,null,!0);return''+w2utils.age(e)+(t?" "+t:"")+""},interval(e,t){var{value:e,params:t}=t=null==t?e:t;return null==e||0===e||""===e?"":w2utils.interval(e)+(t?" "+t:"")},toggle(e,t){e=(t=null==t?e:t).value;return e?w2utils.lang("Yes"):""},password(e,t){var i=(t=null==t?e:t).value;let s="";if(i)for(let e=0;ei||!this.isInt(e[0])||2'+(r=l==e?this.lang("Yesterday"):r)+""}formatSize(e){var t;return this.isFloat(e)&&""!==e?0===(e=parseFloat(e))?0:(t=parseInt(Math.floor(Math.log(e)/Math.log(1024))),(Math.floor(e/Math.pow(1024,t)*10)/10).toFixed(0===t?0:1)+" "+(["Bt","KB","MB","GB","TB","PB","EB","ZB"][t]||"??")):""}formatNumber(e,t,i){return null==e||""===e||"object"==typeof e?"":(i={minimumFractionDigits:parseInt(t),maximumFractionDigits:parseInt(t),useGrouping:!!i},(null==t||t<0)&&(i.minimumFractionDigits=0,i.maximumFractionDigits=20),parseFloat(e).toLocaleString(this.settings.locale,i))}formatDate(e,t){if(t=t||this.settings.dateFormat,""===e||null==e||"object"==typeof e&&!e.getMonth)return"";let i=new Date(e);var s,l;return this.isInt(e)&&(i=new Date(Number(e))),"Invalid Date"===String(i)?"":(e=i.getFullYear(),s=i.getMonth(),l=i.getDate(),t.toLowerCase().replace("month",this.settings.fullmonths[s]).replace("mon",this.settings.shortmonths[s]).replace(/yyyy/g,("000"+e).slice(-4)).replace(/yyy/g,("000"+e).slice(-4)).replace(/yy/g,("0"+e).slice(-2)).replace(/(^|[^a-z$])y/g,"$1"+e).replace(/mm/g,("0"+(s+1)).slice(-2)).replace(/dd/g,("0"+l).slice(-2)).replace(/th/g,1==l?"st":"th").replace(/th/g,2==l?"nd":"th").replace(/th/g,3==l?"rd":"th").replace(/(^|[^a-z$])m/g,"$1"+(s+1)).replace(/(^|[^a-z$])d/g,"$1"+l))}formatTime(e,t){if(t=t||this.settings.timeFormat,""===e||null==e||"object"==typeof e&&!e.getMonth)return"";let i=new Date(e);if(this.isInt(e)&&(i=new Date(Number(e))),this.isTime(e)&&(e=this.isTime(e,!0),(i=new Date).setHours(e.hours),i.setMinutes(e.minutes)),"Invalid Date"===String(i))return"";"h12"==t&&(t="hh:mi pm");let s="am",l=i.getHours();e=i.getHours();let r=i.getMinutes(),n=i.getSeconds();return r<10&&(r="0"+r),n<10&&(n="0"+n),-1===t.indexOf("am")&&-1===t.indexOf("pm")||(12<=l&&(s="pm"),12{i[t]=this.stripSpaces(e)}):(i=this.extend({},i),Object.keys(i).forEach(e=>{i[e]=this.stripSpaces(i[e])}))}return i}stripTags(i){if(null!=i)switch(typeof i){case"number":break;case"string":i=String(i).replace(/<(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>/gi,"");break;case"object":Array.isArray(i)?(i=this.extend([],i)).forEach((e,t)=>{i[t]=this.stripTags(e)}):(i=this.extend({},i),Object.keys(i).forEach(e=>{i[e]=this.stripTags(i[e])}))}return i}encodeTags(i){if(null!=i)switch(typeof i){case"number":break;case"string":i=String(i).replace(/&/g,"&").replace(/>/g,">").replace(/{i[t]=this.encodeTags(e)}):(i=this.extend({},i),Object.keys(i).forEach(e=>{i[e]=this.encodeTags(i[e])}))}return i}decodeTags(i){if(null!=i)switch(typeof i){case"number":break;case"string":i=String(i).replace(/>/g,">").replace(/</g,"<").replace(/"/g,'"').replace(/&/g,"&");break;case"object":Array.isArray(i)?(i=this.extend([],i)).forEach((e,t)=>{i[t]=this.decodeTags(e)}):(i=this.extend({},i),Object.keys(i).forEach(e=>{i[e]=this.decodeTags(i[e])}))}return i}escapeId(e){return""===e||null==e?"":(e+"").replace(/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,(e,t)=>t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e)}unescapeId(e){return""===e||null==e?"":e.replace(/\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\([^\r\n\f])/g,(e,t)=>{e="0x"+e.slice(1)-65536;return t||(e<0?String.fromCharCode(65536+e):String.fromCharCode(e>>10|55296,1023&e|56320))})}base64encode(e){var t;let i="";for(t of(new TextEncoder).encode(e))i+=String.fromCharCode(t);return btoa(i)}base64decode(e){var t=atob(e),i=new Uint8Array(t.length);for(let e=0;eArray.from(new Uint8Array(e)).map(e=>e.toString(16).padStart(2,"0")).join(""))}transition(r,n,a,o){return new Promise((e,t)=>{var i=getComputedStyle(r);let s=parseInt(i.width),l=parseInt(i.height);if(r&&n){switch(r.parentNode.style.cssText+="perspective: 900px; overflow: hidden;",r.style.cssText+="; position: absolute; z-index: 1019; backface-visibility: hidden",n.style.cssText+="; position: absolute; z-index: 1020; backface-visibility: hidden",a){case"slide-left":r.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0)",n.style.cssText+="overflow: hidden; transform: translate3d("+s+"px, 0, 0)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: translate3d(0, 0, 0)",r.style.cssText+="transition: 0.5s; transform: translate3d(-"+s+"px, 0, 0)"},1);break;case"slide-right":r.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0)",n.style.cssText+="overflow: hidden; transform: translate3d(-"+s+"px, 0, 0)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: translate3d(0px, 0, 0)",r.style.cssText+="transition: 0.5s; transform: translate3d("+s+"px, 0, 0)"},1);break;case"slide-down":r.style.cssText+="overflow: hidden; z-index: 1; transform: translate3d(0, 0, 0)",n.style.cssText+="overflow: hidden; z-index: 0; transform: translate3d(0, 0, 0)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: translate3d(0, 0, 0)",r.style.cssText+="transition: 0.5s; transform: translate3d(0, "+l+"px, 0)"},1);break;case"slide-up":r.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0)",n.style.cssText+="overflow: hidden; transform: translate3d(0, "+l+"px, 0)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: translate3d(0, 0, 0)",r.style.cssText+="transition: 0.5s; transform: translate3d(0, 0, 0)"},1);break;case"flip-left":r.style.cssText+="overflow: hidden; transform: rotateY(0deg)",n.style.cssText+="overflow: hidden; transform: rotateY(-180deg)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: rotateY(0deg)",r.style.cssText+="transition: 0.5s; transform: rotateY(180deg)"},1);break;case"flip-right":r.style.cssText+="overflow: hidden; transform: rotateY(0deg)",n.style.cssText+="overflow: hidden; transform: rotateY(180deg)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: rotateY(0deg)",r.style.cssText+="transition: 0.5s; transform: rotateY(-180deg)"},1);break;case"flip-down":r.style.cssText+="overflow: hidden; transform: rotateX(0deg)",n.style.cssText+="overflow: hidden; transform: rotateX(180deg)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: rotateX(0deg)",r.style.cssText+="transition: 0.5s; transform: rotateX(-180deg)"},1);break;case"flip-up":r.style.cssText+="overflow: hidden; transform: rotateX(0deg)",n.style.cssText+="overflow: hidden; transform: rotateX(-180deg)",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: rotateX(0deg)",r.style.cssText+="transition: 0.5s; transform: rotateX(180deg)"},1);break;case"pop-in":r.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0)",n.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0); transform: scale(.8); opacity: 0;",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; transform: scale(1); opacity: 1;",r.style.cssText+="transition: 0.5s;"},1);break;case"pop-out":r.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0); transform: scale(1); opacity: 1;",n.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0); opacity: 0;",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; opacity: 1;",r.style.cssText+="transition: 0.5s; transform: scale(1.7); opacity: 0;"},1);break;default:r.style.cssText+="overflow: hidden; transform: translate3d(0, 0, 0)",n.style.cssText+="overflow: hidden; translate3d(0, 0, 0); opacity: 0;",query(n).show(),setTimeout(()=>{n.style.cssText+="transition: 0.5s; opacity: 1;",r.style.cssText+="transition: 0.5s"},1)}setTimeout(()=>{"slide-down"===a&&(query(r).css("z-index","1019"),query(n).css("z-index","1020")),n&&query(n).css({opacity:"1"}).css({transition:"",transform:""}),r&&query(r).css({opacity:"1"}).css({transition:"",transform:""}),"function"==typeof o&&o(),e()},500)}else console.log("ERROR: Cannot do transition when one of the divs is null")})}lock(s,l={}){if(null!=s){"string"==typeof l&&(l={msg:l}),arguments[2]&&(l.spinner=arguments[2]),l=this.extend({spinner:!1},l),s?.[0]instanceof Node&&(s=Array.isArray(s)?s:s.get()),l.msg||0===l.msg||(l.msg=""),this.unlock(s);var r=query(s).get(0),n=r.scrollWidth;let e="BODY"==r.tagName?"position: fixed; right: 0; bottom: 0;":`height: ${r.scrollHeight}px; width: ${n}px`,t=(query(s).prepend(`
`+'
'),query(s).find(".w2ui-lock"));r=query(s).find(".w2ui-lock-msg"),n=(l.msg||r.css({"background-color":"transparent","background-image":"none",border:"0px","box-shadow":"none"}),!0===l.spinner&&(l.msg=`
`+l.msg),l.msg?r.html(l.msg).css("display","block"):r.remove(),null!=l.opacity&&t.css("opacity",l.opacity),t.css({display:"block"}),l.bgColor&&t.css({"background-color":l.bgColor}),getComputedStyle(t.get(0)));let i=n.opacity??.15;t.on("mousedown",function(){"function"==typeof l.onClick?l.onClick():t.css({transition:".2s",opacity:1.5*i})}).on("mouseup",function(){"function"!=typeof l.onClick&&t.css({transition:".2s",opacity:i})}).on("mousewheel",function(e){e&&(e.stopPropagation(),e.preventDefault())})}}unlock(e,t){var i;null!=e&&(clearTimeout(e._prevUnlock),e?.[0]instanceof Node&&(e=Array.isArray(e)?e:e.get()),this.isInt(t)&&0{query(e).find(".w2ui-lock").remove()},t)):query(e).find(".w2ui-lock").remove(),query(e).find(".w2ui-lock-msg").remove())}message(r,s){let e,t,l;var i=()=>{var e=query(r?.box).find(".w2ui-message");0!=e.length&&"function"==typeof(s=e.get(0)._msg_options||{})?.close&&s.close()};let n=e=>{var t,i=e.box._msg_prevFocus;query(r.box).find(".w2ui-message").length<=1?r.owner?r.owner.unlock(r.param,150):this.unlock(r.box,150):query(r.box).find(`#w2ui-message-${r.owner?.name}-`+(e.msgIndex-1)).css("z-index",1500),i?0<(t=query(i).closest(".w2ui-message")).length?t.get(0)._msg_options.setFocus(i):i.focus():"function"==typeof r.owner?.focus&&r.owner.focus(),query(e.box).remove(),0===e.msgIndex&&(c.css("z-index",e.tmp.zIndex),query(r.box).css("overflow",e.tmp.overflow)),e.trigger&&l.finish()};if("string"!=typeof s&&"number"!=typeof s||(s={width:String(s).length<300?350:550,height:String(s).length<300?170:250,text:String(s)}),1!=arguments.length&&null!=s||(s=r),null==(s??={})||(""===s.text||null==s.text)&&(""===s.body||null==s.body))return void i();null!=s.text&&(s.body=`
${s.text}
`),null==s.width&&(s.width=350),null==s.height&&(s.height=170),null==s.hideOn&&(s.hideOn=["esc"]),s.cancelAction??="Ok",null==s.on&&(h=s,s=new w2base,w2utils.extend(s,h)),s.on("open",e=>{w2utils.bindEvents(query(s.box).find(".w2ui-eaction"),s),query(e.detail.box).find("button, input, textarea, [name=hidden-first]").off(".message").on("keydown.message",function(e){27==e.keyCode&&s.hideOn.includes("esc")&&(s.cancelAction?s.action(s.cancelAction):s.close())}),setTimeout(()=>s.setFocus(s.focus),300)}),s.off(".prom");let a={self:s,action(e){return s.on("action.prom",e),a},close(e){return s.on("close.prom",e),a},open(e){return s.on("open.prom",e),a},then(e){return s.on("open:after.prom",e),a}},o=(null==s.actions&&null==s.buttons&&null==s.html&&(s.actions={Ok(e){e.detail.self.close()}}),s.off(".buttons"),null!=s.actions&&(s.buttons="",Object.keys(s.actions).forEach(e=>{var t=s.actions[e];let i=e;"function"==typeof t&&(s.buttons+=``),"object"==typeof t&&(s.buttons+=``),"object"==typeof t&&(s.buttons+=``,i=Array.isArray(s.actions)?t.text:e),"string"==typeof t&&(s.buttons+=``,i=t),"string"==typeof i&&(i=i[0].toLowerCase()+i.substr(1).replace(/\s+/g,"")),a[i]=function(t){return s.on("action.buttons",e=>{e.detail.action[0].toLowerCase()+e.detail.action.substr(1).replace(/\s+/g,"")==i&&t(e)}),a}})),Array("html","body","buttons").forEach(e=>{s[e]=String(s[e]??"").trim()}),""===s.body&&""===s.buttons||(s.html=`
${s.body||""}
${s.buttons||""}
@@ -25,7 +25,7 @@ class w2event{constructor(e,t){Object.assign(this,{type:t.type??null,detail:t,ow ${i} - `;query(s.where).append(e),query(s.where).find("#w2ui-notify").find(".w2ui-notify-close").on("click",e=>{query(s.where).find("#w2ui-notify").remove(),t()}),s.actions&&query(s.where).find("#w2ui-notify .w2ui-notify-link").on("click",e=>{e=query(e.target).attr("value");s.actions[e](),query(s.where).find("#w2ui-notify").remove(),t()}),0{query(s.where).find("#w2ui-notify").remove(),t()},s.timeout))}})}getSize(e,t){let i=0;if(0<(e=query(e)).length){e=e[0];var s=getComputedStyle(e);switch(t){case"width":i=parseFloat(s.width),"auto"===s.width&&(i=0);break;case"height":i=parseFloat(s.height),"auto"===s.height&&(i=0);break;default:i=parseFloat(s[t]??0)||0}}return i}getStrWidth(e,t,i){let s=query("body > #_tmp_width");return 0===s.length&&(query("body").append('
'),s=query("body > #_tmp_width")),s.html(i?e:this.encodeTags(e??"")).attr("style","position: absolute; top: -9000px; "+(t||"")),s[0].clientWidth}getStrHeight(e,t,i){let s=query("body > #_tmp_width");return 0===s.length&&(query("body").append('
'),s=query("body > #_tmp_width")),s.html(i?e:this.encodeTags(e??"")).attr("style","position: absolute; top: -9000px; "+(t||"")),s[0].clientHeight}execTemplate(e,i){return"string"==typeof e&&i&&"object"==typeof i?e.replace(/\${([^}]+)?}/g,function(e,t){return i[t]||t}):e}marker(t,e,l={onlyFirst:!1,wholeWord:!1}){return l.tag??="span",l.class??="w2ui-marker",l.raplace=e=>`<${l.tag} class="${l.class}">${e}`,Array.isArray(e)||(e=null!=e&&""!==e?[e]:[]),"string"==typeof t?(s(t),e.forEach(e=>{t=i(t,e,l.raplace)})):query(t).each(t=>{s(t),e.forEach(e=>{t.innerHTML=i(t.innerHTML,e,l.raplace)})}),t;function i(e,t,i){var s=l.wholeWord,t=(t=(t="string"!=typeof t?String(t):t).replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/&/g,"&").replace(//g,"<"),new RegExp((s?"\\b":"")+t+(s?"\\b":"")+"(?![^<]*>)","i"+(l.onlyFirst?"":"g")));return e.replace(t,i)}function s(e){var t=new RegExp(`<${l.tag}[^>]*class=["']${l.class.replace(/-/g,"\\-")}["'][^>]*>([\\s\\S]*?)<\\/${l.tag}>`,"ig");if("string"==typeof e)for(;-1!==e.indexOf(`<${l.tag} class="${l.class}"`);)e=e.replace(t,"$1");else for(;-1!==e.innerHTML.indexOf(`<${l.tag} class="${l.class}"`);)e.innerHTML=e.innerHTML.replace(t,"$1")}}lang(e,t){if(!e||null==this.settings.phrases||"string"!=typeof e||"<=>=".includes(e))return this.execTemplate(e,t);let i=this.settings.phrases[e];return null==i?(i=e,this.settings.warnNoPhrase&&(this.settings.missing||(this.settings.missing={}),this.settings.missing[e]="---",this.settings.phrases[e]="---",console.log(`Missing translation for "%c${e}%c", see %c w2utils.settings.phrases %c with value "---"`,"color: orange","","color: #999",""))):"---"!==i||this.settings.warnNoPhrase||(i=e),"---"===i&&(i=`---`),this.execTemplate(i,t)}locale(l,i,r){return new Promise((s,t)=>{if(Array.isArray(l)){this.settings.phrases={};let i=[],t={};l.forEach((e,t)=>{5===e.length&&(e="locale/"+e.toLowerCase()+".json",l[t]=e),i.push(this.locale(e,!0,!1))}),void Promise.allSettled(i).then(e=>{e.forEach(e=>{e.value&&(t[e.value.file]=e.value.data)}),l.forEach(e=>{this.settings=this.extend({},this.settings,t[e])}),s()})}else(l=l||"en-us")instanceof Object?this.settings=this.extend({},this.settings,w2locale,l):(5===l.length&&(l="locale/"+l.toLowerCase()+".json"),fetch(l,{method:"GET"}).then(e=>e.json()).then(e=>{!0!==r&&(this.settings=i?this.extend({},this.settings,e):this.extend({},this.settings,w2locale,{phrases:{}},e)),s({file:l,data:e})}).catch(e=>{console.log("ERROR: Cannot load locale "+l),t(e)}))})}scrollBarSize(){return this.tmp.scrollBarSize||(query("body").append(` + `;query(s.where).append(e),query(s.where).find("#w2ui-notify").find(".w2ui-notify-close").on("click",e=>{query(s.where).find("#w2ui-notify").remove(),t()}),s.actions&&query(s.where).find("#w2ui-notify .w2ui-notify-link").on("click",e=>{e=query(e.target).attr("value");s.actions[e](),query(s.where).find("#w2ui-notify").remove(),t()}),0{query(s.where).find("#w2ui-notify").remove(),t()},s.timeout))}})}getSize(e,t){let i=0;if(0<(e=query(e)).length){e=e[0];var s=getComputedStyle(e);switch(t){case"width":i=parseFloat(s.width),"auto"===s.width&&(i=0);break;case"height":i=parseFloat(s.height),"auto"===s.height&&(i=0);break;default:i=parseFloat(s[t]??0)||0}}return i}getStrWidth(e,t,i){let s=query("body > #_tmp_width");return 0===s.length&&(query("body").append('
'),s=query("body > #_tmp_width")),s.html(i?e:this.encodeTags(e??"")).attr("style","position: absolute; top: -9000px; "+(t||"")),s[0].clientWidth}getStrHeight(e,t,i){let s=query("body > #_tmp_width");return 0===s.length&&(query("body").append('
'),s=query("body > #_tmp_width")),s.html(i?e:this.encodeTags(e??"")).attr("style","position: absolute; top: -9000px; "+(t||"")),s[0].clientHeight}execTemplate(e,i){return"string"==typeof e&&i&&"object"==typeof i?e.replace(/\${([^}]+)?}/g,function(e,t){return i[t]||t}):e}marker(s,e,n={onlyFirst:!1,wholeWord:!1,isRegex:!1}){n.tag??="span",n.class??="w2ui-marker",n.raplace=e=>`<${n.tag} class="${n.class}">${e}`;let l=n.isRegex||!1;return Array.isArray(e)||(e=null!=e&&""!==e?[e]:[]),"string"==typeof s?(t(s),e.forEach(t=>{if(l)try{var e="i"+(n.onlyFirst?"":"g"),i=new RegExp(t,e);s=s.replace(i,n.raplace)}catch(e){console.error("Invalid regular expression:",e),s=r(s,t,n.raplace)}else s=r(s,t,n.raplace)})):query(s).each(i=>{t(i),l?e.forEach(t=>{try{let e="i",s=(n.onlyFirst||(e+="g"),n.wholeWord&&(t="\b"+t+"\b"),new RegExp(t,e)),l=[];!function t(i){if(3===i.nodeType)l.push(i);else if(1===i.nodeType&&"SCRIPT"!==i.tagName&&"STYLE"!==i.tagName)for(let e=0;e{let l=e.nodeValue;var t=[];let i;if(n.onlyFirst)(i=s.exec(l))&&t.push({index:i.index,text:i[0]});else for(;null!==(i=s.exec(l));)t.push({index:i.index,text:i[0]});if(0{e.index>s&&i.appendChild(document.createTextNode(l.substring(s,e.index)));var t=document.createElement(n.tag);t.className=n.class,t.appendChild(document.createTextNode(e.text)),i.appendChild(t),s=e.index+e.text.length}),s{i.innerHTML=r(i.innerHTML,e,n.raplace)})}),s;function r(e,t,i){var s=n.wholeWord,t=(t=(t="string"!=typeof t?String(t):t).replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/&/g,"&").replace(//g,"<"),new RegExp((s?"\\b":"")+t+(s?"\\b":"")+"(?![^<]*>)","i"+(n.onlyFirst?"":"g")));return e.replace(t,i)}function t(e){var t=new RegExp(`<${n.tag}[^>]*class=["']${n.class.replace(/-/g,"\\-")}["'][^>]*>([\\s\\S]*?)<\\/${n.tag}>`,"ig");if("string"==typeof e)for(;-1!==e.indexOf(`<${n.tag} class="${n.class}"`);)e=e.replace(t,"$1");else for(;-1!==e.innerHTML.indexOf(`<${n.tag} class="${n.class}"`);)e.innerHTML=e.innerHTML.replace(t,"$1")}}lang(e,t){if(!e||null==this.settings.phrases||"string"!=typeof e||"<=>=".includes(e))return this.execTemplate(e,t);let i=this.settings.phrases[e];return null==i?(i=e,this.settings.warnNoPhrase&&(this.settings.missing||(this.settings.missing={}),this.settings.missing[e]="---",this.settings.phrases[e]="---",console.log(`Missing translation for "%c${e}%c", see %c w2utils.settings.phrases %c with value "---"`,"color: orange","","color: #999",""))):"---"!==i||this.settings.warnNoPhrase||(i=e),"---"===i&&(i=`---`),this.execTemplate(i,t)}locale(l,i,r){return new Promise((s,t)=>{if(Array.isArray(l)){this.settings.phrases={};let i=[],t={};l.forEach((e,t)=>{5===e.length&&(e="locale/"+e.toLowerCase()+".json",l[t]=e),i.push(this.locale(e,!0,!1))}),void Promise.allSettled(i).then(e=>{e.forEach(e=>{e.value&&(t[e.value.file]=e.value.data)}),l.forEach(e=>{this.settings=this.extend({},this.settings,t[e])}),s()})}else(l=l||"en-us")instanceof Object?this.settings=this.extend({},this.settings,w2locale,l):(5===l.length&&(l="locale/"+l.toLowerCase()+".json"),fetch(l,{method:"GET"}).then(e=>e.json()).then(e=>{!0!==r&&(this.settings=i?this.extend({},this.settings,e):this.extend({},this.settings,w2locale,{phrases:{}},e)),s({file:l,data:e})}).catch(e=>{console.log("ERROR: Cannot load locale "+l),t(e)}))})}scrollBarSize(){return this.tmp.scrollBarSize||(query("body").append(`
1
@@ -168,7 +168,7 @@ class w2event{constructor(e,t){Object.assign(this,{type:t.type??null,detail:t,ow `}return u+=""}openSubMenu(t){var e=query(t.originalEvent.target).get(0);let i=t.overlay;var s=i.options.items,s=s[t.index];let l=[];"function"==typeof s.items?l=s.items(s):Array.isArray(s.items)&&(l=s.items);var r=w2menu.get(i.name+"-submenu");r&&r.hide(),query(t.target).addClass("expanded"),w2menu.show({name:i.name+"-submenu",anchor:e,items:l,class:i.options.class+" "+s.overlay?.class,offsetX:-7,arrowSize:0,parentOverlay:i,parents:[...t.parents,t.index],position:"right|left",hideOn:["doc-click","select"]}).hide(e=>{query(t.target).removeClass("expanded")}),setTimeout(()=>{query("#w2overlay-"+i.name+"-submenu").on("mouseenter",e=>{e.target._keepSubOpen=!0}).on("mouseleave",e=>{e.target._keepSubOpen=!1})},10)}closeSubMenu(e){var t=e.overlay;!0!==e.target._keepSubOpen&&(e=w2menu.get(t.name+"-submenu"))&&e.hide()}refreshIndex(e,t){var i,s,l,e=Tooltip.active[e.replace(/[\s\.#]/g,"_")];e&&(e.displayed||this.show(e.name),i=query(e.box).find(".w2ui-overlay-body").get(0),s=query(e.box).find(".w2ui-menu-search, .w2ui-menu-top").get(0),query(e.box).find(".w2ui-menu-item.w2ui-selected").removeClass("w2ui-selected"),(l=query(e.box).find(`.w2ui-menu-item[index="${e.selected}"]`).addClass("w2ui-selected").get(0))&&(l.offsetTop+l.clientHeight>i.clientHeight+i.scrollTop&&l.scrollIntoView({behavior:t?"instant":"smooth",block:t?"center":"start",inline:t?"center":"start"}),l.offsetTop{var t;this.getCurrent(i,e.getAttribute("index")).item?.hidden?query(e).hide():(t=s.tmp?.search,s.options.markSearch&&w2utils.marker(e,t,{onlyFirst:"begins"==s.options.match}),query(e).show())}),query(s.box).find(".w2ui-sub-menu").each(e=>{var t=query(e).find(".w2ui-menu-item").get().some(e=>"none"!=e.style.display);this.getCurrent(i,e.dataset.parent).item.expanded&&(t?query(e).parent().show():query(e).parent().hide())}),0!=s.tmp.searchCount&&0!=s.options?.items?.length||(0==query(s.box).find(".w2ui-no-items").length&&query(s.box).find(".w2ui-menu:not(.w2ui-sub-menu)").append(`
${w2utils.lang(s.options.msgNoItems)} -
`),query(s.box).find(".w2ui-no-items").show()))}applyFilter(l,t,r,i){let n=0,a=Tooltip.active[l.replace(/[\s\.#]/g,"_")],o=a.options,h,d;var u=new Promise((e,t)=>{h=e,d=t});if(!0!==a.tmp._skip_filter){null==r&&(r=["INPUT","TEXTAREA"].includes(a.anchor.tagName)?a.anchor.value:""),!1===a.tmp._new_search&&(r="");let s=[];o.selected&&(Array.isArray(o.selected)?s=o.selected.map(e=>e?.id??e):o.selected?.id&&(s=[o.selected.id])),a.tmp.activeChain=null;var e,c=a.tmp.remote??{hasMore:!0,emptySet:!1,search:null,cached:-1};if(0==c.hasMore&&(e=c.hasMore_search.length,r.substr(0,e)!=c.hasMore_search)&&(c.hasMore=!0),null==t&&o.url&&c.hasMore&&c.search!==r){let e=!0,t=w2utils.lang("Loading...");r.length{a.tmp._skip_filter=!0,this.update(l,e),delete a.tmp._skip_filter,a.tmp._new_search=!0,this.applyFilter(l,e,r).then(e=>{this.getActiveChain(a.name,o.items),a.tmp.searchCount=e.count,a.tmp.search=e.search,!o.prefilter&&""===r||(0!==e.count&&this.getActiveChain(a.name,o.items).includes(a.selected)||(a.selected=null),this.refreshSearch(a.name)),this.initControls(a),this.refreshIndex(a.name,!0),h(e)})}).catch(e=>{console.log("Server Request error",e)})}else{let e;null==t&&!0===(e=this.trigger("search",{search:r,overlay:a,prom:u,resolve:h,reject:d})).isCancelled||(null==t&&(t=a.options.items),!1===o.filter?h({count:-1,search:r}):(t.forEach(t=>{let e="",i="";-1!==["is","begins","begins with"].indexOf(o.match)&&(e="^"),-1!==["is","ends","ends with"].indexOf(o.match)&&(i="$");try{new RegExp(e+r+i,"i").test(t.text)||"..."===t.text?t.hidden=!1:t.hidden=!0}catch(e){}o.hideSelected&&s.includes(t.id)&&(t.hidden=!0),Array.isArray(t.items)&&0{e=e.count;0a.search.length||r.length>=a.search.length&&r.substr(0,a.search.length)!==a.search||r.length{var e=n.url;let i={search:r,max:n.cacheMax};Object.assign(i,n.postData);var t,s=this.trigger("request",{search:r,overlay:l,url:e,postData:i,httpMethod:n.method??"GET",httpHeaders:{}});!0!==s.isCancelled&&(e=new URL(s.detail.url,location),t=w2utils.prepareParams(e,{method:s.detail.httpMethod,headers:s.detail.httpHeaders,body:s.detail.postData},{caller:this,overlay:l,search:r}),a.controller=new AbortController,t.signal=a.controller.signal,fetch(e,t).then(e=>e.json()).then(e=>{a.controller=null;var t=l.trigger("load",{search:i.search,overlay:l,data:e});!0!==t.isCancelled&&("string"==typeof(e=t.detail.data)&&(e=JSON.parse(e)),null==(e=Array.isArray(e)?{records:e}:e).records&&null!=e.items&&(e.records=e.items,delete e.items),e.error||null!=e.records||(e.records=[]),Array.isArray(e.records)?(e.records.length>=n.cacheMax?(e.records.splice(n.cacheMax,e.records.length),a.hasMore=!0):(a.hasMore=!1,a.hasMore_search=r),null==n.recId&&null!=n.recid&&(n.recId=n.recid),(n.recId||n.recText)&&e.records.forEach(e=>{"string"==typeof n.recId&&(e.id=e[n.recId]),"function"==typeof n.recId&&(e.id=n.recId(e)),"string"==typeof n.recText&&(e.text=e[n.recText]),"function"==typeof n.recText&&(e.text=n.recText(e))}),a.loading=!1,a.search=r,a.cached=0==e.records.length?-1:e.records.length,a.lastError="",a.emptySet=""===r&&0===e.records.length,t.finish(),o(w2utils.normMenu(e.records,e))):console.error("ERROR: server did not return proper JSON data structure","\n"," - it should return",{records:[{id:1,text:"item"}]},"\n"," - or just an array ",[{id:1,text:"item"}],"\n"," - or if errorr ",{error:!0,message:"error message"}))}).catch(e=>{var t=this.trigger("error",{overlay:l,search:r,error:e});!0!==t.isCancelled&&("AbortError"!==e?.name&&console.error("ERROR: Server communication failed.","\n"," - it should return",{records:[{id:1,text:"item"}]},"\n"," - or just an array ",[{id:1,text:"item"}],"\n"," - or if errorr ",{error:!0,message:"error message"}),a.loading=!1,a.search="",a.cached=-1,a.emptySet=!0,a.lastError=t.detail.error||"Server communication failed",n.items=[],t.finish(),h())}),s.finish())},e?n.debounce??350:0)),new Promise((e,t)=>{o=e,h=t})}getActiveChain(i,e,s=[],l=[],t){var r=Tooltip.active[i.replace(/[\s\.#]/g,"_")];return null!=r.tmp.activeChain?r.tmp.activeChain:((e=null==e?r.options.items:e).forEach((e,t)=>{e.hidden||e.disabled||e?.text?.startsWith?.("--")||(l.push(s.concat([t]).join("-")),Array.isArray(e.items)&&0{l=l[e].items}),(l="function"==typeof l?l({overlay:e,index:i,parents:s,event:t}):l)[i]);if(null!=o&&!o.disabled){let l=(i,s)=>{i.forEach((e,t)=>{e.id!=o.id&&(e.group===o.group&&e.checked&&(a.find(`.w2ui-menu-item[index="${(s?s+"-":"")+t}"] .w2ui-icon`).removeClass("w2ui-icon-check").addClass("w2ui-icon-empty"),i[t].checked=!1),Array.isArray(e.items))&&l(e.items,t)})};"check"!==r.type&&"radio"!==r.type||!1===o.group||query(t.target).hasClass("menu-remove")||query(t.target).hasClass("menu-help")||query(t.target).closest(".w2ui-menu-item").hasClass("has-sub-menu")||(o.checked="radio"==r.type||!o.checked,o.checked?("radio"===r.type&&query(t.target).closest(".w2ui-menu").find(".w2ui-icon").removeClass("w2ui-icon-check").addClass("w2ui-icon-empty"),"check"===r.type&&null!=o.group&&l(r.items),n.removeClass("w2ui-icon-empty").addClass("w2ui-icon-check")):"check"===r.type&&n.removeClass("w2ui-icon-check").addClass("w2ui-icon-empty")),query(t.target).hasClass("menu-remove")||query(t.target).hasClass("menu-help")||(a.find(".w2ui-menu-item").removeClass("w2ui-selected"),query(t.delegate).hasClass("has-sub-menu"))||query(t.delegate).addClass("w2ui-selected")}}menuClick(s,l,r,n){var a=s.options;let t=a.items;var o=query(l.delegate).closest(".w2ui-menu-item");let h=!a.hideOn.includes("select"),d=((l.shiftKey||l.metaKey||l.ctrlKey)&&(h=!0),"string"==typeof n&&""!==n?n=n.split("-"):Array.isArray(n)||(n=null),n&&n.forEach(e=>{t=t[e].items}),(t="function"==typeof t?t({overlay:s,index:r,parents:n,event:l}):t)[r]);if(d&&(!d.disabled||query(l.target).hasClass("menu-remove"))){let e;var u=[s];let t=s,i;for(;t.options.parentOverlay;)t=t.options.parentOverlay,i??=t,u.push(t);if(query(l.target).hasClass("menu-remove")){if(!0===(e=t.trigger("remove",{originalEvent:l,target:s.name,overlay:s,topOverlay:t,parentOverlay:i,item:d,index:r,el:o[0]})).isCancelled)return;var n=a.items,c=n.findIndex(e=>e.id==d.id),p=(-1!=c&&(p=n.splice(c,1),s.options.parents)&&(f=s.options.parents[s.options.parents.length-1],(f=i.options.items[f].items)[c].id==p[0].id)&&f.splice(c,1),h=!a.hideOn.includes("item-remove"),o.closest(".w2ui-overlay").attr("name"));s.self.update(p,n)}else if(o.hasClass("has-sub-menu")){if(!0===(e=t.trigger("subMenu",{originalEvent:l,target:s.name,overlay:s,topOverlay:t,parentOverlay:i,item:d,index:r,el:o[0]})).isCancelled)return;h=!0}else{var f=this.findChecked(a.items),c=o.attr("index");if(s.selected=isNaN(c)?c:parseInt(c),!0===(e=t.trigger("select",{originalEvent:l,target:s.name,overlay:s,topOverlay:t,parentOverlay:i,item:d,index:r,selected:f,keepOpen:h,el:o[0]})).isCancelled)return;null!=d.keepOpen&&(h=d.keepOpen),["INPUT","TEXTAREA"].includes(s.anchor.tagName)&&(s.anchor.dataset.selected=d.id,s.anchor.dataset.selectedIndex=s.selected)}h||u.forEach(e=>this.hide(e.name)),e.finish()}}findChecked(e){let t=[];return e.forEach(e=>{e.checked&&t.push(e),Array.isArray(e.items)&&(t=t.concat(this.findChecked(e.items)))}),t}keyUp(s,l){var r=s.options,e=l.target.value;let n=!0,a=!1;switch(l.keyCode){case 46:case 8:""!==e||s.displayed||(n=!1);break;case 13:if(!s.displayed||!s.selected)return;var{index:t,parents:i}=this.getCurrent(s.name);l.delegate=query(s.box).find(".w2ui-selected").get(0),this.menuClick(s,l,parseInt(t),i),n=!1;break;case 27:n=!1,s.displayed?this.hide(s.name):(t=s.anchor,["INPUT","TEXTAREA"].includes(t.tagName)&&(t.value="",delete t.dataset.selected,delete t.dataset.selectedIndex));break;case 37:{if(!s.displayed)return;let{item:e,index:t,parents:i}=this.getCurrent(s.name);i&&(e=r.items[i],t=parseInt(i),i="",a=!0),Array.isArray(e?.items)&&0{s.tmp.searchCount=e.count,s.tmp.search=e.search,0!==e.count&&this.getActiveChain(s.name).includes(s.selected)||(s.selected=null),this.refreshSearch(s.name)}),a&&this.refreshIndex(s.name)}}class DateTooltip extends Tooltip{constructor(){super();var e=new Date;this.daysCount=[31,28,31,30,31,30,31,31,30,31,30,31],this.today=e.getFullYear()+"/"+(Number(e.getMonth())+1)+"/"+e.getDate(),this.defaults=w2utils.extend({},this.defaults,{position:"top|bottom",class:"w2ui-calendar",type:"date",value:"",format:"",start:null,end:null,btnNow:!1,blockDates:[],blockWeekdays:[],colored:{},arrowSize:12,autoResize:!1,anchorClass:"w2ui-focus",autoShowOn:"focus",hideOn:["doc-click","focus-change"],onSelect:null})}attach(e,t){let i;1==arguments.length&&e instanceof Object?e=(i=e).anchor:2===arguments.length&&null!=t&&"object"==typeof t&&((i=t).anchor=e);var t=i.hideOn,e=(i=w2utils.extend({},this.defaults,i||{}),t&&(i.hideOn=t),i.format||(e=w2utils.settings.dateFormat,t=w2utils.settings.timeFormat,"date"==i.type?i.format=e:"time"==i.type?i.format=t:i.format=e+"|"+t),"time"==i.type?this.getHourHTML(i):this.getMonthHTML(i));i.style+="; padding: 0;",i.html=e.html;let s=super.attach(i),l=s.overlay;return Object.assign(l.tmp,e),l.on("show.attach",e=>{var e=e.detail.overlay,t=e.anchor,i=e.options;["INPUT","TEXTAREA"].includes(t.tagName)&&!i.value&&t.value&&(e.tmp.initValue=t.value),delete e.newValue,delete e.newDate}),l.on("show:after.attach",e=>{s.overlay?.box&&this.initControls(s.overlay)}),l.on("update:after.attach",e=>{s.overlay?.box&&this.initControls(s.overlay)}),l.on("hide.attach",e=>{var e=e.detail.overlay,t=e.anchor;null!=e.newValue&&(e.newDate&&(e.newValue=e.newDate+" "+e.newValue),["INPUT","TEXTAREA"].includes(t.tagName)&&t.value!=e.newValue&&(t.value=e.newValue),!0!==(t=this.trigger("select",{date:e.newValue,target:e.name,overlay:e})).isCancelled)&&t.finish()}),s.select=t=>(l.on("select.attach",e=>{t(e)}),s),s}initControls(l){let r=l.options,t=e=>{let{month:t,year:i}=l.tmp;12<(t+=e)&&(t=1,i++),t<1&&(t=12,i--);e=this.getMonthHTML(r,t,i);Object.assign(l.tmp,e),query(l.box).find(".w2ui-overlay-body").html(e.html),this.initControls(l)},i=(e,t)=>{query(e.target).parent().find(".w2ui-jump-month, .w2ui-jump-year").removeClass("w2ui-selected"),query(e.target).addClass("w2ui-selected");e=new Date;let{jumpMonth:i,jumpYear:s}=l.tmp;(i=t&&(null==s&&(s=e.getFullYear()),null==i)?e.getMonth()+1:i)&&s&&(t=this.getMonthHTML(r,i,s),Object.assign(l.tmp,t),query(l.box).find(".w2ui-overlay-body").html(t.html),l.tmp.jump=!1,this.initControls(l))};query(l.box).find(".w2ui-cal-title").off(".calendar").on("click.calendar",e=>{var t,i;Object.assign(l.tmp,{jumpYear:null,jumpMonth:null}),l.tmp.jump?({month:t,year:i}=l.tmp,t=this.getMonthHTML(r,t,i),query(l.box).find(".w2ui-overlay-body").html(t.html),l.tmp.jump=!1):(query(l.box).find(".w2ui-overlay-body .w2ui-cal-days").replace(this.getYearHTML()),(i=query(l.box).find(`[name="${l.tmp.year}"]`).get(0))&&i.scrollIntoView(!0),l.tmp.jump=!0),this.initControls(l),e.stopPropagation()}).find(".w2ui-cal-previous").off(".calendar").on("click.calendar",e=>{t(-1),e.stopPropagation()}).parent().find(".w2ui-cal-next").off(".calendar").on("click.calendar",e=>{t(1),e.stopPropagation()}),query(l.box).find(".w2ui-cal-now").off(".calendar").on("click.calendar",e=>{"datetime"==r.type?l.newDate?l.newValue=w2utils.formatTime(new Date,r.format.split("|")[1]):l.newValue=w2utils.formatDateTime(new Date,r.format):"date"==r.type?l.newValue=w2utils.formatDate(new Date,r.format):"time"==r.type&&(l.newValue=w2utils.formatTime(new Date,r.format)),this.hide(l.name)}),query(l.box).off(".calendar").on("contextmenu.calendar",e=>{e.preventDefault()}).on("click.calendar",{delegate:".w2ui-day.w2ui-date"},e=>{"datetime"==r.type?(l.newDate=query(e.target).attr("date"),query(l.box).find(".w2ui-overlay-body").html(this.getHourHTML(l.options).html),this.initControls(l)):(l.newValue=query(e.target).attr("date"),this.hide(l.name))}).on("click.calendar",{delegate:".w2ui-jump-month"},e=>{l.tmp.jumpMonth=parseInt(query(e.target).attr("name")),i(e)}).on("dblclick.calendar",{delegate:".w2ui-jump-month"},e=>{l.tmp.jumpMonth=parseInt(query(e.target).attr("name")),i(e,!0)}).on("click.calendar",{delegate:".w2ui-jump-year"},e=>{l.tmp.jumpYear=parseInt(query(e.target).attr("name")),i(e)}).on("dblclick.calendar",{delegate:".w2ui-jump-year"},e=>{l.tmp.jumpYear=parseInt(query(e.target).attr("name")),i(e,!0)}).on("click.calendar",{delegate:".w2ui-time.hour"},e=>{var e=query(e.target).attr("hour");let t=this.str2min(r.value)%60;l.tmp.initValue&&!r.value&&(t=this.str2min(l.tmp.initValue)%60),r.noMinutes?(l.newValue=this.min2str(60*e,r.format),this.hide(l.name)):(l.newValue=e+":"+t,e=this.getMinHTML(e,r).html,query(l.box).find(".w2ui-overlay-body").html(e),this.initControls(l))}).on("click.calendar",{delegate:".w2ui-time.min"},e=>{e=60*Math.floor(this.str2min(l.newValue)/60)+parseInt(query(e.target).attr("min"));l.newValue=this.min2str(e,r.format),this.hide(l.name)})}getMonthHTML(l,r,e){var t=w2utils.settings.fulldays.slice(),i=w2utils.settings.shortdays.slice();"M"!==w2utils.settings.weekStarts&&(t.unshift(t.pop()),i.unshift(i.pop()));let n=new Date;var t="datetime"===l.type?w2utils.isDateTime(l.value,l.format,!0):w2utils.isDate(l.value,l.format,!0),a=w2utils.formatDate(t);null!=r&&null!=e||(e=(t||n).getFullYear(),r=t?t.getMonth()+1:n.getMonth()+1),12${i[e]}`}let u=` + `),query(s.box).find(".w2ui-no-items").show()))}applyFilter(s,i,l,r){let n=0,a=Tooltip.active[s.replace(/[\s\.#]/g,"_")],o=a.options,h,d;var u=new Promise((e,t)=>{h=e,d=t});if(!0!==a.tmp._skip_filter){null==l&&(l=["INPUT","TEXTAREA"].includes(a.anchor.tagName)?a.anchor.value:""),!1===a.tmp._new_search&&(l="");let t=[];o.selected&&(Array.isArray(o.selected)?t=o.selected.map(e=>e?.id??e):o.selected?.id&&(t=[o.selected.id])),a.tmp.activeChain=null;var e,c=a.tmp.remote??{hasMore:!0,emptySet:!1,search:null,cached:-1};if(0==c.hasMore&&(e=c.hasMore_search.length,l.substr(0,e)!=c.hasMore_search)&&(c.hasMore=!0),null==i&&o.url&&c.hasMore&&c.search!==l){let e=!0,t=w2utils.lang("Loading...");l.length{a.tmp._skip_filter=!0,this.update(s,e),delete a.tmp._skip_filter,a.tmp._new_search=!0,this.applyFilter(s,e,l).then(e=>{this.getActiveChain(a.name,o.items),a.tmp.searchCount=e.count,a.tmp.search=e.search,!o.prefilter&&""===l||(0!==e.count&&this.getActiveChain(a.name,o.items).includes(a.selected)||(a.selected=null),this.refreshSearch(a.name)),this.initControls(a),this.refreshIndex(a.name,!0),h(e)})}).catch(e=>{console.log("Server Request error",e)})}else{let e;null==i&&!0===(e=this.trigger("search",{search:l,overlay:a,prom:u,resolve:h,reject:d})).isCancelled||(null==i&&(i=a.options.items),!1===o.filter?h({count:-1,search:l}):(i.forEach(i=>{if("regex"==o.match)try{new RegExp(l,"i").test(i.text)||"..."===i.text?i.hidden=!1:i.hidden=!0}catch(e){}else{let e="",t="";-1!==["is","begins","begins with"].indexOf(o.match)&&(e="^"),-1!==["is","ends","ends with"].indexOf(o.match)&&(t="$");try{new RegExp(e+l+t,"i").test(i.text)||"..."===i.text?i.hidden=!1:i.hidden=!0}catch(e){}}o.hideSelected&&t.includes(i.id)&&(i.hidden=!0),Array.isArray(i.items)&&0{e=e.count;0a.search.length||r.length>=a.search.length&&r.substr(0,a.search.length)!==a.search||r.length{var e=n.url;let i={search:r,max:n.cacheMax};Object.assign(i,n.postData);var t,s=this.trigger("request",{search:r,overlay:l,url:e,postData:i,httpMethod:n.method??"GET",httpHeaders:{}});!0!==s.isCancelled&&(e=new URL(s.detail.url,location),t=w2utils.prepareParams(e,{method:s.detail.httpMethod,headers:s.detail.httpHeaders,body:s.detail.postData},{caller:this,overlay:l,search:r}),a.controller=new AbortController,t.signal=a.controller.signal,fetch(e,t).then(e=>e.json()).then(e=>{a.controller=null;var t=l.trigger("load",{search:i.search,overlay:l,data:e});!0!==t.isCancelled&&("string"==typeof(e=t.detail.data)&&(e=JSON.parse(e)),null==(e=Array.isArray(e)?{records:e}:e).records&&null!=e.items&&(e.records=e.items,delete e.items),e.error||null!=e.records||(e.records=[]),Array.isArray(e.records)?(e.records.length>=n.cacheMax?(e.records.splice(n.cacheMax,e.records.length),a.hasMore=!0):(a.hasMore=!1,a.hasMore_search=r),null==n.recId&&null!=n.recid&&(n.recId=n.recid),(n.recId||n.recText)&&e.records.forEach(e=>{"string"==typeof n.recId&&(e.id=e[n.recId]),"function"==typeof n.recId&&(e.id=n.recId(e)),"string"==typeof n.recText&&(e.text=e[n.recText]),"function"==typeof n.recText&&(e.text=n.recText(e))}),a.loading=!1,a.search=r,a.cached=0==e.records.length?-1:e.records.length,a.lastError="",a.emptySet=""===r&&0===e.records.length,t.finish(),o(w2utils.normMenu(e.records,e))):console.error("ERROR: server did not return proper JSON data structure","\n"," - it should return",{records:[{id:1,text:"item"}]},"\n"," - or just an array ",[{id:1,text:"item"}],"\n"," - or if errorr ",{error:!0,message:"error message"}))}).catch(e=>{var t=this.trigger("error",{overlay:l,search:r,error:e});!0!==t.isCancelled&&("AbortError"!==e?.name&&console.error("ERROR: Server communication failed.","\n"," - it should return",{records:[{id:1,text:"item"}]},"\n"," - or just an array ",[{id:1,text:"item"}],"\n"," - or if errorr ",{error:!0,message:"error message"}),a.loading=!1,a.search="",a.cached=-1,a.emptySet=!0,a.lastError=t.detail.error||"Server communication failed",n.items=[],t.finish(),h())}),s.finish())},e?n.debounce??350:0)),new Promise((e,t)=>{o=e,h=t})}getActiveChain(i,e,s=[],l=[],t){var r=Tooltip.active[i.replace(/[\s\.#]/g,"_")];return null!=r.tmp.activeChain?r.tmp.activeChain:((e=null==e?r.options.items:e).forEach((e,t)=>{e.hidden||e.disabled||e?.text?.startsWith?.("--")||(l.push(s.concat([t]).join("-")),Array.isArray(e.items)&&0{l=l[e].items}),(l="function"==typeof l?l({overlay:e,index:i,parents:s,event:t}):l)[i]);if(null!=o&&!o.disabled){let l=(i,s)=>{i.forEach((e,t)=>{e.id!=o.id&&(e.group===o.group&&e.checked&&(a.find(`.w2ui-menu-item[index="${(s?s+"-":"")+t}"] .w2ui-icon`).removeClass("w2ui-icon-check").addClass("w2ui-icon-empty"),i[t].checked=!1),Array.isArray(e.items))&&l(e.items,t)})};"check"!==r.type&&"radio"!==r.type||!1===o.group||query(t.target).hasClass("menu-remove")||query(t.target).hasClass("menu-help")||query(t.target).closest(".w2ui-menu-item").hasClass("has-sub-menu")||(o.checked="radio"==r.type||!o.checked,o.checked?("radio"===r.type&&query(t.target).closest(".w2ui-menu").find(".w2ui-icon").removeClass("w2ui-icon-check").addClass("w2ui-icon-empty"),"check"===r.type&&null!=o.group&&l(r.items),n.removeClass("w2ui-icon-empty").addClass("w2ui-icon-check")):"check"===r.type&&n.removeClass("w2ui-icon-check").addClass("w2ui-icon-empty")),query(t.target).hasClass("menu-remove")||query(t.target).hasClass("menu-help")||(a.find(".w2ui-menu-item").removeClass("w2ui-selected"),query(t.delegate).hasClass("has-sub-menu"))||query(t.delegate).addClass("w2ui-selected")}}menuClick(s,l,r,n){var a=s.options;let t=a.items;var o=query(l.delegate).closest(".w2ui-menu-item");let h=!a.hideOn.includes("select"),d=((l.shiftKey||l.metaKey||l.ctrlKey)&&(h=!0),"string"==typeof n&&""!==n?n=n.split("-"):Array.isArray(n)||(n=null),n&&n.forEach(e=>{t=t[e].items}),(t="function"==typeof t?t({overlay:s,index:r,parents:n,event:l}):t)[r]);if(d&&(!d.disabled||query(l.target).hasClass("menu-remove"))){let e;var u=[s];let t=s,i;for(;t.options.parentOverlay;)t=t.options.parentOverlay,i??=t,u.push(t);if(query(l.target).hasClass("menu-remove")){if(!0===(e=t.trigger("remove",{originalEvent:l,target:s.name,overlay:s,topOverlay:t,parentOverlay:i,item:d,index:r,el:o[0]})).isCancelled)return;var n=a.items,c=n.findIndex(e=>e.id==d.id),p=(-1!=c&&(p=n.splice(c,1),s.options.parents)&&(f=s.options.parents[s.options.parents.length-1],(f=i.options.items[f].items)[c].id==p[0].id)&&f.splice(c,1),h=!a.hideOn.includes("item-remove"),o.closest(".w2ui-overlay").attr("name"));s.self.update(p,n)}else if(o.hasClass("has-sub-menu")){if(!0===(e=t.trigger("subMenu",{originalEvent:l,target:s.name,overlay:s,topOverlay:t,parentOverlay:i,item:d,index:r,el:o[0]})).isCancelled)return;h=!0}else{var f=this.findChecked(a.items),c=o.attr("index");if(s.selected=isNaN(c)?c:parseInt(c),!0===(e=t.trigger("select",{originalEvent:l,target:s.name,overlay:s,topOverlay:t,parentOverlay:i,item:d,index:r,selected:f,keepOpen:h,el:o[0]})).isCancelled)return;null!=d.keepOpen&&(h=d.keepOpen),["INPUT","TEXTAREA"].includes(s.anchor.tagName)&&(s.anchor.dataset.selected=d.id,s.anchor.dataset.selectedIndex=s.selected)}h||u.forEach(e=>this.hide(e.name)),e.finish()}}findChecked(e){let t=[];return e.forEach(e=>{e.checked&&t.push(e),Array.isArray(e.items)&&(t=t.concat(this.findChecked(e.items)))}),t}keyUp(s,l){var r=s.options,e=l.target.value;let n=!0,a=!1;switch(l.keyCode){case 46:case 8:""!==e||s.displayed||(n=!1);break;case 13:if(!s.displayed||!s.selected)return;var{index:t,parents:i}=this.getCurrent(s.name);l.delegate=query(s.box).find(".w2ui-selected").get(0),this.menuClick(s,l,parseInt(t),i),n=!1;break;case 27:n=!1,s.displayed?this.hide(s.name):(t=s.anchor,["INPUT","TEXTAREA"].includes(t.tagName)&&(t.value="",delete t.dataset.selected,delete t.dataset.selectedIndex));break;case 37:{if(!s.displayed)return;let{item:e,index:t,parents:i}=this.getCurrent(s.name);i&&(e=r.items[i],t=parseInt(i),i="",a=!0),Array.isArray(e?.items)&&0{s.tmp.searchCount=e.count,s.tmp.search=e.search,0!==e.count&&this.getActiveChain(s.name).includes(s.selected)||(s.selected=null),this.refreshSearch(s.name)}),a&&this.refreshIndex(s.name)}}class DateTooltip extends Tooltip{constructor(){super();var e=new Date;this.daysCount=[31,28,31,30,31,30,31,31,30,31,30,31],this.today=e.getFullYear()+"/"+(Number(e.getMonth())+1)+"/"+e.getDate(),this.defaults=w2utils.extend({},this.defaults,{position:"top|bottom",class:"w2ui-calendar",type:"date",value:"",format:"",start:null,end:null,btnNow:!1,blockDates:[],blockWeekdays:[],colored:{},arrowSize:12,autoResize:!1,anchorClass:"w2ui-focus",autoShowOn:"focus",hideOn:["doc-click","focus-change"],onSelect:null})}attach(e,t){let i;1==arguments.length&&e instanceof Object?e=(i=e).anchor:2===arguments.length&&null!=t&&"object"==typeof t&&((i=t).anchor=e);var t=i.hideOn,e=(i=w2utils.extend({},this.defaults,i||{}),t&&(i.hideOn=t),i.format||(e=w2utils.settings.dateFormat,t=w2utils.settings.timeFormat,"date"==i.type?i.format=e:"time"==i.type?i.format=t:i.format=e+"|"+t),"time"==i.type?this.getHourHTML(i):this.getMonthHTML(i));i.style+="; padding: 0;",i.html=e.html;let s=super.attach(i),l=s.overlay;return Object.assign(l.tmp,e),l.on("show.attach",e=>{var e=e.detail.overlay,t=e.anchor,i=e.options;["INPUT","TEXTAREA"].includes(t.tagName)&&!i.value&&t.value&&(e.tmp.initValue=t.value),delete e.newValue,delete e.newDate}),l.on("show:after.attach",e=>{s.overlay?.box&&this.initControls(s.overlay)}),l.on("update:after.attach",e=>{s.overlay?.box&&this.initControls(s.overlay)}),l.on("hide.attach",e=>{var e=e.detail.overlay,t=e.anchor;null!=e.newValue&&(e.newDate&&(e.newValue=e.newDate+" "+e.newValue),["INPUT","TEXTAREA"].includes(t.tagName)&&t.value!=e.newValue&&(t.value=e.newValue),!0!==(t=this.trigger("select",{date:e.newValue,target:e.name,overlay:e})).isCancelled)&&t.finish()}),s.select=t=>(l.on("select.attach",e=>{t(e)}),s),s}initControls(l){let r=l.options,t=e=>{let{month:t,year:i}=l.tmp;12<(t+=e)&&(t=1,i++),t<1&&(t=12,i--);e=this.getMonthHTML(r,t,i);Object.assign(l.tmp,e),query(l.box).find(".w2ui-overlay-body").html(e.html),this.initControls(l)},i=(e,t)=>{query(e.target).parent().find(".w2ui-jump-month, .w2ui-jump-year").removeClass("w2ui-selected"),query(e.target).addClass("w2ui-selected");e=new Date;let{jumpMonth:i,jumpYear:s}=l.tmp;(i=t&&(null==s&&(s=e.getFullYear()),null==i)?e.getMonth()+1:i)&&s&&(t=this.getMonthHTML(r,i,s),Object.assign(l.tmp,t),query(l.box).find(".w2ui-overlay-body").html(t.html),l.tmp.jump=!1,this.initControls(l))};query(l.box).find(".w2ui-cal-title").off(".calendar").on("click.calendar",e=>{var t,i;Object.assign(l.tmp,{jumpYear:null,jumpMonth:null}),l.tmp.jump?({month:t,year:i}=l.tmp,t=this.getMonthHTML(r,t,i),query(l.box).find(".w2ui-overlay-body").html(t.html),l.tmp.jump=!1):(query(l.box).find(".w2ui-overlay-body .w2ui-cal-days").replace(this.getYearHTML()),(i=query(l.box).find(`[name="${l.tmp.year}"]`).get(0))&&i.scrollIntoView(!0),l.tmp.jump=!0),this.initControls(l),e.stopPropagation()}).find(".w2ui-cal-previous").off(".calendar").on("click.calendar",e=>{t(-1),e.stopPropagation()}).parent().find(".w2ui-cal-next").off(".calendar").on("click.calendar",e=>{t(1),e.stopPropagation()}),query(l.box).find(".w2ui-cal-now").off(".calendar").on("click.calendar",e=>{"datetime"==r.type?l.newDate?l.newValue=w2utils.formatTime(new Date,r.format.split("|")[1]):l.newValue=w2utils.formatDateTime(new Date,r.format):"date"==r.type?l.newValue=w2utils.formatDate(new Date,r.format):"time"==r.type&&(l.newValue=w2utils.formatTime(new Date,r.format)),this.hide(l.name)}),query(l.box).off(".calendar").on("contextmenu.calendar",e=>{e.preventDefault()}).on("click.calendar",{delegate:".w2ui-day.w2ui-date"},e=>{"datetime"==r.type?(l.newDate=query(e.target).attr("date"),query(l.box).find(".w2ui-overlay-body").html(this.getHourHTML(l.options).html),this.initControls(l)):(l.newValue=query(e.target).attr("date"),this.hide(l.name))}).on("click.calendar",{delegate:".w2ui-jump-month"},e=>{l.tmp.jumpMonth=parseInt(query(e.target).attr("name")),i(e)}).on("dblclick.calendar",{delegate:".w2ui-jump-month"},e=>{l.tmp.jumpMonth=parseInt(query(e.target).attr("name")),i(e,!0)}).on("click.calendar",{delegate:".w2ui-jump-year"},e=>{l.tmp.jumpYear=parseInt(query(e.target).attr("name")),i(e)}).on("dblclick.calendar",{delegate:".w2ui-jump-year"},e=>{l.tmp.jumpYear=parseInt(query(e.target).attr("name")),i(e,!0)}).on("click.calendar",{delegate:".w2ui-time.hour"},e=>{var e=query(e.target).attr("hour");let t=this.str2min(r.value)%60;l.tmp.initValue&&!r.value&&(t=this.str2min(l.tmp.initValue)%60),r.noMinutes?(l.newValue=this.min2str(60*e,r.format),this.hide(l.name)):(l.newValue=e+":"+t,e=this.getMinHTML(e,r).html,query(l.box).find(".w2ui-overlay-body").html(e),this.initControls(l))}).on("click.calendar",{delegate:".w2ui-time.min"},e=>{e=60*Math.floor(this.str2min(l.newValue)/60)+parseInt(query(e.target).attr("min"));l.newValue=this.min2str(e,r.format),this.hide(l.name)})}getMonthHTML(l,r,e){var t=w2utils.settings.fulldays.slice(),i=w2utils.settings.shortdays.slice();"M"!==w2utils.settings.weekStarts&&(t.unshift(t.pop()),i.unshift(i.pop()));let n=new Date;var t="datetime"===l.type?w2utils.isDateTime(l.value,l.format,!0):w2utils.isDate(l.value,l.format,!0),a=w2utils.formatDate(t);null!=r&&null!=e||(e=(t||n).getFullYear(),r=t?t.getMonth()+1:n.getMonth()+1),12${i[e]}`}let u=`
diff --git a/dist/w2ui.js b/dist/w2ui.js index 068c72e7b..63abc5fa3 100644 --- a/dist/w2ui.js +++ b/dist/w2ui.js @@ -1,4 +1,4 @@ -/* w2ui 2.0.x (nightly) (10/31/2025, 8:43:03 AM) (c) http://w2ui.com, vitmalina@gmail.com */ +/* w2ui 2.0.x (nightly) (11/23/2025, 8:17:07 PM) (c) http://w2ui.com, vitmalina@gmail.com */ /** * Part of w2ui 2.0 library * - Dependencies: w2utils @@ -2575,7 +2575,6 @@ class Utils { options.tag ??= 'span' options.class ??= 'w2ui-marker' options.raplace = (matched) => `<${options.tag} class="${options.class}">${matched}` - const isRegexSearch = options.isRegex || false; if (!Array.isArray(items)) { if (items != null && items !== '') { @@ -2618,9 +2617,7 @@ class Utils { // If wholeWord is true, wrap the pattern with word boundary markers pattern = '\b' + pattern + '\b' } - let regex = new RegExp(pattern, flags) - // Get all text nodes let textNodes = [] function getTextNodes(node) { @@ -2635,15 +2632,12 @@ class Utils { } } } - getTextNodes(el) - // Process each text node textNodes.forEach(textNode => { let text = textNode.nodeValue let matches = [] let match - // Find all matches if (options.onlyFirst) { match = regex.exec(text) @@ -2659,13 +2653,11 @@ class Utils { }) } } - // Apply highlighting if (matches.length > 0) { let parent = textNode.parentNode let fragment = document.createDocumentFragment() let lastIndex = 0 - matches.forEach(match => { // Add text before match if (match.index > lastIndex) { @@ -2673,23 +2665,19 @@ class Utils { text.substring(lastIndex, match.index) )) } - // Add highlighted match let span = document.createElement(options.tag) span.className = options.class span.appendChild(document.createTextNode(match.text)) fragment.appendChild(span) - lastIndex = match.index + match.text.length }) - // Add remaining text if (lastIndex < text.length) { fragment.appendChild(document.createTextNode( text.substring(lastIndex) )) } - // Replace the text node with our fragment parent.replaceChild(fragment, textNode) } @@ -12213,12 +12201,12 @@ class w2grid extends w2base { 'save' : { type: 'button', id: 'w2ui-save', text: 'Save', tooltip: w2utils.lang('Save changed records'), icon: 'w2ui-icon-check' } } this.operators = { // for search fields - 'text' : ['is', 'begins', 'contains', 'ends', 'regex'], // could have "in" and "not in" - 'number' : ['=', 'between', '>', '<', '>=', '<=', 'regex'], + 'text' : ['is', 'begins', 'contains', 'ends'], // could have "in" and "not in" + 'number' : ['=', 'between', '>', '<', '>=', '<='], 'date' : ['is', { oper: 'less', text: 'before'}, { oper: 'more', text: 'since' }, 'between'], 'list' : ['is'], 'hex' : ['is', 'between'], - 'color' : ['is', 'begins', 'contains', 'ends', 'regex'], + 'color' : ['is', 'begins', 'contains', 'ends'], 'enum' : ['in', 'not in'] // -- all possible // "text" : ['is', 'begins', 'contains', 'ends'], @@ -13127,16 +13115,6 @@ class w2grid extends w2base { let lastIndex = val1.lastIndexOf(val2) if (lastIndex !== -1 && lastIndex == val1.length - val2.length) fl++ // do not hide record break - case 'regex': - try { - const re = new RegExp(val2, 'i') // Case-insensitive regex - if (re.test(val1)) fl++ // do not hide record - } catch (e) { - console.error('Invalid regular expression:', e) - // use fallback - if (val1.indexOf(val2) >= 0) fl++ - } - break; } } if ((obj.last.logic == 'OR' && fl !== 0) || (obj.last.logic == 'AND' && fl == obj.searchData.length)) { @@ -17057,17 +17035,12 @@ class w2grid extends w2base { let fld = this.getSearch(sdata.field) if (!fld || fld.hidden) continue let ind = this.getColumn(sdata.field, true) - if (sdata.operator == 'regex') { - const defaultOptions = { onlyFirst: false, wholeWord: false, isRegex: true } - search.push({ field: sdata.field, search: sdata.value, col: ind, options: defaultOptions }) - } else { - search.push({ field: sdata.field, search: sdata.value, col: ind }) - } + search.push({ field: sdata.field, search: sdata.value, col: ind }) } if (search.length > 0) { search.forEach((item) => { let el = query(this.box).find('td[col="'+ item.col +'"]:not(.w2ui-head)') - w2utils.marker(el, item.search, item.options) + w2utils.marker(el, item.search) }) } }, 50) @@ -19709,17 +19682,12 @@ class w2grid extends w2base { let fld = obj.getSearch(sdata.field) if (!fld || fld.hidden) continue let ind = obj.getColumn(sdata.field, true) - if (sdata.operator == 'regex') { - const defaultOptions = { onlyFirst: false, wholeWord: false, isRegex: true } - search.push({ field: sdata.field, search: sdata.value, col: ind, options: defaultOptions }) - } else { - search.push({ field: sdata.field, search: sdata.value, col: ind }) - } + search.push({ field: sdata.field, search: sdata.value, col: ind }) } if (search.length > 0) { search.forEach((item) => { let el = query(obj.box).find('td[col="'+ item.col +'"]:not(.w2ui-head)') - w2utils.marker(el, item.search, item.options) + w2utils.marker(el, item.search) }) } }, 50) @@ -23124,7 +23092,7 @@ class w2field extends w2base { items : [], // array of items, can be a function selected : {}, // selected item itemMap : null, // can be { id: 'id', text: 'text' } to specify field mapping for an item - match : 'begins', // ['contains', 'is', 'begins', 'ends', 'regex'] + match : 'begins', // ['contains', 'is', 'begins', 'ends'] filter : true, // weather to filter at all compare : null, // compare function for filtering prefix : '', // prefix for input @@ -23178,7 +23146,7 @@ class w2field extends w2base { } options = w2utils.extend({}, defaults, options) // validate match - let valid = ['is', 'begins', 'contains', 'ends', 'regex'] + let valid = ['is', 'begins', 'contains', 'ends'] if (!valid.includes(options.match)) { console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(', ')}.`) } @@ -23201,7 +23169,7 @@ class w2field extends w2base { selected : [], itemMap : null, // can be { id: 'id', text: 'text' } to specify field mapping for an item max : 0, // max number of selected items, 0 - unlimited - match : 'begins', // ['contains', 'is', 'begins', 'ends', 'regex'] + match : 'begins', // ['contains', 'is', 'begins', 'ends'] filter : true, // if true, will apply filtering compare : null, // compare function for filtering // -- remote items -- @@ -23246,7 +23214,7 @@ class w2field extends w2base { options._items_fun = options.items } // validate match - let valid = ['is', 'begins', 'contains', 'ends', 'regex'] + let valid = ['is', 'begins', 'contains', 'ends'] if (!valid.includes(options.match)) { console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(', ')}.`) } @@ -24915,4 +24883,4 @@ if (global) { w2popup, w2alert, w2confirm, w2prompt, Dialog, w2tooltip, w2menu, w2color, w2date, Tooltip, w2toolbar, w2sidebar, w2tabs, w2layout, w2grid, w2form, w2field -}); +}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 59b7bdbb5..7328390b7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,7 +1,8 @@ /* eslint-env node */ const gulp = require('gulp') const header = require('gulp-header') -const iconfont = require('gulp-iconfont') +// lazy load iconfont to avoid ESM compatibility issues +let iconfont = null const less = require('gulp-less') const cleanCSS = require('gulp-clean-css') const uglify = require('gulp-uglify') @@ -175,6 +176,10 @@ let tasks = { }, icons(cb) { + // Lazy load iconfont + if (!iconfont) { + iconfont = require('gulp-iconfont') + } let fs = require('fs') let css = `@font-face { font-family: "w2ui-font"; diff --git a/package.json b/package.json index d745114f1..e207c2593 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,13 @@ "del": "^6.0.0", "eslint": "^8.25.0", "eslint-plugin-align-assignments": "^1.1.2", - "gulp": "^4.0.2", + "gulp": "^5.0.1", "gulp-babel": "^8.0.0", "gulp-clean-css": "^4.3.0", "gulp-concat": "^2.6.1", - "gulp-header": "^2.0.9", - "gulp-iconfont": "^11.0.0", - "gulp-less": "^4.0.1", + "gulp-header": "^1.8.9", + "gulp-iconfont": "^12.0.0", + "gulp-less": "^5.0.0", "gulp-rename": "^2.0.0", "gulp-replace": "^1.0.0", "gulp-uglify": "^3.0.2", diff --git a/patch.diff b/patch.diff new file mode 100644 index 0000000000000000000000000000000000000000..f5ccc0d3e0af6c5f9790344c1eb5285d4ef61885 GIT binary patch literal 35330 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 { - let prefix = '' - let suffix = '' - if (['is', 'begins', 'begins with'].indexOf(options.match) !== -1) prefix = '^' - if (['is', 'ends', 'ends with'].indexOf(options.match) !== -1) suffix = '$' - try { - let re = new RegExp(prefix + search + suffix, 'i') - if (re.test(item.text) || item.text === '...') { - item.hidden = false - } else { - item.hidden = true - } - } catch (e) {} + if (options.match == 'regex') { + try { + let re = new RegExp(search, 'i') + if (re.test(item.text) || item.text === '...') { + item.hidden = false + } else { + item.hidden = true + } + } catch (e) {} + } else { + let prefix = '' + let suffix = '' + if (['is', 'begins', 'begins with'].indexOf(options.match) !== -1) prefix = '^' + if (['is', 'ends', 'ends with'].indexOf(options.match) !== -1) suffix = '$' + try { + let re = new RegExp(prefix + search + suffix, 'i') + if (re.test(item.text) || item.text === '...') { + item.hidden = false + } else { + item.hidden = true + } + } catch (e) {} + } // do not show selected items if (options.hideSelected && selectedIds.includes(item.id)) { item.hidden = true diff --git a/src/w2utils.js b/src/w2utils.js index 171b910fb..36c386887 100644 --- a/src/w2utils.js +++ b/src/w2utils.js @@ -1656,10 +1656,12 @@ class Utils { return str.replace(/\${([^}]+)?}/g, function($1, $2) { return replace_obj[$2]||$2 }) } - marker(el, items, options = { onlyFirst: false, wholeWord: false }) { + marker(el, items, options = { onlyFirst: false, wholeWord: false, isRegex: false}) { options.tag ??= 'span' options.class ??= 'w2ui-marker' options.raplace = (matched) => `<${options.tag} class="${options.class}">${matched}` + + const isRegexSearch = options.isRegex || false; if (!Array.isArray(items)) { if (items != null && items !== '') { items = [items] @@ -1670,14 +1672,125 @@ class Utils { if (typeof el == 'string') { _clearMerkers(el) items.forEach(item => { - el = _replace(el, item, options.raplace) + if (isRegexSearch) { + // For regex searches with string elements + try { + let flags = 'i' + (!options.onlyFirst ? 'g' : '') + let regex = new RegExp(item, flags) + el = el.replace(regex, options.raplace) + } catch (e) { + console.error('Invalid regular expression:', e) + // Fallback to standard replace + el = _replace(el, item, options.raplace) + } + } else { + // Standard string replace + el = _replace(el, item, options.raplace) + } }) } else { query(el).each(el => { _clearMerkers(el) - items.forEach(item => { - el.innerHTML = _replace(el.innerHTML, item, options.raplace) - }) + if (isRegexSearch) { + // For regex searches, use DOM traversal approach + items.forEach(pattern => { + try { + let flags = 'i' // Always case-insensitive + if (!options.onlyFirst) { + flags += 'g' // Add 'g' for global unless onlyFirst is true + } + if (options.wholeWord) { + // If wholeWord is true, wrap the pattern with word boundary markers + pattern = '\b' + pattern + '\b' + } + + let regex = new RegExp(pattern, flags) + + // Get all text nodes + let textNodes = [] + function getTextNodes(node) { + if (node.nodeType === 3) { // Text node + textNodes.push(node) + } else if (node.nodeType === 1) { // Element node + // Skip script and style tags + if (node.tagName !== 'SCRIPT' && node.tagName !== 'STYLE') { + for (let i = 0; i < node.childNodes.length; i++) { + getTextNodes(node.childNodes[i]) + } + } + } + } + + getTextNodes(el) + + // Process each text node + textNodes.forEach(textNode => { + let text = textNode.nodeValue + let matches = [] + let match + + // Find all matches + if (options.onlyFirst) { + match = regex.exec(text) + if (match) matches.push({ + index: match.index, + text: match[0] + }) + } else { + while ((match = regex.exec(text)) !== null) { + matches.push({ + index: match.index, + text: match[0] + }) + } + } + + // Apply highlighting + if (matches.length > 0) { + let parent = textNode.parentNode + let fragment = document.createDocumentFragment() + let lastIndex = 0 + + matches.forEach(match => { + // Add text before match + if (match.index > lastIndex) { + fragment.appendChild(document.createTextNode( + text.substring(lastIndex, match.index) + )) + } + + // Add highlighted match + let span = document.createElement(options.tag) + span.className = options.class + span.appendChild(document.createTextNode(match.text)) + fragment.appendChild(span) + + lastIndex = match.index + match.text.length + }) + + // Add remaining text + if (lastIndex < text.length) { + fragment.appendChild(document.createTextNode( + text.substring(lastIndex) + )) + } + + // Replace the text node with our fragment + parent.replaceChild(fragment, textNode) + } + }) + } catch (e) { + console.error('Invalid regular expression:', e) + // Fallback to standard innerHTML replace + el.innerHTML = _replace(el.innerHTML, pattern, options.raplace) + } + }) + } else { + // Standard innerHTML replace for non-regex + items.forEach(item => { + el.innerHTML = _replace(el.innerHTML, item, options.raplace) + }) + } }) } return el