(function (app) { 'use strict'; const util = {}; util.h = function (tag, attrs) { const el = document.createElement(tag); if (attrs) { for (const k of Object.keys(attrs)) { const v = attrs[k]; if (v == null || v === false) { continue; } if (k === 'className') { el.className = v; } else if (k.length > 2 && k.slice(0, 2) === 'on' && typeof v === 'function') { el.addEventListener(k.slice(2).toLowerCase(), v); } else if (v === true) { el.setAttribute(k, ''); } else { el.setAttribute(k, v); } } } for (let i = 2; i < arguments.length; i++) { const c = arguments[i]; if (c == null || c === false) { continue; } if (typeof c === 'string' || typeof c === 'number') { el.appendChild(document.createTextNode(String(c))); } else { el.appendChild(c); } } return el; }; // Resolve a column's `field` against a row data object. // - "" or "/" → the whole object // - "/foo/bar" → JSON Pointer (RFC 6901) lookup // - "foo" → top-level key util.resolveField = function (data, field) { if (data == null) { return undefined; } if (!field || field === '/') { return data; } if (field.charAt(0) !== '/') { return data[field]; } const segments = field.split('/').slice(1).map(function (s) { return s.replace(/~1/g, '/').replace(/~0/g, '~'); }); let cur = data; for (let i = 0; i < segments.length; i++) { if (cur == null) { return undefined; } if (Array.isArray(cur)) { const idx = parseInt(segments[i], 10); if (Number.isNaN(idx)) { return undefined; } cur = cur[idx]; } else if (typeof cur === 'object') { cur = cur[segments[i]]; } else { return undefined; } } return cur; }; // Format a raw cell value per column's `format` hint. util.formatCell = function (value, format) { if (value == null || value === '') { return ''; } if (format === 'date') { const d = new Date(value); if (!isNaN(d.getTime())) { return d.toISOString().slice(0, 10); } return String(value); } if (format === 'datetime') { const d = new Date(value); if (!isNaN(d.getTime())) { return d.toLocaleString(); } return String(value); } if (format === 'number') { const n = Number(value); if (Number.isFinite(n)) { return n.toLocaleString(); } return String(value); } if (format === 'bool' || typeof value === 'boolean') { return value ? '✓' : ''; } if (typeof value === 'object') { try { return JSON.stringify(value); } catch (e) { return String(value); } } return String(value); }; // Compare two cell values for sorting. null/undefined sort last. // Numbers compared numerically, dates compared as Date, otherwise string compare. util.compareCells = function (a, b, format) { const aMissing = a == null || a === ''; const bMissing = b == null || b === ''; if (aMissing && bMissing) { return 0; } if (aMissing) { return 1; } if (bMissing) { return -1; } if (format === 'date' || format === 'datetime') { const da = new Date(a).getTime(); const db = new Date(b).getTime(); if (!isNaN(da) && !isNaN(db)) { return da - db; } } if (format === 'number' || (typeof a === 'number' && typeof b === 'number')) { const na = Number(a); const nb = Number(b); if (Number.isFinite(na) && Number.isFinite(nb)) { return na - nb; } } const sa = String(a).toLowerCase(); const sb = String(b).toLowerCase(); if (sa < sb) return -1; if (sa > sb) return 1; return 0; }; app.modules.util = util; })(window.tablesApp);