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:

+ +
+ + + + +
+ +
+ +

5. Features

+
+ +
+
+ + + + 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

+
+ +
+
+ + + + + + 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 12629fe8f..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 @@ -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/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 000000000..f5ccc0d3e Binary files /dev/null and b/patch.diff differ diff --git a/src/w2locale.js b/src/w2locale.js index 0343c5431..d3c75b478 100644 --- a/src/w2locale.js +++ b/src/w2locale.js @@ -95,6 +95,8 @@ const w2locale = { 'record': '---', 'records': '---', 'Refreshing...': '---', + 'RegEx': '---', + 'regex': '---', 'Reload data in the list': '---', 'Remove': '---', 'Remove This Field': '---', diff --git a/src/w2tooltip.js b/src/w2tooltip.js index 70a70f1c3..9c308bab8 100644 --- a/src/w2tooltip.js +++ b/src/w2tooltip.js @@ -1441,7 +1441,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, @@ -2126,18 +2126,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/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