web-mojo 2.1.678 → 2.1.697

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +18 -10
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/ChatView-BN2Rl2UP.js +2 -0
  12. package/dist/chunks/ChatView-BN2Rl2UP.js.map +1 -0
  13. package/dist/chunks/{ChatView-iDY-MsDn.js → ChatView-CGiNfhMt.js} +11 -10
  14. package/dist/chunks/ChatView-CGiNfhMt.js.map +1 -0
  15. package/dist/chunks/{ContextMenu-zGKcw_a6.js → ContextMenu-9lS3qUwW.js} +2 -2
  16. package/dist/chunks/{ContextMenu-zGKcw_a6.js.map → ContextMenu-9lS3qUwW.js.map} +1 -1
  17. package/dist/chunks/{ContextMenu-DZLL0Aeu.js → ContextMenu-BBy3nk5G.js} +2 -2
  18. package/dist/chunks/{ContextMenu-DZLL0Aeu.js.map → ContextMenu-BBy3nk5G.js.map} +1 -1
  19. package/dist/chunks/DataView-CvD3EvJ9.js +2 -0
  20. package/dist/chunks/DataView-CvD3EvJ9.js.map +1 -0
  21. package/dist/chunks/{DataView-Dx78M3yt.js → DataView-PSRYZfrq.js} +29 -15
  22. package/dist/chunks/DataView-PSRYZfrq.js.map +1 -0
  23. package/dist/chunks/{Dialog-B3bD_iSn.js → Dialog-BUZ3RRPM.js} +5 -5
  24. package/dist/chunks/{Dialog-B3bD_iSn.js.map → Dialog-BUZ3RRPM.js.map} +1 -1
  25. package/dist/chunks/{Dialog-D9EXh_A9.js → Dialog-C_HOebyH.js} +2 -2
  26. package/dist/chunks/{Dialog-D9EXh_A9.js.map → Dialog-C_HOebyH.js.map} +1 -1
  27. package/dist/chunks/{FormView-NSlCK9fF.js → FormView-CeoubZ-s.js} +2 -2
  28. package/dist/chunks/{FormView-NSlCK9fF.js.map → FormView-CeoubZ-s.js.map} +1 -1
  29. package/dist/chunks/{FormView-zgLKURWJ.js → FormView-Di5zdS39.js} +2 -2
  30. package/dist/chunks/{FormView-zgLKURWJ.js.map → FormView-Di5zdS39.js.map} +1 -1
  31. package/dist/chunks/{MetricsMiniChartWidget-CmEzlyAO.js → MetricsMiniChartWidget-Df_aqXiH.js} +50 -13
  32. package/dist/chunks/{MetricsMiniChartWidget-CmEzlyAO.js.map → MetricsMiniChartWidget-Df_aqXiH.js.map} +1 -1
  33. package/dist/chunks/{MetricsMiniChartWidget-DXNa0bT2.js → MetricsMiniChartWidget-RKmQrR86.js} +2 -2
  34. package/dist/chunks/{MetricsMiniChartWidget-DXNa0bT2.js.map → MetricsMiniChartWidget-RKmQrR86.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-C8xUA4Po.js → PDFViewer-CQDTUfSe.js} +3 -3
  36. package/dist/chunks/{PDFViewer-C8xUA4Po.js.map → PDFViewer-CQDTUfSe.js.map} +1 -1
  37. package/dist/chunks/{PDFViewer-lbh_aAcj.js → PDFViewer-DL3ALPrt.js} +2 -2
  38. package/dist/chunks/{PDFViewer-lbh_aAcj.js.map → PDFViewer-DL3ALPrt.js.map} +1 -1
  39. package/dist/chunks/{Page-UNb6Zq-s.js → Page-B2QytRMU.js} +2 -2
  40. package/dist/chunks/{Page-UNb6Zq-s.js.map → Page-B2QytRMU.js.map} +1 -1
  41. package/dist/chunks/{Page-B4C7paFp.js → Page-DpnZbCfy.js} +2 -2
  42. package/dist/chunks/{Page-B4C7paFp.js.map → Page-DpnZbCfy.js.map} +1 -1
  43. package/dist/chunks/{TopNav-DLu1JgLn.js → TopNav-BUxuDl2O.js} +5 -5
  44. package/dist/chunks/{TopNav-DLu1JgLn.js.map → TopNav-BUxuDl2O.js.map} +1 -1
  45. package/dist/chunks/{TopNav-3cDtaRel.js → TopNav-DB42WdTi.js} +2 -2
  46. package/dist/chunks/{TopNav-3cDtaRel.js.map → TopNav-DB42WdTi.js.map} +1 -1
  47. package/dist/chunks/{WebApp-Zl6sz13N.js → WebApp-BsUzNFg3.js} +85 -17
  48. package/dist/chunks/WebApp-BsUzNFg3.js.map +1 -0
  49. package/dist/chunks/WebApp-DdLjexxg.js +2 -0
  50. package/dist/chunks/WebApp-DdLjexxg.js.map +1 -0
  51. package/dist/docit.cjs.js +1 -1
  52. package/dist/docit.es.js +5 -5
  53. package/dist/index.cjs.js +1 -1
  54. package/dist/index.es.js +11 -11
  55. package/dist/lightbox.cjs.js +1 -1
  56. package/dist/lightbox.es.js +4 -4
  57. package/package.json +1 -1
  58. package/dist/chunks/ChatView-DoOb4dyF.js +0 -2
  59. package/dist/chunks/ChatView-DoOb4dyF.js.map +0 -1
  60. package/dist/chunks/ChatView-iDY-MsDn.js.map +0 -1
  61. package/dist/chunks/DataView-CJ10Saok.js +0 -2
  62. package/dist/chunks/DataView-CJ10Saok.js.map +0 -1
  63. package/dist/chunks/DataView-Dx78M3yt.js.map +0 -1
  64. package/dist/chunks/WebApp-CylQA6ix.js +0 -2
  65. package/dist/chunks/WebApp-CylQA6ix.js.map +0 -1
  66. package/dist/chunks/WebApp-Zl6sz13N.js.map +0 -1
@@ -1,2 +0,0 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./WebApp-CylQA6ix.js");class DataView extends e.View{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:n,showEmptyValues:l,emptyValueText:r,...o}=e;super({tagName:"div",className:"data-view",...o}),this.data=t||{},this.fields=i||[],this.model=s||null,this.model&&(this.data=this.model),this.dataViewOptions={columns:a||2,responsive:!1!==n,showEmptyValues:l||!1,emptyValueText:r||"—",rowClass:"row g-3",itemClass:"data-view-item",labelClass:"data-view-label fw-semibold text-muted small text-uppercase",valueClass:"data-view-value"}}async onBeforeRender(){0===this.fields.length&&this.getData()&&this.generateFieldsFromData()}async renderTemplate(){const e=this.buildItemsHTML();return`\n <div class="${this.dataViewOptions.rowClass}">\n ${e}\n </div>\n `}generateFieldsFromData(){const e=this.getData();e&&"object"==typeof e&&(this.fields=Object.keys(e).map(t=>{const s=e[t],i=this.inferFieldType(s,t),a=this.inferFormatter(s,t,i);return{name:t,label:this.formatLabel(t),type:i,format:a}}))}formatLabel(e){return e.replace(/([A-Z])/g," $1").replace(/[_-]/g," ").replace(/\b\w/g,e=>e.toUpperCase()).trim()}inferFieldType(e,t=""){if(null==e)return"text";const s=t.toLowerCase(),i=typeof e;if(s.includes("date")||s.includes("time")||s.includes("created")||s.includes("updated")||s.includes("modified")||s.includes("last_login")||s.includes("expires")||s.includes("last_activity"))return"datetime";if(s.includes("email")||s.includes("mail"))return"email";if(s.includes("url")||s.includes("link")||s.includes("website")||s.includes("homepage"))return"url";if(s.includes("phone")||s.includes("tel")||s.includes("mobile")||s.includes("cell"))return"phone";if(s.includes("price")||s.includes("cost")||s.includes("amount")||s.includes("fee")||s.includes("salary")||s.includes("revenue"))return"currency";if(s.includes("size")||s.includes("bytes"))return"filesize";if(s.includes("percent")||s.includes("rate")||s.includes("ratio")&&"number"===i)return"percent";if("boolean"===i)return"boolean";if("number"===i)return"number";if("object"===i)return Array.isArray(e)?"array":e&&e.renditions?"file":this.shouldUseDataView(e,s)?"dataview":"object";if("string"===i){if(e.includes("@")&&e.includes("."))return"email";if(e.match(/^\d{4}-\d{2}-\d{2}/))return"date";if(e.match(/^https?:\/\//))return"url";if(e.match(/^\+?[\d\s\-\(\)]+$/))return"phone"}return"text"}inferFormatter(e,t,s){const i=t.toLowerCase(),a=[];switch(s){case"datetime":i.includes("time")&&!i.includes("date")?a.push("time"):i.includes("relative")||i.includes("ago")||i.includes("last_")?a.push("relative"):i.includes("created")||i.includes("updated")||i.includes("modified")?a.push('date("MMM D, YYYY")'):a.push('date("MMMM D, YYYY")');break;case"date":i.includes("birth")||i.includes("dob")?a.push('date("MMMM D, YYYY")'):a.push('date("MMM D, YYYY")');break;case"email":case"url":case"boolean":case"array":case"object":case"dataview":break;case"phone":a.push("phone");break;case"currency":a.push("currency"),i.includes("eur")||i.includes("euro")?a[a.length-1]='currency("EUR")':(i.includes("gbp")||i.includes("pound"))&&(a[a.length-1]='currency("GBP")');break;case"filesize":a.push("filesize");break;case"percent":a.push("percent");break;case"number":if("number"==typeof e)if(i.includes("count")||i.includes("total")||i.includes("followers")||i.includes("views"))e>=1e3?a.push("compact"):a.push("number");else if(i.includes("score")||i.includes("rating"))a.push("number"),e%1!=0&&(a[a.length-1]="number(1)");else{if(i.includes("version")||i.includes("id"))return null;a.push("number")}break;case"text":"string"==typeof e&&(i.includes("description")||i.includes("content")||i.includes("body")?e.length>200?a.push("truncate(200)"):e.length>100&&a.push("truncate(100)"):i.includes("summary")||i.includes("excerpt")?e.length>150&&a.push("truncate(150)"):i.includes("name")||i.includes("title")||i.includes("label")?(a.push("capitalize"),e.length>50&&a.unshift("truncate(50)")):i.includes("slug")||i.includes("handle")||i.includes("username")?a.push("slug"):i.includes("code")||i.includes("token")||i.includes("key")?e.length>20&&a.push("mask"):e.length>100&&a.push("truncate(100)"));break;default:"string"==typeof e&&e.length>100&&a.push("truncate(100)")}return a.length>0?a.join("|"):null}shouldUseDataView(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return!1;if(window.utils&&window.utils.isObject(e)&&e.id)return!0;if(["permissions","perms","access","rights","settings","config","configuration","options","profile","info","details","data","metadata","meta","attributes","props","preferences","prefs","user_data","contact","address","location","stats","statistics","metrics","counts"].some(e=>t.includes(e))){const t=Object.keys(e);if(t.length>=2&&t.length<=20&&!t.some(t=>"object"==typeof e[t]&&null!==e[t]&&!Array.isArray(e[t])&&Object.keys(e[t]).length>3))return!0}return!1}getData(){return this.model&&this.model.attributes?{...this.model.attributes}:this.data||{}}getFieldValue(t){let s;if(s=this.model&&"function"==typeof this.model.get?this.model.get(t.name):this.getData()[t.name],t.format&&(s=e.dataFormatter.pipe(s,t.format)),null==s||""===s)return this.dataViewOptions.showEmptyValues?this.dataViewOptions.emptyValueText:null;if(t.template){const e=this.model?this.model:this.data;return this.renderTemplateString(t.template,e)}return s}renderTemplateString(t,s){return t&&s?t.replace(/\{\{([^}]+)\}\}/g,(t,i)=>{let a;const n=i.trim().split("|"),l=n[0],r=n.slice(1).join("|");return a=this.model&&"function"==typeof this.model.get?this.model.get(l):l.split(".").reduce((e,t)=>e?e[t]:void 0,s),r&&(a=e.dataFormatter.pipe(a,r)),null!=a?a:""}):""}getColumnClasses(e){let t=this.getColumnSizeClasses(e);return"right"==e.justify?t+=" d-flex justify-content-end":"center"==e.justify&&(t+=" d-flex justify-content-center"),t}getColumnSizeClasses(e){if("array"===e.type||"object"===e.type||"dataview"===e.type)return"col-12";const t=e.columns||e.colSize||e.cols||Math.floor(12/this.dataViewOptions.columns);return this.dataViewOptions.responsive?`col-12 col-md-${t}`:`col-${t}`}buildItemsHTML(){return this.fields.map(e=>this.buildItemHTML(e)).filter(Boolean).join("")}buildItemHTML(e){const t=this.getFieldValue(e);if(null===t&&!this.dataViewOptions.showEmptyValues)return"";const s=e.label||this.formatLabel(e.name);return`\n <div class="${this.getColumnClasses(e)}">\n <div class="${this.dataViewOptions.itemClass} ${e.className}" data-field="${e.name}">\n ${this.buildLabelHTML(s,e)}\n ${this.buildValueHTML(t,e)}\n </div>\n </div>\n `}buildLabelHTML(e,t){return`<div class="${t.labelClass||this.dataViewOptions.labelClass}">${this.escapeHtml(e)}:</div>`}buildValueHTML(e,t){return`<div class="${t.valueClass||this.dataViewOptions.valueClass}">${this.formatDisplayValue(e,t)}</div>`}formatDisplayValue(t,s){if(null==t)return this.dataViewOptions.emptyValueText;if(s.template)return String(t);if(s.format){const i=["badge","email","url","icon","status","image","avatar","phone","highlight","pre"],a=e.dataFormatter.parsePipeString(s.format),n=a.length>0?a[a.length-1].name.toLowerCase():null;return n&&i.includes(n)?String(t):this.escapeHtml(String(t))}const i=this.getData()[s.name];switch(s.type){case"boolean":return i?'<span class="badge bg-success">Yes</span>':'<span class="badge bg-secondary">No</span>';case"email":const e=String(t);return`<a href="mailto:${this.escapeHtml(e)}" class="text-decoration-none">${this.escapeHtml(e)}</a>`;case"url":const a=String(t);return`<a href="${this.escapeHtml(a)}" target="_blank" rel="noopener" class="text-decoration-none">${this.escapeHtml(a)} <i class="bi bi-box-arrow-up-right"></i></a>`;case"array":case"object":return this.formatAsJson(i);case"dataview":return this.formatAsDataView(i,s);case"phone":const n=String(t);return`<a href="tel:${n.replace(/[^\d\+]/g,"")}" class="text-decoration-none">${this.escapeHtml(n)}</a>`;default:return this.escapeHtml(String(t))}}formatAsJson(e){try{const t=JSON.stringify(e,null,2),s=this.escapeHtml(t),i=t.split("\n").length,a=i>10||t.length>500,n=`json-${Math.random().toString(36).substr(2,9)}`;if(a){const a=JSON.stringify(e).substring(0,100)+(JSON.stringify(e).length>100?"...":""),l=this.escapeHtml(a);return`\n <div class="json-container">\n <div class="d-flex align-items-center justify-content-between mb-1">\n <small class="text-muted">${Array.isArray(e)?"Array":"Object"} (${i} lines)</small>\n <div class="btn-group btn-group-sm" role="group">\n <button type="button" class="btn btn-outline-secondary btn-sm json-toggle" data-bs-toggle="collapse" data-bs-target="#${n}" aria-expanded="false">\n <i class="bi bi-eye"></i> Show\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">\n <i class="bi bi-clipboard"></i>\n </button>\n </div>\n </div>\n <div class="json-preview bg-light p-2 rounded small border" style="font-family: 'Courier New', monospace;">\n <code class="text-muted">${l}</code>\n </div>\n <div class="collapse mt-2" id="${n}">\n <pre class="json-display p-3 rounded small mb-0" style="max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>\n </div>\n </div>\n `}return`\n <div class="json-container">\n <div class="d-flex align-items-center justify-content-between mb-1">\n <small class="text-muted">${Array.isArray(e)?"Array":"Object"}</small>\n <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">\n <i class="bi bi-clipboard"></i>\n </button>\n </div>\n <pre class="json-display bg-light p-2 rounded small mb-0 border" style="white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>\n </div>\n `}catch(t){return`<span class="text-muted fst-italic">[Object: ${typeof e}] - Cannot display as JSON</span>`}}syntaxHighlightJson(e){return e.replace(/("([^"\\]|\\.)*")\s*:/g,'<span style="color: #0969da;">$1</span>:').replace(/:\s*("([^"\\]|\\.)*")/g,': <span style="color: #0a3069;">$1</span>').replace(/:\s*(true|false)/g,': <span style="color: #8250df;">$1</span>').replace(/:\s*(null)/g,': <span style="color: #656d76;">$1</span>').replace(/:\s*(-?\d+\.?\d*)/g,': <span style="color: #0550ae;">$1</span>')}bindEvents(){super.bindEvents(),this.element&&this.element.addEventListener("click",e=>{const t=e.target.closest("[data-field]");if(t){const s=t.dataset.field,i=this.fields.find(e=>e.name===s);this.emit("field:click",{field:i,fieldName:s,element:t,event:e})}e.target.closest(".json-copy")&&(e.preventDefault(),e.stopPropagation(),this.handleJsonCopy(e.target.closest(".json-copy"))),e.target.closest(".json-toggle")&&this.handleJsonToggle(e.target.closest(".json-toggle"))})}handleJsonCopy(e){const t=e.getAttribute("data-json");if(t)try{if(navigator.clipboard&&window.isSecureContext)navigator.clipboard.writeText(t).then(()=>{this.showCopyFeedback(e)});else{const s=document.createElement("textarea");s.value=t,document.body.appendChild(s),s.select(),document.execCommand("copy"),document.body.removeChild(s),this.showCopyFeedback(e)}}catch(s){console.warn("Failed to copy JSON:",s)}}handleJsonToggle(e){const t=e.querySelector("i"),s="true"===e.getAttribute("aria-expanded");setTimeout(()=>{s?(t.className="bi bi-eye-slash",e.innerHTML='<i class="bi bi-eye-slash"></i> Hide'):(t.className="bi bi-eye",e.innerHTML='<i class="bi bi-eye"></i> Show')},10)}showCopyFeedback(e){const t=e.querySelector("i").className,s=e.querySelector("i");s.className="bi bi-check text-success",e.classList.add("btn-success"),e.classList.remove("btn-outline-secondary"),setTimeout(()=>{s.className=t,e.classList.remove("btn-success"),e.classList.add("btn-outline-secondary")},1e3)}formatAsDataView(e,t){if(!e||"object"!=typeof e)return'<span class="text-muted fst-italic">No data available</span>';try{const s=new this.constructor({data:e,columns:t.dataViewColumns||2,showEmptyValues:t.showEmptyValues??!0,emptyValueText:t.emptyValueText||"Not set",...t.dataViewOptions||{}});s.onInit(),s.generateFieldsFromData();const i=s.buildItemsHTML();return`\n <div class="nested-dataview border rounded p-3 bg-light">\n <div class="${s.dataViewOptions.rowClass}">\n ${i}\n </div>\n </div>\n `}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&"function"==typeof this.model.set&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&"function"==typeof this.model.fetch)try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?s.format=t:this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?s.format+=`|${t}`:s.format=t),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData();t.format=this.inferFormatter(s[e],e,t.type)}return this}getFormattedValue(t,s=null){const i=this.getField(t);if(!i)return null;const a=null!==s?s:this.getData()[t];return i.format&&null!=a?e.dataFormatter.pipe(a,i.format):a}setFieldFormats(e){return Object.entries(e).forEach(([e,t])=>{this.setFieldFormat(e,t)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{t.format&&(e[t.name]=t.format)}),e}onInit(){super.onInit(),this.model&&"function"==typeof this.model.on&&this.model.on("change",()=>{this.render()})}static create(e={}){return new DataView(e)}}exports.default=DataView;
2
- //# sourceMappingURL=DataView-CJ10Saok.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DataView-CJ10Saok.js","sources":["../../src/core/views/data/DataView.js"],"sourcesContent":["/**\n * DataView - Key/Value data display component for MOJO framework\n * Extends View to display object data in a responsive grid layout with intelligent formatting support\n *\n * Features:\n * - Automatic field generation from data with intelligent type inference\n * - Smart DataFormatter pipe chains (e.g., \"date('MMM D, YYYY')|capitalize\")\n * - Contextual formatting based on field names and values\n * - Support for complex pipe chains like \"truncate(100)|capitalize|badge\"\n * - Nested DataView support with type=\"dataview\" for complex objects\n * - Custom format overrides with fluent API\n * - No format collision: custom field.format completely overrides DataView formatting\n *\n * Format Behavior:\n * - No field.format: DataView applies type-based HTML formatting (badges, links, etc.)\n * - With field.format: DataFormatter handles ALL formatting, DataView only escapes HTML\n *\n * Example Usage:\n * ```javascript\n * const dataView = new DataView({\n * data: {\n * name: 'john doe',\n * email: 'john@example.com',\n * createdAt: '2024-01-15T10:30:00Z',\n * price: 99.99,\n * description: 'A very long description that should be truncated...',\n * isActive: true\n * }\n * });\n *\n * // Auto-inferred formats (DataView adds HTML styling):\n * // name: \"truncate(50)|capitalize\"\n * // email: no format → DataView creates mailto: link\n * // createdAt: \"date('MMM D, YYYY')\"\n * // price: \"currency\"\n * // description: \"truncate(200)\"\n * // isActive: no format → DataView creates badge styling\n *\n * // Custom format overrides (DataFormatter handles ALL formatting):\n * dataView\n * .setFieldFormat('name', 'uppercase|truncate(20)') // No DataView HTML styling\n * .setFieldFormat('email', 'email|uppercase') // No mailto: link\n * .setFieldFormat('isActive', 'boolean|uppercase') // No badge styling\n * .setFieldFormats({\n * description: 'truncate(50)|capitalize',\n * createdAt: 'relative'\n * });\n *\n * // Nested DataView for complex objects:\n * dataView.setFieldFormat('permissions', null); // Clear auto-format\n * // Then configure field: { name: 'permissions', type: 'dataview', label: 'User Permissions' }\n * ```\n */\n\nimport View from '@core/View.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass DataView extends View {\n constructor(options = {}) {\n // Extract DataView-specific options\n const {\n data,\n model,\n fields,\n columns,\n responsive,\n showEmptyValues,\n emptyValueText,\n ...viewOptions\n } = options;\n\n // Set default view options\n super({\n tagName: 'div',\n className: 'data-view',\n ...viewOptions\n });\n\n // Core properties\n this.data = data || {};\n this.fields = fields || [];\n this.model = model || null;\n\n // If a model is provided, use it as data source\n if (this.model) {\n this.data = this.model;\n }\n\n // DataView-specific configuration\n this.dataViewOptions = {\n columns: columns || 2,\n responsive: responsive !== false, // Default to true\n showEmptyValues: showEmptyValues || false,\n emptyValueText: emptyValueText || '—',\n rowClass: 'row g-3',\n itemClass: 'data-view-item',\n labelClass: 'data-view-label fw-semibold text-muted small text-uppercase',\n valueClass: 'data-view-value'\n };\n }\n\n /**\n * Lifecycle hook - prepare data and fields before rendering\n */\n async onBeforeRender() {\n\n // Auto-generate fields from data if none provided\n if (this.fields.length === 0 && this.getData()) {\n this.generateFieldsFromData();\n }\n }\n\n /**\n * Override renderTemplate to generate HTML directly\n * @returns {string} Complete HTML string\n */\n async renderTemplate() {\n const items = this.buildItemsHTML();\n\n return `\n <div class=\"${this.dataViewOptions.rowClass}\">\n ${items}\n </div>\n `;\n }\n\n /**\n * Auto-generate field definitions from data object with intelligent type inference\n */\n generateFieldsFromData() {\n const dataObj = this.getData();\n\n if (dataObj && typeof dataObj === 'object') {\n this.fields = Object.keys(dataObj).map(key => {\n const value = dataObj[key];\n const fieldType = this.inferFieldType(value, key);\n const formatter = this.inferFormatter(value, key, fieldType);\n\n return {\n name: key,\n label: this.formatLabel(key),\n type: fieldType,\n format: formatter\n };\n });\n }\n }\n\n /**\n * Format field name into a readable label\n * @param {string} name - Field name\n * @returns {string} Formatted label\n */\n formatLabel(name) {\n return name\n .replace(/([A-Z])/g, ' $1') // Add space before capital letters\n .replace(/[_-]/g, ' ') // Replace underscores and hyphens with spaces\n .replace(/\\b\\w/g, l => l.toUpperCase()) // Title case\n .trim();\n }\n\n /**\n * Infer field type from value and key with improved intelligence\n * @param {*} value - Field value\n * @param {string} key - Field key\n * @returns {string} Field type\n */\n inferFieldType(value, key = '') {\n if (value === null || value === undefined) return 'text';\n\n const keyLower = key.toLowerCase();\n const type = typeof value;\n\n // Date/time patterns\n if (keyLower.includes('date') || keyLower.includes('time') ||\n keyLower.includes('created') || keyLower.includes('updated') ||\n keyLower.includes('modified') || keyLower.includes('last_login') ||\n keyLower.includes('expires') || keyLower.includes('last_activity')) {\n return 'datetime';\n }\n\n // Email patterns\n if (keyLower.includes('email') || keyLower.includes('mail')) {\n return 'email';\n }\n\n // URL patterns\n if (keyLower.includes('url') || keyLower.includes('link') ||\n keyLower.includes('website') || keyLower.includes('homepage')) {\n return 'url';\n }\n\n // Phone patterns\n if (keyLower.includes('phone') || keyLower.includes('tel') ||\n keyLower.includes('mobile') || keyLower.includes('cell')) {\n return 'phone';\n }\n\n // Currency/price patterns\n if (keyLower.includes('price') || keyLower.includes('cost') ||\n keyLower.includes('amount') || keyLower.includes('fee') ||\n keyLower.includes('salary') || keyLower.includes('revenue')) {\n return 'currency';\n }\n\n // File size patterns\n if (keyLower.includes('size') || keyLower.includes('bytes')) {\n return 'filesize';\n }\n\n // Percentage patterns\n if (keyLower.includes('percent') || keyLower.includes('rate') ||\n keyLower.includes('ratio') && type === 'number') {\n return 'percent';\n }\n\n // Type-based inference\n if (type === 'boolean') return 'boolean';\n if (type === 'number') return 'number';\n\n if (type === 'object') {\n if (Array.isArray(value)) return 'array';\n if (value && value.renditions) return 'file';\n\n // Check if object should be displayed as nested DataView\n if (this.shouldUseDataView(value, keyLower)) {\n return 'dataview';\n }\n\n return 'object';\n }\n\n if (type === 'string') {\n // Pattern matching for strings\n if (value.includes('@') && value.includes('.')) return 'email';\n if (value.match(/^\\d{4}-\\d{2}-\\d{2}/)) return 'date';\n if (value.match(/^https?:\\/\\//)) return 'url';\n if (value.match(/^\\+?[\\d\\s\\-\\(\\)]+$/)) return 'phone';\n }\n\n return 'text';\n }\n\n /**\n * Infer appropriate formatter based on type and context\n * @param {*} value - Field value\n * @param {string} key - Field key\n * @param {string} fieldType - Inferred field type\n * @returns {string|null} Formatter pipe string\n */\n inferFormatter(value, key, fieldType) {\n const keyLower = key.toLowerCase();\n const formatters = [];\n\n switch (fieldType) {\n case 'datetime':\n if (keyLower.includes('time') && !keyLower.includes('date')) {\n formatters.push('time');\n } else if (keyLower.includes('relative') || keyLower.includes('ago') || keyLower.includes('last_')) {\n formatters.push('relative');\n } else if (keyLower.includes('created') || keyLower.includes('updated') || keyLower.includes('modified')) {\n formatters.push('date(\"MMM D, YYYY\")');\n } else {\n formatters.push('date(\"MMMM D, YYYY\")');\n }\n break;\n\n case 'date':\n if (keyLower.includes('birth') || keyLower.includes('dob')) {\n formatters.push('date(\"MMMM D, YYYY\")');\n } else {\n formatters.push('date(\"MMM D, YYYY\")');\n }\n break;\n\n case 'email':\n // Don't apply email formatter - DataView handles mailto: links automatically\n break;\n\n case 'url':\n // Don't apply url formatter - DataView handles clickable links automatically\n break;\n\n case 'phone':\n formatters.push('phone');\n break;\n\n case 'currency':\n formatters.push('currency');\n // Add currency symbol detection if needed\n if (keyLower.includes('eur') || keyLower.includes('euro')) {\n formatters[formatters.length - 1] = 'currency(\"EUR\")';\n } else if (keyLower.includes('gbp') || keyLower.includes('pound')) {\n formatters[formatters.length - 1] = 'currency(\"GBP\")';\n }\n break;\n\n case 'filesize':\n formatters.push('filesize');\n break;\n\n case 'percent':\n formatters.push('percent');\n break;\n\n case 'number':\n // Smart number formatting with pipe chains\n if (typeof value === 'number') {\n if (keyLower.includes('count') || keyLower.includes('total') || keyLower.includes('followers') || keyLower.includes('views')) {\n if (value >= 1000) {\n formatters.push('compact');\n } else {\n formatters.push('number');\n }\n } else if (keyLower.includes('score') || keyLower.includes('rating')) {\n formatters.push('number');\n // Add decimal places for scores\n if (value % 1 !== 0) {\n formatters[formatters.length - 1] = 'number(1)';\n }\n } else if (keyLower.includes('version') || keyLower.includes('id')) {\n // Don't format IDs and versions\n return null;\n } else {\n formatters.push('number');\n }\n }\n break;\n\n case 'boolean':\n // Don't apply boolean formatter - DataView handles badge styling automatically\n break;\n\n case 'text':\n // Smart text formatting with contextual pipe chains\n if (typeof value === 'string') {\n // Handle different text contexts\n if (keyLower.includes('description') || keyLower.includes('content') || keyLower.includes('body')) {\n if (value.length > 200) {\n formatters.push('truncate(200)');\n } else if (value.length > 100) {\n formatters.push('truncate(100)');\n }\n } else if (keyLower.includes('summary') || keyLower.includes('excerpt')) {\n if (value.length > 150) {\n formatters.push('truncate(150)');\n }\n } else if (keyLower.includes('name') || keyLower.includes('title') || keyLower.includes('label')) {\n formatters.push('capitalize');\n if (value.length > 50) {\n formatters.unshift('truncate(50)'); // Truncate first, then capitalize\n }\n } else if (keyLower.includes('slug') || keyLower.includes('handle') || keyLower.includes('username')) {\n formatters.push('slug');\n } else if (keyLower.includes('code') || keyLower.includes('token') || keyLower.includes('key')) {\n // Show codes/tokens with masking if long\n if (value.length > 20) {\n formatters.push('mask');\n }\n } else {\n // Generic text handling\n if (value.length > 100) {\n formatters.push('truncate(100)');\n }\n }\n }\n break;\n\n case 'array':\n case 'object':\n // Don't apply json formatter - DataView handles JSON display automatically\n break;\n\n case 'dataview':\n // Don't apply any formatter - nested DataView handles its own formatting\n break;\n\n default:\n // Handle any missed cases with basic text formatting\n if (typeof value === 'string' && value.length > 100) {\n formatters.push('truncate(100)');\n }\n break;\n }\n\n return formatters.length > 0 ? formatters.join('|') : null;\n }\n\n /**\n * Determine if an object should be displayed as nested DataView vs JSON\n * @param {object} value - Object value to check\n * @param {string} keyLower - Lowercase field key\n * @returns {boolean} True if should use DataView\n */\n shouldUseDataView(value, keyLower) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false;\n }\n\n // Check for common patterns that benefit from DataView display\n const dataViewPatterns = [\n 'permissions', 'perms', 'access', 'rights',\n 'settings', 'config', 'configuration', 'options',\n 'profile', 'info', 'details', 'data',\n 'metadata', 'meta', 'attributes', 'props',\n 'preferences', 'prefs', 'user_data',\n 'contact', 'address', 'location',\n 'stats', 'statistics', 'metrics', 'counts'\n ];\n\n if (window.utils && window.utils.isObject(value) && value.id) {\n return true;\n }\n\n // Check if key matches common patterns\n const matchesPattern = dataViewPatterns.some(pattern => keyLower.includes(pattern));\n\n if (matchesPattern) {\n // Additional checks to ensure it's suitable for DataView\n const keys = Object.keys(value);\n\n // Good candidates: objects with multiple simple key-value pairs\n if (keys.length >= 2 && keys.length <= 20) {\n const hasComplexNesting = keys.some(k =>\n typeof value[k] === 'object' &&\n value[k] !== null &&\n !Array.isArray(value[k]) &&\n Object.keys(value[k]).length > 3\n );\n\n // Use DataView if not too deeply nested\n if (!hasComplexNesting) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get data object (handles both raw objects and Models)\n * @returns {object} Data object\n */\n getData() {\n if (this.model && this.model.attributes) {\n return { ...this.model.attributes };\n }\n return this.data || {};\n }\n\n /**\n * Get field value with formatting support\n * @param {object} field - Field definition\n * @returns {*} Field value (formatted if specified)\n */\n getFieldValue(field) {\n let value;\n\n // Get raw value from data source\n if (this.model && typeof this.model.get === 'function') {\n // For models, get raw value first, then apply formatting separately\n // This ensures we don't break pipe chains by concatenating strings\n value = this.model.get(field.name);\n } else {\n // Plain object access\n value = this.getData()[field.name];\n }\n\n // Apply formatting using DataFormatter pipe system if specified\n if (field.format) {\n value = dataFormatter.pipe(value, field.format);\n }\n\n // Handle empty values\n if (value === null || value === undefined || value === '') {\n return this.dataViewOptions.showEmptyValues ? this.dataViewOptions.emptyValueText : null;\n }\n\n // Apply template if provided\n if (field.template) {\n const modelData = this.model ? this.model : this.data;\n return this.renderTemplateString(field.template, modelData);\n }\n\n return value;\n }\n\n /**\n * Render a template string with data from a model or object.\n * Replaces {{key}} and {{nested.key}} placeholders.\n * @param {string} templateString - The template string.\n * @param {object} data - The data object or model.\n * @returns {string} The rendered string.\n */\n renderTemplateString(templateString, data) {\n if (!templateString || !data) {\n return '';\n }\n\n // Regex to find all {{...}} placeholders\n return templateString.replace(/\\{\\{([^}]+)\\}\\}/g, (match, key) => {\n const trimmedKey = key.trim();\n let value;\n\n // Handle potential formatters in the template key\n const parts = trimmedKey.split('|');\n const dataKey = parts[0];\n const formatters = parts.slice(1).join('|');\n\n // Get value from model or plain object\n if (this.model && typeof this.model.get === 'function') {\n value = this.model.get(dataKey);\n } else {\n // Handle nested keys for plain objects\n value = dataKey.split('.').reduce((o, i) => (o ? o[i] : undefined), data);\n }\n\n // Apply formatters if any\n if (formatters) {\n value = dataFormatter.pipe(value, formatters);\n }\n\n return value !== undefined && value !== null ? value : '';\n });\n }\n\n /**\n * Generate column classes based on configuration\n * @param {object} field - Field definition\n * @returns {string} CSS classes\n */\n getColumnClasses(field) {\n let classes = this.getColumnSizeClasses(field);\n if (field.justify == \"right\") {\n classes += ` d-flex justify-content-end`;\n } else if (field.justify == \"center\") {\n classes += ` d-flex justify-content-center`;\n }\n return classes;\n }\n\n /**\n * Generate column size classes based on configuration\n * @param {object} field - Field definition\n * @returns {string} CSS classes\n */\n getColumnSizeClasses(field) {\n // JSON objects and nested DataViews always use full width for better display\n if (field.type === 'array' || field.type === 'object' || field.type === 'dataview') {\n return 'col-12';\n }\n\n const colSize = field.columns || field.colSize || field.cols || Math.floor(12 / this.dataViewOptions.columns);\n\n if (this.dataViewOptions.responsive) {\n // Responsive breakpoints: 1 column on small, configured on larger screens\n return `col-12 col-md-${colSize}`;\n }\n\n return `col-${colSize}`;\n }\n\n /**\n * Build HTML for all data items\n * @returns {string} Items HTML\n */\n buildItemsHTML() {\n return this.fields\n .map(field => this.buildItemHTML(field))\n .filter(Boolean) // Remove empty items\n .join('');\n }\n\n /**\n * Build HTML for a single data item\n * @param {object} field - Field definition\n * @returns {string} Item HTML\n */\n buildItemHTML(field) {\n const value = this.getFieldValue(field);\n\n // Skip fields with no value if showEmptyValues is false\n if (value === null && !this.dataViewOptions.showEmptyValues) {\n return '';\n }\n\n const label = field.label || this.formatLabel(field.name);\n const colClasses = this.getColumnClasses(field);\n\n return `\n <div class=\"${colClasses}\">\n <div class=\"${this.dataViewOptions.itemClass} ${field.className}\" data-field=\"${field.name}\">\n ${this.buildLabelHTML(label, field)}\n ${this.buildValueHTML(value, field)}\n </div>\n </div>\n `;\n }\n\n /**\n * Build label HTML\n * @param {string} label - Label text\n * @param {object} field - Field definition\n * @returns {string} Label HTML\n */\n buildLabelHTML(label, field) {\n const labelClass = field.labelClass || this.dataViewOptions.labelClass;\n return `<div class=\"${labelClass}\">${this.escapeHtml(label)}:</div>`;\n }\n\n /**\n * Build value HTML with type-specific formatting\n * @param {*} value - Field value\n * @param {object} field - Field definition\n * @returns {string} Value HTML\n */\n buildValueHTML(value, field) {\n const valueClass = field.valueClass || this.dataViewOptions.valueClass;\n const displayValue = this.formatDisplayValue(value, field);\n return `<div class=\"${valueClass}\">${displayValue}</div>`;\n }\n\n /**\n * Format value for display with enhanced type handling\n * @param {*} value - Formatted value from DataFormatter (or raw if no format)\n * @param {object} field - Field definition\n * @returns {string} Formatted display value with HTML markup\n */\n formatDisplayValue(value, field) {\n if (value === null || value === undefined) {\n return this.dataViewOptions.emptyValueText;\n }\n\n // If a template was used, the value is already the final HTML. Return it directly.\n if (field.template) {\n return String(value);\n }\n\n // If a custom format is specified, we trust the DataFormatter.\n // However, we must determine if the output is intended to be HTML or plain text.\n if (field.format) {\n // A list of formatters known to produce safe HTML output.\n // A list of formatters known to produce safe HTML output.\n // A list of formatters known to produce safe HTML output.\n const htmlSafeFormatters = [\n 'badge', 'email', 'url', 'icon', 'status',\n 'image', 'avatar', 'phone', 'highlight', 'pre'\n ];\n\n // Parse the pipe string to find the last formatter applied.\n const pipes = dataFormatter.parsePipeString(field.format);\n const lastFormatter = pipes.length > 0 ? pipes[pipes.length - 1].name.toLowerCase() : null;\n\n // If the last formatter is in our safe list, render the HTML directly.\n if (lastFormatter && htmlSafeFormatters.includes(lastFormatter)) {\n return String(value);\n }\n\n // Otherwise, escape the output for security.\n return this.escapeHtml(String(value));\n }\n\n // No custom format - apply DataView's default type-specific HTML formatting\n // Get original raw value for special cases\n const rawValue = this.getData()[field.name];\n\n // Handle types that need special HTML presentation (only when no custom format)\n switch (field.type) {\n case 'boolean':\n // Use standard boolean badges (no custom format was applied)\n return rawValue ?\n '<span class=\"badge bg-success\">Yes</span>' :\n '<span class=\"badge bg-secondary\">No</span>';\n\n case 'email':\n // Create clickable email links (no custom format was applied)\n const emailStr = String(value);\n return `<a href=\"mailto:${this.escapeHtml(emailStr)}\" class=\"text-decoration-none\">${this.escapeHtml(emailStr)}</a>`;\n\n case 'url':\n // Create clickable URL links (no custom format was applied)\n const urlStr = String(value);\n return `<a href=\"${this.escapeHtml(urlStr)}\" target=\"_blank\" rel=\"noopener\" class=\"text-decoration-none\">${this.escapeHtml(urlStr)} <i class=\"bi bi-box-arrow-up-right\"></i></a>`;\n\n case 'array':\n case 'object':\n // Display as JSON with special HTML formatting (no custom format was applied)\n return this.formatAsJson(rawValue);\n\n case 'dataview':\n // Create nested DataView for complex objects\n return this.formatAsDataView(rawValue, field);\n\n case 'phone':\n // Create tel: links for phone numbers (no custom format was applied)\n const phoneStr = String(value);\n const telHref = phoneStr.replace(/[^\\d\\+]/g, ''); // Clean for tel: link\n return `<a href=\"tel:${telHref}\" class=\"text-decoration-none\">${this.escapeHtml(phoneStr)}</a>`;\n\n default:\n // For all other types with no custom format, just escape and return\n return this.escapeHtml(String(value));\n }\n }\n\n /**\n * Format object/array values as styled JSON\n * @param {*} value - Object or array value\n * @returns {string} Formatted JSON HTML\n */\n formatAsJson(value) {\n try {\n const jsonString = JSON.stringify(value, null, 2);\n const escapedJson = this.escapeHtml(jsonString);\n const lines = jsonString.split('\\n').length;\n const isLarge = lines > 10 || jsonString.length > 500;\n const uniqueId = `json-${Math.random().toString(36).substr(2, 9)}`;\n\n // Create collapsible JSON display for large objects\n if (isLarge) {\n const preview = JSON.stringify(value).substring(0, 100) + (JSON.stringify(value).length > 100 ? '...' : '');\n const escapedPreview = this.escapeHtml(preview);\n\n return `\n <div class=\"json-container\">\n <div class=\"d-flex align-items-center justify-content-between mb-1\">\n <small class=\"text-muted\">${Array.isArray(value) ? 'Array' : 'Object'} (${lines} lines)</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-toggle\" data-bs-toggle=\"collapse\" data-bs-target=\"#${uniqueId}\" aria-expanded=\"false\">\n <i class=\"bi bi-eye\"></i> Show\n </button>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-copy\" data-json='${this.escapeHtml(jsonString)}' title=\"Copy JSON\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n </div>\n <div class=\"json-preview bg-light p-2 rounded small border\" style=\"font-family: 'Courier New', monospace;\">\n <code class=\"text-muted\">${escapedPreview}</code>\n </div>\n <div class=\"collapse mt-2\" id=\"${uniqueId}\">\n <pre class=\"json-display p-3 rounded small mb-0\" style=\"max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;\"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>\n </div>\n </div>\n `;\n } else {\n // Small objects - show directly with copy button\n return `\n <div class=\"json-container\">\n <div class=\"d-flex align-items-center justify-content-between mb-1\">\n <small class=\"text-muted\">${Array.isArray(value) ? 'Array' : 'Object'}</small>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-copy\" data-json='${this.escapeHtml(jsonString)}' title=\"Copy JSON\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n <pre class=\"json-display bg-light p-2 rounded small mb-0 border\" style=\"white-space: pre-wrap; font-family: 'Courier New', monospace;\"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>\n </div>\n `;\n }\n } catch (error) {\n // Fallback for objects that can't be stringified\n return `<span class=\"text-muted fst-italic\">[Object: ${typeof value}] - Cannot display as JSON</span>`;\n }\n }\n\n /**\n * Apply basic syntax highlighting to JSON\n * @param {string} json - Escaped JSON string\n * @returns {string} JSON with basic syntax highlighting\n */\n syntaxHighlightJson(json) {\n return json\n .replace(/(\"([^\"\\\\]|\\\\.)*\")\\s*:/g, '<span style=\"color: #0969da;\">$1</span>:') // Keys (blue)\n .replace(/:\\s*(\"([^\"\\\\]|\\\\.)*\")/g, ': <span style=\"color: #0a3069;\">$1</span>') // String values (dark blue)\n .replace(/:\\s*(true|false)/g, ': <span style=\"color: #8250df;\">$1</span>') // Booleans (purple)\n .replace(/:\\s*(null)/g, ': <span style=\"color: #656d76;\">$1</span>') // Null (gray)\n .replace(/:\\s*(-?\\d+\\.?\\d*)/g, ': <span style=\"color: #0550ae;\">$1</span>'); // Numbers (blue)\n }\n\n /**\n * Bind events including JSON interaction handlers\n */\n bindEvents() {\n super.bindEvents();\n\n if (!this.element) return;\n\n // Add click handler for field interactions\n this.element.addEventListener('click', (e) => {\n const fieldElement = e.target.closest('[data-field]');\n if (fieldElement) {\n const fieldName = fieldElement.dataset.field;\n const field = this.fields.find(f => f.name === fieldName);\n this.emit('field:click', { field, fieldName, element: fieldElement, event: e });\n }\n\n // Handle JSON copy button clicks\n if (e.target.closest('.json-copy')) {\n e.preventDefault();\n e.stopPropagation();\n this.handleJsonCopy(e.target.closest('.json-copy'));\n }\n\n // Handle JSON toggle button clicks\n if (e.target.closest('.json-toggle')) {\n this.handleJsonToggle(e.target.closest('.json-toggle'));\n }\n });\n }\n\n /**\n * Handle copying JSON to clipboard\n * @param {HTMLElement} button - Copy button element\n */\n handleJsonCopy(button) {\n const jsonData = button.getAttribute('data-json');\n if (!jsonData) return;\n\n try {\n // Use modern clipboard API if available\n if (navigator.clipboard && window.isSecureContext) {\n navigator.clipboard.writeText(jsonData).then(() => {\n this.showCopyFeedback(button);\n });\n } else {\n // Fallback for older browsers\n const textarea = document.createElement('textarea');\n textarea.value = jsonData;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n this.showCopyFeedback(button);\n }\n } catch (error) {\n console.warn('Failed to copy JSON:', error);\n }\n }\n\n /**\n * Handle JSON toggle button state\n * @param {HTMLElement} button - Toggle button element\n */\n handleJsonToggle(button) {\n const icon = button.querySelector('i');\n const isExpanded = button.getAttribute('aria-expanded') === 'true';\n\n // Update button text and icon\n setTimeout(() => {\n if (isExpanded) {\n icon.className = 'bi bi-eye-slash';\n button.innerHTML = '<i class=\"bi bi-eye-slash\"></i> Hide';\n } else {\n icon.className = 'bi bi-eye';\n button.innerHTML = '<i class=\"bi bi-eye\"></i> Show';\n }\n }, 10);\n }\n\n /**\n * Show visual feedback for successful copy\n * @param {HTMLElement} button - Copy button element\n */\n showCopyFeedback(button) {\n const originalIcon = button.querySelector('i').className;\n const icon = button.querySelector('i');\n\n // Show success state\n icon.className = 'bi bi-check text-success';\n button.classList.add('btn-success');\n button.classList.remove('btn-outline-secondary');\n\n // Reset after 1 second\n setTimeout(() => {\n icon.className = originalIcon;\n button.classList.remove('btn-success');\n button.classList.add('btn-outline-secondary');\n }, 1000);\n }\n\n /**\n * Format complex objects as nested DataView\n * @param {object} value - Object value to display as DataView\n * @param {object} field - Field definition\n * @returns {string} Formatted DataView HTML\n */\n formatAsDataView(value, field) {\n if (!value || typeof value !== 'object') {\n return `<span class=\"text-muted fst-italic\">No data available</span>`;\n }\n\n try {\n // Create nested DataView instance\n const nestedView = new (this.constructor)({\n data: value,\n columns: field.dataViewColumns || 2,\n showEmptyValues: field.showEmptyValues ?? true,\n emptyValueText: field.emptyValueText || 'Not set',\n // Pass any other dataView-specific options from field config\n ...(field.dataViewOptions || {})\n });\n\n nestedView.onInit();\n nestedView.generateFieldsFromData();\n // Generate the nested DataView HTML\n const nestedHtml = nestedView.buildItemsHTML();\n\n // Wrap in a styled container with optional label\n // const labelHtml = field.label ?\n // `<h6 class=\"fw-semibold text-muted mb-3 border-bottom pb-2\">${this.escapeHtml(field.label)}</h6>` :\n // '';\n\n return `\n <div class=\"nested-dataview border rounded p-3 bg-light\">\n <div class=\"${nestedView.dataViewOptions.rowClass}\">\n ${nestedHtml}\n </div>\n </div>\n `;\n } catch (error) {\n console.error('Error creating nested DataView:', error);\n return `<span class=\"text-danger\">Error displaying nested data</span>`;\n }\n }\n\n /**\n * Update the data and re-render\n * @param {object} newData - New data object\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateData(newData) {\n this.data = newData;\n\n // If model is provided, update it instead\n if (this.model && typeof this.model.set === 'function') {\n this.model.set(newData);\n }\n\n // Clear fields to trigger regeneration on next render\n if (this.fields.length > 0 && !this.options.fields) {\n this.fields = [];\n }\n\n await this.render();\n this.emit('data:updated', { data: newData });\n return this;\n }\n\n /**\n * Update field configuration and re-render\n * @param {array} newFields - New field configuration\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateFields(newFields) {\n this.fields = newFields;\n await this.render();\n this.emit('fields:updated', { fields: newFields });\n return this;\n }\n\n /**\n * Update configuration and re-render\n * @param {object} newOptions - New configuration options\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateConfig(newOptions) {\n this.dataViewOptions = { ...this.dataViewOptions, ...newOptions };\n await this.render();\n this.emit('config:updated', { options: this.dataViewOptions });\n return this;\n }\n\n /**\n * Refresh data from model if available\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async refresh() {\n if (this.model && typeof this.model.fetch === 'function') {\n try {\n await this.model.fetch();\n this.emit('data:refreshed', { model: this.model });\n } catch (error) {\n this.emit('error', { error, message: 'Failed to refresh data' });\n throw error;\n }\n }\n return this;\n }\n\n /**\n * Get current data\n * @returns {object} Current data object\n */\n getCurrentData() {\n return this.getData();\n }\n\n /**\n * Get field definition by name\n * @param {string} name - Field name\n * @returns {object|null} Field definition\n */\n getField(name) {\n return this.fields.find(field => field.name === name) || null;\n }\n\n /**\n * Set custom format for a specific field\n * @param {string} fieldName - Name of the field\n * @param {string} format - Pipe format string (e.g., \"currency|uppercase\")\n * @returns {DataView} This instance for chaining\n */\n setFieldFormat(fieldName, format) {\n const field = this.getField(fieldName);\n if (field) {\n field.format = format;\n } else {\n // Create new field if it doesn't exist\n this.fields.push({\n name: fieldName,\n label: this.formatLabel(fieldName),\n type: this.inferFieldType(this.getData()[fieldName], fieldName),\n format: format\n });\n }\n return this;\n }\n\n /**\n * Add additional formatter to existing field format pipe chain\n * @param {string} fieldName - Name of the field\n * @param {string} formatter - Formatter to add (e.g., \"uppercase\", \"truncate(50)\")\n * @returns {DataView} This instance for chaining\n */\n addFormatPipe(fieldName, formatter) {\n const field = this.getField(fieldName);\n if (field) {\n if (field.format) {\n field.format += `|${formatter}`;\n } else {\n field.format = formatter;\n }\n }\n return this;\n }\n\n /**\n * Clear custom format for a field (revert to auto-inferred format)\n * @param {string} fieldName - Name of the field\n * @returns {DataView} This instance for chaining\n */\n clearFieldFormat(fieldName) {\n const field = this.getField(fieldName);\n if (field) {\n const data = this.getData();\n field.format = this.inferFormatter(data[fieldName], fieldName, field.type);\n }\n return this;\n }\n\n /**\n * Get formatted value for a specific field without rendering\n * @param {string} fieldName - Name of the field\n * @param {*} value - Optional value to format (uses current data if not provided)\n * @returns {*} Formatted value\n */\n getFormattedValue(fieldName, value = null) {\n const field = this.getField(fieldName);\n if (!field) return null;\n\n const targetValue = value !== null ? value : this.getData()[fieldName];\n\n if (field.format && targetValue != null) {\n return dataFormatter.pipe(targetValue, field.format);\n }\n\n return targetValue;\n }\n\n /**\n * Set multiple field formats at once\n * @param {object} formats - Object mapping field names to format strings\n * @returns {DataView} This instance for chaining\n */\n setFieldFormats(formats) {\n Object.entries(formats).forEach(([fieldName, format]) => {\n this.setFieldFormat(fieldName, format);\n });\n return this;\n }\n\n /**\n * Get all current field formats as an object\n * @returns {object} Object mapping field names to their current formats\n */\n getFieldFormats() {\n const formats = {};\n this.fields.forEach(field => {\n if (field.format) {\n formats[field.name] = field.format;\n }\n });\n return formats;\n }\n\n /**\n * Set up model event listeners if model is provided\n */\n onInit() {\n super.onInit();\n\n // Listen for model changes\n if (this.model && typeof this.model.on === 'function') {\n this.model.on('change', () => {\n this.render();\n });\n }\n }\n\n /**\n * Static factory method\n * @param {object} options - DataView options\n * @returns {DataView} New DataView instance\n */\n static create(options = {}) {\n return new DataView(options);\n }\n}\n\nexport default DataView;\n"],"names":["DataView","View","constructor","options","data","model","fields","columns","responsive","showEmptyValues","emptyValueText","viewOptions","super","tagName","className","this","dataViewOptions","rowClass","itemClass","labelClass","valueClass","onBeforeRender","length","getData","generateFieldsFromData","renderTemplate","items","buildItemsHTML","dataObj","Object","keys","map","key","value","fieldType","inferFieldType","formatter","inferFormatter","name","label","formatLabel","type","format","replace","l","toUpperCase","trim","keyLower","toLowerCase","includes","Array","isArray","renditions","shouldUseDataView","match","formatters","push","unshift","join","window","utils","isObject","id","some","pattern","k","attributes","getFieldValue","field","get","dataFormatter","pipe","template","modelData","renderTemplateString","templateString","parts","split","dataKey","slice","reduce","o","i","getColumnClasses","classes","getColumnSizeClasses","justify","colSize","cols","Math","floor","buildItemHTML","filter","Boolean","buildLabelHTML","buildValueHTML","escapeHtml","formatDisplayValue","String","htmlSafeFormatters","pipes","parsePipeString","lastFormatter","rawValue","emailStr","urlStr","formatAsJson","formatAsDataView","phoneStr","jsonString","JSON","stringify","escapedJson","lines","isLarge","uniqueId","random","toString","substr","preview","substring","escapedPreview","syntaxHighlightJson","error","json","bindEvents","element","addEventListener","e","fieldElement","target","closest","fieldName","dataset","find","f","emit","event","preventDefault","stopPropagation","handleJsonCopy","handleJsonToggle","button","jsonData","getAttribute","navigator","clipboard","isSecureContext","writeText","then","showCopyFeedback","textarea","document","createElement","body","appendChild","select","execCommand","removeChild","console","warn","icon","querySelector","isExpanded","setTimeout","innerHTML","originalIcon","classList","add","remove","nestedView","dataViewColumns","onInit","nestedHtml","updateData","newData","set","render","updateFields","newFields","updateConfig","newOptions","refresh","fetch","message","getCurrentData","getField","setFieldFormat","addFormatPipe","clearFieldFormat","getFormattedValue","targetValue","setFieldFormats","formats","entries","forEach","getFieldFormats","on","create"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"DataView-Dx78M3yt.js","sources":["../../src/core/views/data/DataView.js"],"sourcesContent":["/**\n * DataView - Key/Value data display component for MOJO framework\n * Extends View to display object data in a responsive grid layout with intelligent formatting support\n *\n * Features:\n * - Automatic field generation from data with intelligent type inference\n * - Smart DataFormatter pipe chains (e.g., \"date('MMM D, YYYY')|capitalize\")\n * - Contextual formatting based on field names and values\n * - Support for complex pipe chains like \"truncate(100)|capitalize|badge\"\n * - Nested DataView support with type=\"dataview\" for complex objects\n * - Custom format overrides with fluent API\n * - No format collision: custom field.format completely overrides DataView formatting\n *\n * Format Behavior:\n * - No field.format: DataView applies type-based HTML formatting (badges, links, etc.)\n * - With field.format: DataFormatter handles ALL formatting, DataView only escapes HTML\n *\n * Example Usage:\n * ```javascript\n * const dataView = new DataView({\n * data: {\n * name: 'john doe',\n * email: 'john@example.com',\n * createdAt: '2024-01-15T10:30:00Z',\n * price: 99.99,\n * description: 'A very long description that should be truncated...',\n * isActive: true\n * }\n * });\n *\n * // Auto-inferred formats (DataView adds HTML styling):\n * // name: \"truncate(50)|capitalize\"\n * // email: no format → DataView creates mailto: link\n * // createdAt: \"date('MMM D, YYYY')\"\n * // price: \"currency\"\n * // description: \"truncate(200)\"\n * // isActive: no format → DataView creates badge styling\n *\n * // Custom format overrides (DataFormatter handles ALL formatting):\n * dataView\n * .setFieldFormat('name', 'uppercase|truncate(20)') // No DataView HTML styling\n * .setFieldFormat('email', 'email|uppercase') // No mailto: link\n * .setFieldFormat('isActive', 'boolean|uppercase') // No badge styling\n * .setFieldFormats({\n * description: 'truncate(50)|capitalize',\n * createdAt: 'relative'\n * });\n *\n * // Nested DataView for complex objects:\n * dataView.setFieldFormat('permissions', null); // Clear auto-format\n * // Then configure field: { name: 'permissions', type: 'dataview', label: 'User Permissions' }\n * ```\n */\n\nimport View from '@core/View.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass DataView extends View {\n constructor(options = {}) {\n // Extract DataView-specific options\n const {\n data,\n model,\n fields,\n columns,\n responsive,\n showEmptyValues,\n emptyValueText,\n ...viewOptions\n } = options;\n\n // Set default view options\n super({\n tagName: 'div',\n className: 'data-view',\n ...viewOptions\n });\n\n // Core properties\n this.data = data || {};\n this.fields = fields || [];\n this.model = model || null;\n\n // If a model is provided, use it as data source\n if (this.model) {\n this.data = this.model;\n }\n\n // DataView-specific configuration\n this.dataViewOptions = {\n columns: columns || 2,\n responsive: responsive !== false, // Default to true\n showEmptyValues: showEmptyValues || false,\n emptyValueText: emptyValueText || '—',\n rowClass: 'row g-3',\n itemClass: 'data-view-item',\n labelClass: 'data-view-label fw-semibold text-muted small text-uppercase',\n valueClass: 'data-view-value'\n };\n }\n\n /**\n * Lifecycle hook - prepare data and fields before rendering\n */\n async onBeforeRender() {\n\n // Auto-generate fields from data if none provided\n if (this.fields.length === 0 && this.getData()) {\n this.generateFieldsFromData();\n }\n }\n\n /**\n * Override renderTemplate to generate HTML directly\n * @returns {string} Complete HTML string\n */\n async renderTemplate() {\n const items = this.buildItemsHTML();\n\n return `\n <div class=\"${this.dataViewOptions.rowClass}\">\n ${items}\n </div>\n `;\n }\n\n /**\n * Auto-generate field definitions from data object with intelligent type inference\n */\n generateFieldsFromData() {\n const dataObj = this.getData();\n\n if (dataObj && typeof dataObj === 'object') {\n this.fields = Object.keys(dataObj).map(key => {\n const value = dataObj[key];\n const fieldType = this.inferFieldType(value, key);\n const formatter = this.inferFormatter(value, key, fieldType);\n\n return {\n name: key,\n label: this.formatLabel(key),\n type: fieldType,\n format: formatter\n };\n });\n }\n }\n\n /**\n * Format field name into a readable label\n * @param {string} name - Field name\n * @returns {string} Formatted label\n */\n formatLabel(name) {\n return name\n .replace(/([A-Z])/g, ' $1') // Add space before capital letters\n .replace(/[_-]/g, ' ') // Replace underscores and hyphens with spaces\n .replace(/\\b\\w/g, l => l.toUpperCase()) // Title case\n .trim();\n }\n\n /**\n * Infer field type from value and key with improved intelligence\n * @param {*} value - Field value\n * @param {string} key - Field key\n * @returns {string} Field type\n */\n inferFieldType(value, key = '') {\n if (value === null || value === undefined) return 'text';\n\n const keyLower = key.toLowerCase();\n const type = typeof value;\n\n // Date/time patterns\n if (keyLower.includes('date') || keyLower.includes('time') ||\n keyLower.includes('created') || keyLower.includes('updated') ||\n keyLower.includes('modified') || keyLower.includes('last_login') ||\n keyLower.includes('expires') || keyLower.includes('last_activity')) {\n return 'datetime';\n }\n\n // Email patterns\n if (keyLower.includes('email') || keyLower.includes('mail')) {\n return 'email';\n }\n\n // URL patterns\n if (keyLower.includes('url') || keyLower.includes('link') ||\n keyLower.includes('website') || keyLower.includes('homepage')) {\n return 'url';\n }\n\n // Phone patterns\n if (keyLower.includes('phone') || keyLower.includes('tel') ||\n keyLower.includes('mobile') || keyLower.includes('cell')) {\n return 'phone';\n }\n\n // Currency/price patterns\n if (keyLower.includes('price') || keyLower.includes('cost') ||\n keyLower.includes('amount') || keyLower.includes('fee') ||\n keyLower.includes('salary') || keyLower.includes('revenue')) {\n return 'currency';\n }\n\n // File size patterns\n if (keyLower.includes('size') || keyLower.includes('bytes')) {\n return 'filesize';\n }\n\n // Percentage patterns\n if (keyLower.includes('percent') || keyLower.includes('rate') ||\n keyLower.includes('ratio') && type === 'number') {\n return 'percent';\n }\n\n // Type-based inference\n if (type === 'boolean') return 'boolean';\n if (type === 'number') return 'number';\n\n if (type === 'object') {\n if (Array.isArray(value)) return 'array';\n if (value && value.renditions) return 'file';\n\n // Check if object should be displayed as nested DataView\n if (this.shouldUseDataView(value, keyLower)) {\n return 'dataview';\n }\n\n return 'object';\n }\n\n if (type === 'string') {\n // Pattern matching for strings\n if (value.includes('@') && value.includes('.')) return 'email';\n if (value.match(/^\\d{4}-\\d{2}-\\d{2}/)) return 'date';\n if (value.match(/^https?:\\/\\//)) return 'url';\n if (value.match(/^\\+?[\\d\\s\\-\\(\\)]+$/)) return 'phone';\n }\n\n return 'text';\n }\n\n /**\n * Infer appropriate formatter based on type and context\n * @param {*} value - Field value\n * @param {string} key - Field key\n * @param {string} fieldType - Inferred field type\n * @returns {string|null} Formatter pipe string\n */\n inferFormatter(value, key, fieldType) {\n const keyLower = key.toLowerCase();\n const formatters = [];\n\n switch (fieldType) {\n case 'datetime':\n if (keyLower.includes('time') && !keyLower.includes('date')) {\n formatters.push('time');\n } else if (keyLower.includes('relative') || keyLower.includes('ago') || keyLower.includes('last_')) {\n formatters.push('relative');\n } else if (keyLower.includes('created') || keyLower.includes('updated') || keyLower.includes('modified')) {\n formatters.push('date(\"MMM D, YYYY\")');\n } else {\n formatters.push('date(\"MMMM D, YYYY\")');\n }\n break;\n\n case 'date':\n if (keyLower.includes('birth') || keyLower.includes('dob')) {\n formatters.push('date(\"MMMM D, YYYY\")');\n } else {\n formatters.push('date(\"MMM D, YYYY\")');\n }\n break;\n\n case 'email':\n // Don't apply email formatter - DataView handles mailto: links automatically\n break;\n\n case 'url':\n // Don't apply url formatter - DataView handles clickable links automatically\n break;\n\n case 'phone':\n formatters.push('phone');\n break;\n\n case 'currency':\n formatters.push('currency');\n // Add currency symbol detection if needed\n if (keyLower.includes('eur') || keyLower.includes('euro')) {\n formatters[formatters.length - 1] = 'currency(\"EUR\")';\n } else if (keyLower.includes('gbp') || keyLower.includes('pound')) {\n formatters[formatters.length - 1] = 'currency(\"GBP\")';\n }\n break;\n\n case 'filesize':\n formatters.push('filesize');\n break;\n\n case 'percent':\n formatters.push('percent');\n break;\n\n case 'number':\n // Smart number formatting with pipe chains\n if (typeof value === 'number') {\n if (keyLower.includes('count') || keyLower.includes('total') || keyLower.includes('followers') || keyLower.includes('views')) {\n if (value >= 1000) {\n formatters.push('compact');\n } else {\n formatters.push('number');\n }\n } else if (keyLower.includes('score') || keyLower.includes('rating')) {\n formatters.push('number');\n // Add decimal places for scores\n if (value % 1 !== 0) {\n formatters[formatters.length - 1] = 'number(1)';\n }\n } else if (keyLower.includes('version') || keyLower.includes('id')) {\n // Don't format IDs and versions\n return null;\n } else {\n formatters.push('number');\n }\n }\n break;\n\n case 'boolean':\n // Don't apply boolean formatter - DataView handles badge styling automatically\n break;\n\n case 'text':\n // Smart text formatting with contextual pipe chains\n if (typeof value === 'string') {\n // Handle different text contexts\n if (keyLower.includes('description') || keyLower.includes('content') || keyLower.includes('body')) {\n if (value.length > 200) {\n formatters.push('truncate(200)');\n } else if (value.length > 100) {\n formatters.push('truncate(100)');\n }\n } else if (keyLower.includes('summary') || keyLower.includes('excerpt')) {\n if (value.length > 150) {\n formatters.push('truncate(150)');\n }\n } else if (keyLower.includes('name') || keyLower.includes('title') || keyLower.includes('label')) {\n formatters.push('capitalize');\n if (value.length > 50) {\n formatters.unshift('truncate(50)'); // Truncate first, then capitalize\n }\n } else if (keyLower.includes('slug') || keyLower.includes('handle') || keyLower.includes('username')) {\n formatters.push('slug');\n } else if (keyLower.includes('code') || keyLower.includes('token') || keyLower.includes('key')) {\n // Show codes/tokens with masking if long\n if (value.length > 20) {\n formatters.push('mask');\n }\n } else {\n // Generic text handling\n if (value.length > 100) {\n formatters.push('truncate(100)');\n }\n }\n }\n break;\n\n case 'array':\n case 'object':\n // Don't apply json formatter - DataView handles JSON display automatically\n break;\n\n case 'dataview':\n // Don't apply any formatter - nested DataView handles its own formatting\n break;\n\n default:\n // Handle any missed cases with basic text formatting\n if (typeof value === 'string' && value.length > 100) {\n formatters.push('truncate(100)');\n }\n break;\n }\n\n return formatters.length > 0 ? formatters.join('|') : null;\n }\n\n /**\n * Determine if an object should be displayed as nested DataView vs JSON\n * @param {object} value - Object value to check\n * @param {string} keyLower - Lowercase field key\n * @returns {boolean} True if should use DataView\n */\n shouldUseDataView(value, keyLower) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false;\n }\n\n // Check for common patterns that benefit from DataView display\n const dataViewPatterns = [\n 'permissions', 'perms', 'access', 'rights',\n 'settings', 'config', 'configuration', 'options',\n 'profile', 'info', 'details', 'data',\n 'metadata', 'meta', 'attributes', 'props',\n 'preferences', 'prefs', 'user_data',\n 'contact', 'address', 'location',\n 'stats', 'statistics', 'metrics', 'counts'\n ];\n\n if (window.utils && window.utils.isObject(value) && value.id) {\n return true;\n }\n\n // Check if key matches common patterns\n const matchesPattern = dataViewPatterns.some(pattern => keyLower.includes(pattern));\n\n if (matchesPattern) {\n // Additional checks to ensure it's suitable for DataView\n const keys = Object.keys(value);\n\n // Good candidates: objects with multiple simple key-value pairs\n if (keys.length >= 2 && keys.length <= 20) {\n const hasComplexNesting = keys.some(k =>\n typeof value[k] === 'object' &&\n value[k] !== null &&\n !Array.isArray(value[k]) &&\n Object.keys(value[k]).length > 3\n );\n\n // Use DataView if not too deeply nested\n if (!hasComplexNesting) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get data object (handles both raw objects and Models)\n * @returns {object} Data object\n */\n getData() {\n if (this.model && this.model.attributes) {\n return { ...this.model.attributes };\n }\n return this.data || {};\n }\n\n /**\n * Get field value with formatting support\n * @param {object} field - Field definition\n * @returns {*} Field value (formatted if specified)\n */\n getFieldValue(field) {\n let value;\n\n // Get raw value from data source\n if (this.model && typeof this.model.get === 'function') {\n // For models, get raw value first, then apply formatting separately\n // This ensures we don't break pipe chains by concatenating strings\n value = this.model.get(field.name);\n } else {\n // Plain object access\n value = this.getData()[field.name];\n }\n\n // Apply formatting using DataFormatter pipe system if specified\n if (field.format) {\n value = dataFormatter.pipe(value, field.format);\n }\n\n // Handle empty values\n if (value === null || value === undefined || value === '') {\n return this.dataViewOptions.showEmptyValues ? this.dataViewOptions.emptyValueText : null;\n }\n\n // Apply template if provided\n if (field.template) {\n const modelData = this.model ? this.model : this.data;\n return this.renderTemplateString(field.template, modelData);\n }\n\n return value;\n }\n\n /**\n * Render a template string with data from a model or object.\n * Replaces {{key}} and {{nested.key}} placeholders.\n * @param {string} templateString - The template string.\n * @param {object} data - The data object or model.\n * @returns {string} The rendered string.\n */\n renderTemplateString(templateString, data) {\n if (!templateString || !data) {\n return '';\n }\n\n // Regex to find all {{...}} placeholders\n return templateString.replace(/\\{\\{([^}]+)\\}\\}/g, (match, key) => {\n const trimmedKey = key.trim();\n let value;\n\n // Handle potential formatters in the template key\n const parts = trimmedKey.split('|');\n const dataKey = parts[0];\n const formatters = parts.slice(1).join('|');\n\n // Get value from model or plain object\n if (this.model && typeof this.model.get === 'function') {\n value = this.model.get(dataKey);\n } else {\n // Handle nested keys for plain objects\n value = dataKey.split('.').reduce((o, i) => (o ? o[i] : undefined), data);\n }\n\n // Apply formatters if any\n if (formatters) {\n value = dataFormatter.pipe(value, formatters);\n }\n\n return value !== undefined && value !== null ? value : '';\n });\n }\n\n /**\n * Generate column classes based on configuration\n * @param {object} field - Field definition\n * @returns {string} CSS classes\n */\n getColumnClasses(field) {\n let classes = this.getColumnSizeClasses(field);\n if (field.justify == \"right\") {\n classes += ` d-flex justify-content-end`;\n } else if (field.justify == \"center\") {\n classes += ` d-flex justify-content-center`;\n }\n return classes;\n }\n\n /**\n * Generate column size classes based on configuration\n * @param {object} field - Field definition\n * @returns {string} CSS classes\n */\n getColumnSizeClasses(field) {\n // JSON objects and nested DataViews always use full width for better display\n if (field.type === 'array' || field.type === 'object' || field.type === 'dataview') {\n return 'col-12';\n }\n\n const colSize = field.columns || field.colSize || field.cols || Math.floor(12 / this.dataViewOptions.columns);\n\n if (this.dataViewOptions.responsive) {\n // Responsive breakpoints: 1 column on small, configured on larger screens\n return `col-12 col-md-${colSize}`;\n }\n\n return `col-${colSize}`;\n }\n\n /**\n * Build HTML for all data items\n * @returns {string} Items HTML\n */\n buildItemsHTML() {\n return this.fields\n .map(field => this.buildItemHTML(field))\n .filter(Boolean) // Remove empty items\n .join('');\n }\n\n /**\n * Build HTML for a single data item\n * @param {object} field - Field definition\n * @returns {string} Item HTML\n */\n buildItemHTML(field) {\n const value = this.getFieldValue(field);\n\n // Skip fields with no value if showEmptyValues is false\n if (value === null && !this.dataViewOptions.showEmptyValues) {\n return '';\n }\n\n const label = field.label || this.formatLabel(field.name);\n const colClasses = this.getColumnClasses(field);\n\n return `\n <div class=\"${colClasses}\">\n <div class=\"${this.dataViewOptions.itemClass} ${field.className}\" data-field=\"${field.name}\">\n ${this.buildLabelHTML(label, field)}\n ${this.buildValueHTML(value, field)}\n </div>\n </div>\n `;\n }\n\n /**\n * Build label HTML\n * @param {string} label - Label text\n * @param {object} field - Field definition\n * @returns {string} Label HTML\n */\n buildLabelHTML(label, field) {\n const labelClass = field.labelClass || this.dataViewOptions.labelClass;\n return `<div class=\"${labelClass}\">${this.escapeHtml(label)}:</div>`;\n }\n\n /**\n * Build value HTML with type-specific formatting\n * @param {*} value - Field value\n * @param {object} field - Field definition\n * @returns {string} Value HTML\n */\n buildValueHTML(value, field) {\n const valueClass = field.valueClass || this.dataViewOptions.valueClass;\n const displayValue = this.formatDisplayValue(value, field);\n return `<div class=\"${valueClass}\">${displayValue}</div>`;\n }\n\n /**\n * Format value for display with enhanced type handling\n * @param {*} value - Formatted value from DataFormatter (or raw if no format)\n * @param {object} field - Field definition\n * @returns {string} Formatted display value with HTML markup\n */\n formatDisplayValue(value, field) {\n if (value === null || value === undefined) {\n return this.dataViewOptions.emptyValueText;\n }\n\n // If a template was used, the value is already the final HTML. Return it directly.\n if (field.template) {\n return String(value);\n }\n\n // If a custom format is specified, we trust the DataFormatter.\n // However, we must determine if the output is intended to be HTML or plain text.\n if (field.format) {\n // A list of formatters known to produce safe HTML output.\n // A list of formatters known to produce safe HTML output.\n // A list of formatters known to produce safe HTML output.\n const htmlSafeFormatters = [\n 'badge', 'email', 'url', 'icon', 'status',\n 'image', 'avatar', 'phone', 'highlight', 'pre'\n ];\n\n // Parse the pipe string to find the last formatter applied.\n const pipes = dataFormatter.parsePipeString(field.format);\n const lastFormatter = pipes.length > 0 ? pipes[pipes.length - 1].name.toLowerCase() : null;\n\n // If the last formatter is in our safe list, render the HTML directly.\n if (lastFormatter && htmlSafeFormatters.includes(lastFormatter)) {\n return String(value);\n }\n\n // Otherwise, escape the output for security.\n return this.escapeHtml(String(value));\n }\n\n // No custom format - apply DataView's default type-specific HTML formatting\n // Get original raw value for special cases\n const rawValue = this.getData()[field.name];\n\n // Handle types that need special HTML presentation (only when no custom format)\n switch (field.type) {\n case 'boolean':\n // Use standard boolean badges (no custom format was applied)\n return rawValue ?\n '<span class=\"badge bg-success\">Yes</span>' :\n '<span class=\"badge bg-secondary\">No</span>';\n\n case 'email':\n // Create clickable email links (no custom format was applied)\n const emailStr = String(value);\n return `<a href=\"mailto:${this.escapeHtml(emailStr)}\" class=\"text-decoration-none\">${this.escapeHtml(emailStr)}</a>`;\n\n case 'url':\n // Create clickable URL links (no custom format was applied)\n const urlStr = String(value);\n return `<a href=\"${this.escapeHtml(urlStr)}\" target=\"_blank\" rel=\"noopener\" class=\"text-decoration-none\">${this.escapeHtml(urlStr)} <i class=\"bi bi-box-arrow-up-right\"></i></a>`;\n\n case 'array':\n case 'object':\n // Display as JSON with special HTML formatting (no custom format was applied)\n return this.formatAsJson(rawValue);\n\n case 'dataview':\n // Create nested DataView for complex objects\n return this.formatAsDataView(rawValue, field);\n\n case 'phone':\n // Create tel: links for phone numbers (no custom format was applied)\n const phoneStr = String(value);\n const telHref = phoneStr.replace(/[^\\d\\+]/g, ''); // Clean for tel: link\n return `<a href=\"tel:${telHref}\" class=\"text-decoration-none\">${this.escapeHtml(phoneStr)}</a>`;\n\n default:\n // For all other types with no custom format, just escape and return\n return this.escapeHtml(String(value));\n }\n }\n\n /**\n * Format object/array values as styled JSON\n * @param {*} value - Object or array value\n * @returns {string} Formatted JSON HTML\n */\n formatAsJson(value) {\n try {\n const jsonString = JSON.stringify(value, null, 2);\n const escapedJson = this.escapeHtml(jsonString);\n const lines = jsonString.split('\\n').length;\n const isLarge = lines > 10 || jsonString.length > 500;\n const uniqueId = `json-${Math.random().toString(36).substr(2, 9)}`;\n\n // Create collapsible JSON display for large objects\n if (isLarge) {\n const preview = JSON.stringify(value).substring(0, 100) + (JSON.stringify(value).length > 100 ? '...' : '');\n const escapedPreview = this.escapeHtml(preview);\n\n return `\n <div class=\"json-container\">\n <div class=\"d-flex align-items-center justify-content-between mb-1\">\n <small class=\"text-muted\">${Array.isArray(value) ? 'Array' : 'Object'} (${lines} lines)</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-toggle\" data-bs-toggle=\"collapse\" data-bs-target=\"#${uniqueId}\" aria-expanded=\"false\">\n <i class=\"bi bi-eye\"></i> Show\n </button>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-copy\" data-json='${this.escapeHtml(jsonString)}' title=\"Copy JSON\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n </div>\n <div class=\"json-preview bg-light p-2 rounded small border\" style=\"font-family: 'Courier New', monospace;\">\n <code class=\"text-muted\">${escapedPreview}</code>\n </div>\n <div class=\"collapse mt-2\" id=\"${uniqueId}\">\n <pre class=\"json-display p-3 rounded small mb-0\" style=\"max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;\"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>\n </div>\n </div>\n `;\n } else {\n // Small objects - show directly with copy button\n return `\n <div class=\"json-container\">\n <div class=\"d-flex align-items-center justify-content-between mb-1\">\n <small class=\"text-muted\">${Array.isArray(value) ? 'Array' : 'Object'}</small>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-copy\" data-json='${this.escapeHtml(jsonString)}' title=\"Copy JSON\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n <pre class=\"json-display bg-light p-2 rounded small mb-0 border\" style=\"white-space: pre-wrap; font-family: 'Courier New', monospace;\"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>\n </div>\n `;\n }\n } catch (error) {\n // Fallback for objects that can't be stringified\n return `<span class=\"text-muted fst-italic\">[Object: ${typeof value}] - Cannot display as JSON</span>`;\n }\n }\n\n /**\n * Apply basic syntax highlighting to JSON\n * @param {string} json - Escaped JSON string\n * @returns {string} JSON with basic syntax highlighting\n */\n syntaxHighlightJson(json) {\n return json\n .replace(/(\"([^\"\\\\]|\\\\.)*\")\\s*:/g, '<span style=\"color: #0969da;\">$1</span>:') // Keys (blue)\n .replace(/:\\s*(\"([^\"\\\\]|\\\\.)*\")/g, ': <span style=\"color: #0a3069;\">$1</span>') // String values (dark blue)\n .replace(/:\\s*(true|false)/g, ': <span style=\"color: #8250df;\">$1</span>') // Booleans (purple)\n .replace(/:\\s*(null)/g, ': <span style=\"color: #656d76;\">$1</span>') // Null (gray)\n .replace(/:\\s*(-?\\d+\\.?\\d*)/g, ': <span style=\"color: #0550ae;\">$1</span>'); // Numbers (blue)\n }\n\n /**\n * Bind events including JSON interaction handlers\n */\n bindEvents() {\n super.bindEvents();\n\n if (!this.element) return;\n\n // Add click handler for field interactions\n this.element.addEventListener('click', (e) => {\n const fieldElement = e.target.closest('[data-field]');\n if (fieldElement) {\n const fieldName = fieldElement.dataset.field;\n const field = this.fields.find(f => f.name === fieldName);\n this.emit('field:click', { field, fieldName, element: fieldElement, event: e });\n }\n\n // Handle JSON copy button clicks\n if (e.target.closest('.json-copy')) {\n e.preventDefault();\n e.stopPropagation();\n this.handleJsonCopy(e.target.closest('.json-copy'));\n }\n\n // Handle JSON toggle button clicks\n if (e.target.closest('.json-toggle')) {\n this.handleJsonToggle(e.target.closest('.json-toggle'));\n }\n });\n }\n\n /**\n * Handle copying JSON to clipboard\n * @param {HTMLElement} button - Copy button element\n */\n handleJsonCopy(button) {\n const jsonData = button.getAttribute('data-json');\n if (!jsonData) return;\n\n try {\n // Use modern clipboard API if available\n if (navigator.clipboard && window.isSecureContext) {\n navigator.clipboard.writeText(jsonData).then(() => {\n this.showCopyFeedback(button);\n });\n } else {\n // Fallback for older browsers\n const textarea = document.createElement('textarea');\n textarea.value = jsonData;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n this.showCopyFeedback(button);\n }\n } catch (error) {\n console.warn('Failed to copy JSON:', error);\n }\n }\n\n /**\n * Handle JSON toggle button state\n * @param {HTMLElement} button - Toggle button element\n */\n handleJsonToggle(button) {\n const icon = button.querySelector('i');\n const isExpanded = button.getAttribute('aria-expanded') === 'true';\n\n // Update button text and icon\n setTimeout(() => {\n if (isExpanded) {\n icon.className = 'bi bi-eye-slash';\n button.innerHTML = '<i class=\"bi bi-eye-slash\"></i> Hide';\n } else {\n icon.className = 'bi bi-eye';\n button.innerHTML = '<i class=\"bi bi-eye\"></i> Show';\n }\n }, 10);\n }\n\n /**\n * Show visual feedback for successful copy\n * @param {HTMLElement} button - Copy button element\n */\n showCopyFeedback(button) {\n const originalIcon = button.querySelector('i').className;\n const icon = button.querySelector('i');\n\n // Show success state\n icon.className = 'bi bi-check text-success';\n button.classList.add('btn-success');\n button.classList.remove('btn-outline-secondary');\n\n // Reset after 1 second\n setTimeout(() => {\n icon.className = originalIcon;\n button.classList.remove('btn-success');\n button.classList.add('btn-outline-secondary');\n }, 1000);\n }\n\n /**\n * Format complex objects as nested DataView\n * @param {object} value - Object value to display as DataView\n * @param {object} field - Field definition\n * @returns {string} Formatted DataView HTML\n */\n formatAsDataView(value, field) {\n if (!value || typeof value !== 'object') {\n return `<span class=\"text-muted fst-italic\">No data available</span>`;\n }\n\n try {\n // Create nested DataView instance\n const nestedView = new (this.constructor)({\n data: value,\n columns: field.dataViewColumns || 2,\n showEmptyValues: field.showEmptyValues ?? true,\n emptyValueText: field.emptyValueText || 'Not set',\n // Pass any other dataView-specific options from field config\n ...(field.dataViewOptions || {})\n });\n\n nestedView.onInit();\n nestedView.generateFieldsFromData();\n // Generate the nested DataView HTML\n const nestedHtml = nestedView.buildItemsHTML();\n\n // Wrap in a styled container with optional label\n // const labelHtml = field.label ?\n // `<h6 class=\"fw-semibold text-muted mb-3 border-bottom pb-2\">${this.escapeHtml(field.label)}</h6>` :\n // '';\n\n return `\n <div class=\"nested-dataview border rounded p-3 bg-light\">\n <div class=\"${nestedView.dataViewOptions.rowClass}\">\n ${nestedHtml}\n </div>\n </div>\n `;\n } catch (error) {\n console.error('Error creating nested DataView:', error);\n return `<span class=\"text-danger\">Error displaying nested data</span>`;\n }\n }\n\n /**\n * Update the data and re-render\n * @param {object} newData - New data object\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateData(newData) {\n this.data = newData;\n\n // If model is provided, update it instead\n if (this.model && typeof this.model.set === 'function') {\n this.model.set(newData);\n }\n\n // Clear fields to trigger regeneration on next render\n if (this.fields.length > 0 && !this.options.fields) {\n this.fields = [];\n }\n\n await this.render();\n this.emit('data:updated', { data: newData });\n return this;\n }\n\n /**\n * Update field configuration and re-render\n * @param {array} newFields - New field configuration\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateFields(newFields) {\n this.fields = newFields;\n await this.render();\n this.emit('fields:updated', { fields: newFields });\n return this;\n }\n\n /**\n * Update configuration and re-render\n * @param {object} newOptions - New configuration options\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateConfig(newOptions) {\n this.dataViewOptions = { ...this.dataViewOptions, ...newOptions };\n await this.render();\n this.emit('config:updated', { options: this.dataViewOptions });\n return this;\n }\n\n /**\n * Refresh data from model if available\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async refresh() {\n if (this.model && typeof this.model.fetch === 'function') {\n try {\n await this.model.fetch();\n this.emit('data:refreshed', { model: this.model });\n } catch (error) {\n this.emit('error', { error, message: 'Failed to refresh data' });\n throw error;\n }\n }\n return this;\n }\n\n /**\n * Get current data\n * @returns {object} Current data object\n */\n getCurrentData() {\n return this.getData();\n }\n\n /**\n * Get field definition by name\n * @param {string} name - Field name\n * @returns {object|null} Field definition\n */\n getField(name) {\n return this.fields.find(field => field.name === name) || null;\n }\n\n /**\n * Set custom format for a specific field\n * @param {string} fieldName - Name of the field\n * @param {string} format - Pipe format string (e.g., \"currency|uppercase\")\n * @returns {DataView} This instance for chaining\n */\n setFieldFormat(fieldName, format) {\n const field = this.getField(fieldName);\n if (field) {\n field.format = format;\n } else {\n // Create new field if it doesn't exist\n this.fields.push({\n name: fieldName,\n label: this.formatLabel(fieldName),\n type: this.inferFieldType(this.getData()[fieldName], fieldName),\n format: format\n });\n }\n return this;\n }\n\n /**\n * Add additional formatter to existing field format pipe chain\n * @param {string} fieldName - Name of the field\n * @param {string} formatter - Formatter to add (e.g., \"uppercase\", \"truncate(50)\")\n * @returns {DataView} This instance for chaining\n */\n addFormatPipe(fieldName, formatter) {\n const field = this.getField(fieldName);\n if (field) {\n if (field.format) {\n field.format += `|${formatter}`;\n } else {\n field.format = formatter;\n }\n }\n return this;\n }\n\n /**\n * Clear custom format for a field (revert to auto-inferred format)\n * @param {string} fieldName - Name of the field\n * @returns {DataView} This instance for chaining\n */\n clearFieldFormat(fieldName) {\n const field = this.getField(fieldName);\n if (field) {\n const data = this.getData();\n field.format = this.inferFormatter(data[fieldName], fieldName, field.type);\n }\n return this;\n }\n\n /**\n * Get formatted value for a specific field without rendering\n * @param {string} fieldName - Name of the field\n * @param {*} value - Optional value to format (uses current data if not provided)\n * @returns {*} Formatted value\n */\n getFormattedValue(fieldName, value = null) {\n const field = this.getField(fieldName);\n if (!field) return null;\n\n const targetValue = value !== null ? value : this.getData()[fieldName];\n\n if (field.format && targetValue != null) {\n return dataFormatter.pipe(targetValue, field.format);\n }\n\n return targetValue;\n }\n\n /**\n * Set multiple field formats at once\n * @param {object} formats - Object mapping field names to format strings\n * @returns {DataView} This instance for chaining\n */\n setFieldFormats(formats) {\n Object.entries(formats).forEach(([fieldName, format]) => {\n this.setFieldFormat(fieldName, format);\n });\n return this;\n }\n\n /**\n * Get all current field formats as an object\n * @returns {object} Object mapping field names to their current formats\n */\n getFieldFormats() {\n const formats = {};\n this.fields.forEach(field => {\n if (field.format) {\n formats[field.name] = field.format;\n }\n });\n return formats;\n }\n\n /**\n * Set up model event listeners if model is provided\n */\n onInit() {\n super.onInit();\n\n // Listen for model changes\n if (this.model && typeof this.model.on === 'function') {\n this.model.on('change', () => {\n this.render();\n });\n }\n }\n\n /**\n * Static factory method\n * @param {object} options - DataView options\n * @returns {DataView} New DataView instance\n */\n static create(options = {}) {\n return new DataView(options);\n }\n}\n\nexport default DataView;\n"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use strict";const e="2.1.678",t="2025-10-03T13:49:16.581Z",s={full:e,major:2,minor:1,revision:678,buildTime:t,toString(){return this.full},compare(e){const t=e=>e.split(".").map(Number),[s,r,n]=t(this.full),[i,o,a]=t(e);return s!==i?s-i:r!==o?r-o:n-a}};"undefined"!=typeof window&&(window.MOJO=window.MOJO||{},window.MOJO.VERSION=e,window.MOJO.VERSION_INFO=s,window.MOJO.version=e);const r=Object.prototype.toString,n=Array.isArray||function(e){return"[object Array]"===r.call(e)},i=function(e){return"function"==typeof e},o=function(e){return null!=e&&"object"==typeof e},a=function(e){const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(e).replace(/[&<>"'`=\/]/g,function(e){return t[e]})};class Scanner{constructor(e){this.string=e,this.tail=e,this.pos=0}eos(){return""===this.tail}scan(e){const t=this.tail.match(e);if(!t||0!==t.index)return"";const s=t[0];return this.tail=this.tail.substring(s.length),this.pos+=s.length,s}scanUntil(e){const t=this.tail.search(e);let s;switch(t){case-1:s=this.tail,this.tail="";break;case 0:s="";break;default:s=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=s.length,s}}class Context{constructor(e,t){this.view=e,this.cache={".":this.view},this.parent=t,this.view?._cacheId||this.view&&"object"==typeof this.view&&(this.view._cacheId=Math.random().toString(36).substring(2))}push(e){return new Context(e,this)}lookup(e){if(this.renderCache&&this.view?._cacheId){const t=`${this.view._cacheId}:${e}`;if(this.renderCache.has(t))return this.renderCache.get(t)}if("."===e)return this.view;if(e&&e.startsWith(".")){let t=e.substring(1),s=!1;if(t.endsWith("|iter")&&(t=t.substring(0,t.length-5),s=!0),this.view&&"object"==typeof this.view){let e;if("function"==typeof this.view.getContextValue)try{e=this.view.getContextValue(t),void 0!==e&&i(e)&&(e=e.call(this.view))}catch(r){e=void 0}return void 0===e&&t in this.view&&(e=this.view[t],i(e)&&(e=e.call(this.view))),n(e)?s?e:e.length>0:o(e)?s?Object.entries(e).map(([e,t])=>({key:e,value:t})):Object.keys(e).length>0:e}return}const t=this.cache;let s;if(t.hasOwnProperty(e))s=t[e];else{let n,i,o,a=this,c=!1;for(;a;){if(a.view&&"function"==typeof a.view.getContextValue)try{n=a.view.getContextValue(e),void 0!==n&&(c=!0)}catch(r){c=!1}if(!c)if(e.indexOf(".")>0)for(n=a.view,i=e.split("."),o=0;null!=n&&o<i.length;)if(n&&"function"==typeof n.getContextValue&&o<i.length)try{const e=i.slice(o).join(".");n=n.getContextValue(e),o=i.length,void 0!==n&&(c=!0)}catch(r){o===i.length-1&&(c=l(n,i[o])||h(n,i[o])),n=n[i[o++]]}else o===i.length-1&&(c=l(n,i[o])||h(n,i[o])),n=n[i[o++]];else n=a.view[e],c=l(a.view,e);if(c){s=n;break}a=a.parent}t[e]=s}if(i(s)&&(s=s.call(this.view)),this.renderCache&&this.view?._cacheId){const t=`${this.view._cacheId}:${e}`;this.renderCache.set(t,s)}return s}}function l(e,t){return null!=e&&"object"==typeof e&&t in e}function h(e,t){return null!=e&&"object"!=typeof e&&e.hasOwnProperty&&e.hasOwnProperty(t)}class Writer{constructor(){this.templateCache=/* @__PURE__ */new Map}clearCache(){this.templateCache.clear()}parse(e,t){const s=e+":"+(t=t||["{{","}}"]).join(":");let r=this.templateCache.get(s);return null==r&&(r=this.parseTemplate(e,t),this.templateCache.set(s,r)),r}parseTemplate(e,t){if(!e)return[];const s=t[0],r=t[1],n=new Scanner(e),i=[];let o,a,l,h,u;const d=new RegExp(c(s)+"\\s*"),p=new RegExp("\\s*"+c(r)),g=new RegExp("\\s*"+c("}"+r));for(;!n.eos();){if(o=n.pos,l=n.scanUntil(d),l)for(let e=0;e<l.length;++e)h=l.charAt(e),i.push(["text",h]);if(!n.scan(d))break;if(a=n.scan(/[#^\/>{&=!]/),a||(a="name"),n.scan(/\s*/),"="===a?(l=n.scanUntil(/\s*=/),n.scan(/\s*=/),n.scanUntil(p)):"{"===a?(l=n.scanUntil(g),n.scan(g),a="&"):l=n.scanUntil(p),n.scan(p),"#"===a||"^"===a)u=[a,l,o,n.pos],i.push(u);else if("/"===a){let e;for(let t=i.length-1;t>=0;--t)if(("#"===i[t][0]||"^"===i[t][0])&&i[t][1]===l){e=i[t];break}e&&4===e.length&&e.push(n.pos),i.push([a,l,o,n.pos])}else i.push([a,l,o,n.pos])}return this.nestSections(this.squashTokens(i))}squashTokens(e){const t=[];let s,r;for(let n=0;n<e.length;++n)s=e[n],s&&("text"===s[0]&&r&&"text"===r[0]?(r[1]+=s[1],r[3]=s[3]):(t.push(s),r=s));return t}nestSections(e){const t=[];let s=t;const r=[];for(let n=0;n<e.length;++n){const i=e[n];switch(i[0]){case"#":case"^":const e=[i[0],i[1],i[2],i[3],[],i[4]||null];s.push(e),r.push(e),s=e[4];break;case"/":const n=r.pop();n&&(n[5]=i[2],s=r.length>0?r[r.length-1][4]:t);break;default:s.push(i)}}return t}render(e,t,s,r){const n=this.getConfigTags(r)||["{{","}}"],i=this.parse(e,n),o=/* @__PURE__ */new Map;return this.renderTokens(i,new Context(t),s,e,r,o)}renderTokens(e,t,s,r,o,l){l&&!t.renderCache&&(t.renderCache=l);let h="";for(let c=0;c<e.length;++c){const u=e[c];let d;switch(u[0]){case"#":if(d=t.lookup(u[1]),!d)continue;const e=u[4];if(!e||!n(e)){console.warn(`MUSTACHE WARNING - Section ${u[1]} has no child tokens:`,u);continue}if(n(d))for(let n=0;n<d.length;++n){const i=t.push(d[n]);t.renderCache&&(i.renderCache=t.renderCache),h+=this.renderTokens(e,i,s,r,o,l)}else if("object"==typeof d||"string"==typeof d||"number"==typeof d){const n=t.push(d);t.renderCache&&(n.renderCache=t.renderCache),h+=this.renderTokens(e,n,s,r,o,l)}else if(i(d)){const e=null==r?null:r.slice(u[3],u[5]);d=d.call(t.view,e,e=>this.render(e,t.view,s,o)),null!=d&&(h+=d)}else d&&(h+=this.renderTokens(e,t,s,r,o,l));break;case"^":if(d=t.lookup(u[1]),!d||n(d)&&0===d.length){const e=u[4];e&&n(e)&&(h+=this.renderTokens(e,t,s,r,o,l))}break;case">":if(!s)continue;d=i(s)?s(u[1]):s[u[1]],null!=d&&(h+=this.render(d,t.view,s,o));break;case"&":d=t.lookup(u[1]),null!=d&&(h+=d);break;case"name":d=t.lookup(u[1]),null!=d&&(h+=a(d));break;case"text":h+=u[1]}}return h}getConfigTags(e){return o(e)&&n(e.tags)?e.tags:null}}function c(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const u=new Writer,d={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:Scanner,Context:Context,Writer:Writer,escape:a,clearCache:()=>u.clearCache(),parse:(e,t)=>u.parse(e,t),render(e,t,s,r){if("string"!=typeof e)throw new TypeError('Invalid template! Template should be a "string"');return u.render(e,t,s,r)}},p=new class{constructor(){this.formatters=/* @__PURE__ */new Map,this.registerBuiltInFormatters()}escapeHtml(e){if(null==e)return"";const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};return String(e).replace(/[&<>"']/g,e=>t[e])}registerBuiltInFormatters(){this.register("date",this.date.bind(this)),this.register("time",this.time.bind(this)),this.register("datetime",this.datetime.bind(this)),this.register("datetime_tz",this.datetime_tz.bind(this)),this.register("datatime_tz",this.datetime_tz.bind(this)),this.register("relative",this.relative.bind(this)),this.register("fromNow",this.relative.bind(this)),this.register("iso",this.iso.bind(this)),this.register("epoch",e=>{if(null==e||""===e)return e;const t=parseFloat(e);return isNaN(t)?e:1e3*t}),this.register("number",this.number.bind(this)),this.register("currency",this.currency.bind(this)),this.register("percent",this.percent.bind(this)),this.register("filesize",this.filesize.bind(this)),this.register("ordinal",this.ordinal.bind(this)),this.register("compact",this.compact.bind(this)),this.register("uppercase",e=>String(e).toUpperCase()),this.register("lowercase",e=>String(e).toLowerCase()),this.register("upper",e=>String(e).toUpperCase()),this.register("lower",e=>String(e).toLowerCase()),this.register("capitalize",this.capitalize.bind(this)),this.register("caps",this.capitalize.bind(this)),this.register("truncate",this.truncate.bind(this)),this.register("truncate_middle",this.truncate_middle.bind(this)),this.register("slug",this.slug.bind(this)),this.register("initials",this.initials.bind(this)),this.register("mask",this.mask.bind(this)),this.register("email",this.email.bind(this)),this.register("phone",this.phone.bind(this)),this.register("url",this.url.bind(this)),this.register("badge",this.badge.bind(this)),this.register("status",this.status.bind(this)),this.register("status_text",this.status_text.bind(this)),this.register("status_icon",this.status_icon.bind(this)),this.register("boolean",this.boolean.bind(this)),this.register("bool",this.boolean.bind(this)),this.register("yesno",e=>this.boolean(e,"Yes","No")),this.register("yesnoicon",this.yesnoicon.bind(this)),this.register("icon",this.icon.bind(this)),this.register("avatar",this.avatar.bind(this)),this.register("image",this.image.bind(this)),this.register("tooltip",this.tooltip.bind(this)),this.register("default",this.default.bind(this)),this.register("json",this.json.bind(this)),this.register("raw",e=>e),this.register("custom",(e,t)=>"function"==typeof t?t(e):e),this.register("iter",this.iter.bind(this)),this.register("keys",e=>e&&"object"==typeof e&&!Array.isArray(e)?Object.keys(e):null),this.register("values",e=>e&&"object"==typeof e&&!Array.isArray(e)?Object.values(e):null),this.register("plural",this.plural.bind(this)),this.register("list",this.formatList.bind(this)),this.register("duration",this.duration.bind(this)),this.register("hash",this.hash.bind(this)),this.register("stripHtml",this.stripHtml.bind(this)),this.register("highlight",this.highlight.bind(this)),this.register("pre",e=>`<pre class="bg-light p-2 rounded border">${this.escapeHtml(String(e))}</pre>`)}register(e,t){if("function"!=typeof t)throw new Error("Formatter must be a function, got "+typeof t);return this.formatters.set(e.toLowerCase(),t),this}apply(e,t,...s){try{const r=this.formatters.get(e.toLowerCase());return r?r(t,...s):(console.warn(`Formatter '${e}' not found`),t)}catch(r){return console.error(`Error in formatter '${e}':`,r),t}}pipe(e,t,s=null){return t?this.parsePipeString(t,s).reduce((e,t)=>this.apply(t.name,e,...t.args),e):e}parsePipeString(e,t=null){const s=[],r=e.split("|").map(e=>e.trim());for(const n of r){const e=this.parseFormatter(n,t);e&&s.push(e)}return s}parseFormatter(e,t=null){const s=e.match(/^([a-zA-Z_]\w*)\s*\((.*)\)$/);if(s){const[,e,r]=s;return{name:e,args:r?this.parseArguments(r,t):[]}}const r=e.match(/^([a-zA-Z_]\w*)(?::(.+))?$/);if(r){const[,e,s]=r;return{name:e,args:s?this.parseColonArguments(s,t):[]}}return null}parseArguments(e,t=null){const s=[];let r="",n=!1,i=null,o=0;for(let a=0;a<e.length;a++){const l=e[a];n||'"'!==l&&"'"!==l?n&&l===i&&"\\"!==e[a-1]?(n=!1,i=null,r+=l):n||"{"!==l?n||"}"!==l?n||0!==o||","!==l?r+=l:(s.push(this.parseValue(r.trim(),t)),r=""):(o--,r+=l):(o++,r+=l):(n=!0,i=l,r+=l)}return r.trim()&&s.push(this.parseValue(r.trim(),t)),s}parseColonArguments(e,t=null){const s=[];let r="",n=!1,i=null;for(let o=0;o<e.length;o++){const a=e[o];n||'"'!==a&&"'"!==a?n&&a===i&&"\\"!==e[o-1]?(n=!1,i=null,r+=a):n||":"!==a?r+=a:(s.push(this.parseValue(r.trim(),t)),r=""):(n=!0,i=a,r+=a)}return r.trim()&&s.push(this.parseValue(r.trim(),t)),s}parseValue(e,t=null){if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if("true"===e)return!0;if("false"===e)return!1;if("null"===e)return null;if("undefined"!==e){if(!isNaN(e)&&""!==e)return Number(e);if(e.startsWith("{")&&e.endsWith("}"))try{return JSON.parse(e)}catch(s){}if(t&&this.isIdentifier(e)){if(!e.includes(".")&&t.hasOwnProperty&&t.hasOwnProperty(e))return t[e];if(t.get&&"function"==typeof t.get){const s=t.get(e);if(void 0!==s)return s}if(t.getContextValue&&"function"==typeof t.getContextValue){const s=t.getContextValue(e);if(void 0!==s)return s}if(e.includes(".")){const s=window.MOJOUtils||("undefined"!=typeof require?require("./MOJOUtils.js").default:null);if(s){const r=s.getNestedValue(t,e);if(void 0!==r)return r}}}return e}}isIdentifier(e){return/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(e)}date(e,t="MM/DD/YYYY"){if(!e)return"";let s;if((e=this.normalizeEpoch(e))instanceof Date)s=e;else if("string"==typeof e)if(/^\d{4}-\d{2}-\d{2}$/.test(e)){const[t,r,n]=e.split("-").map(Number);s=new Date(t,r-1,n)}else s=new Date(e);else s=new Date(e);if(isNaN(s.getTime()))return String(e);const r={YYYY:s.getFullYear(),YY:String(s.getFullYear()).slice(-2),MMMM:s.toLocaleDateString("en-US",{month:"long"}),MMM:s.toLocaleDateString("en-US",{month:"short"}),MM:String(s.getMonth()+1).padStart(2,"0"),M:s.getMonth()+1,dddd:s.toLocaleDateString("en-US",{weekday:"long"}),ddd:s.toLocaleDateString("en-US",{weekday:"short"}),DD:String(s.getDate()).padStart(2,"0"),D:s.getDate()};let n=t;const i=new RegExp(`(${Object.keys(r).join("|")})`,"g");return n=n.replace(i,e=>r[e]||e),n}time(e,t="HH:mm:ss"){if(!e)return"";const s=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const r=s.getHours(),n={HH:String(r).padStart(2,"0"),H:r,hh:String(r%12||12).padStart(2,"0"),h:r%12||12,mm:String(s.getMinutes()).padStart(2,"0"),m:s.getMinutes(),ss:String(s.getSeconds()).padStart(2,"0"),s:s.getSeconds(),A:r>=12?"PM":"AM",a:r>=12?"pm":"am"};let i=t;const o=Object.keys(n).sort((e,t)=>t.length-e.length);for(const a of o)i=i.replace(new RegExp(a,"g"),n[a]);return i}datetime(e,t="MM/DD/YYYY",s="HH:mm:ss"){e=this.normalizeEpoch(e);const r=this.date(e,t),n=this.time(e,s);return r&&n?`${r} ${n}`:""}datetime_tz(e,t="MM/DD/YYYY",s="HH:mm:ss",r={}){if(!e)return"";const n=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);if(isNaN(n.getTime()))return String(e);const i=r&&r.locale||"en-US",o=r&&r.timeZone?r.timeZone:void 0,a=()=>{let e="";try{const s=new Intl.DateTimeFormat(i,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...o?{timeZone:o}:{}}).formatToParts(n).find(e=>"timeZoneName"===e.type);if(e=s?s.value:"",e&&/^GMT[+-]/i.test(e))try{const t=new Intl.DateTimeFormat(i,{timeStyle:"short",timeZoneName:"short",...o?{timeZone:o}:{}}).formatToParts(n).find(e=>"timeZoneName"===e.type);t&&t.value&&!/^GMT[+-]/i.test(t.value)&&(e=t.value)}catch(t){}if(e&&/\s/.test(e)){const t=e.split(/\s+/).map(e=>e[0]).join("").toUpperCase();t.length>=2&&t.length<=4&&(e=t)}}catch(t){e=""}return e};if(!o){const e=this.date(n,t),r=this.time(n,s),i=a();return e&&r?`${e} ${r} ${i}`.trim():""}const l=new Intl.DateTimeFormat(i,{timeZone:o,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(n),h=e=>{const t=l.find(t=>t.type===e);return t?t.value:""},c=h("year"),u=h("month"),d=h("day"),p=h("hour"),g=h("minute"),f=h("second"),m=u?String(parseInt(u,10)):"",w=d?String(parseInt(d,10)):"",y=p?String(parseInt(p,10)):"",b=p?parseInt(p,10)%12||12:"",v=p?parseInt(p,10)>=12?"PM":"AM":"",C=v?v.toLowerCase():"",E=new Intl.DateTimeFormat(i,{timeZone:o,month:"long"}).format(n),S=new Intl.DateTimeFormat(i,{timeZone:o,month:"short"}).format(n),$=new Intl.DateTimeFormat(i,{timeZone:o,weekday:"long"}).format(n),P=new Intl.DateTimeFormat(i,{timeZone:o,weekday:"short"}).format(n),M={YYYY:c,YY:c?c.slice(-2):"",MMMM:E,MMM:S,MM:u,M:m,dddd:$,ddd:P,DD:d,D:w},x={HH:p,H:y,hh:""!==b?String(b).padStart(2,"0"):"",h:""!==b?String(b):"",mm:g,m:g?String(parseInt(g,10)):"",ss:f,s:f?String(parseInt(f,10)):"",A:v,a:C},O=(e,t)=>{if(!e)return"";const s=new RegExp(`(${Object.keys(t).sort((e,t)=>t.length-e.length).join("|")})`,"g");return e.replace(s,e=>t[e]??e)},A=O(t,M),T=O(s,x),_=a();return A&&T?`${A} ${T} ${_}`.trim():""}normalizeEpoch(e){if("number"!=typeof e&&(e=Number(e)),isNaN(e))return"";if(e<1e11)return 1e3*e;if(e>1e12&&e<1e13)return e;throw new Error("Value doesn't look like epoch seconds or ms")}relative(e,t=!1){if(!e)return"";const s=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const r=/* @__PURE__ */new Date-s,n=Math.floor(r/1e3),i=Math.floor(n/60),o=Math.floor(i/60),a=Math.floor(o/24);if(t)return a>365?Math.floor(a/365)+"y":a>30?Math.floor(a/30)+"mo":a>7?Math.floor(a/7)+"w":a>0?a+"d":o>0?o+"h":i>0?i+"m":"now";if(a>365){const e=Math.floor(a/365);return e+" year"+(e>1?"s":"")+" ago"}if(a>30){const e=Math.floor(a/30);return e+" month"+(e>1?"s":"")+" ago"}if(a>7){const e=Math.floor(a/7);return e+" week"+(e>1?"s":"")+" ago"}return 1===a?"yesterday":a>0?a+" days ago":o>0?o+" hour"+(o>1?"s":"")+" ago":i>0?i+" minute"+(i>1?"s":"")+" ago":n>30?n+" seconds ago":"just now"}iso(e,t=!1){if(!e)return"";const s=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);return isNaN(s.getTime())?String(e):t?s.toISOString().split("T")[0]:s.toISOString()}number(e,t=2,s="en-US"){const r=parseFloat(e);return isNaN(r)?String(e):r.toLocaleString(s,{minimumFractionDigits:t,maximumFractionDigits:t})}currency(e,t="$",s=2){const r=parseInt(e);if(isNaN(r))return String(e);const n=Math.abs(r).toString(),i=r<0?"-":"";let o,a,l;return n.length<=2?(o="0",a=n.padStart(2,"0")):(o=n.slice(0,-2),a=n.slice(-2)),o=o.replace(/\B(?=(\d{3})+(?!\d))/g,","),0===s?(parseInt(a)>=50&&(o=(parseInt(o.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),l=o):l=2===s?`${o}.${a}`:`${o}.${a.slice(0,s).padEnd(s,"0")}`,i+t+l}percent(e,t=0,s=!0){const r=parseFloat(e);if(isNaN(r))return String(e);const n=s?100*r:r;return this.number(n,t)+"%"}filesize(e,t=!1,s=1){const r=parseInt(e);if(isNaN(r))return String(e);const n=t?["B","KiB","MiB","GiB","TiB"]:["B","KB","MB","GB","TB"],i=t?1024:1e3;let o=r,a=0;for(;o>=i&&a<n.length-1;)o/=i,a++;const l=0===a?0:s;return`${o.toFixed(l)} ${n[a]}`}ordinal(e,t=!1){const s=parseInt(e);if(isNaN(s))return String(e);const r=s%10,n=s%100;let i="th";return 1===r&&11!==n?i="st":2===r&&12!==n?i="nd":3===r&&13!==n&&(i="rd"),t?i:s+i}compact(e,t=1){const s=parseFloat(e);if(isNaN(s))return String(e);const r=Math.abs(s),n=s<0?"-":"";return r>=1e9?n+(r/1e9).toFixed(t)+"B":r>=1e6?n+(r/1e6).toFixed(t)+"M":r>=1e3?n+(r/1e3).toFixed(t)+"K":String(s)}capitalize(e,t=!0){const s=String(e);return s?t?s.replace(/\b\w/g,e=>e.toUpperCase()):s.charAt(0).toUpperCase()+s.slice(1):""}truncate(e,t=50,s="..."){const r=String(e);return r.length<=t?r:r.substring(0,t)+s}truncate_middle(e,t=8,s="***"){const r=String(e);if(r.length<=t)return r;const n=Math.floor(t/2);return`${r.substring(0,n)}${s}${r.substring(r.length-n)}`}slug(e,t="-"){return String(e).toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,t).replace(new RegExp(`${t}+`,"g"),t).replace(new RegExp(`^${t}|${t}$`,"g"),"")}initials(e,t=2){return String(e).split(/\s+/).filter(e=>e.length>0).slice(0,t).map(e=>e.charAt(0).toUpperCase()).join("")}mask(e,t="*",s=4){const r=String(e);return r.length<=s?r:t.repeat(Math.max(0,r.length-s))+r.slice(-s)}email(e,t={}){const s=String(e).trim();return s?!1===t.link?s:`<a href="mailto:${s}${t.subject?`?subject=${encodeURIComponent(t.subject)}`:""}${t.body?`&body=${encodeURIComponent(t.body)}`:""}"${t.class?` class="${t.class}"`:""}>${s}</a>`:""}phone(e,t="US",s=!0){let r=String(e).replace(/\D/g,""),n=r;return"US"===t&&(10===r.length?n=`(${r.slice(0,3)}) ${r.slice(3,6)}-${r.slice(6)}`:11===r.length&&"1"===r[0]&&(n=`+1 (${r.slice(1,4)}) ${r.slice(4,7)}-${r.slice(7)}`)),s?`<a href="tel:${r}">${n}</a>`:n}url(e,t=null,s=!0){let r=String(e).trim();return r?(/^https?:\/\//.test(r)||(r="https://"+r),`<a href="${r}"${s?' target="_blank"':""}${s?' rel="noopener noreferrer"':""}>${t||r}</a>`):""}badge(e,t="auto"){if(Array.isArray(e))return e.map(e=>this.badge(e,t)).join(" ");const s=String(e),r="auto"===t?this.inferBadgeType(s):t;return`<span class="badge ${r?`bg-${r}`:"bg-secondary"}">${s}</span>`}badgeClass(e,t="auto"){const s=String(e),r="auto"===t?this.inferBadgeType(s):t;return r?`bg-${r}`:"bg-secondary"}inferBadgeType(e){const t=e.toLowerCase();return["active","success","complete","completed","approved","done","true","on","yes"].includes(t)?"success":["error","failed","rejected","deleted","cancelled","false","off","no","declined"].includes(t)?"danger":["warning","pending","review","processing","uploading"].includes(t)?"warning":["info","new","draft"].includes(t)?"info":(["inactive","disabled","archived","suspended"].includes(t),"secondary")}status(e){return this._status(e)}status_icon(e){return this._status(e,{},{},!1,!0)}status_text(e){return this._status(e,{},{},!0,!1)}_status(e,t={},s={},r=!1,n=!1){const i=String(e).toLowerCase(),o=t[i]||{active:"bi bi-check-circle-fill",approved:"bi bi-check-circle-fill",declined:"bi bi-x-circle-fill",inactive:"bi bi-pause-circle-fill",pending:"bi bi-clock-fill",success:"bi bi-check-circle-fill",error:"bi bi-exclamation-triangle-fill",warning:"bi bi-exclamation-triangle-fill"}[i]||"";let a="";!r&&o&&(a=`<i class="${o}"></i>`);let l="";return n||(l=e),`<span class="text-${s[i]||{active:"success",approved:"success",declined:"danger",inactive:"secondary",pending:"warning",success:"success",error:"danger",warning:"warning"}[i]||"secondary"}">${a}${a?" ":""}${l}</span>`}boolean(e,t="True",s="False",r=!1){const n=e?t:s;return r?`<span class="text-${e?"success":"danger"}">${n}</span>`:n}icon(e,t={}){const s=t[String(e).toLowerCase()]||"";return s?`<i class="${s}"></i>`:""}yesnoicon(e,t="bi bi-check-circle-fill text-success",s="bi bi-x-circle-fill text-danger"){return e?`<i class="${t}"></i>`:`<i class="${s}"></i>`}image(e,t="thumbnail",s="img-fluid",r=""){const n=this._extractImageUrl(e,t);return n?`<img src="${n}" class="${s}" alt="${r}" />`:""}avatar(e,t="md",s="rounded-circle",r=""){const n=this._extractImageUrl(e,"square_sm")||"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==",i={xs:"width: 1.5rem; height: 1.5rem;",sm:"width: 2rem; height: 2rem;",md:"width: 3rem; height: 3rem;",lg:"width: 4rem; height: 4rem;",xl:"width: 5rem; height: 5rem;"},o=i[t]||i.md;return`<img src="${n}" class="${`object-fit-cover ${s}`.trim()}" style="${o}" alt="${r}" />`}tooltip(e,t="",s="top",r=""){if(null==e)return"";const n=String(e);return`<span data-bs-toggle="tooltip" data-bs-placement="${s}" ${"html"===r?'data-bs-html="true"':""} data-bs-title="${"html"===r?t:this.escapeHtml(t)}">${n}</span>`}_extractImageUrl(e,t="thumbnail"){if(!e)return null;if("string"==typeof e)return e;if("object"==typeof e){if(e.attributes&&(e=e.attributes),"thumbnail"===t&&e.thumbnail&&"string"==typeof e.thumbnail)return e.thumbnail;if(e.renditions&&"object"==typeof e.renditions){const s=e.renditions[t];if(s&&s.url)return s.url;const r=Object.values(e.renditions);if(r.length>0&&r[0].url)return r[0].url}if(e.url)return e.url}return null}default(e,t=""){return null==e||""===e?t:e}plural(e,t,s=null,r=!0){if(null==e||null==t)return r?`${e} ${t}`:t||"";const n=parseInt(e);if(isNaN(n))return r?`${e} ${t}`:t||"";const i=1===Math.abs(n)?t:s||t+"s";return r?`${n} ${i}`:i}formatList(e,t={}){if(!Array.isArray(e))return String(e);const{conjunction:s="and",limit:r=null,moreText:n="others"}=t;if(0===e.length)return"";if(1===e.length)return String(e[0]);let i=e.slice(),o=!1;if(r&&e.length>r&&(i=e.slice(0,r),o=!0),o){const t=e.length-r;return`${i.join(", ")}, ${s} ${t} ${n}`}return 2===i.length?`${i[0]} ${s} ${i[1]}`:`${i.slice(0,-1).join(", ")}, ${s} ${i[i.length-1]}`}duration(e,t={}){const{short:s=!1,precision:r=2}=t;if(null==e)return"";const n=parseInt(e);if(isNaN(n))return String(e);const i=[{name:"day",short:"d",value:864e5},{name:"hour",short:"h",value:36e5},{name:"minute",short:"m",value:6e4},{name:"second",short:"s",value:1e3}];if(0===n)return s?"0s":"0 seconds";const o=Math.abs(n),a=n<0?"-":"",l=[];let h=o;for(const c of i)if(h>=c.value){const e=Math.floor(h/c.value);h%=c.value;const t=s?c.short:1===e?c.name:c.name+"s";if(l.push(s?`${e}${t}`:`${e} ${t}`),l.length>=r)break}return 0===l.length?s?`${Math.round(o)}ms`:`${Math.round(o)} milliseconds`:a+(s?l.join(""):l.join(" "))}hash(e,t=8,s="",r="..."){if(null==e)return"";const n=String(e);return n.length<=t?s+n:s+n.substring(0,t)+r}stripHtml(e){return null==e?"":String(e).replace(/<[^>]*>/g,"")}highlight(e,t,s="highlight"){if(null==e||!t)return String(e||"");const r=String(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`(${r})`,"gi");return String(e).replace(n,`<mark class="${s}">$1</mark>`)}json(e,t=2){try{return JSON.stringify(e,null,t)}catch(s){return String(e)}}has(e){return this.formatters.has(e.toLowerCase())}unregister(e){return this.formatters.delete(e.toLowerCase())}listFormatters(){return Array.from(this.formatters.keys()).sort()}iter(e){return null==e?[]:Array.isArray(e)?e:"object"==typeof e?Object.entries(e).map(([e,t])=>({key:e,value:t})):[{key:"0",value:e}]}};window.dataFormatter=p;class MOJOUtils{static getContextData(e,t){if(!t||null==e)return;let s=t,r="",n=0,i=-1;for(let a=0;a<t.length;a++){const e=t[a];if("("===e)n++;else if(")"===e)n--;else if("|"===e&&0===n){i=a;break}}i>-1&&(s=t.substring(0,i).trim(),r=t.substring(i+1).trim());const o=this.getNestedValue(e,s);return r?p.pipe(o,r,e):o}static getNestedValue(e,t){if(!t||null==e)return;if(!t.includes(".")){if(t in e){const s=e[t];return"function"==typeof s?s.call(e):s}return}const s=t.split(".");let r=e;for(let n=0;n<s.length;n++){const t=s[n];if(null==r)return;if(0===n){if(!r.hasOwnProperty(t))return;{const s=r[t];r="function"==typeof s?s.call(e):s}}else{if(r&&"function"==typeof r.getContextValue){const e=s.slice(n).join(".");return r.getContextValue(e)}if(Array.isArray(r)&&!isNaN(t))r=r[parseInt(t)];else if(r.hasOwnProperty(t))r=r[t];else{if("function"!=typeof r[t])return;r=r[t].call(r)}}}return r}static isNullOrUndefined(e){return null==e}static deepClone(e){if(null===e||"object"!=typeof e)return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof Array)return e.map(e=>this.deepClone(e));if(e instanceof Object){const t={};for(const s in e)e.hasOwnProperty(s)&&(t[s]=this.deepClone(e[s]));return t}}static deepMerge(e,...t){if(!t.length)return e;const s=t.shift();if(this.isObject(e)&&this.isObject(s))for(const r in s)this.isObject(s[r])?(e[r]||Object.assign(e,{[r]:{}}),this.deepMerge(e[r],s[r])):Object.assign(e,{[r]:s[r]});return this.deepMerge(e,...t)}static isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}static debounce(e,t){let s;return function(...r){clearTimeout(s),s=setTimeout(()=>{clearTimeout(s),e(...r)},t)}}static throttle(e,t){let s;return function(...r){s||(e.apply(this,r),s=!0,setTimeout(()=>s=!1,t))}}static generateId(e=""){const t=Date.now().toString(36),s=Math.random().toString(36).substr(2,9);return e?`${e}_${t}_${s}`:`${t}_${s}`}static escapeHtml(e){const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(e).replace(/[&<>"'`=\/]/g,e=>t[e])}static checkPasswordStrength(e){if(!e||"string"!=typeof e)return{score:0,strength:"invalid",feedback:["Password must be a non-empty string"],details:{length:0,hasLowercase:!1,hasUppercase:!1,hasNumbers:!1,hasSpecialChars:!1,hasCommonPatterns:!1,isCommonPassword:!1}};const t=[],s={length:e.length,hasLowercase:/[a-z]/.test(e),hasUppercase:/[A-Z]/.test(e),hasNumbers:/[0-9]/.test(e),hasSpecialChars:/[^a-zA-Z0-9]/.test(e),hasCommonPatterns:!1,isCommonPassword:!1};let r=0;e.length<6?t.push("Password should be at least 6 characters long"):e.length<8?(r+=1,t.push("Consider using at least 8 characters for better security")):e.length<12?r+=3:r+=4,s.hasLowercase?r+=1:t.push("Include lowercase letters"),s.hasUppercase?r+=1:t.push("Include uppercase letters"),s.hasNumbers?r+=1:t.push("Include numbers"),s.hasSpecialChars?r+=2:t.push("Include special characters (!@#$%^&* etc.)");const n=[/123/,/abc/i,/qwerty/i,/asdf/i,/(.)\1{2,}/,/password/i,/admin/i,/user/i,/login/i];for(const o of n)if(o.test(e)){s.hasCommonPatterns=!0,r-=1,t.push("Avoid common patterns and dictionary words");break}let i;return["123456","password","123456789","12345678","12345","1234567","1234567890","qwerty","abc123","111111","123123","admin","letmein","welcome","monkey","password123","123qwe","qwerty123","000000","dragon","sunshine","princess","azerty","1234","iloveyou","trustno1","superman","shadow","master","jennifer"].includes(e.toLowerCase())&&(s.isCommonPassword=!0,r=Math.max(0,r-3),t.push("This password is too common and easily guessed")),i=r<2?"very-weak":r<4?"weak":r<6?"fair":r<8?"good":"strong",r>=7&&0===t.length?t.push("Strong password! Consider using a password manager."):r>=5&&t.length<=1&&t.push("Good password strength. Consider adding more variety."),{score:Math.max(0,r),strength:i,feedback:t,details:s}}static generatePassword(e={}){const t={length:12,includeLowercase:!0,includeUppercase:!0,includeNumbers:!0,includeSpecialChars:!0,customChars:"",excludeAmbiguous:!1,...e};if(t.length<4)throw new Error("Password length must be at least 4 characters");let s="abcdefghijklmnopqrstuvwxyz",r="ABCDEFGHIJKLMNOPQRSTUVWXYZ",n="0123456789",i="!@#$%^&*()_+-=[]{}|;:,.<>?";t.excludeAmbiguous&&(s=s.replace(/[il]/g,""),r=r.replace(/[IOL]/g,""),n=n.replace(/[01]/g,""),i=i.replace(/[|]/g,""));let o="";const a=[];if(t.customChars?o=t.customChars:(t.includeLowercase&&(o+=s,a.push(s[Math.floor(Math.random()*s.length)])),t.includeUppercase&&(o+=r,a.push(r[Math.floor(Math.random()*r.length)])),t.includeNumbers&&(o+=n,a.push(n[Math.floor(Math.random()*n.length)])),t.includeSpecialChars&&(o+=i,a.push(i[Math.floor(Math.random()*i.length)]))),!o)throw new Error("No character types selected for password generation");let l="";for(const h of a)l+=h;for(let h=l.length;h<t.length;h++)l+=o[Math.floor(Math.random()*o.length)];return l.split("").sort(()=>Math.random()-.5).join("")}static parseQueryString(e){const t={},s=new URLSearchParams(e);for(const[r,n]of s.entries())t[r]=n;return t}static toQueryString(e){return new URLSearchParams(e).toString()}static wrapData(e,t=null,s=3){return e&&"object"==typeof e?e instanceof Date||e instanceof RegExp||e instanceof Error||s<=0||"function"==typeof e.getContextValue?e:Array.isArray(e)?e.map(e=>e&&"object"==typeof e&&!e.getContextValue?new DataWrapper(e,t):e):new DataWrapper(e,t):e}}class DataWrapper{constructor(e,t=null){if(Object.defineProperty(this,"_data",{value:e,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(this,"_rootContext",{value:t,writable:!1,enumerable:!1,configurable:!1}),e&&"object"==typeof e)for(const s in e)if(e.hasOwnProperty(s)){const r=e[s];this[s]=MOJOUtils.wrapData(r,t)}}getContextValue(e){let t,s=e,r="",n=0,i=-1;for(let o=0;o<e.length;o++){const t=e[o];if("("===t)n++;else if(")"===t)n--;else if("|"===t&&0===n){i=o;break}}return i>-1&&(s=e.substring(0,i).trim(),r=e.substring(i+1).trim()),t=this._data&&this._data.hasOwnProperty(s)?s in this&&"_data"!==s&&"_rootContext"!==s?this[s]:MOJOUtils.getNestedValue(this._data,s):void 0,r&&void 0!==t?p.pipe(t,r,this._rootContext||this._data):t}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}MOJOUtils.DataWrapper=DataWrapper,"undefined"!=typeof window&&(window.utils=MOJOUtils);class EventDelegate{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=/* @__PURE__ */new Map}bind(e){if(this.unbind(),!e)return;const t=async e=>{const t=e.target.closest("[data-action]");if(t&&this.shouldHandle(t,e)){const s=t.getAttribute("data-action");if(await this.dispatch(s,e,t))return e.preventDefault(),e.stopPropagation(),void(e.handledByChild=!0)}const s=e.target.closest("a[href], [data-page]");if(s&&!s.hasAttribute("data-action")&&this.shouldHandle(s,e)){if(e.ctrlKey||e.metaKey||e.shiftKey||1===e.button)return;if("A"===s.tagName){const e=s.getAttribute("href");if(e&&"#"!==e&&!e.startsWith("#")&&(this.view.isExternalLink(e)||s.hasAttribute("data-external")))return}e.preventDefault(),e.stopPropagation(),e.handledByChild=!0,s.hasAttribute("data-page")?await this.view.handlePageNavigation(s):await this.view.handleHrefNavigation(s)}},s=e=>{const t=e.target.closest("[data-change-action]");if(!t||!this.shouldHandle(t,e))return;const s=t.getAttribute("data-change-action");this.dispatchChange(s,e,t).then(t=>{t&&(e.stopPropagation(),e.handledByChild=!0)})},r=e=>{const t=e.target.closest("[data-change-action]");if(!t||!this.shouldHandle(t,e))return;if(!e.target.matches('[data-filter="live-search"]'))return;const s=t.getAttribute("data-change-action"),r=parseInt(t.getAttribute("data-filter-debounce"))||300,n=`${s}-${t.getAttribute("data-container")||"default"}`;this.debounceTimers.has(n)&&clearTimeout(this.debounceTimers.get(n));const i=setTimeout(()=>{this.debounceTimers.delete(n),this.dispatchChange(s,e,t).then(t=>{t&&(e.stopPropagation(),e.handledByChild=!0)})},r);this.debounceTimers.set(n,i)},n=e=>{if(e.target.matches('[data-filter="search"]'))return;const t=e.target.closest("[data-change-action]");if(!t||!this.shouldHandle(t,e))return;let s=["Enter"];if(t.getAttribute("data-change-keys")&&(s=t.getAttribute("data-change-keys").split(",").map(e=>e.trim())),s.includes(e.key)){e.preventDefault();const s=t.getAttribute("data-change-action");this.dispatch(s,e,t).then(t=>{t&&(e.stopPropagation(),e.handledByChild=!0)})}},i=e=>{const t=e.target.closest("form[data-action]");if(!t||!this.shouldHandle(t,e))return;e.preventDefault();const s=t.getAttribute("data-action");this.dispatch(s,e,t)};e.addEventListener("click",t),e.addEventListener("change",s),e.addEventListener("input",r),e.addEventListener("keydown",n),e.addEventListener("submit",i),this.domListeners.push({el:e,type:"click",fn:t},{el:e,type:"change",fn:s},{el:e,type:"input",fn:r},{el:e,type:"keydown",fn:n},{el:e,type:"submit",fn:i})}unbind(){for(const{el:e,type:t,fn:s}of this.domListeners)e.removeEventListener(t,s);this.domListeners=[];for(const e of this.debounceTimers.values())clearTimeout(e);this.debounceTimers.clear()}hideDropdown(e){const t=e.closest(".dropdown-menu").closest(".dropdown");if(!t)return;const s=t.querySelector('[data-bs-toggle="dropdown"]');if(s&&window.bootstrap?.Dropdown){const e=window.bootstrap.Dropdown.getInstance(s);e?.hide()}}async dispatch(e,t,s){const r=this.view,n=e=>e.includes("-")?e.split("-").map(e=>e[0].toUpperCase()+e.slice(1)).join(""):e[0].toUpperCase()+e.slice(1),i=`handleAction${n(e)}`;if("function"==typeof r[i])try{return t.preventDefault(),await r[i](t,s),!0}catch(l){return console.error(`Error in action ${e}:`,l),r.handleActionError(e,l,t,s),!0}const o=`onAction${n(e)}`;if("function"==typeof r[o])try{return!!(await r[o](t,s))&&(!!s.closest(".dropdown-menu")&&this.hideDropdown(s),t.preventDefault(),t.stopPropagation(),!0)}catch(l){return console.error(`Error in action ${e}:`,l),r.handleActionError(e,l,t,s),!0}const a=`onPassThruAction${n(e)}`;if("function"==typeof r[a])try{return await r[a](t,s),!1}catch(l){return console.error(`Error in action ${e}:`,l),r.handleActionError(e,l,t,s),!0}if("function"==typeof r.onActionDefault)try{return await r.onActionDefault(e,t,s)}catch(l){return console.error(`Error in default action handler for ${e}:`,l),r.handleActionError(e,l,t,s),!0}return r.emit?.(`action:${e}`,{action:e,event:t,element:s}),!1}async dispatchChange(e,t,s){const r=this.view,n=`onChange${i=e,i.includes("-")?i.split("-").map(e=>e[0].toUpperCase()+e.slice(1)).join(""):i[0].toUpperCase()+i.slice(1)}`;var i;if("function"==typeof r[n])try{return await r[n](t,s),!0}catch(o){return console.error(`Error in onChange ${e}:`,o),r.handleActionError?.(e,o,t,s),!0}return await this.dispatch(e,t,s)}shouldHandle(e,t){return!!this.owns(e)||!(!this.contains(e)||t.handledByChild)}owns(e){const t=this.view.element;if(!t||!t.contains(e))return!1;for(const s of Object.values(this.view.children))if(s.element&&s.element.contains(e))return!1;return!0}contains(e){return!!this.view.element&&this.view.element.contains(e)}}const g={on(e,t,s){this._listeners||(this._listeners={}),this._listeners[e]||(this._listeners[e]=[]);const r={callback:t,context:s,fn:s?t.bind(s):t};return this._listeners[e].push(r),this},off(e,t,s){return this._listeners&&this._listeners[e]?(t?(this._listeners[e]=this._listeners[e].filter(e=>e.callback!==t||3===arguments.length&&e.context!==s),0===this._listeners[e].length&&delete this._listeners[e]):delete this._listeners[e],this):this},once(e,t,s){const r=(...n)=>{this.off(e,r),(s?t.bind(s):t).apply(s||this,n)};return this.on(e,r),this},emit(e,...t){if(!this._listeners||!this._listeners[e])return this;const s=this._listeners[e].slice();for(const n of s)try{n.fn.apply(n.context||this,t)}catch(r){console&&console.error&&console.error(`Error in ${e} event handler:`,r)}return this}};"undefined"!=typeof window&&(window.Mustache=d);class View{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??View._genId(),this.containerId=e.containerId??null,this.container=e.container??null,"string"==typeof this.container&&(this.containerId=this.container,this.container=null),this.parent=e.parent??null,this.children=e.children??{},this.template=e.template||e.templateUrl||"",this.data=e.data??{},this.isRendering=!1,this.lastRenderTime=0,this.mounted=!1,this.debug=e.debug??!1,this.app=e.app??null,this.cacheTemplate=e.cacheTemplate??!0,this.enableTooltips=e.enableTooltips??!1,this.options={...e},this.element=this._ensureElement(),this.events=new EventDelegate(this),e.model&&this.setModel(e.model)}async onInit(){}async onInitView(){this.initialized||(this.initialized=!0,await this.onInit())}async onBeforeRender(){}async onAfterRender(){}async onBeforeMount(){}async onAfterMount(){}async onBeforeUnmount(){}async onAfterUnmount(){}async onBeforeDestroy(){}async onAfterDestroy(){}setModel(e={}){let t=e!==this.model;if(!t)return this;this.model&&this.model.off&&this.model.off("change",this._onModelChange,this),this.model=e,this.model&&this.model.on&&this.model.on("change",this._onModelChange,this);for(const s in this.children){const t=this.children[s];t&&"function"==typeof t.setModel&&t.setModel(e)}return t&&this._onModelChange(),this}_onModelChange(){this.isMounted()&&this.render()}setTemplate(e){return this.template=e??"",this}addChild(e){try{if(!e||"object"!=typeof e)return this;e.parent=this,this.getApp()&&(e.app=this.app),this.children[e.id]=e}catch(t){View._warn("addChild error",t)}return e}removeChild(e){try{const t="string"==typeof e?e:e&&e.id;if(!t)return this;const s=this.children[t];s&&(Promise.resolve(s.destroy()).catch(e=>View._warn("removeChild destroy error",e)),delete this.children[t])}catch(t){View._warn("removeChild error",t)}return this}getChild(e){return this.children[e]}async updateData(e,t=!1){return Object.assign(this.data,e),t&&this.isMounted()&&await this.render(),this}toggleClass(e,t){return void 0===t&&(t=!this.element.classList.contains(e)),this.element.classList.toggle(e,t),this}addClass(e){return this.element.classList.add(e),this}setClass(e){return this.element.className=e,this}removeClass(e){return this.element.classList.remove(e),this}canRender(){if(this.isRendering)return!1;if(this.options.renderCooldown>0&&now-this.lastRenderTime<this.options.renderCooldown)return View._warn(`View ${this.id}: Render called too quickly, cooldown active`),!1;if(this.options.noAppend&&this.parent){if(!this.parent.contains(this.containerId||this.container))return!1;if(this.containerId&&!document.getElementById(this.containerId))return!1;if(this.container&&!document.contains(this.container))return!1}return!0}async render(e=!0,t=null){const s=Date.now();if(!this.canRender())return this;this.isRendering=!0,this.lastRenderTime=s;try{this.initialized||await this.onInitView(),this.unbindEvents(),await this.onBeforeRender(),this.getViewData&&(this.data=await this.getViewData());const s=await this.renderTemplate();this.element.innerHTML=s,e&&!this.isMounted()&&await this.mount(t),await this._renderChildren(),await this.onAfterRender(),this.bindEvents()}catch(r){View._warn(`Render error in ${this.id}`,r)}finally{this.isRendering=!1}return this}async _renderChildren(){for(const e in this.children){const t=this.children[e];t&&(t.parent=this,await Promise.resolve(t.render()).catch(t=>View._warn(`Child render error (${e})`,t)))}}async _unmountChildren(){for(const e in this.children){const t=this.children[e];t&&t.unbindEvents()}}isMounted(){return this.element?.isConnected}getChildElementById(e,t=null){const s=e.startsWith("#")?e.substring(1):e;return t?t.querySelector(`#${s}`):this.element.querySelector(`#${s}`)}getChildElement(e){if(e.startsWith("#"))return this.getChildElementById(e);let t=this.element?.querySelector(`[data-container="${e}"]`);return t||this.getChildElementById(e)}getContainer(){return this.replaceById?this.parent?this.parent.getChildElementById(this.id):null:this.containerId?this.parent?this.parent.getChildElement(this.containerId):this.getChildElementById(this.containerId,document.body):null}async mount(e=null){await this.onBeforeMount(),e||(e=this.getContainer()),!this.containerId||e?(e&&this.replaceById?e.replaceWith(this.element):e?e.replaceChildren(this.element):!this.containerId&&this.parent?this.parent.element.appendChild(this.element):this.containerId||this.parent||!this.options.allowAppendToBody?console.error(`Container not found for ${this.containerId}`):(console.log("APPENDING TO BODY!!!!"),document.body.appendChild(this.element)),await this.onAfterMount(),this.mounted=!0):console.error(`Container not found for ${this.containerId}`)}async unmount(){this.element&&this.element.parentNode&&(await this.onBeforeUnmount(),await this._unmountChildren(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.events.unbind(),await this.onAfterUnmount(),this.mounted=!1)}async destroy(){try{this.events.unbind();for(const e in this.children){const t=this.children[e];t&&await Promise.resolve(t.destroy()).catch(t=>View._warn(`Child destroy error (${e})`,t))}this.mounted=!1,this.element&&this.element.parentNode&&(await this.onBeforeDestroy(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),await this.onAfterDestroy())}catch(e){View._warn(`Destroy error in ${this.id}`,e)}}_ensureElement(){try{if(this.element&&this.element.tagName?.toLowerCase()===this.tagName)return this._syncAttrs(),this.element;const e=document.createElement(this.tagName);return this.element=e,this.el=e,this._syncAttrs(),e}catch(e){View._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||View._genId(),t}}_syncAttrs(){try{if(!this.element)return;this.id&&(this.element.id=this.id),this.element.className=this.className||"",null==this.style?this.element.removeAttribute("style"):this.element.style.cssText=String(this.style)}catch(e){View._warn("_syncAttrs error",e)}}bindEvents(){this.events.bind(this.element),this.enableTooltips&&this.initializeTooltips()}unbindEvents(){this.events.unbind(),this.enableTooltips&&this.disposeTooltips()}async renderTemplate(){const e=await this.getTemplate();if(!e)return"";const t=this.getPartials();return d.render(e,this,t)}renderTemplateString(e,t,s){return d.render(e,t,s)}getPartials(){return{}}async getTemplate(){if(this._templateCache&&this.cacheTemplate)return this._templateCache;const e=this.template||this.templateUrl;if(!e)throw new Error("Template not found");let t="";if("string"==typeof e)if(e.includes("<")||e.includes("{"))t=e;else try{let s=e;this.app||(this.app=this.getApp()),!this.app||!this.app.basePath||s.startsWith("/")||s.startsWith("http://")||s.startsWith("https://")||(s=`${this.app.basePath.endsWith("/")?this.app.basePath.slice(0,-1):this.app.basePath}/${s}`);const r=await fetch(s);if(!r.ok)throw new Error(`HTTP ${r.status}: ${r.statusText}`);t=await r.text()}catch(s){View._warn(`Failed to load template from ${e}: ${s}`),this.showError?.(`Failed to load template from ${e}: ${s.message}`)}else"function"==typeof e&&(t=await this.template(this.data,this.state));return this.cacheTemplate&&t&&(this._templateCache=t),t}getContextValue(e){const t=MOJOUtils.getContextData(this,e);return e&&e.startsWith("data.")&&t&&"object"==typeof t?MOJOUtils.wrapData(t,this):e&&e.startsWith("model.")&&"model"!==e&&t&&"object"==typeof t&&"function"!=typeof t.getContextValue?MOJOUtils.wrapData(t,null):t}async handlePageNavigation(e){const t=e.getAttribute("data-page"),s=e.getAttribute("data-params");let r={};if(s)try{r=JSON.parse(s)}catch(o){console.warn("Invalid JSON in data-params:",s)}const n=this.getApp();if(n)return void n.showPage(t,r);const i=this.findRouter();i&&"function"==typeof i.navigateToPage?await i.navigateToPage(t,r):console.error(`No router found for page navigation to '${t}'`)}async handleHrefNavigation(e){const t=e.getAttribute("href");if(this.isExternalLink(t)||e.hasAttribute("data-external"))return;const s=this.findRouter();if(s){if(s.options&&"param"===s.options.mode&&t.startsWith("?")){const e="/"+t;return void(await s.navigate(e))}if(s.options&&"hash"===s.options.mode&&t.startsWith("#"))return void(await s.navigate(t));const e=this.hrefToRoutePath(t);await s.navigate(e)}else console.warn("No router found for navigation, using default behavior"),window.location.href=t}isExternalLink(e){return!e||(e.startsWith("/")&&this.getApp()?!e.startsWith(this.findRouter().basePath):e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//"))}hrefToRoutePath(e){if(e.startsWith("/")){const t=this.findRouter();if(t&&t.options&&t.options.base){const s=t.options.base;if(e.startsWith(s))return e.substring(s.length)||"/"}return e}return e.startsWith("./")?e.substring(2):e}findRouter(){return this.getApp(),this.app?.router||null}getApp(){if(this.app)return this.app;const e=[window.__app__,window.MOJO?.app,window.APP,window.app,window.WebApp,window.matchUUID?window[window.matchUUID]():window[window.matchUUID]];return this.app=e.find(e=>e&&"function"==typeof e.showPage)||null,this.app}handleActionError(e,t,s,r){this.showError(`Action '${e}' failed: ${t}`,s,r)}escapeHtml(e){if("string"!=typeof e)return e;const t=document.createElement("div");return t.textContent=e,t.innerHTML}contains(e){if("string"==typeof e){if(!this.element)return!1;e=document.getElementById(e)}return!!e&&this.element.contains(e)}initializeTooltips(){this.element&&window.bootstrap?.Tooltip&&[...this.element.querySelectorAll('[data-bs-toggle="tooltip"]')].map(e=>new window.bootstrap.Tooltip(e))}disposeTooltips(){this.element&&window.bootstrap?.Tooltip&&this.element.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(e=>{const t=window.bootstrap.Tooltip.getInstance(e);t&&t.dispose()})}async showError(e){console.error(`View ${this.id} error:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showError?await t.showError(e):alert(`Error: ${e}`)}async showSuccess(e){this.debug&&console.log(`View ${this.id} success:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showSuccess?await t.showSuccess(e):alert(`Success: ${e}`)}async showInfo(e){console.info(`View ${this.id} info:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showInfo?await t.showInfo(e):alert(`Info: ${e}`)}async showWarning(e){console.warn(`View ${this.id} warning:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showWarning?await t.showWarning(e):alert(`Warning: ${e}`)}static _genId(){return`view_${Math.random().toString(36).substr(2,9)}`}static _warn(e,t){try{t?console.warn(`[View] ${e}:`,t):console.warn(`[View] ${e}`)}catch{}}}Object.assign(View.prototype,g);class Router{constructor(e={}){this.defaultRoute=e.defaultRoute||"home",this.routes=[],this.currentRoute=null,this.eventEmitter=e.eventEmitter||null,this.boundHandlePopState=this.handlePopState.bind(this)}start(){window.addEventListener("popstate",this.boundHandlePopState),this.handleCurrentLocation()}stop(){window.removeEventListener("popstate",this.boundHandlePopState)}addRoute(e,t){this.routes.push({pattern:this.normalizePattern(e),regex:this.patternToRegex(e),pageName:t,paramNames:this.extractParamNames(e)})}async navigate(e,t={}){const{replace:s=!1,state:r=null,trigger:n=!0}=t,{pageName:i,queryParams:o}=this.parseInput(e);n&&await this.handleRouteChange(i,o)}back(){window.history.back()}forward(){window.history.forward()}getCurrentRoute(){return this.currentRoute}getCurrentPath(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();return this.buildPublicUrl(e,t)}handlePopState(e){this.handleCurrentLocation()}async handleCurrentLocation(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();await this.handleRouteChange(e,t)}async handleRouteChange(e,t){const s="/"+e,r=this.matchRoute(s),n=this.buildPublicUrl(e,t);return r?(this.currentRoute=r,this.eventEmitter&&this.eventEmitter.emit("route:changed",{path:n,pageName:r.pageName,params:r.params,query:t,route:r}),r):(console.log("No route matched for page:",e),this.eventEmitter&&this.eventEmitter.emit("route:notfound",{path:n}),null)}matchRoute(e){for(const t of this.routes){const s=e.match(t.regex);if(s){const r={};return t.paramNames.forEach((e,t)=>{r[e]=s[t+1]}),{...t,params:r,path:e}}}return null}parseInput(e){let t=this.defaultRoute,s={};if(!e)return{pageName:t,queryParams:s};try{if(e.includes("?")){const[r,n]=e.split("?",2),i=new URLSearchParams(n);if(i.has("page")){t=i.get("page")||this.defaultRoute;for(const[e,t]of i)"page"!==e&&(s[e]=t)}else{t=r.startsWith("/")?r.substring(1)||this.defaultRoute:r||this.defaultRoute;for(const[e,t]of i)s[e]=t}}else t=e.startsWith("/")?e.substring(1)||this.defaultRoute:e}catch(r){console.warn("Failed to parse input:",e,r),t=this.defaultRoute,s={}}return{pageName:t,queryParams:s}}parseCurrentUrl(){const e=new URLSearchParams(window.location.search),t=e.get("page")||this.defaultRoute,s={};for(const[r,n]of e)"page"!==r&&(s[r]=n);return{pageName:t,queryParams:s}}buildPublicUrl(e,t={}){const s=new URLSearchParams;return s.set("page",e),Object.entries(t).forEach(([e,t])=>{null!=t&&""!==t&&s.set(e,String(t))}),"?"+s.toString()}updateBrowserUrl(e,t,s,r){const n=new URL(window.location.origin+window.location.pathname);n.searchParams.set("page",e),Object.entries(t).forEach(([e,t])=>{null!=t&&""!==t&&n.searchParams.set(e,String(t))});const i=n.toString();s?window.history.replaceState(r,"",i):window.history.pushState(r,"",i)}patternToRegex(e){let t=e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/\/:([^/?]+)\?/g,"(?:/([^/]+))?").replace(/:([^/]+)/g,"([^/]+)");return new RegExp(`^${t}$`)}extractParamNames(e){return(e.match(/:([^/?]+)\??/g)||[]).map(e=>e.replace(/[:?]/g,""))}normalizePattern(e){return e.startsWith("/")?e:`/${e}`}updateUrl(e={},t={}){const{replace:s=!1}=t,{pageName:r}=this.parseCurrentUrl();this.updateBrowserUrl(r,e,s)}buildUrl(e,t={}){return this.buildPublicUrl(e,t)}doRoutesMatch(e,t){if(!e||!t)return!1;const{pageName:s}=this.parseInput(e),{pageName:r}=this.parseInput(t);return s===r}}const f=new class{constructor(){this.config={baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"},trackDevice:!0,duidHeader:"X-Mojo-UID",duidTransport:"header"},this.interceptors={request:[],response:[]},this.duid=null,this.config.trackDevice&&this._initializeDuid()}_initializeDuid(){const e="mojo_device_uid";try{let t=localStorage.getItem(e);t?this.duid=t:(this.duid=this._generateDuid(),localStorage.setItem(e,this.duid))}catch(t){console.error("Could not access localStorage to get/set DUID.",t),this.duid=this._generateDuid()}}_generateDuid(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}configure(e){e.baseUrl&&(e.baseURL=e.baseUrl);const t=this.config.trackDevice;this.config={...this.config,...e,headers:{...this.config.headers,...e.headers}},this.config.trackDevice&&!t&&this._initializeDuid()}addInterceptor(e,t){this.interceptors[e]&&this.interceptors[e].push(t)}buildUrl(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.config.baseURL.endsWith("/")?this.config.baseURL.slice(0,-1):this.config.baseURL}${e.startsWith("/")?e:`/${e}`}`}categorizeError(e,t=0){if("TypeError"===e.name&&e.message.includes("fetch"))return{reason:"not_reachable",message:"Service is not reachable - please check your connection"};if("AbortError"===e.name)return{reason:"cancelled",message:"Request was cancelled"};if("TimeoutError"===e.name||e.message.includes("timeout"))return{reason:"timed_out",message:"Request timed out - please try again"};if(t>=400){if(400===t)return{reason:"bad_request",message:"Invalid request data"};if(401===t)return{reason:"unauthorized",message:"Authentication required"};if(403===t)return{reason:"forbidden",message:"Access denied"};if(404===t)return{reason:"not_found",message:"Resource not found"};if(409===t)return{reason:"conflict",message:"Resource conflict"};if(422===t)return{reason:"validation_error",message:"Validation failed"};if(429===t)return{reason:"rate_limited",message:"Too many requests - please wait"};if(t>=500)return{reason:"server_error",message:"Server error - please try again later"};if(t>=400)return{reason:"client_error",message:"Request error"}}return e.message.includes("CORS")?{reason:"cors_error",message:"Cross-origin request blocked"}:e.message.includes("DNS")||e.message.includes("ENOTFOUND")?{reason:"dns_error",message:"Unable to resolve server address"}:{reason:"unknown_error",message:`Network error: ${e.message}`}}buildQueryString(e={}){const t=new URLSearchParams;Object.entries(e).forEach(([e,s])=>{null!=s&&(Array.isArray(s)?s.forEach(s=>t.append(`${e}[]`,s)):t.append(e,s))});const s=t.toString();return s?`?${s}`:""}async processRequestInterceptors(e){let t={...e};for(const r of this.interceptors.request)try{t=await r(t)}catch(s){throw console.error("Request interceptor error:",s),s}return t}async processResponseInterceptors(e,t){let s={success:e.ok,status:e.status,statusText:e.statusText,headers:Object.fromEntries(e.headers.entries()),data:null,errors:null,message:null,reason:null};try{const t=e.headers.get("content-type");if(t&&t.includes("application/json")){const t=await e.json();if(s.data=t,!e.ok){const r=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=t.errors||{},s.message=t.message||r.message,s.reason=r.reason}}else if(s.data=await e.text(),!e.ok){const t=this.categorizeError(new Error("HTTP Error"),e.status);s.message=t.message,s.reason=t.reason}}catch(r){s.errors={parse:"Failed to parse response"},s.message="Invalid response format"}for(const n of this.interceptors.response)try{s=await n(s,t)}catch(r){console.error("Response interceptor error:",r)}return s}async request(e,t,s=null,r={},n={}){let i={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(r),headers:{...this.config.headers,...n.headers},data:s,options:{timeout:this.config.timeout,...n}};if(i=await this.processRequestInterceptors(i),this.config.trackDevice&&this.duid)if("header"===this.config.duidTransport)i.headers[this.config.duidHeader]=this.duid;else if("GET"===i.method){const e=new URL(i.url);e.searchParams.append("duid",this.duid),i.url=e.toString()}else!i.data||"object"!=typeof i.data||i.data instanceof FormData||(i.data.duid=this.duid);const o={method:i.method,headers:i.headers},a=[];i.options.timeout&&a.push(AbortSignal.timeout(i.options.timeout)),i.options.signal&&a.push(i.options.signal),a.length>1?o.signal=AbortSignal.any?AbortSignal.any(a):a[0]:1===a.length&&(o.signal=a[0]),i.data&&["POST","PUT","PATCH"].includes(i.method)&&(i.data instanceof FormData?(o.body=i.data,delete o.headers["Content-Type"]):"object"==typeof i.data?o.body=JSON.stringify(i.data):o.body=i.data);try{const e=await fetch(i.url,o);return await this.processResponseInterceptors(e,i)}catch(l){if("AbortError"===l.name)throw l;const e=this.categorizeError(l),t={success:!1,status:0,statusText:"Network Error",headers:{},data:null,errors:{network:l.message},message:e.message,reason:e.reason},s={ok:!1,status:0,statusText:"Network Error",headers:new Headers,json:async()=>({}),text:async()=>""};return await this.processResponseInterceptors(s,i),t}}async GET(e,t={},s={}){return this.request("GET",e,null,t,s)}async POST(e,t={},s={},r={}){return this.request("POST",e,t,s,r)}async PUT(e,t={},s={},r={}){return this.request("PUT",e,t,s,r)}async PATCH(e,t={},s={},r={}){return this.request("PATCH",e,t,s,r)}async DELETE(e,t={},s={}){return this.request("DELETE",e,null,t,s)}async download(e,t={},s={}){const r={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete r.headers["Content-Type"];try{const e=await fetch(r.url,{method:r.method,headers:r.headers,signal:r.options.signal});if(!e.ok)throw new Error(`Download failed: ${e.status} ${e.statusText}`);const t=e.headers.get("content-disposition");let n=s.filename||"download";if(t){const e=t.match(/filename="?(.+)"?/);e&&e.length>1&&(n=e[1])}const i=e.body.getReader(),o=new ReadableStream({start:e=>function t(){return i.read().then(({done:s,value:r})=>{if(!s)return e.enqueue(r),t();e.close()})}()}),a=await new Response(o).blob(),l=window.URL.createObjectURL(a),h=document.createElement("a");return h.style.display="none",h.href=l,h.download=n,document.body.appendChild(h),h.click(),window.URL.revokeObjectURL(l),h.remove(),{success:!0,message:"Download initiated"}}catch(n){return console.error("Download error:",n),{success:!1,message:n.message}}}async downloadBlob(e,t={},s={}){const r={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete r.headers["Content-Type"];try{const e=await fetch(r.url,{method:r.method,headers:r.headers,signal:r.options.signal});if(!e.ok)throw new Error(`Download failed: ${e.status} ${e.statusText}`);const t=await e.blob(),n=e.headers.get("content-disposition");let i=s.filename||"download";if(n){const e=n.match(/filename="?(.+)"?/);e&&e.length>1&&(i=e[1])}const o=window.URL.createObjectURL(t),a=document.createElement("a");return a.style.display="none",a.href=o,a.download=i,document.body.appendChild(a),a.click(),window.URL.revokeObjectURL(o),a.remove(),{success:!0,message:"Download initiated"}}catch(n){return console.error("Download error:",n),{success:!1,message:n.message}}}async upload(e,t,s={}){return new Promise((r,n)=>{if(!(t instanceof File))return void n(new Error("Only single File objects are supported for legacy backend compatibility"));const i=new XMLHttpRequest;s.onProgress&&"function"==typeof s.onProgress&&(i.upload.onprogress=s.onProgress),i.onload=function(){i.status>=200&&i.status<300?r({data:i.response,status:i.status,statusText:i.statusText,xhr:i}):n(new Error(`Upload failed: ${i.status} ${i.statusText}`))},i.onerror=function(){n(new Error("Upload failed: Network error"))},i.ontimeout=function(){n(new Error("Upload failed: Timeout"))},i.open("PUT",e),i.setRequestHeader("Content-Type",t.type),s.timeout&&(i.timeout=s.timeout),i.send(t)})}async uploadMultipart(e,t,s={},r={}){const n=new FormData;if(t instanceof FileList)Array.from(t).forEach((e,t)=>{n.append(`file_${t}`,e)});else if(t instanceof File)n.append("file",t);else if(t instanceof FormData)return this.POST(e,t,{},r);return Object.entries(s).forEach(([e,t])=>{n.append(e,t)}),this.POST(e,n,{},r)}setAuthToken(e,t="Bearer"){e?this.config.headers.Authorization=`${t} ${e}`:delete this.config.headers.Authorization}clearAuth(){delete this.config.headers.Authorization}isRetryableError(e){return["not_reachable","timed_out","server_error","dns_error"].includes(e.reason)}requiresAuth(e){return"unauthorized"===e.reason}isNetworkError(e){return["not_reachable","timed_out","cancelled","cors_error","dns_error"].includes(e.reason)}getUserMessage(e){return e.message?e.message:{not_reachable:"Unable to connect to the server. Please check your internet connection.",timed_out:"The request took too long. Please try again.",cancelled:"The request was cancelled.",unauthorized:"Please log in to continue.",forbidden:"You don't have permission to perform this action.",not_found:"The requested resource was not found.",validation_error:"Please check your input and try again.",rate_limited:"Too many requests. Please wait a moment before trying again.",server_error:"Server error. Please try again later.",cors_error:"Access blocked by security policy.",dns_error:"Unable to reach the server.",unknown_error:"An unexpected error occurred."}[e.reason]||"An error occurred. Please try again."}};class EventBus{constructor(){this.listeners={},this.onceListeners={},this.maxListeners=100,this.debugMode=!1,this.eventStats={}}on(e,t){if("function"!=typeof t)throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(e=>this.on(e,t)),this):(this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].length>=this.maxListeners&&console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${e}`),this.listeners[e].push(t),this)}once(e,t){if("function"!=typeof t)throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(e=>this.once(e,t)),this):(this.onceListeners[e]||(this.onceListeners[e]=[]),this.onceListeners[e].push(t),this)}off(e,t){if(Array.isArray(e))return e.forEach(e=>this.off(e,t)),this;if(!t)return delete this.listeners[e],delete this.onceListeners[e],this;if(this.listeners[e]){const s=this.listeners[e].indexOf(t);-1!==s&&(this.listeners[e].splice(s,1),0===this.listeners[e].length&&delete this.listeners[e])}if(this.onceListeners[e]){const s=this.onceListeners[e].indexOf(t);-1!==s&&(this.onceListeners[e].splice(s,1),0===this.onceListeners[e].length&&delete this.onceListeners[e])}return this}emit(e,t){this.updateEventStats(e),this.debugMode&&console.log(`[EventBus] Emitting: ${e}`,t);const s=[];return this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]),this.debugMode&&s.length>0&&console.log(`[EventBus] ${s.length} listener(s) for '${e}'`),s.forEach(s=>{try{s(t,e)&&(e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault())}catch(r){console.error(`Error in event listener for '${e}':`,r),this.emitError(r,e,s)}}),this}async emitAsync(e,t){const s=[];this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]);const r=s.map(s=>new Promise(r=>{try{r(s(t,e))}catch(n){console.error(`Error in async event listener for '${e}':`,n),this.emitError(n,e,s),r()}}));return await Promise.all(r),this}removeAllListeners(){return this.listeners={},this.onceListeners={},this}listenerCount(e){return(this.listeners[e]?this.listeners[e].length:0)+(this.onceListeners[e]?this.onceListeners[e].length:0)}eventNames(){const e=Object.keys(this.listeners),t=Object.keys(this.onceListeners);return[.../* @__PURE__ */new Set([...e,...t])]}setMaxListeners(e){if("number"!=typeof e||e<0)throw new Error("Max listeners must be a non-negative number");return this.maxListeners=e,this}namespace(e){const t=t=>`${e}:${t}`;return{on:(e,s)=>this.on(t(e),s),once:(e,s)=>this.once(t(e),s),off:(e,s)=>this.off(t(e),s),emit:(e,s)=>this.emit(t(e),s),emitAsync:(e,s)=>this.emitAsync(t(e),s)}}use(e){if("function"!=typeof e)throw new Error("Middleware must be a function");const t=this.emit;return this.emit=(s,r)=>{try{const n=e(s,r);if(!1===n)return this;const i=void 0!==n?n:r;return t.call(this,s,i)}catch(n){return console.error("Error in event middleware:",n),t.call(this,s,r)}},this}emitError(e,t,s){"error"!==t&&setTimeout(()=>{this.emit("error",{error:e,originalEvent:t,callback:s.toString()})},0)}waitFor(e,t=null){return new Promise((s,r)=>{let n=null;const i=e=>{n&&clearTimeout(n),s(e)};this.once(e,i),t&&(n=setTimeout(()=>{this.off(e,i),r(new Error(`Timeout waiting for event: ${e}`))},t))})}debug(e=!0){return this.debugMode=e,e?console.log("[EventBus] Debug mode enabled"):console.log("[EventBus] Debug mode disabled"),this}getStats(){const e=this.eventNames(),t={totalEvents:e.length,totalListeners:0,events:{},emissions:{...this.eventStats}};return e.forEach(e=>{const s=this.listenerCount(e);t.events[e]=s,t.totalListeners+=s}),t}updateEventStats(e){this.eventStats[e]||(this.eventStats[e]={count:0,firstEmission:Date.now(),lastEmission:null}),this.eventStats[e].count++,this.eventStats[e].lastEmission=Date.now()}getEventStats(e){const t=this.eventStats[e];return t?{...t,listenerCount:this.listenerCount(e),avgEmissionsPerMinute:this.calculateEmissionRate(t)}:null}calculateEmissionRate(e){if(!e.firstEmission||!e.lastEmission)return 0;const t=e.lastEmission-e.firstEmission;if(0===t)return 0;const s=t/6e4;return Math.round(e.count/s*100)/100}resetStats(){return this.eventStats={},this}getTopEvents(e=10){return Object.entries(this.eventStats).map(([e,t])=>({event:e,count:t.count,rate:this.calculateEmissionRate(t),listeners:this.listenerCount(e)})).sort((e,t)=>t.count-e.count).slice(0,e)}debugInfo(){console.group("[EventBus] Debug Information"),console.log("Debug Mode:",this.debugMode),console.log("Max Listeners:",this.maxListeners);const e=this.getStats();return console.log("Total Events:",e.totalEvents),console.log("Total Listeners:",e.totalListeners),Object.keys(this.eventStats).length>0&&console.log("Top Events:",this.getTopEvents(5)),console.groupEnd(),this}}class WebApp{constructor(e={}){this.config=e,this.initPluginRegistry(),this.name=e.name||"MOJO App",this.version=e.version||"1.0.0",this.debug=e.debug||!1,this.container=e.container||"#app",this.layoutType=e.layout||"portal",this.layoutConfig=e.layoutConfig||{},e.sidebar&&(this.layoutConfig.sidebarConfig=e.sidebar),e.topbar&&(this.layoutConfig.topbarConfig=e.topbar),this.layout=null,this.layoutConfig.containerId=this.container||this.containerId||"#app",this.pageContainer=e.pageContainer||"#page-container",this.basePath=e.basePath||"",this.routerMode=e.routerMode||e.router?.mode||"param",this.basePath=e.basePath||e.router?.base||"",this.defaultRoute=e.defaultRoute||"home",this.session=e.session||{},this.router=null,this.navigation=e.navigation||{},this.state={currentPage:null,previousPage:null,loading:!1},this.events=new EventBus,this.rest=f,e.api&&this.rest.configure(e.api),this.router=new Router({mode:"param"===this.routerMode?"params":this.routerMode,basePath:this.basePath,defaultRoute:this.defaultRoute,eventEmitter:this.events}),this.events.on("route:changed",async e=>{const{pageName:t,params:s,query:r}=e;await this.showPage(t,r,s,{fromRouter:!0})}),"undefined"!=typeof window&&(window.MOJO=window.MOJO||{},window.MOJO.router=this.router),this.setupFocusTracking(),this.pageCache=/* @__PURE__ */new Map,this.pageClasses=/* @__PURE__ */new Map,this.componentClasses=/* @__PURE__ */new Map,this.modelClasses=/* @__PURE__ */new Map,this.currentPage=null,this.isStarted=!1,window.matchUUID?window[window.matchUUID]=this:window.MOJO?window.MOJO.app=this:window.__app__=this,console.log(`WebApp initialized: ${this.name} v${this.version}`)}async start(){if(this.isStarted)console.warn("WebApp already started");else try{console.log(`Starting ${this.name}...`),this.setupPageContainer(),this.validateDefaultRoute(),console.log("Setting up router..."),await this.setupRouter(),this.isStarted=!0,this.events.emit("app:ready",{app:this}),console.log(`✅ ${this.name} started successfully`)}catch(e){throw console.error(`Failed to start ${this.name}:`,e),this.showError("Failed to start application"),e}}async setupRouter(){this.router?(this.events.on("route:notfound",async e=>{console.warn(`Route not found: ${e.path}`),this._show404(e.path)}),this.router.start(),console.log(`Router started in ${this.routerMode} mode`)):console.error("Router not initialized")}setupPageContainer(){const e="string"==typeof this.container?document.querySelector(this.container):this.container;e&&!e.querySelector("#page-container")&&(e.innerHTML='<div id="page-container"></div>'),this.pageContainer="#page-container"}registerPage(e,t,s={}){if("string"!=typeof e||!e)return console.error("registerPage: pageName must be a non-empty string"),this;if("function"!=typeof t)return console.error("registerPage: PageClass must be a constructor function"),this;if(s.containerId||(s.containerId=this.pageContainer),this.pageClasses.set(e,{PageClass:t,constructorOptions:s}),this.router){let t=s.route||`/${e}`;t.startsWith("/")||(t=`/${t}`),s.route=t,this.router.addRoute(t,e),console.log(`📝 Registered route: "${t}" -> ${e}`)}return this}getPage(e){return this.pageCache.get(e)}getPagePermissions(e){if(this.pageCache.has(e))return this.pageCache.get(e).permissions;const t=this.pageClasses.get(e);if(!t)return null;const{PageClass:s,constructorOptions:r}=t;return r?r.permissions:null}getOrCreatePage(e){if(this.pageCache.has(e))return this.pageCache.get(e);const t=this.pageClasses.get(e);if(!t)return console.error(`Page not registered: ${e}`),null;const{PageClass:s,constructorOptions:r}=t;try{const t=new s({pageName:e,...r,app:this});return r.route&&(t.route=r.route),this.pageCache.set(e,t),console.log(`Created page: ${e} with route: ${t.route}`),t}catch(n){return console.error(`Failed to create page ${e}:`,n),null}}async showPage(e,t={},s={},r={}){const{fromRouter:n=!1,replace:i=!1,force:o=!1}=r;try{let r,i;"string"==typeof e?(i=e,r=this.getOrCreatePage(e)):e&&"object"==typeof e&&(r=e,i=e.pageName);const o=this.currentPage;if(!r)return void this._show404(i,s,t,n);if(!r.canEnter())return void this._showDeniedPage(r,s,t,n);o&&o!==r&&await this._exitOldPage(o),await r.onParams(s,t),o!==r&&await r.onEnter(),r.syncUrl(),this.events.emit("page:show",{page:r,pageName:r.pageName,params:s,query:t,fromRouter:n}),await r.render(),this.currentPage=r,console.log(`✅ Showing page: ${r.pageName}`,{query:t,params:s})}catch(a){console.error("Error in showPage:",a),this.showError(`Failed to load page: ${a.message}`),"error"!==e&&await this.showPage("error",{},{error:a,originalPage:e},{fromRouter:n})}}async _show404(e,t,s,r){const n=this.getOrCreatePage("404");n&&(n.setInfo&&n.setInfo(e),await this._exitOldPage(this.currentPage),await n.render(),this.currentPage=n,this.events.emit("page:404",{page:null,pageName:e,params:t,query:s,fromRouter:r}))}async _showDeniedPage(e,t,s,r){const n=this.getOrCreatePage("denied");n.setDeniedPage&&n.setDeniedPage(e),await this._exitOldPage(this.currentPage),await n.render(),this.currentPage=n,this.events.emit("page:denied",{page:e,pageName:e.pageName,params:t,query:s,fromRouter:r})}async _exitOldPage(e){if(e)try{await e.onExit(),await e.unmount(),this.events.emit("page:hide",{page:e})}catch(t){console.error(`Error exiting page ${e.pageName}:`,t)}}async navigate(e,t={},s={}){if(!this.router)return void console.error("Router not initialized");console.log("🧭 WebApp.navigate:",{route:e,query:t,options:s});let r=e;if(Object.keys(t).length>0){const s=new URLSearchParams(t).toString();r+=(e.includes("?")?"&":"?")+s}return await this.router.navigate(r,s)}async navigateToDefault(e={}){return await this.showPage(this.defaultRoute,{},{},e)}back(){this.router?this.router.back():console.warn("Router not initialized")}forward(){this.router?this.router.forward():console.warn("Router not initialized")}getCurrentPage(){return this.currentPage}getPageContainer(){return this.layout&&this.layout.getPageContainer?this.layout.getPageContainer():"string"==typeof this.pageContainer?document.querySelector(this.pageContainer):this.pageContainer}async showError(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;await t.alert(e,"Error",{size:"md",class:"text-danger"})}catch(t){this.events.emit("notification",{message:e,type:"error"}),"undefined"!=typeof window&&window?.console&&console.error("[WebApp] showError fallback:",t),"undefined"!=typeof window&&alert(`Error: ${e}`)}}async showSuccess(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;await t.alert(e,"Success",{size:"md",class:"text-success"})}catch(t){this.events.emit("notification",{message:e,type:"success"}),"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] showSuccess fallback:",t),"undefined"!=typeof window&&alert(`Success: ${e}`)}}async showInfo(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;await t.alert(e,"Information",{size:"md",class:"text-info"})}catch(t){this.events.emit("notification",{message:e,type:"info"}),"undefined"!=typeof window&&window?.console&&console.info("[WebApp] showInfo fallback:",t),"undefined"!=typeof window&&alert(`Info: ${e}`)}}async showWarning(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;await t.alert(e,"Warning",{size:"md",class:"text-warning"})}catch(t){this.events.emit("notification",{message:e,type:"warning"}),"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] showWarning fallback:",t),"undefined"!=typeof window&&alert(`Warning: ${e}`)}}showNotification(e,t="info"){this.events.emit("notification",{message:e,type:t})}async showLoading(e={}){"string"==typeof e&&(e={message:e});try{(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default.showBusy(e)}catch(t){"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] showLoading fallback:",t,e),this.events.emit("notification",{message:e.message||"Loading...",type:"info"})}}async hideLoading(){try{(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default.hideBusy()}catch(e){"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] hideLoading fallback:",e)}}async showModelForm(e={}){try{const t=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;return await t.showModelForm(e)}catch(t){throw"undefined"!=typeof window&&window?.console&&console.error("[WebApp] showModelForm failed:",t),t}}async showForm(e={}){try{const t=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;return await t.showForm(e)}catch(t){throw"undefined"!=typeof window&&window?.console&&console.error("[WebApp] showForm failed:",t),t}}async confirm(e,t="Confirm",s={}){const r=(await Promise.resolve().then(()=>require("./Dialog-D9EXh_A9.js"))).default;return await r.confirm(e,t,s)}setupFocusTracking(){if("undefined"==typeof window)return;this.isFocused=!document.hidden;const e=()=>{const e=this.isFocused;this.isFocused=!document.hidden,e!==this.isFocused&&(this.isFocused?this.events.emit("browser:focus"):this.events.emit("browser:blur"))},t=()=>{this.isFocused||(this.isFocused=!0,this.events.emit("browser:focus"))},s=()=>{this.isFocused&&(this.isFocused=!1,this.events.emit("browser:blur"))};document.addEventListener("visibilitychange",e),window.addEventListener("focus",t),window.addEventListener("blur",s),this._focusHandlers={visibilitychange:e,focus:t,blur:s}}setupErrorHandling(){window.addEventListener("error",e=>{console.error("Global error:",e.error),this.debug&&this.showError(`Error: ${e.error?.message||"Unknown error"}`)}),window.addEventListener("unhandledrejection",e=>{console.error("Unhandled promise rejection:",e.reason),this.debug&&this.showError(`Promise rejected: ${e.reason?.message||"Unknown error"}`)})}escapeHtml(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}getState(e){return e?this.state[e]:this.state}setState(e){const t={...this.state};Object.assign(this.state,e),this.events.emit("state:changed",{oldState:t,newState:this.state,updates:e})}registerComponent(e,t){this.componentClasses.set(e,t)}getComponent(e){return this.componentClasses.get(e)}registerModel(e,t){this.modelClasses.set(e,t)}getModel(e){return this.modelClasses.get(e)}setupRest(){this.rest=f,f.configure(this.api)}async destroy(){console.log("Destroying WebApp..."),this.router&&this.router.stop(),this._focusHandlers&&"undefined"!=typeof window&&(document.removeEventListener("visibilitychange",this._focusHandlers.visibilitychange),window.removeEventListener("focus",this._focusHandlers.focus),window.removeEventListener("blur",this._focusHandlers.blur));const e=Array.from(this.pageCache.values());if(await Promise.allSettled(e.map(async e=>{try{e.destroy&&await e.destroy()}catch(t){console.error("Error destroying page:",t)}})),this.layout&&this.layout.destroy)try{await this.layout.destroy()}catch(t){console.error("Error destroying layout:",t)}this.pageCache.clear(),this.pageClasses.clear(),this.componentClasses.clear(),this.modelClasses.clear(),"undefined"!=typeof window&&window.MOJO&&delete window.MOJO.router,this.isStarted=!1,console.log(`✨ ${this.name} destroyed`)}buildPagePath(e,t,s){let r=e.route||`/${e.pageName.toLowerCase()}`;if(Object.keys(t).forEach(e=>{"string"!=typeof t[e]&&"number"!=typeof t[e]||(r=r.replace(`:${e}`,t[e]))}),s&&Object.keys(s).length>0){const e=new URLSearchParams(s).toString();r+=(r.includes("?")?"&":"?")+e}return r}validateDefaultRoute(){this.pageClasses.has(this.defaultRoute)?console.log(`✅ Default route '${this.defaultRoute}' is registered`):(console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`),console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`),console.warn(" Or change default route: new WebApp({ defaultRoute: 'your-page' });"))}findFallbackPage(){const e=["404","error","denied"];for(const[t]of this.pageClasses.entries())if(!e.includes(t))return t;return null}static create(e={}){return new WebApp(e)}initPluginRegistry(){"undefined"!=typeof window&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.app=this)}static registerPlugin(e,t){"undefined"!=typeof window&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.plugins[e]=t)}}exports.BUILD_TIME=t,exports.DataWrapper=DataWrapper,exports.EventBus=EventBus,exports.EventDelegate=EventDelegate,exports.EventEmitter=g,exports.MOJOUtils=MOJOUtils,exports.Mustache=d,exports.Router=Router,exports.VERSION=e,exports.VERSION_INFO=s,exports.VERSION_MAJOR=2,exports.VERSION_MINOR=1,exports.VERSION_REVISION=678,exports.View=View,exports.WebApp=WebApp,exports.dataFormatter=p,exports.rest=f;
2
- //# sourceMappingURL=WebApp-CylQA6ix.js.map