@evotecit/canopyx 0.1.2 → 0.2.0
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.
- package/CanopyX.Html/Assets/canopyx-calendar-edit.js +113 -0
- package/CanopyX.Html/Assets/canopyx-calendar-ics.js +87 -0
- package/CanopyX.Html/Assets/canopyx-calendar-recurrence.js +36 -0
- package/CanopyX.Html/Assets/canopyx-calendar.css +279 -0
- package/CanopyX.Html/Assets/canopyx-calendar.js +307 -0
- package/CanopyX.Html/Assets/canopyx-cell-range.css +38 -0
- package/CanopyX.Html/Assets/canopyx-cell-range.js +141 -0
- package/CanopyX.Html/Assets/canopyx-column-groups.css +18 -0
- package/CanopyX.Html/Assets/canopyx-column-groups.js +51 -0
- package/CanopyX.Html/Assets/canopyx-data.js +14 -0
- package/CanopyX.Html/Assets/canopyx-detail-rows.css +18 -0
- package/CanopyX.Html/Assets/canopyx-detail-rows.js +51 -0
- package/CanopyX.Html/Assets/canopyx-editing.css +44 -0
- package/CanopyX.Html/Assets/canopyx-editing.js +134 -0
- package/CanopyX.Html/Assets/canopyx-enhance.js +126 -0
- package/CanopyX.Html/Assets/canopyx-export.js +18 -0
- package/CanopyX.Html/Assets/canopyx-facets.css +86 -0
- package/CanopyX.Html/Assets/canopyx-facets.js +65 -0
- package/CanopyX.Html/Assets/canopyx-filter-editor.css +15 -18
- package/CanopyX.Html/Assets/canopyx-filter-editor.js +23 -17
- package/CanopyX.Html/Assets/canopyx-format-editor.css +77 -0
- package/CanopyX.Html/Assets/canopyx-format-editor.js +30 -0
- package/CanopyX.Html/Assets/canopyx-formatting.css +40 -0
- package/CanopyX.Html/Assets/canopyx-formatting.js +101 -0
- package/CanopyX.Html/Assets/canopyx-grid-module.mjs +352 -246
- package/CanopyX.Html/Assets/canopyx-grid.css +139 -117
- package/CanopyX.Html/Assets/canopyx-grid.js +352 -246
- package/CanopyX.Html/Assets/canopyx-grouping.css +39 -0
- package/CanopyX.Html/Assets/canopyx-grouping.js +85 -0
- package/CanopyX.Html/Assets/canopyx-highlight.css +34 -0
- package/CanopyX.Html/Assets/canopyx-highlight.js +80 -0
- package/CanopyX.Html/Assets/canopyx-history.js +13 -0
- package/CanopyX.Html/Assets/canopyx-interactive.css +18 -6
- package/CanopyX.Html/Assets/canopyx-interactive.js +179 -100
- package/CanopyX.Html/Assets/canopyx-labels-de.js +304 -0
- package/CanopyX.Html/Assets/canopyx-labels-es.js +304 -0
- package/CanopyX.Html/Assets/canopyx-labels-fr.js +529 -0
- package/CanopyX.Html/Assets/canopyx-labels-it.js +529 -0
- package/CanopyX.Html/Assets/canopyx-labels-pl.js +304 -0
- package/CanopyX.Html/Assets/canopyx-layouts.css +48 -0
- package/CanopyX.Html/Assets/canopyx-layouts.js +39 -0
- package/CanopyX.Html/Assets/canopyx-link.css +19 -0
- package/CanopyX.Html/Assets/canopyx-link.js +38 -0
- package/CanopyX.Html/Assets/canopyx-links.js +14 -0
- package/CanopyX.Html/Assets/canopyx-live.css +24 -0
- package/CanopyX.Html/Assets/canopyx-live.js +88 -0
- package/CanopyX.Html/Assets/canopyx-module.mjs +180 -100
- package/CanopyX.Html/Assets/canopyx-pager.css +10 -0
- package/CanopyX.Html/Assets/canopyx-pager.js +14 -0
- package/CanopyX.Html/Assets/canopyx-record-panel.css +111 -0
- package/CanopyX.Html/Assets/canopyx-record-panel.js +178 -0
- package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +406 -342
- package/CanopyX.Html/Assets/canopyx-reporting.css +140 -135
- package/CanopyX.Html/Assets/canopyx-reporting.js +406 -342
- package/CanopyX.Html/Assets/canopyx-search.js +32 -0
- package/CanopyX.Html/Assets/canopyx-timeline.css +80 -0
- package/CanopyX.Html/Assets/canopyx-timeline.js +88 -0
- package/CanopyX.Html/Assets/canopyx-tree-tools.css +103 -0
- package/CanopyX.Html/Assets/canopyx-tree-tools.js +92 -0
- package/CanopyX.Html/Assets/canopyx.css +17 -5
- package/CanopyX.Html/Assets/canopyx.js +175 -96
- package/JavaScript/calendar.d.ts +362 -0
- package/JavaScript/cell-ranges.d.ts +76 -0
- package/JavaScript/column-groups.d.ts +32 -0
- package/JavaScript/editing.d.ts +257 -0
- package/JavaScript/enhance.d.ts +202 -0
- package/JavaScript/facets.d.ts +58 -0
- package/JavaScript/formatting.d.ts +88 -0
- package/JavaScript/grid.d.ts +409 -56
- package/JavaScript/hosts.d.ts +177 -2
- package/JavaScript/index.d.ts +53 -4
- package/JavaScript/live.d.ts +115 -0
- package/JavaScript/locales/de.mjs +304 -0
- package/JavaScript/locales/es.mjs +304 -0
- package/JavaScript/locales/fr.d.ts +6 -0
- package/JavaScript/locales/fr.mjs +527 -0
- package/JavaScript/locales/it.d.ts +6 -0
- package/JavaScript/locales/it.mjs +527 -0
- package/JavaScript/locales/pl.mjs +304 -0
- package/JavaScript/plugins.d.ts +113 -0
- package/JavaScript/protocol.d.ts +139 -6
- package/JavaScript/records.d.ts +136 -0
- package/README.md +61 -9
- package/README.nuget.md +1 -1
- package/package.json +99 -3
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const PROTOCOL="canopyx/2";const OVERSCAN=8;const TYPEAHEAD_MS=600;const INDENT=18;const COUNTS_BATCH=50;const SHARD_CACHE=24;const ENTRY_CACHE=32;const ENTRY_BATCH=8;const PACK_CACHE=6;const PACK_CACHE_BYTES=2*1024*1024;const DATE_CACHE=4096;const SEARCH_CANDIDATES=5;const MAX_PROTOCOL_INTEGER=2147483647;const MAX_COLUMN_WIDTH=MAX_PROTOCOL_INTEGER;const activeGrids=new WeakMap();let instanceSeq=0;function h(tag,className){const element=document.createElement(tag);if(className){element.className=className}
|
|
4
4
|
return element}
|
|
5
5
|
function setText(element,value){element.textContent=value==null?"":String(value)}
|
|
6
|
+
function filterChipContent(button,text){const label=h("span","cx-chip-label"),remove=h("span","cx-chip-remove");setText(label,text);setText(remove," ×");remove.setAttribute("aria-hidden","true");button.classList.add("cx-filter-chip");button.replaceChildren(label,remove)}
|
|
6
7
|
function validStructuralCount(value){return Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
|
|
7
8
|
function requireFlags(flags,name){if(!Array.isArray(flags)||flags.some((flag)=>typeof flag!=="string"||!flag.length)){throw new Error("CanopyX: "+name+" must be an array of nonempty strings.")}}
|
|
8
9
|
function checkDocument(doc,type){if(!doc||typeof doc!=="object"){throw new Error("CanopyX: expected a "+type+" document, got "+typeof doc)}
|
|
@@ -31,29 +32,21 @@ return tone}
|
|
|
31
32
|
function applyTone(element,base,tone){element.className=base+tone.cls;toneColors(element,tone)}
|
|
32
33
|
function toneColors(element,tone){if((element._tone||null)===tone.host){return}
|
|
33
34
|
element._tone=tone.host;if(tone.host){element.style.setProperty("--cx-t","var(--cx-tone-"+tone.host+")");element.style.setProperty("--cx-t-bg","var(--cx-tone-"+tone.host+"-bg)")}else{element.style.removeProperty("--cx-t");element.style.removeProperty("--cx-t-bg")}}
|
|
34
|
-
const numberFormats=new Map();function numberFormat(lang){const key=lang||"";let format=numberFormats.get(key);if(!format){try{format=new Intl.NumberFormat(lang||undefined,{maximumFractionDigits:3})}catch(e){format=new Intl.NumberFormat(undefined,{maximumFractionDigits:3})}
|
|
35
|
-
if(numberFormats.size>=16)numberFormats.delete(numberFormats.keys().next().value);numberFormats.set(key,format)}
|
|
36
|
-
return format}
|
|
37
35
|
function pad2(value){return value<10?"0"+value:String(value)}
|
|
38
36
|
const dateTexts=new Map();function dateText(value){let texts=dateTexts.get(value);if(texts===undefined){const time=Date.parse(value);const d=new Date(time);texts=isNaN(time)?null:[String(d.getUTCFullYear()).padStart(4,"0")+ "-"+pad2(d.getUTCMonth()+ 1)+ "-"+pad2(d.getUTCDate())+ " "+pad2(d.getUTCHours())+ ":"+pad2(d.getUTCMinutes()),String(d.getFullYear()).padStart(4,"0")+ "-"+pad2(d.getMonth()+ 1)+ "-"+pad2(d.getDate())+ " "+pad2(d.getHours())+ ":"+pad2(d.getMinutes())];if(dateTexts.size>=DATE_CACHE)dateTexts.clear();dateTexts.set(value,texts)}
|
|
39
37
|
return texts}
|
|
40
|
-
let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name))throw new Error("CanopyX: there is no label '"+name+"'.");if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
41
|
-
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
42
|
-
return Object.freeze(labels)}
|
|
43
|
-
function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
|
|
44
|
-
}
|
|
45
38
|
function formatting(lang,local,labels){const split=(template)=>{const at=template.indexOf("{time}");return[template.slice(0,at),template.slice(at+6)]};return{lang,local,labels,utc:split(labels.utcTime),other:split(labels.localTime)}}
|
|
46
39
|
function formatDate(value,fmt){const texts=dateText(value);return texts?(fmt.local?texts[1]:fmt.utc[0]+ texts[0]+ fmt.utc[1]):String(value)}
|
|
47
40
|
function otherZone(cell,column,fmt){const value=cellValue(cell);if(column.kind!=="datetime"||typeof value!=="string"||(isCellObject(cell)&&typeof cell.text==="string")){return null}
|
|
48
41
|
const texts=dateText(value);return texts?(fmt.local?fmt.utc[0]+ texts[0]+ fmt.utc[1]:fmt.other[0]+ texts[1]+ fmt.other[1]):null}
|
|
49
|
-
function formatValue(value,kind,fmt){if(value===null||value===undefined){return ""}
|
|
42
|
+
function formatValue(value,kind,fmt,format){if(value===null||value===undefined){return ""}
|
|
50
43
|
if(typeof value==="boolean"){return value?fmt.labels.yes:fmt.labels.no}
|
|
51
|
-
if(typeof value==="number"&&kind==="number"){return
|
|
44
|
+
if(typeof value==="number"&&kind==="number"){return numberText(value,fmt.lang,format)}
|
|
52
45
|
if(typeof value==="string"&&kind==="datetime"){return formatDate(value,fmt)}
|
|
53
46
|
return String(value)}
|
|
54
47
|
function cellText(cell,column,fmt){if(isCellObject(cell)&&typeof cell.text==="string"){return cell.text}
|
|
55
|
-
return formatValue(cellValue(cell),column.kind,fmt)}
|
|
56
|
-
const collator=new Intl.Collator(undefined,{numeric:true,sensitivity:"base"});function sortKey(cell,column){let value=cellValue(cell);if((value===null||value==="")&&isCellObject(cell)&&typeof cell.text==="string"&&cell.text!==""){value=cell.text}
|
|
48
|
+
return formatValue(cellValue(cell),column.kind,fmt,column.format)}
|
|
49
|
+
const collator=new Intl.Collator(undefined,{numeric:true,sensitivity:"base"});const safeGridNumber=value=>Number.isFinite(value)&&Math.abs(value)<=Number.MAX_SAFE_INTEGER;function sortKey(cell,column){let value=isCellObject(cell)&&(typeof cell.sort==="string"||safeGridNumber(cell.sort))?cell.sort:cellValue(cell);if((value===null||value==="")&&isCellObject(cell)&&typeof cell.text==="string"&&cell.text!==""){value=cell.text}
|
|
57
50
|
if(value===null||value===""){return{rank:2,value:0}}
|
|
58
51
|
if(typeof value==="boolean"){return{rank:0,value:value?1:0}}
|
|
59
52
|
if(typeof value==="number"){return{rank:0,value}}
|
|
@@ -62,12 +55,46 @@ return{rank:1,value:String(value)}}
|
|
|
62
55
|
function compareKeys(a,b,direction){if(a.rank!==b.rank){return a.rank-b.rank}
|
|
63
56
|
if(a.rank===2){return 0}
|
|
64
57
|
const order=a.rank===0?(a.value<b.value?-1:a.value>b.value?1:0):collator.compare(a.value,b.value);return order*direction}
|
|
58
|
+
let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
|
|
59
|
+
if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
60
|
+
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
61
|
+
return Object.freeze(labels)}
|
|
62
|
+
function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
|
|
63
|
+
}
|
|
64
|
+
const pluralRules=new Map();function plural(template,count,lang){if(!template.startsWith("["))return template;const forms={};for(const match of template.matchAll(/\[(zero|one|two|few|many|other)\]([^[]*)/g)) forms[match[1]] = match[2];
|
|
65
|
+
let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
|
|
66
|
+
return forms[rules.select(Number(count))]??forms.other??template}
|
|
67
|
+
const numberFormats=new Map();function numberFormat(lang,minimum=0,maximum=3){const key=(lang||"")+ "|"+minimum+"|"+maximum;let format=numberFormats.get(key);if(!format){const digits={minimumFractionDigits:minimum,maximumFractionDigits:maximum};try{format=new Intl.NumberFormat(lang||undefined,digits)}catch(e){format=new Intl.NumberFormat(undefined,digits)}
|
|
68
|
+
if(numberFormats.size>=16)numberFormats.delete(numberFormats.keys().next().value);numberFormats.set(key,format)}
|
|
69
|
+
return format}
|
|
70
|
+
function numberText(value,lang,format){if(!format)return numberFormat(lang).format(value);const minimum=format.minimumFractionDigits??0,maximum=format.maximumFractionDigits??3;const magnitude=Math.abs(value),fixed=magnitude>=1e21?BigInt(magnitude).toString():magnitude.toFixed(maximum);const signed=value<0&&Number(fixed)!==0?"-"+fixed:fixed;const parts=numberFormat(lang,minimum,maximum).formatToParts(signed);const at=parts.findIndex(part=>!["minusSign","literal"].includes(part.type));return parts.map((part,index)=>(index===at&&format.prefix||"")+ part.value).join("")+(format.suffix||"")}
|
|
71
|
+
let pageNonce=typeof document!=="undefined"&&document.currentScript?.nonce||"",trustPolicy;function setNonce(nonce){if(typeof nonce!=="string"||!nonce)throw new Error("CanopyX: invalid nonce.");pageNonce=nonce}
|
|
72
|
+
function withNonce(element){const nonce=pageNonce||document.querySelector("script[nonce]")?.nonce;if(nonce)element.nonce=nonce;return element}
|
|
73
|
+
function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.trustedTypes?.createPolicy("canopyx",{createHTML:text=>text,createScript:text=>text,createScriptURL:text=>text})||null}
|
|
74
|
+
catch(_){trustPolicy=null}}
|
|
75
|
+
return trustPolicy?trustPolicy["create"+kind](value):value}
|
|
76
|
+
function hostHtml(element,html,sanitize,kind){const text=String(html??""),value=sanitize?sanitize(text,{kind}):text;if(value?.nodeType)element.replaceChildren(value);else if(typeof value==="string")element.innerHTML=trusted("HTML",value);else if(global.trustedTypes?.isHTML?.(value))element.innerHTML=value;else throw new Error("CanopyX: invalid sanitizeHtml result.")}
|
|
77
|
+
DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridApply:"Apply",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right",gridResponsiveHidden:"Hidden at this width",gridExport:"Export",gridVisibleRows:"Visible rows",gridSelectedRows:"Selected rows",gridAllRows:"All rows",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export",gridMore:"More",gridPrint:"Print",gridDetails:"Record details",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close record details"}));DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{recordBack:"Back",recordBackTo:"Back to {record}",recordTrail:"Record trail",recordShowing:"Showing {record}",recordTabs:"Record views",recordSections:"Jump to a section",recordOtherFields:"Other fields",recordShowEmpty:"Show empty fields ({count})",recordHideEmpty:"Hide empty fields",recordActions:"Record actions",recordCopyValues:"Copy values",recordCopyJson:"Copy as JSON",recordCopyLink:"Copy link",recordCopied:"Copied to the clipboard",recordPrint:"Print record",recordExportCsv:"Export CSV",recordExportJson:"Export JSON",recordOpenAll:"Open all in grid",recordLoadMore:"Load more",recordCount:"{shown} of {total}",recordItems:"{count} items",recordMore:"+{count}",recordShowAll:"Show all {count}",recordRetry:"Retry",recordLoadFailed:"Could not load the record details: {error}",recordUnavailable:"Record {id} is not available here.",recordField:"Field",recordValue:"Value",recordSection:"Section",recordBefore:"Before",recordAfter:"After",recordAdded:"Added",recordRemoved:"Removed",recordChanged:"{count} changed fields"}));function boundedPattern(source,exact=true,whole=false){const bad=()=>{throw new Error("CanopyX: pattern requires literals, character classes and at most one variable repetition anchored at the start; groups, alternatives and backreferences are unsupported.")};if(typeof source!=="string"||source.length>256)bad();let width=0,variable=0;for(let i=0;i<source.length;){const unit=source[i++];if(unit==="^"&&i===1||unit==="$"&&i===source.length)continue;if(unit==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbBnrtfv\\.^$*+?{}\[\]()|/]$/.test(escape)) bad();
|
|
78
|
+
}else if(unit==="["){let closed=false;while(i<source.length){const c=source[i++];if(c==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbnrtfv\\.^$*+?{}\[\]()|/-]$/.test(escape)) bad(); }
|
|
79
|
+
else if(c==="]"){closed=true;break}}
|
|
80
|
+
if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
|
|
81
|
+
else if(source[i]==="{"){const match=/^\{(\d+)(?:,(\d*))?\}/.exec(source.slice(i));
|
|
82
|
+
if(!match)bad();const low=Number(match[1]),high=match[2]===undefined?low:match[2]===""?Infinity:Number(match[2]);if(low>256||high<low||high!==Infinity&&high>256)bad();if(low!==high)variable++;count=low===high?low:1;i +=match[0].length}
|
|
83
|
+
width +=count;if(width>256||variable>1)bad()}
|
|
84
|
+
if(variable&&!whole&&source[0]!=="^")bad();return new RegExp(whole?"^(?:"+source+")$":source,exact?"u":"iu")}
|
|
85
|
+
function wildcardMatch(pattern,value,exact){if(!exact){pattern=pattern.toLowerCase();value=value.toLowerCase()}
|
|
86
|
+
let p=0,v=0,star=-1,retry=0;while(v<value.length){if(p<pattern.length&&(pattern[p]==="?"||pattern[p]===value[v])){p++;v++}
|
|
87
|
+
else if(pattern[p]==="*"){star=p++;retry=v}
|
|
88
|
+
else if(star>=0){p=star+1;v=++retry}
|
|
89
|
+
else return false}
|
|
90
|
+
while(pattern[p]==="*")p++;return p===pattern.length}
|
|
65
91
|
const EXTRA_FOLD_PAIRS="1t.w.q.1,2u.w.n.1,2.w.7.1,y.1.o.2,4.1.3.2,3.1.8.2,3.1.n.2,2.-3d,1.1.3.2,4.5u,1.1,2.1,2.5q,1.1,2.5p,1.5p,1.1,3.27,1.5m,1.5n,1.1,2.5p,1.5r,2.5v,1.5t,1.1,4.5v,1.5x,2.5y,1.1.3.2,2.62,1.1,2.62,3.1,2.62,1.1,2.61,1.61,1.1,2.1,2.63,1.1,4.1,8.2,1.1,2.2,1.1,2.2,1.1.9.2,3.1.9.2,3.2,1.1,2.1,2.-2p,1.-1k,1.1.k.2,2.-3m,2.1.9.2,8.8bv,1.1,2.-4j,1.8bs,3.1,2.-5f,1.1x,1.1z,1.1.5.2,82.1,2.1,4.1,9.38,7.12,2.11.3.1,2.1s,2.1r,1.1r,2.w.h.1,2.w.9.1,10.8,9.1.c.2,6.-1o,3.1,2.-7,1.1,3.-3m.3.1,1.28.g.1,1.w.w.1,1d.1.h.2,a.1.r.2,2.f,1.1.7.2,3.1.1c.2,3.1c.12.1,28a.5ls.12.1,2.5ls,6.5ls,k3.tzk.28.1,1.8.6.1,1p0.1,7.-2bk.17.1,3.-2bk.3.1,8x.1.23.2,a.-5vj,2.1.1c.2,a.-8.8.1,9.-8.6.1,b.-8.8.1,9.-8.8.1,9.-8.6.1,c.-8.4.2,9.-8.8.1,p.-8.8.1,9.-8.8.1,9.-8.8.1,9.-8,1.-8,1.-22,1.-22,1.-9,c.-2e.4.1,1.-9,c.-8,1.-8,1.-2s,1.-2s,d.-8,1.-8,1.-34,1.-34,1.-7,c.-3k,1.-3k,1.-3i,1.-3i,1.-9,8a.-5st,4.-6gv,1.-6di,7.s,1a.g.g.1,k.1,mr.q.q.1,1f5.1c.1c.1,1d.1,2.-8af,1.-2xy,1.-89z,3.1.3.2,2.-8bg,1.-8al,1.-8bj,1.-8bi,2.1,3.1,9.-8cf,1.-8cf,1.1.1e.2,9.1,2.1,5.1,nym.1.n.2,k.1.e.2,3s.1.7.2,4.1.v.2,b.1,2.1,2.-r9g,1.1.5.2,5.1,2.-wmg,3.1,2.1,4.1.a.2,2.-wn8,1.-wnj,1.-wnf,1.-wn5,1.-wn8,2.-wlu,1.-wmi,1.-wlx,1.ps,1.1.8.2,2.-1c,1.-wn7,1.-raw,1.1,2.1,2.-wo7,1.1,4.1,6.1.3.2,2.-wu9,p.1,h7w.w.q.1";const EXTRA_LOWERCASE=new Map();let extraUnit=0;for(const record of EXTRA_FOLD_PAIRS.split(",")){const[gap,delta,count=1,step=0]=record.split(".").map(part=>parseInt(part,36));extraUnit +=gap;for(let i=0;i<count;i++)EXTRA_LOWERCASE.set(extraUnit+i*step,extraUnit+i*step+delta);extraUnit +=(count-1)*step}
|
|
66
92
|
function foldCode(c){if(c<0x80){return c>=65&&c<=90?c+32:c}
|
|
67
93
|
return EXTRA_LOWERCASE.get(c)||c}
|
|
68
94
|
function foldUnit(unit){return String.fromCharCode(foldCode(unit.charCodeAt(0)))}
|
|
69
95
|
function isSeparator(code){return code<128&&!((code>=48&&code<=57)||(code>=65&&code<=90)||(code>=97&&code<=122))}
|
|
70
|
-
function foldText(text){const source=String(text==null?"":text);
|
|
96
|
+
function foldText(text){const source=String(text==null?"":text);if(!/[^\0-\x7f]/.test(source)) return source.toLowerCase();
|
|
97
|
+
let out="";for(let i=0;i<source.length;i++){out +=foldUnit(source.charAt(i))}
|
|
71
98
|
return out}
|
|
72
99
|
function words(text){const folded=foldText(text);const out=[];let current="";for(let i=0;i<folded.length;i++){if(isSeparator(folded.charCodeAt(i))){if(current){out.push(current);current=""}}else{current +=folded.charAt(i)}}
|
|
73
100
|
if(current){out.push(current)}
|
|
@@ -77,6 +104,37 @@ function trimSeparators(text){let start=0,end=text.length;while(start<end&&isSep
|
|
|
77
104
|
function rankLabel(label,foldedQuery){const l=trimSeparators(foldText(label));return l===foldedQuery?0:l.startsWith(foldedQuery)?1:2}
|
|
78
105
|
function shardKey(prefix){let out="";for(let i=0;i<prefix.length;i++){const code=prefix.charCodeAt(i);out +=(code>=97&&code<=122)||(code>=48&&code<=57)?prefix.charAt(i):"_" +("000"+code.toString(16)).slice(-4)}
|
|
79
106
|
return out}
|
|
107
|
+
function svgIcon(shapes){const ns="http://www.w3.org/2000/svg",svg=document.createElementNS(ns,"svg");svg.classList.add("cx-ico");svg.setAttribute("viewBox","0 0 24 24");svg.setAttribute("aria-hidden","true");for(const[,tag,attributes]of shapes.matchAll(/<(\w+)([^>]*)\/>/g)) {
|
|
108
|
+
const shape=svg.appendChild(document.createElementNS(ns,tag));for(const[,attribute,value]of attributes.matchAll(/([\w-]+)="([^"]*)"/g)) shape.setAttribute(attribute, value);
|
|
109
|
+
}
|
|
110
|
+
return svg}
|
|
111
|
+
function gridExportField(value,delimiter,typed=false){if(value==null)return "";let text=String(value);if(typeof value!=="number"&&!typed&&/^(?:[\t\r]|\s*[=+\-@\uFF1D\uFF0B\uFF0D\uFF20])/.test(text)) text = "'" + text;
|
|
112
|
+
return text.includes(delimiter)||/["\r\n]/.test(text) ? '"' + text.replace(/"/g, '""') + '"' : text;
|
|
113
|
+
}
|
|
114
|
+
function gridScalar(cell,column){const value=cellValue(cell);if(value==null)return null;if(column.kind==="number"){if(!safeGridNumber(value))
|
|
115
|
+
throw new Error("CanopyX: numeric grid cells need finite values within the safe integer magnitude.");return value}
|
|
116
|
+
if(column.kind==="datetime"){const date=typeof value==="string"&&/^\d{4}-\d\d-\d\dT.*(?:Z|[+-]\d\d:\d\d)$/.test(value) ? Date.parse(value) : NaN;
|
|
117
|
+
if(!Number.isFinite(date))
|
|
118
|
+
throw new Error("CanopyX: date grid cells need ISO dates with an explicit time zone.");return date}
|
|
119
|
+
if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: text grid cells need strings or booleans.");return value}
|
|
120
|
+
function cellMatches(cell,predicate,column){const scalar=gridScalar(cell,column),op=predicate.operator;if(op==="isNull")return scalar===null;if(op==="isNotNull")return scalar!==null;if(scalar===null)return false;const normalize=value=>column.kind==="text"&&typeof value==="string"?foldText(value):value;const value=normalize(scalar),values=predicate.values.map(operand=>normalize(gridScalar(operand,column))),operand=values[0];switch(op){case "eq":return value===operand;case "ne":return value!==operand;case "in":return values.includes(value);case "contains":return String(value).includes(String(operand));case "startsWith":return String(value).startsWith(String(operand));case "gt":return value>operand;case "ge":return value>=operand;case "lt":return value<operand;case "le":return value<=operand}
|
|
121
|
+
return false}
|
|
122
|
+
function validateGridRows(rows,columns,ids=new Set()){if(!Array.isArray(rows))throw new Error("CanopyX: grid rows must be an array.");const declared=new Map(columns.map(column=>[column.id,column]));for(const row of rows){requireIdentifier(row&&row.id,"grid row ID");if(ids.has(row.id))throw new Error("CanopyX: duplicate grid row ID "+row.id);ids.add(row.id);if(!row.cells||typeof row.cells!=="object"||Array.isArray(row.cells))
|
|
123
|
+
throw new Error("CanopyX: invalid flat row fields.");gridDecoration(row.tone,row.className);for(const id of Object.keys(row.cells)){if(!declared.has(id))throw new Error("CanopyX: undeclared grid column "+id);const cell=row.cells[id];if(isCellObject(cell)){if(Array.isArray(cell)||
|
|
124
|
+
["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
|
|
125
|
+
cell.secondary!==undefined&&cell.secondary.length>4096||cell.sort!==undefined&&!(typeof cell.sort==="string"?cell.sort.length<=4096:Number.isFinite(cell.sort))||cell.series!==undefined&&!gridSeriesValid(cell.series)||
|
|
126
|
+
cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
|
|
127
|
+
["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
|
|
128
|
+
throw new Error("CanopyX: malformed grid cell.");if(typeof cell.sort==="number")gridScalar(cell.sort,{kind:"number"})}
|
|
129
|
+
gridScalar(cell,declared.get(id))}}
|
|
130
|
+
return rows}
|
|
131
|
+
function gridDecoration(tone,className){if(tone!==undefined&&(typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone) || tone.endsWith("-bg")) ||
|
|
132
|
+
className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
|
|
133
|
+
throw new Error("CanopyX: invalid highlight tone or className.")}
|
|
134
|
+
function gridSeriesValid(points){return Array.isArray(points)&&points.length<=4096&&points.every(point=>point&&typeof point==="object"&&!Array.isArray(point)&&
|
|
135
|
+
typeof point.period==="string"&&!!point.period.trim()&&point.period.length<=256&&
|
|
136
|
+
(point.value==null||Number.isFinite(point.value)&&Math.abs(point.value)<=Number.MAX_SAFE_INTEGER)&&
|
|
137
|
+
(point.state==null||typeof point.state==="string"&&point.state.length<=4096)&&(point.tone===undefined||typeof point.tone==="string"))}
|
|
80
138
|
const sidecarDocs=new Map();const sidecarWaiters=new Map();function sidecarKey(view,ref){return JSON.stringify([view,ref])}
|
|
81
139
|
function acceptSidecar(entry){if(!Array.isArray(entry)||entry.length!==3){return}
|
|
82
140
|
const key=sidecarKey(entry[0],entry[1]);const waiter=sidecarWaiters.get(key);if(waiter){sidecarWaiters.delete(key);waiter.resolve(entry[2])}else{sidecarDocs.set(key,entry[2])}}
|
|
@@ -84,7 +142,7 @@ const sidecarQueue=global.CanopyXSidecar=global.CanopyXSidecar||[];sidecarQueue.
|
|
|
84
142
|
return 0};function loadSidecar(base,view,ref){if(ref.split("/").some((part)=>part==="."||part==="..")){return Promise.reject(new Error("Sidecar reference cannot contain a dot path component: "+ref))}
|
|
85
143
|
const key=sidecarKey(view,ref);if(sidecarDocs.has(key)){const doc=sidecarDocs.get(key);sidecarDocs.delete(key);return Promise.resolve(doc)}
|
|
86
144
|
const pending=sidecarWaiters.get(key);if(pending){return pending.promise}
|
|
87
|
-
const waiter={};waiter.promise=new Promise((resolve,reject)=>{waiter.resolve=resolve;waiter.reject=reject});sidecarWaiters.set(key,waiter);const script=document.createElement("script");script.src=base+ref.split("/").map(encodeURIComponent).join("/")+ ".js";script.async=true;script.onload=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file did not register its data: "+ref))}};script.onerror=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file is missing: "+ref))}};document.head.appendChild(script);return waiter.promise}
|
|
145
|
+
const waiter={};waiter.promise=new Promise((resolve,reject)=>{waiter.resolve=resolve;waiter.reject=reject});sidecarWaiters.set(key,waiter);const script=withNonce(document.createElement("script"));script.src=trusted("ScriptURL",base+ref.split("/").map(encodeURIComponent).join("/")+ ".js");script.async=true;script.onload=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file did not register its data: "+ref))}};script.onerror=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file is missing: "+ref))}};document.head.appendChild(script);return waiter.promise}
|
|
88
146
|
function badIndex(what){return new Error("CanopyX: search "+what+" does not match the manifest or is malformed.")}
|
|
89
147
|
function isCount(value){return Number.isInteger(value)&&value>=0}
|
|
90
148
|
function checkSearchIndex(index){if(!index||!isCount(index.entryCount)||!Array.isArray(index.entryFiles)||!Array.isArray(index.kinds)||
|
|
@@ -95,41 +153,11 @@ if(index.entryPacks===undefined&&index.shardPacks===undefined)return;const keys=
|
|
|
95
153
|
(index.entryFiles.length>0)!==(index.entryPacks.length>0)||(keys.length>0)!==(index.shardPacks.length>0)||
|
|
96
154
|
index.entryPacks.some((first,i)=>!isCount(first)||first>=index.entryFiles.length||(i?first<=index.entryPacks[i-1]:first))||
|
|
97
155
|
index.shardPacks.some((key,i)=>typeof key!=="string"||!Object.hasOwn(index.shards,key)||(i?key<=index.shardPacks[i-1]:key!==keys[0]))){throw badIndex("index packs")}}
|
|
98
|
-
function resolveShards(shards,keys,word){const bases=keys.filter((key)=>!/\.\d+$/.test(key));
|
|
99
|
-
const parts=(selected)=>selected.flatMap((key)=>[key].concat(keys.filter((part)=>part.startsWith(key+"."))
|
|
100
|
-
.sort((a,b)=>Number(a.slice(a.lastIndexOf(".")+ 1))- Number(b.slice(b.lastIndexOf(".")+ 1)))));for(let length=Math.min(2,word.length);length<=word.length;length++){const key=shardKey(word.slice(0,length));if(Object.hasOwn(shards,key))return parts([key]);const deeper=bases.filter((k)=>k.length>key.length&&k.startsWith(key));if(!deeper.length)return[];if(length===word.length)return parts(deeper)}
|
|
101
|
-
return[]}
|
|
102
156
|
function recentCache(limit,loadDoc,maxBytes=Infinity,bytesOf=()=>0){const cache=new Map();let bytes=0;const get=(key)=>{let entry=cache.get(key);if(entry){cache.delete(key)}else{entry={pending:loadDoc(key),ready:false,bytes:0};entry.pending.then(doc=>{if(cache.get(key)!==entry)return;entry.ready=true;entry.bytes=bytesOf(doc);bytes +=entry.bytes;for(const[oldKey,old]of cache){if(cache.size<=limit&&bytes<=maxBytes)break;if(old.ready){cache.delete(oldKey);bytes -=old.bytes}}},()=>{if(cache.get(key)===entry)cache.delete(key)})}
|
|
103
157
|
cache.set(key,entry);return entry.pending};get.clear=()=>{cache.clear();bytes=0};return get}
|
|
104
|
-
const packBytes=pages=>pages.reduce((bytes,text)=>bytes +(typeof text==="string"?text.length*2:0),0);function
|
|
105
|
-
total +=list.length;if(last>=entryCount)throw badIndex("shard")});if(total!==count)throw badIndex("shard posting count");return doc}
|
|
106
|
-
function mergePostings(lists){const out=[];let ascending=true;for(const list of lists){let value=0;list.forEach((step,i)=>{for(let n=i&&step<0?-step:1;n>0;n--){value=i?value +(step<0?1:step):step;if(out.length&&value<=out[out.length-1])ascending=false;out.push(value)}})}
|
|
107
|
-
if(ascending)return out;out.sort((a,b)=>a-b);return out.filter((value,i)=>!i||value!==out[i-1])}
|
|
108
|
-
function intersectPostings(a,b){let j=0;return a.filter((value)=>{while(j<b.length&&b[j]<value)j++;return b[j]===value})}
|
|
109
|
-
function labelToken(folded){let hash=2166136261;for(let i=0;i<folded.length;i++){hash=Math.imul(hash^folded.charCodeAt(i),16777619)}
|
|
110
|
-
return "\u0000" +("0000000" +(hash>>>0).toString(16)).slice(-8)}
|
|
111
|
-
function entryReference(value){return value===undefined||value===null||isIdentifier(value)}
|
|
112
|
-
function entryFile(doc,first,count){checkDocument(doc,"searchEntries");if(doc.first!==first||!Array.isArray(doc.stem)||!Array.isArray(doc.entries)||doc.entries.length!==count)throw badIndex("entry file");const stem=new Map(doc.stem.map((item)=>(Array.isArray(item)?[item[0],item.slice(1)]:[])));const records=new Map();const raw=(n)=>{const item=n>=first&&n<first+count?doc.entries[n-first]:stem.get(n);if(!Array.isArray(item)||item.length<5||item.length>8||!Number.isInteger(item[0])||item[0]<-1||item[0]>=n||
|
|
113
|
-
!isCount(item[1])||typeof item[2]!=="string"||!isCount(item[3])||typeof item[4]!=="string"||
|
|
114
|
-
(item[5]!=null&&!isCount(item[5]))||!entryReference(item[6])||!(item[7]===undefined||isIdentifier(item[7]))){throw badIndex("entry "+n)}
|
|
115
|
-
return item};return(n)=>{const pending=[];for(let k=n;k>=0&&!records.has(k);k=raw(k)[0])pending.push(k);while(pending.length){const number=pending.pop();const item=raw(number);const parent=item[0]<0?null:records.get(item[0]);const base=parent?parent.id:"";if(item[1]+ item[3]>base.length)throw badIndex("entry "+number);records.set(number,{parent,id:base.slice(0,item[1])+ item[2]+ base.slice(base.length-item[3]),label:item[4],kind:item[5]==null?undefined:item[5],ref:item[6]||null,at:item[7]||null})}
|
|
116
|
-
return records.get(n)}}
|
|
117
|
-
function entryHit(record,kinds){const path=[];for(let r=record;r;r=r.parent)path.unshift(r.id);const hit={path,label:record.label};if(record.kind!==undefined){if(record.kind>=kinds.length)throw badIndex("entry kind");hit.kind=kinds[record.kind]}
|
|
118
|
-
const parent=record.parent;if(parent)hit.context=parent.parent?parent.parent.label+" / "+parent.label:parent.label;validateSearchHit(hit);return hit}
|
|
119
|
-
function entryFileOf(starts,n){let low=0;let high=starts.length-1;while(low<high){const mid=(low+high+1)>>1;if(starts[mid]<=n)low=mid;else high=mid-1}
|
|
120
|
-
return low}
|
|
121
|
-
function sidecarSearch(load,manifest,hints){const index=async()=>(await manifest()).search;const loadPack=recentCache(PACK_CACHE,async(number)=>{const doc=checkDocument(await load("q/"+number),"pack");if(!Array.isArray(doc.pages)||doc.pages.some(text=>typeof text!=="string"))throw badIndex("pack");return doc.pages},PACK_CACHE_BYTES,packBytes);const sortedKeys=new WeakMap();const packed=async(pack,slot)=>{const text=(await loadPack(pack))[slot];if(typeof text!=="string")throw badIndex("pack");return JSON.parse(text)};const loadIndexDoc=async(search,kind,name)=>{if(!search.entryPacks)return load(kind==="e"?"e/"+name:"s/k-"+name);if(kind==="e"){const pack=entryFileOf(search.entryPacks,name);return packed(pack,name-search.entryPacks[pack])}
|
|
122
|
-
let keys=sortedKeys.get(search);if(!keys)sortedKeys.set(search,keys=Object.keys(search.shards).sort());let pack=0;while(pack+1<search.shardPacks.length&&search.shardPacks[pack+1]<=name)pack++;return packed(search.entryPacks.length+pack,keys.indexOf(name)- keys.indexOf(search.shardPacks[pack]))};const loadShard=recentCache(SHARD_CACHE,async(key)=>{const search=await index();return checkShard(await loadIndexDoc(search,"s",key),key,search.shards[key],search.entryCount)});const loadEntries=recentCache(ENTRY_CACHE,async(file)=>{const search=await index();const first=search.entryFiles[file];const end=file+1<search.entryFiles.length?search.entryFiles[file+1]:search.entryCount;return entryFile(await loadIndexDoc(search,"e",file),first,end-first)});async function postingsOf(search,keys,word){const lists=[];for(const shard of await Promise.all(resolveShards(search.shards,keys,word).map(loadShard))){shard.words.forEach((indexed,i)=>indexed.startsWith(word)&&lists.push(shard.postings[i]))}
|
|
123
|
-
return mergePostings(lists)}
|
|
124
|
-
async function run(query,queryWords,limit,sequence){const search=await index();if(!search){return{hits:[],total:0,truncated:false}}
|
|
125
|
-
const keyWords=queryWords.filter((word,i)=>queryWords.indexOf(word)===i&&
|
|
126
|
-
!queryWords.some((other)=>other.length>word.length&&other.startsWith(word)));const keys=Object.keys(search.shards);const folded=trimSeparators(foldText(query));const[exact,lists]=await Promise.all([postingsOf(search,keys,labelToken(folded)),Promise.all(keyWords.map((word)=>postingsOf(search,keys,word)))]);const candidates=lists.sort((a,b)=>a.length-b.length).reduce(intersectPostings);const order=exact.concat(candidates);const wanted=limit*SEARCH_CANDIDATES;const taken=new Set();const hits=[];const records=new Map();let next=0;while(next<order.length&&hits.length<limit){const files=[];let end=next;for(;end<order.length&&hits.length+end-next<wanted;end++){const file=entryFileOf(search.entryFiles,order[end]);if(files.indexOf(file)<0){if(files.length>=ENTRY_BATCH&&hits.length+end-next>=limit)break;files.push(file)}}
|
|
127
|
-
const loaded=await Promise.all(files.map(loadEntries));for(;next<end;next++){const number=order[next];if(taken.has(number))continue;const record=loaded[files.indexOf(entryFileOf(search.entryFiles,number))](number);if(next>=exact.length||trimSeparators(foldText(record.label))===folded){taken.add(number);const hit=entryHit(record,search.kinds);records.set(hit,record);hits.push(hit)}}}
|
|
128
|
-
const ranked=rankHits(hits,query,limit);ranked.total=candidates.length;ranked.truncated=candidates.length>ranked.hits.length;if(sequence===latest){hints.clear();ranked.hits.forEach((hit)=>{for(let r=records.get(hit);r;r=r.parent)hints.set(r.id,r)})}
|
|
129
|
-
return ranked}
|
|
130
|
-
let latest=0;return(query,limit)=>{const queryWords=words(query);if(!queryWords.length){return Promise.resolve({hits:[],total:0,truncated:false})}
|
|
158
|
+
const packBytes=pages=>pages.reduce((bytes,text)=>bytes +(typeof text==="string"?text.length*2:0),0);function sidecarSearch(load,manifest,hints){let search;return(query,limit)=>{const queryWords=words(query);if(!queryWords.length){return Promise.resolve({hits:[],total:0,truncated:false})}
|
|
131
159
|
if(!queryWords.some((word)=>word.length>=2)){const error=new Error("CanopyX: sidecar search needs a word of at least two UTF-16 units.");error.code="too-short";return Promise.reject(error)}
|
|
132
|
-
return
|
|
160
|
+
return loadPart(SEARCH_PART).then(()=>(search||=SEARCH_IMPL.create(load,manifest,hints))(query,queryWords,limit))}}
|
|
133
161
|
function slicePage(all,cursor,pageSize){const offset=cursor?Number(cursor)||0:0;const items=all.slice(offset,offset+pageSize);const end=offset+items.length;return{items,total:all.length,next:end<all.length?String(end):null,offset}}
|
|
134
162
|
function rankHits(hits,query,limit){const folded=trimSeparators(foldText(query));const ranked=hits.map((hit)=>({hit,rank:rankLabel(hit.label,folded)}));ranked.sort((a,b)=>a.rank-b.rank||collator.compare(a.hit.label,b.hit.label));return{hits:ranked.slice(0,limit).map((item)=>item.hit),total:hits.length,truncated:hits.length>limit}}
|
|
135
163
|
function inlineSource(view){const pageSize=view.pageSize||100;const labelWords=new WeakMap();const wordsOf=(node)=>{let cached=labelWords.get(node);if(!cached){cached=words(node.label);labelWords.set(node,cached)}
|
|
@@ -160,14 +188,15 @@ if(kind==="inline"){if(!Array.isArray(view.roots))throw new Error("CanopyX: inli
|
|
|
160
188
|
throw new Error("CanopyX: unknown source kind '"+kind+"'.")}
|
|
161
189
|
async function remoteJson(url,type,timeoutMs,lifetime,signal){const controller=new AbortController();const signals=Array.from(new Set([lifetime,signal].filter(Boolean)));const abort=()=>controller.abort();signals.forEach((item)=>{if(item.aborted)abort();else item.addEventListener("abort",abort,{once:true})});let timedOut=false;const timer=setTimeout(()=>{timedOut=true;abort()},timeoutMs);try{const response=await fetch(url,{headers:{Accept:"application/json"},signal:controller.signal});if(!response.ok){if(response.body)await response.body.cancel().catch(()=>{});throw new Error(response.status+" "+response.statusText)}
|
|
162
190
|
return checkDocument(await response.json(),type)}catch(error){if(timedOut)throw new Error("CanopyX: remote request timed out after "+timeoutMs+" milliseconds.");throw error}finally{abort();clearTimeout(timer);signals.forEach((item)=>item.removeEventListener("abort",abort))}}
|
|
163
|
-
class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");requireIdentifier(view.id,"view ID");this.host=host;this.view=view;this.opts=options;for(const name of["isSelectable","beforeSelect"]){if(options[name]!==undefined&&typeof options[name]!=="function")throw new Error("CanopyX: "+name+" must be a function.")}
|
|
164
|
-
this.id="cx" + ++instanceSeq;this.lifetime=new AbortController();this.source=options.source||sourceFor(view,{signal:this.lifetime.signal,timeoutMs:options.requestTimeoutMs});this.queryAware=this.source.queryAware===true;this.dataLifetime=new AbortController();this.acceptedQuery=Object.freeze({version:1,sort:null,filters:Object.freeze([])});this.datasetRevision=null;this.rowH=view.rowHeight||28;this.pageSize=view.pageSize||100;this.retainedNodeLimit=options.retainedNodeLimit===undefined?1024:options.retainedNodeLimit;if(!validStructuralCount(this.retainedNodeLimit))throw new Error("CanopyX: retainedNodeLimit must be a nonnegative 32-bit integer.");this.inactiveNodeLimit=options.inactiveNodeLimit===undefined?4096:options.inactiveNodeLimit;if(!validStructuralCount(this.inactiveNodeLimit))throw new Error("CanopyX: inactiveNodeLimit must be a nonnegative 32-bit integer.");this.multi=view.selection==="multiple";this.lang=document.documentElement.lang||undefined;this.labels=labelsFor(view.labels,options.labels);this.
|
|
191
|
+
class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");requireIdentifier(view.id,"view ID");if(view.columns.some(column=>column.format!==undefined))throw new Error("CanopyX: number formats apply to flat grids only.");this.host=host;this.view=view;this.opts=options;for(const name of["isSelectable","beforeSelect","sanitizeHtml"]){if(options[name]!==undefined&&typeof options[name]!=="function")throw new Error("CanopyX: "+name+" must be a function.")}
|
|
192
|
+
this.id="cx" + ++instanceSeq;this.lifetime=new AbortController();this.source=options.source||sourceFor(view,{signal:this.lifetime.signal,timeoutMs:options.requestTimeoutMs});this.queryAware=this.source.queryAware===true;this.dataLifetime=new AbortController();this.acceptedQuery=Object.freeze({version:1,sort:null,filters:Object.freeze([])});this.datasetRevision=null;this.rowH=view.rowHeight||28;this.pageSize=view.pageSize||100;this.retainedNodeLimit=options.retainedNodeLimit===undefined?1024:options.retainedNodeLimit;if(!validStructuralCount(this.retainedNodeLimit))throw new Error("CanopyX: retainedNodeLimit must be a nonnegative 32-bit integer.");this.inactiveNodeLimit=options.inactiveNodeLimit===undefined?4096:options.inactiveNodeLimit;if(!validStructuralCount(this.inactiveNodeLimit))throw new Error("CanopyX: inactiveNodeLimit must be a nonnegative 32-bit integer.");this.multi=view.selection==="multiple";this.lang=document.documentElement.lang||undefined;this.labels=labelsFor(view.labels,options.labels);this.tools=Object.assign({},view.toolbar,options.toolbar);if(Object.entries(this.tools).some(([name,value])=>!/^(search|filters|columns|views|export|more|matches|layout)$/.test(name) || typeof value !== "boolean"))
|
|
193
|
+
throw new Error("CanopyX: toolbar options are booleans by control name.");this.local=view.timeZone==="local";this.fmt=formatting(this.lang,this.local,this.labels);this.kind="tree";this.plugins=viewPlugins(options,this.kind);const renderers=hostRenderers(options.renderers||{},view.columns,this.plugins);if(!options.formatLink!==!options.parseLink){throw new Error("CanopyX: formatLink and parseLink define one link scheme and are given together.")}
|
|
165
194
|
this.columns=view.columns.map((column,index)=>({...column,hidden:index>0&&!!column.hidden,render:Object.hasOwn(renderers,column.id)?renderers[column.id]:null}));this.columns.forEach(prepareWidth);this.keyMap=new Map();this.root={key:null,h:null,parent:null,level:0,idx:0,expanded:true,children:null,total:null,next:null,loading:false,error:null};if(!options.source&&(!view.source||view.source.kind==="inline")){this.root.inlineItems=view.roots||[]}
|
|
166
|
-
this.rows=[];this.tops=[0];this.total=0;this.version=1;this.rendered=new Map();this.pool=[];this.focused=null;this.selected=new Set();this.anchor=null;this.filter=null;this.declaredFilter=null;this.filterExpand=false;this.activeFilters=new Set();this.sortState=null;this.typeBuf="";this.typeAt=0;this.rafId=0;this.iconTemplates=this.buildIcons(view.icons||{});this.initializing=true;this.build();this.buildToolbar();this.ready=this.init()}
|
|
195
|
+
this.rows=[];this.tops=[0];this.total=0;this.version=1;this.rendered=new Map();this.pool=[];this.focused=null;this.selected=new Set();this.anchor=null;this.filter=null;this.declaredFilter=null;this.filterExpand=false;this.activeFilters=new Set();this.sortState=null;this.typeBuf="";this.typeAt=0;this.rafId=0;this.iconTemplates=this.buildIcons(view.icons||{});this.initializing=true;this.prepareViewModes();this.build();this.buildToolbar();if(view.deepLink)loadPart(LINKS).catch(()=>{});this.ready=this.modesReady?Promise.all([this.init(),this.modesReady]).then(([result])=>result):this.init()}
|
|
167
196
|
async search(query,limit){if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");const navigation=navigationScope(this,this.dataLifetime.signal);try{const result=await navigation.wait(this.source.search(query,limit,this.sourceRequest(this.dataLifetime.signal)));if(navigation.cancelled())throw new DOMException("Search snapshot was retired.","AbortError");if(this.queryAware)snapshotRevision(result,this.datasetRevision);return validateSearchResult(result)}finally{navigation.dispose()}}
|
|
168
|
-
buildIcons(icons){const templates=new Map();Object.keys(icons).forEach((key)=>{const holder=h("span","cx-icon");holder.setAttribute("aria-hidden","true");holder
|
|
197
|
+
buildIcons(icons){const templates=new Map();Object.keys(icons).forEach((key)=>{const holder=h("span","cx-icon");holder.setAttribute("aria-hidden","true");hostHtml(holder,icons[key],this.opts.sanitizeHtml,"icon");// trusted host SVG, declared once per view
|
|
169
198
|
templates.set(key,holder)});return templates}
|
|
170
|
-
async init(){try{this.rootsReady=this.loadRoots();await this.rootsReady;await this.restoreState();await this.expandInitial(this.root);if(!this.linkChanged||!this.linkChanged.aborted)await this.openDeepLink()}finally{this.initializing=false}
|
|
199
|
+
async init(){try{this.rootsReady=this.loadRoots();await this.rootsReady;await this.toolsReady;await this.restoreState();await this.expandInitial(this.root);if(!this.linkChanged||!this.linkChanged.aborted)await this.openDeepLink()}finally{this.initializing=false}
|
|
171
200
|
if(this.destroyed)return;await this.flushPendingToggles();if(this.linkChanged&&!this.linkChanged.aborted)await this.openDeepLink();if(!this.destroyed){this.host.setAttribute("data-cx-ready","true");this.emit("ready",{});this.scheduleRender()}}
|
|
172
201
|
loadRoots(){if(!this.root.inlineItems){return this.loadPage(this.root,null)}
|
|
173
202
|
validateNodes(this.root.inlineItems,this.keyMap,true);this.root.inline=true;const first=this.inlinePage(this.root,null);this.root.children=first.items.map((node,i)=>this.wrap(node,this.root,first.positions[i]));this.root.total=first.total;this.root.next=first.next;this.applySort(this.root,false);this.relayout();return Promise.resolve()}
|
|
@@ -229,7 +258,7 @@ throw new Error("CanopyX: query filters must be unique declared IDs.");const fil
|
|
|
229
258
|
function snapshotRevision(value,expected){requireIdentifier(value&&value.revision,"dataset revision");if(expected!=null&&value.revision!==expected)throw new Error("CanopyX: dataset revision changed; reload the roots before continuing.");return value.revision}
|
|
230
259
|
Object.assign(TreeGrid.prototype,{getQuery(){return{version:1,sort:this.sortState&&Object.assign({},this.sortState),filters:Array.from(this.activeFilters)}},sourceRequest(signal){return this.queryAware?{signal:signal||this.dataLifetime.signal,query:this.acceptedQuery,revision:this.datasetRevision}:{signal}},syncQueryControls(){this.toolbar.querySelectorAll("[data-cx-filter]").forEach(input=>{input.checked=this.activeFilters.has(input.getAttribute("data-cx-filter"))})},querySortHeader(id){const query=this.pendingQuery||this.acceptedQuery,state=query.sort&&query.sort.column===id?query.sort.direction:null;this.sort(id,state==="asc"?"desc":state==="desc"?null:"asc").catch(()=>{})}});Object.assign(TreeGrid.prototype,{retireWrapper(w){w._invalidated=true;this.retirePage(w);if(this.inlineRetained&&this.inlineRetained.get(w.key)===w)this.inlineRetained.delete(w.key);if(this.inactiveCache)this.inactiveCache.delete(w);if(this.keyMap.get(w.key)===w)this.keyMap.delete(w.key);w.children=null;w.inlineCache=null;w.inlineOrder=null;const selected=this.selected.delete(w);if(this.anchor===w)this.anchor=null;return selected?w:null}});Object.assign(TreeGrid.prototype,{build(){const host=this.host;host.classList.add("cx-host");if(!host.hasAttribute("data-cx-theme")){host.setAttribute("data-cx-theme","auto")}
|
|
231
260
|
host.style.setProperty("--cx-row-h",this.rowH+"px");this.setTreeColumnPinned(this.view.pinTreeColumn);this.toolbar=h("div","cx-toolbar");this.toolbar.hidden=true;const grid=this.grid=h("div","cx-grid");grid.id=this.id;grid.setAttribute("role","treegrid");grid.setAttribute("aria-label",this.view.label);if(this.multi){grid.setAttribute("aria-multiselectable","true")}
|
|
232
|
-
this.head=h("div","cx-head");this.head.setAttribute("role","rowgroup");this.headRow=h("div","cx-hrow");this.headRow.setAttribute("role","row");this.headRow.setAttribute("aria-rowindex","1");this.head.appendChild(this.headRow);this.canvas=h("div","cx-canvas");this.canvas.setAttribute("role","rowgroup");grid.appendChild(this.head);grid.appendChild(this.canvas);this.live=h("div","cx-live cx-sr-only");this.live.setAttribute("aria-live","polite");this.outline=h("div","cx-outline");host.appendChild(this.toolbar);host.appendChild(grid);host.appendChild(this.live);host.appendChild(this.outline);this.buildHeader();this.onScroll=()=>{this.scheduleRender();this.stateChanged()};this.onPrint=()=>{this.outline.
|
|
261
|
+
this.head=h("div","cx-head");this.head.setAttribute("role","rowgroup");this.headRow=h("div","cx-hrow");this.headRow.setAttribute("role","row");this.headRow.setAttribute("aria-rowindex","1");this.head.appendChild(this.headRow);this.canvas=h("div","cx-canvas");this.canvas.setAttribute("role","rowgroup");grid.appendChild(this.head);grid.appendChild(this.canvas);this.live=h("div","cx-live cx-sr-only");this.live.setAttribute("aria-live","polite");this.outline=h("div","cx-outline");host.appendChild(this.toolbar);host.appendChild(grid);host.appendChild(this.live);host.appendChild(this.outline);this.buildHeader();this.onScroll=()=>{this.scheduleRender();this.stateChanged()};this.onPrint=()=>{const table=this.outlineElement();this.outline.replaceChildren(...table?[table]:[])};grid.addEventListener("scroll",this.onScroll,{passive:true});grid.addEventListener("click",(e)=>this.onClick(e));grid.addEventListener("dblclick",(e)=>this.onDblClick(e));grid.addEventListener("pointerover",(e)=>this.cellTip(e));grid.addEventListener("keydown",(e)=>this.onKey(e));this.canvas.addEventListener("focusin",(e)=>{const rowEl=e.target.closest(".cx-row");if(rowEl&&rowEl._w&&this.focused!==rowEl._w){this.focusRevision=(this.focusRevision||0)+ 1;this.focused=rowEl._w;this.scheduleRender()}});this.canvas.addEventListener("focusout",(e)=>this.settleCell(e.relatedTarget));global.addEventListener("beforeprint",this.onPrint);this.onHash=(e)=>{const href=global.location.href;const last=this.locationEvent;this.locationEvent=last&&last.href===href&&last.type!==e.type?null:{href,type:e.type};if(this.locationEvent)this.readLocation()};global.addEventListener("hashchange",this.onHash);global.addEventListener("popstate",this.onHash);const viewWidth=()=>{if(grid.clientWidth)host.style.setProperty("--cx-view-w",grid.clientWidth+"px")};viewWidth();this.resizeObserver=new ResizeObserver(()=>{viewWidth();this.scheduleRender()});this.resizeObserver.observe(grid)},visibleColumns(){return this.columns.filter((column)=>!column.hidden)},buildHeader(){const active=document.activeElement;const focusedColumn=active&&this.headRow.contains(active)?active.closest(".cx-hcell").getAttribute("data-cx-column"):null;const focusedResize=active&&active.classList.contains("cx-resize");const nextCols=this.visibleColumns();const layoutChanged=!this.cols||this.cols.length!==nextCols.length||nextCols.some((column,i)=>column!==this.cols[i]);this.cols=nextCols;this.columnIndex=this.cols.map((column)=>this.columns.indexOf(column));if(layoutChanged){this.layout=(this.layout||0)+ 1;this.version++}
|
|
233
262
|
this.grid.setAttribute("aria-colcount",String(this.cols.length));this.headRow.textContent="";this.headCells=this.cols.map((column,i)=>{const cell=h("div","cx-hcell cx-kind-" +(column.kind||"text"));cell.setAttribute("role","columnheader");cell.setAttribute("aria-label",column.title);cell.setAttribute("data-cx-column",column.id);cell.setAttribute("aria-colindex",String(i+1));if(column.sortable){const sorted=this.sortState&&this.sortState.column===column.id?this.sortState.direction:null;cell.setAttribute("aria-sort",sorted==="asc"?"ascending":sorted==="desc"?"descending":"none");const button=h("button","cx-sort");button.type="button";setText(button,column.title);button.addEventListener("click",(event)=>this.queryAware?this.querySortHeader(column.id,event):this.sort(column.id,sorted==="asc"?"desc":sorted==="desc"?null:"asc"));cell.appendChild(button)}else{const title=h("span","cx-htext");setText(title,column.title);cell.appendChild(title)}
|
|
234
263
|
const grip=h("span","cx-resize");grip.setAttribute("aria-label",fill(this.labels.resizeColumn,{column:column.title}));grip.tabIndex=0;grip.setAttribute("role","separator");grip.setAttribute("aria-orientation","vertical");grip.setAttribute("aria-valuemin",column.minW);grip.setAttribute("aria-valuemax",column.maxW);grip.addEventListener("keydown",(e)=>{if(!["ArrowLeft","ArrowRight","Home"].includes(e.key))return;e.preventDefault();e.stopPropagation();column.width=columnWidth(column,e.key==="Home"?40:column.width +(e.key==="ArrowLeft"?-1:1)*(e.shiftKey?50:10));this.applyWidths();this.stateChanged()});grip.addEventListener("pointerdown",(e)=>this.startResize(e,column));cell.appendChild(grip);this.headRow.appendChild(cell);return cell});this.applyWidths();if(focusedColumn){const index=this.cols.findIndex((c)=>c.id===focusedColumn);const button=index>=0?this.headCells[index].querySelector(focusedResize?".cx-resize":".cx-sort"):null;if(button){button.focus()}}},rowCount(){return this.rows.length},rowItem(index){return this.rows[index]},rowTop(index){return this.tops[index]},rowAt(y){const tops=this.tops;let lo=0;let hi=this.rows.length-1;while(lo<hi){const mid=(lo+hi+1)>>1;if(tops[mid]<=y){lo=mid}else{hi=mid-1}}
|
|
235
264
|
return Math.max(0,lo)},indexOf(w){const i=w?w._ri:undefined;return i!==undefined&&this.rows[i]===w?i:-1},render(){let measured=false;for(let pass=0;!this.destroyed;pass++){this.renderPass();if(!this.measureDetails())break;measured=true;if(pass===3){this.scheduleRender();break}}
|
|
@@ -263,7 +292,7 @@ return false}};Object.assign(TreeGrid.prototype,ROW_SCROLLING);const ROW_VIEWPOR
|
|
|
263
292
|
const tabRow=visibleFocus||rows.rowItem(n?rows.rowAt(top):0)||null;const fragment=document.createDocumentFragment();for(const w of want){const index=rows.indexOf(w);if(index<0)continue;let rowEl=this.rendered.get(w);if(!rowEl){rowEl=free.pop()||this.pool.pop()||h("div");fragment.appendChild(rowEl);this.rendered.set(w,rowEl)}
|
|
264
293
|
if(w._v!==this.version||rowEl._w!==w){this.fillRow(rowEl,w);w._v=this.version}else if(!w.status)this.refreshRowState(rowEl,w);const y=this.scaledScroll&&(index<first||index>last)?
|
|
265
294
|
(index<first?-w.hpx-viewHeight:NATIVE_HEIGHT+viewHeight):rows.rowTop(index)- shift;if(rowEl._top!==y){rowEl.style.transform="translateY("+y+"px)";rowEl._top=y}
|
|
266
|
-
if(rowEl._index!==index){rowEl.setAttribute("aria-rowindex",index+2);rowEl._index=index}
|
|
295
|
+
if(rowEl._index!==index){rowEl.setAttribute("aria-rowindex",index+2 +(this.headDepth||0));rowEl._index=index}
|
|
267
296
|
const alt=index%2===1&&!w.status&&!w.h.detail&&(this.view.type!=="grid"||!!this.view.zebra);if(rowEl._alt!==alt){rowEl.classList.toggle("cx-alt",alt);rowEl._alt=alt}
|
|
268
297
|
const tab=w===tabRow?0:-1;if(rowEl._tab!==tab){rowEl.tabIndex=tab;rowEl._tab=tab}}
|
|
269
298
|
for(const rowEl of free){rowEl._w=null;this.observeDetail(rowEl,false);if(this.pool.length<200)this.pool.push(rowEl)}
|
|
@@ -274,13 +303,15 @@ Object.assign(TreeGrid.prototype,{fillRow(rowEl,w){rowEl._w=w;const kind=w.statu
|
|
|
274
303
|
setAttr(rowEl,"aria-level",String(w.level));const auto=kind==="detail"&&w.auto;this.observeDetail(rowEl,auto);const height=auto?"auto":w.hpx+"px";if(rowEl._height!==height){rowEl.style.height=height;rowEl._height=height}
|
|
275
304
|
if(kind==="status"){this.paintStatusRow(rowEl,w)}else if(kind==="detail"){this.paintDetailRow(rowEl,w)}else{this.paintNodeRow(rowEl,w)}},buildRow(rowEl,kind){disposeRowHostCells(rowEl);rowEl.textContent="";["aria-expanded","aria-busy","aria-setsize","aria-posinset","aria-selected","data-cx-id","data-cx-status","data-kind"].forEach((name)=>rowEl.removeAttribute(name));rowEl._attrs={};rowEl._cls=null;rowEl._kind=kind;rowEl._layout=this.layout;rowEl._height=null;rowEl.setAttribute("role","row");rowEl.style.width=this.rowWidth+"px";if(kind!=="node"){const cell=this.cell(rowEl,0,this.rowWidth);cell.setAttribute("aria-colspan",String(this.cols.length));rowEl._cells=[{cell}];if(kind==="status"){const indent=cell.appendChild(h("span","cx-indent"));const label=cell.appendChild(h("span"));rowEl._cells[0].indent=indent;rowEl._cells[0].label=label;rowEl._cells[0].text=label.appendChild(document.createTextNode(""))}
|
|
276
305
|
return}
|
|
277
|
-
rowEl._cells=this.cols.map((column,i)=>{const cell=this.cell(rowEl,i,column.width,column.kind);const slot={cell,base:cell.className +(i===0?" cx-tree":""),cls:null,title:null,badges:[],iconKey:null,iconEl:null,mode:"default"};cell.className=slot.base;if(i===0){slot.indent=cell.appendChild(h("span","cx-indent"));slot.exp=cell.appendChild(h("span","cx-exp"));slot.exp.setAttribute("aria-hidden","true");slot.content=cell.appendChild(h("span","cx-content"))}else{slot.content=cell}
|
|
306
|
+
rowEl._cells=this.cols.map((column,i)=>{const cell=this.cell(rowEl,i,column.renderWidth||column.width,column.kind);const slot={cell,base:cell.className +(i===0?" cx-tree":""),cls:null,title:null,badges:[],iconKey:null,iconEl:null,mode:"default"};cell.className=slot.base;if(i===0){slot.indent=cell.appendChild(h("span","cx-indent"));slot.exp=cell.appendChild(h("span","cx-exp"));slot.exp.setAttribute("aria-hidden","true");slot.content=cell.appendChild(h("span","cx-content"))}else{slot.content=cell}
|
|
278
307
|
slot.textSpan=slot.content.appendChild(h("span","cx-text"));slot.text=slot.textSpan.appendChild(document.createTextNode(""));return slot})},paintStatusRow(rowEl,w){setClass(rowEl,"cx-row cx-status cx-"+w.status);setAttr(rowEl,"data-cx-status",w.status);const slot=rowEl._cells[0];slot.label.className=w.status==="more"?"cx-more-btn":"cx-status-text";slot.indent.style.width=(w.level-1)*INDENT+"px";if(slot.text.data!==w.h.label){slot.text.data=w.h.label}},paintDetailRow(rowEl,w){const node=w.h;setClass(rowEl,"cx-row cx-detail" +(w.auto?" cx-detail-auto":"")+(node.group?" cx-group":"")+(node.className?" "+node.className:""));this.paintRowState(rowEl,w);const old=rowEl._cells[0].cell;const cell=this.cell(rowEl,0,this.rowWidth);rowEl.replaceChild(cell,old);rowEl._cells[0].cell=cell;cell.setAttribute("aria-colspan",String(this.cols.length));const left=(w.level-1)*INDENT+8+"px";cell.style.paddingLeft=left;cell.style.setProperty("--cx-detail-left",left);this.renderDetail(node,cell);quietControls(cell);if(w.auto)this.measureNeeded=true},paintNodeRow(rowEl,w){const node=w.h;setClass(rowEl,"cx-row" +(node.group?" cx-group":"")+(node.className?" "+node.className:""));this.paintRowState(rowEl,w);setAttr(rowEl,"aria-expanded",this.expandable(w)?(w._open?"true":"false"):null);setAttr(rowEl,"aria-busy",w.loading?"true":null);const slots=rowEl._cells;for(let i=0;i<this.cols.length;i++){const slot=slots[i];if(i===0){this.paintTreeSlot(slot,w)}
|
|
279
308
|
this.paintSlot(slot,node,this.cols[i],this.columnIndex[i],w)}},paintRowState(rowEl,w){const node=w.h;setAttr(rowEl,"data-cx-id",node.id);setAttr(rowEl,"data-kind",node.kind||null);setAttr(rowEl,"data-cx-status",null);setAttr(rowEl,"aria-selected",this.selected.has(w)?"true":"false");this.setPosition(rowEl,w)},refreshRowState(rowEl,w){setAttr(rowEl,"aria-expanded",this.expandable(w)?(w._open?"true":"false"):null);setAttr(rowEl,"aria-busy",w.loading?"true":null);this.setPosition(rowEl,w);if(rowEl._kind==="node")this.paintTreeSlot(rowEl._cells[0],w)},setPosition(rowEl,w){const parent=w.parent;const loaded=parent.children?parent.children.length:0;const size=parent.childrenFiltered?(parent.next?-1:parent.visibleCount):parent.total!=null?Math.max(parent.total,loaded):parent.next?-1:loaded;setAttr(rowEl,"aria-setsize",String(size));setAttr(rowEl,"aria-posinset",String(w.pos))},paintTreeSlot(slot,w){const level=w.level;if(slot.level!==level){slot.indent.style.width=(level-1)*INDENT+"px";slot.level=level}
|
|
280
309
|
const expander=this.expandable(w)?"cx-exp":"cx-exp cx-leaf";if(slot.expCls!==expander){slot.exp.className=expander;slot.expCls=expander}
|
|
281
310
|
const key=w.h.icon&&this.iconTemplates.has(w.h.icon)?w.h.icon:null;if(slot.iconKey!==key){if(slot.iconEl){slot.iconEl.remove()}
|
|
282
311
|
slot.iconEl=key?this.iconTemplates.get(key).cloneNode(true):null;if(slot.iconEl){slot.cell.insertBefore(slot.iconEl,slot.content)}
|
|
283
|
-
slot.iconKey=key}},
|
|
312
|
+
slot.iconKey=key}},cellTip(event){const cell=event.target.closest?.(".cx-cell"),row=cell?.parentNode._kind==="node"&&cell.parentNode._w;const column=row&&!row.status&&this.cols[[...cell.parentNode.children].indexOf(cell)],tip=column?.tooltip;const source=tip==="auto"?column:this.columns.find(item=>item.id===tip);if(!source||cell.title&&!cell._cxTip)return;cell._cxTip=true;cell.title=tip!=="auto"||[cell,...cell.querySelectorAll("*")].some(item=>item.scrollWidth>item.clientWidth)?
|
|
313
|
+
cellText(this.cellOf(row.h,source,this.columns.indexOf(source)),source,this.fmt):""},paintSlot(slot,node,column,index,w){const value=this.cellOf(node,column,index);if(slot.cell._cxTip){slot.cell._cxTip=false;slot.cell.removeAttribute("title");slot.title=null}
|
|
314
|
+
const hosted=column.render?this.paintHostSlot(slot,node,column,index,w):null;if(hosted==="host"){return}
|
|
284
315
|
const decorated=hosted==="decorated";const text=cellText(value,column,this.fmt);if(slot.text.data!==text){slot.text.data=text}
|
|
285
316
|
const object=isCellObject(value);const tone=object?toneOf(value.tone):NO_TONE;const cls=slot.base+tone.cls;if(slot.cls!==cls){if(decorated){if(tone.cls)slot.cell.className +=tone.cls}else{slot.cell.className=cls}
|
|
286
317
|
slot.cls=cls}
|
|
@@ -299,16 +330,17 @@ function hostBadge(badge){if(!badge||typeof badge!=="object"||typeof badge.text!
|
|
|
299
330
|
return badgeElement(badge)}
|
|
300
331
|
function attributeShape(element){let shape="";for(const attribute of element.attributes){shape +=attribute.name+"="+attribute.value+"\n"}
|
|
301
332
|
return shape}
|
|
302
|
-
function hostRenderers(renderers,columns){if(![Object.prototype,null].includes(Object.getPrototypeOf(renderers))||
|
|
333
|
+
function hostRenderers(renderers,columns,plugins){if(![Object.prototype,null].includes(Object.getPrototypeOf(renderers))||
|
|
303
334
|
Object.keys(renderers).some(id=>typeof renderers[id]!=="function"||!columns.some(column=>column.id===id)))
|
|
304
|
-
throw new Error("CanopyX: invalid column renderers.");return
|
|
335
|
+
throw new Error("CanopyX: invalid column renderers.");const named=Object.assign(Object.create(null),...plugins.map(item=>item.cellRenderers));return Object.assign(Object.create(null),...columns.filter(column=>typeof column.renderer==="string"&&named[column.renderer])
|
|
336
|
+
.map(column=>({[column.id]:named[column.renderer]})),renderers)}
|
|
305
337
|
function disposeHostSlot(slot){const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
|
|
306
338
|
function disposeRowHostCells(row){row._cells?.forEach(disposeHostSlot)}
|
|
307
339
|
class HostCellContext{#controller;#cleanups;constructor(slot,column,level,expanded,index){slot.hostLifetime=this;this.column=column;this.level=level;this.expanded=expanded;this.index=index}
|
|
308
340
|
get signal(){this.#controller||=new AbortController();if(this.#cleanups===null)this.#controller.abort();return this.#controller.signal}
|
|
309
341
|
onDispose=cleanup=>{if(typeof cleanup!=="function")throw new TypeError("Invalid cleanup.");if(this.#cleanups===null)cleanup();else(this.#cleanups||=[]).push(cleanup)};get used(){return this.#controller||this.#cleanups}
|
|
310
342
|
dispose(){const cleanups=this.#cleanups;this.#cleanups=null;this.#controller?.abort();while(cleanups?.length){try{cleanups.pop()()}catch(error){global.console?.error(error)}}}}
|
|
311
|
-
Object.assign(TreeGrid.prototype,{paintHostSlot(slot,node,column,index,w){const tree=slot.content!==slot.cell;const previous=slot.content;const fresh=tree?h("span","cx-content"):this.newCell(this.cols.indexOf(column),column.width,column.kind);const shape=attributeShape(fresh);disposeHostSlot(slot);previous.replaceWith(fresh);this.setSlotContent(slot,fresh,tree);let drawn;try{drawn=column.render(node,fresh,column.hostRenderer===false?{column:column.id,level:w.level,expanded:w.expanded,index}:
|
|
343
|
+
Object.assign(TreeGrid.prototype,{paintHostSlot(slot,node,column,index,w){const tree=slot.content!==slot.cell;const previous=slot.content;const fresh=tree?h("span","cx-content"):this.newCell(this.cols.indexOf(column),column.renderWidth||column.width,column.kind);const shape=attributeShape(fresh);disposeHostSlot(slot);previous.replaceWith(fresh);this.setSlotContent(slot,fresh,tree);let drawn;try{drawn=column.render(node,fresh,column.hostRenderer===false?{column:column.id,level:w.level,expanded:w.expanded,index}:
|
|
312
344
|
new HostCellContext(slot,column.id,w.level,w.expanded,index))===true}catch(e){disposeHostSlot(slot);if(fresh.parentNode)fresh.replaceWith(previous);this.setSlotContent(slot,previous,tree);throw e}
|
|
313
345
|
const untouched=!drawn&&!slot.hostLifetime?.used&&!fresh.firstChild&&attributeShape(fresh)===shape;if(untouched&&slot.mode==="default"){disposeHostSlot(slot);fresh.replaceWith(previous);this.setSlotContent(slot,previous,tree);return "default"}
|
|
314
346
|
if(tree){slot.cell.className=slot.base;slot.cell.removeAttribute("title");toneColors(slot.cell,NO_TONE)}
|
|
@@ -322,7 +354,7 @@ Object.assign(TreeGrid.prototype,{renderDetail(node,element,plainHtml=false){con
|
|
|
322
354
|
const detail=node.detail;if(detail.type==="table"){const table=h("table","cx-detail-table");if(detail.caption){const caption=h("caption");setText(caption,detail.caption);table.appendChild(caption)}
|
|
323
355
|
const columns=detail.columns||[];const widths=Array.isArray(detail.widths)&&detail.widths.length===columns.length?detail.widths:null;if(widths){const total=widths.reduce((sum,share)=>sum+share,0);const group=table.appendChild(h("colgroup"));widths.forEach((share)=>{group.appendChild(h("col")).style.width=(share / total * 100) + "%"; });
|
|
324
356
|
}
|
|
325
|
-
const head=h("thead");const headRow=h("tr");columns.forEach((title)=>{const th=h("th");th.scope="col";setText(th,title);headRow.appendChild(th)});head.appendChild(headRow);table.appendChild(head);const tbody=h("tbody");(detail.rows||[]).forEach((row)=>{const tr=h("tr");row.forEach((cell)=>tr.appendChild(detailCell("td",cell,this.fmt)));tbody.appendChild(tr)});table.appendChild(tbody);body.appendChild(table)}else if(detail.type==="properties"){const list=h("dl","cx-props");(detail.items||[]).forEach((item)=>{const term=h("dt");setText(term,item.name);list.appendChild(term);list.appendChild(detailCell("dd",item.value,this.fmt))});body.appendChild(list)}else if(detail.type==="html"){if(plainHtml)setText(body,detail.html||"");else body
|
|
357
|
+
const head=h("thead");const headRow=h("tr");columns.forEach((title)=>{const th=h("th");th.scope="col";setText(th,title);headRow.appendChild(th)});head.appendChild(headRow);table.appendChild(head);const tbody=h("tbody");(detail.rows||[]).forEach((row)=>{const tr=h("tr");row.forEach((cell)=>tr.appendChild(detailCell("td",cell,this.fmt)));tbody.appendChild(tr)});table.appendChild(tbody);body.appendChild(table)}else if(detail.type==="properties"){const list=h("dl","cx-props");(detail.items||[]).forEach((item)=>{const term=h("dt");setText(term,item.name);list.appendChild(term);list.appendChild(detailCell("dd",item.value,this.fmt))});body.appendChild(list)}else if(detail.type==="html"){if(plainHtml)setText(body,detail.html||"");else hostHtml(body,detail.html,this.opts.sanitizeHtml,"detail");// trusted host markup, see docs/security.md
|
|
326
358
|
}},detailHeight(detail){if(detail.height>0){return detail.height*this.rowH}
|
|
327
359
|
const slots=detail.type==="table"?(detail.rows||[]).length +(detail.caption?2:1):detail.type==="properties"?(detail.items||[]).length:6;return Math.max(1,Math.min(DETAIL_ESTIMATE_SLOTS,slots))*this.rowH},observeDetail(rowEl,auto){if(!!rowEl._auto===auto){return}
|
|
328
360
|
rowEl._auto=auto;if(!this.detailObserver){this.detailObserver=new ResizeObserver(()=>{this.measureNeeded=true;this.scheduleRender()})}
|
|
@@ -362,9 +394,10 @@ function cellControls(cell){const controls=Array.prototype.filter.call(cell.quer
|
|
|
362
394
|
function quietControls(cell){cell.querySelectorAll(CELL_CONTROLS).forEach((control)=>{if(control.tabIndex>=0)control.tabIndex=-1})}
|
|
363
395
|
Object.assign(TreeGrid.prototype,{rowCells(rowEl){return rowEl&&rowEl._cells&&rowEl._kind!=="status"?rowEl._cells.map((slot)=>slot.cell):[]},enterCells(w){const cells=this.rowCells(this.rendered.get(w));const index=cells.findIndex((cell)=>cellControls(cell).length>0);if(index<0)return false;this.focusCell(w,index,0);return true},focusCell(w,index,control,enter){this.setFocus(w,false);const cells=this.rowCells(this.rendered.get(w));if(!cells.length){this.setFocus(w,true);return}
|
|
364
396
|
const cell=cells[Math.max(0,Math.min(cells.length-1,index))];const controls=cellControls(cell);let target=cell;if(typeof control==="number"&&controls.length)target=controls[control<0?controls.length-1:Math.min(control,controls.length-1)];else if(control===undefined&&controls.length===1)target=controls[0];let parked=-1;if(target!==cell&&!enter&&!target.classList.contains("cx-detail-body")&&target.matches(OWN_ARROWS)){parked=controls.indexOf(target);target=cell}
|
|
365
|
-
cell._cxAt=parked;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInRow(cell,target,cells[0])},revealInRow(cell,target,treeCell){const grid=this.grid;if(cell.parentNode&&cell.parentNode._kind==="node"){this.revealInCell(cell,target);const
|
|
397
|
+
cell._cxAt=parked;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInRow(cell,target,cells[0])},revealInRow(cell,target,treeCell){const grid=this.grid;if(cell.parentNode&&cell.parentNode._kind==="node"){this.revealInCell(cell,target);const pins=cell.dataset.cxPin?[]:[...cell.parentNode.children].filter(item=>item!==cell&&
|
|
398
|
+
(item.dataset.cxPin||item===treeCell&&this.host.classList.contains("cx-pin-tree")));const edge=right=>pins.reduce((sum,item)=>sum +((item.dataset.cxPin==="right")===right?item.offsetWidth:0),0),pinned=edge(false),end=edge(true);const left=grid.getBoundingClientRect().left+grid.clientLeft;const box=cell.getBoundingClientRect();if(box.left<left+pinned)grid.scrollLeft -=left+pinned-box.left;else if(box.right>left+grid.clientWidth-end)grid.scrollLeft +=Math.min(box.right-left-grid.clientWidth+end,box.left-left-pinned)}
|
|
366
399
|
const body=target.closest(".cx-detail-body");if(body&&body!==target){const outer=body.getBoundingClientRect();const inner=target.getBoundingClientRect();if(inner.top<outer.top)body.scrollTop -=outer.top-inner.top;else if(inner.bottom>outer.bottom)body.scrollTop +=Math.min(inner.bottom-outer.bottom,inner.top-outer.top)}
|
|
367
|
-
const frame=grid.getBoundingClientRect();const top=frame.top+grid.clientTop+this.head.offsetHeight;const bottom=frame.top+grid.clientTop+grid.clientHeight;const box=target.getBoundingClientRect();if(box.top<top)this.setLogicalTop(this.logicalTop()-(top-box.top));else if(box.bottom>bottom)this.setLogicalTop(this.logicalTop()+ Math.min(box.bottom-bottom,box.top-top))},revealInCell(cell,target){if(target===cell||!cell.contains(target)||!cell.parentNode||cell.parentNode._kind!=="node")return;const outer=cell.getBoundingClientRect();const inner=target.getBoundingClientRect();const start=outer.left+cell.clientLeft;const end=start+cell.clientWidth;if(inner.left<start)cell.scrollLeft -=start-inner.left;else if(inner.right>end)cell.scrollLeft +=Math.min(inner.right-end,inner.left-start);if(cell.scrollLeft)this.scrolledCell=cell},settleCell(next){const cell=this.scrolledCell;if(cell&&!cell.contains(next||document.activeElement)){cell.scrollLeft=0;this.scrolledCell=null}},focusBeforeRows(event){const previous=this.headRow.querySelector(".cx-hcell:last-child .cx-resize");if(!previous)return;event.preventDefault();previous.focus()},heldCell(){const active=document.activeElement;const rowEl=active&&active.closest?active.closest(".cx-row"):null;if(!rowEl||rowEl===active||!rowEl._w||!this.canvas.contains(rowEl))return null;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(active));if(index<0)return null;return{w:rowEl._w,index,control:active===cells[index]?-1:Array.prototype.indexOf.call(cells[index].querySelectorAll(CELL_CONTROLS),active)}},restoreCell(held){const active=document.activeElement;if(!held||this.destroyed||(active&&active!==document.body)||!this.rendered.has(held.w))return;const cell=this.rowCells(this.rendered.get(held.w))[held.index];if(!cell)return;const candidate=held.control>=0?cell.querySelectorAll(CELL_CONTROLS)[held.control]:null;const target=candidate&&reachable(candidate)?candidate:cell;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInCell(cell,target)},onCellKey(e){const rowEl=e.target.closest(".cx-row");const w=rowEl&&rowEl._w;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(e.target));if(!w||w.status||index<0||e.isComposing||e.defaultPrevented)return index>=0;const cell=cells[index];const control=e.target!==cell?e.target:null;if(control&&control.matches(OWN_ARROWS)){if(e.key!=="Escape")return true;const from=cellControls(cell).indexOf(control);this.focusCell(w,index,null);cell._cxAt=from;e.preventDefault();e.stopPropagation();return true}
|
|
400
|
+
const frame=grid.getBoundingClientRect();const top=frame.top+grid.clientTop+this.head.offsetHeight;const bottom=frame.top+grid.clientTop+grid.clientHeight;const box=target.getBoundingClientRect();if(box.top<top)this.setLogicalTop(this.logicalTop()-(top-box.top));else if(box.bottom>bottom)this.setLogicalTop(this.logicalTop()+ Math.min(box.bottom-bottom,box.top-top))},revealInCell(cell,target){if(target===cell||!cell.contains(target)||!cell.parentNode||cell.parentNode._kind!=="node")return;const outer=cell.getBoundingClientRect();const inner=target.getBoundingClientRect();const start=outer.left+cell.clientLeft;const end=start+cell.clientWidth;if(inner.left<start)cell.scrollLeft -=start-inner.left;else if(inner.right>end)cell.scrollLeft +=Math.min(inner.right-end,inner.left-start);if(cell.scrollLeft)this.scrolledCell=cell},settleCell(next){const cell=this.scrolledCell;if(cell&&!cell.contains(next||document.activeElement)){cell.scrollLeft=0;this.scrolledCell=null}},focusBeforeRows(event){const previous=this.headRow.querySelector(".cx-hcell:last-child .cx-resize");if(!previous)return;event.preventDefault();previous.focus()},heldCell(){const active=document.activeElement;const rowEl=active&&active.closest?active.closest(".cx-row"):null;if(!rowEl||rowEl===active||!rowEl._w||!this.canvas.contains(rowEl))return null;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(active));if(index<0)return null;return{w:rowEl._w,index,control:active===cells[index]?-1:Array.prototype.indexOf.call(cells[index].querySelectorAll(CELL_CONTROLS),active)}},restoreCell(held){const active=document.activeElement;if(!held||this.destroyed||(active&&active!==document.body)||!this.rendered.has(held.w))return;const cell=this.rowCells(this.rendered.get(held.w))[held.index];if(!cell)return;const candidate=held.control>=0?cell.querySelectorAll(CELL_CONTROLS)[held.control]:null;const target=candidate&&reachable(candidate)?candidate:cell;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInCell(cell,target)},onCellKey(e){const rowEl=e.target.closest(".cx-row");const w=rowEl&&rowEl._w;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(e.target));if(!w||w.status&&!w.group||index<0||e.isComposing||e.defaultPrevented)return index>=0;const cell=cells[index];const control=e.target!==cell?e.target:null;if(control&&control.matches(OWN_ARROWS)){if(e.key!=="Escape")return true;const from=cellControls(cell).indexOf(control);this.focusCell(w,index,null);cell._cxAt=from;e.preventDefault();e.stopPropagation();return true}
|
|
368
401
|
const controls=cellControls(cell);const at=control?controls.indexOf(control):cell._cxAt>=0&&cell._cxAt<controls.length?cell._cxAt:-1;const detail=rowEl._kind==="detail";switch(e.key){case "ArrowRight":
|
|
369
402
|
if(at>=0&&at<controls.length-1)this.focusCell(w,index,at+1);else if(!detail&&index<cells.length-1)this.focusCell(w,index+1,0);break;case "ArrowLeft":
|
|
370
403
|
if(at>0)this.focusCell(w,index,at-1);else if(!detail&&index>0)this.focusCell(w,index-1,-1);else this.setFocus(w,true);break;case "Home":
|
|
@@ -406,8 +439,7 @@ if(w.status){this.setFocus(w,true);this.activate(w,true);return}
|
|
|
406
439
|
const interactive=hostControl(e.target);if(interactive&&!e.target.closest(".cx-exp")){this.setFocus(w,false);return}
|
|
407
440
|
if(w.h.detail){this.setFocus(w,true);this.select(w,{toggle:e.ctrlKey||e.metaKey,range:e.shiftKey});return}
|
|
408
441
|
this.setFocus(w,true);if(e.target.classList.contains("cx-exp")){this.toggle(w)}else{this.select(w,{toggle:e.ctrlKey||e.metaKey,range:e.shiftKey})}},onDblClick(e){const w=this.rowFromEvent(e);if(w&&!w.status&&!hostControl(e.target)){this.toggle(w)}},setFilter(filter,options){this.filter=typeof filter==="function"?{match:filter}:filter||null;this.cacheDirty=true;this.filterExpand=!!(options&&options.autoExpand);this.keyMap.forEach((w)=>{w.filterCollapsed=false});this.relayout()},setFilterActive(id,active){return this.updateActiveFilters(active?{add:[id]}:{remove:[id]})},updateActiveFilters(change){const{add=[],remove=[]}=change||{};if(!change||typeof change!=="object"||!Array.isArray(add)||!Array.isArray(remove))throw new Error("CanopyX: filter changes need add/remove arrays.");this.validateFilterIds(add.concat(remove));const next=new Set(this.queryAware?(this.pendingQuery||this.acceptedQuery).filters:this.activeFilters);remove.forEach(id=>next.delete(id));add.forEach(id=>next.add(id));return this.setActiveFilters(Array.from(next))},setActiveFilters(ids){if(!Array.isArray(ids))throw new Error("CanopyX: setActiveFilters needs an array of filter ids.");if(this.queryAware)return this.setQuery({version:1,sort:(this.pendingQuery||this.acceptedQuery).sort,filters:ids});this.applyActiveFilters(ids,new Set(ids))},validateFilterIds(ids){const declared=this.view.filters||[];ids.forEach((id)=>{if(!declared.some((f)=>f.id===id))throw new Error("CanopyX: the view declares no filter '"+id+"'.")})},applyActiveFilters(named,next){this.validateFilterIds(named);const declared=this.view.filters||[];const changed=next.size!==this.activeFilters.size||Array.from(next).some((id)=>!this.activeFilters.has(id));if(!changed&&this.filter===this.declaredFilter)return;this.activeFilters=next;this.toolbar.querySelectorAll("[data-cx-filter]").forEach((input)=>{input.checked=next.has(input.getAttribute("data-cx-filter"))});const filters=declared.filter((f)=>next.has(f.id));const has=(node,flag)=>!!(flag&&node.flags&&node.flags.indexOf(flag)>=0);this.setFilter(filters.length?{match:(node)=>filters.every((f)=>has(node,f.flag)),keep:(node)=>filters.every((f)=>has(node,f.flag)||has(node,f.ancestorFlag))}:null,{autoExpand:true});this.declaredFilter=this.filter;if(changed)this.emit("filter",{active:Array.from(next)});this.stateChanged()},sort(columnId,direction){if(this.queryAware)return this.setQuery({version:1,sort:direction?{column:columnId,direction}:null,filters:(this.pendingQuery||this.acceptedQuery).filters});this.sortState=direction?{column:columnId,direction}:null;this.inlineOrderVersion=(this.inlineOrderVersion||0)+ 1;this.buildHeader();this.applySort(this.root,true);this.relayout();this.stateChanged()},setColumnVisible(columnId,visible){const index=this.columns.findIndex((c)=>c.id===columnId);if(index<=0){return}
|
|
409
|
-
this.columns[index].hidden=!visible;const
|
|
410
|
-
this.buildHeader();this.relayout();this.stateChanged()}});function navigationScope(grid,signal,dataBound=false){const signals=Array.from(new Set([grid.lifetime.signal,dataBound&&grid.dataLifetime.signal].concat(signal||[]).filter(Boolean)));const controller=new AbortController();let abort;const stopped=new Promise((resolve)=>{abort=()=>{controller.abort();resolve()}});signals.forEach((item)=>item.addEventListener("abort",abort,{once:true}));const cancelled=()=>signals.some((item)=>item.aborted);if(cancelled())abort();return{cancelled,signal:controller.signal,wait:(pending)=>Promise.race([pending,stopped]),dispose:()=>{signals.forEach((item)=>item.removeEventListener("abort",abort));controller.abort()}}}
|
|
442
|
+
this.columns[index].hidden=!visible;this.syncColumnControls();this.buildHeader();this.relayout();this.stateChanged()}});function navigationScope(grid,signal,dataBound=false){const signals=Array.from(new Set([grid.lifetime.signal,dataBound&&grid.dataLifetime.signal].concat(signal||[]).filter(Boolean)));const controller=new AbortController();let abort;const stopped=new Promise((resolve)=>{abort=()=>{controller.abort();resolve()}});signals.forEach((item)=>item.addEventListener("abort",abort,{once:true}));const cancelled=()=>signals.some((item)=>item.aborted);if(cancelled())abort();return{cancelled,signal:controller.signal,wait:(pending)=>Promise.race([pending,stopped]),dispose:()=>{signals.forEach((item)=>item.removeEventListener("abort",abort));controller.abort()}}}
|
|
411
443
|
Object.assign(TreeGrid.prototype,{async openForPath(w,navigation){if(navigation.cancelled()||w._invalidated)return false;const wasOpen=w.expanded&&!w.filterCollapsed;w.expanded=true;w.filterCollapsed=false;if(!wasOpen)this.relayout();const revision=w.toggleRevision||0;if(!w.children){await navigation.wait(this.loadPage(w,null));if(navigation.cancelled()||w._invalidated||!w.expanded||(w.toggleRevision||0)!==revision)return false;if(!w.children){throw new Error("CanopyX: could not load the children of '"+w.key+"': " +(w.error||"no data"))}}else if(!wasOpen){await navigation.wait(this.expandInitial(w))}
|
|
412
444
|
return!navigation.cancelled()&&!w._invalidated&&w.expanded&&(w.toggleRevision||0)===revision},async expandPath(ids,options){const navigation=navigationScope(this,options&&options.signal,true);try{return await this.navigatePath(ids,options,navigation)}
|
|
413
445
|
finally{navigation.dispose()}},async navigatePath(ids,options,navigation){const o=options||{};const cancelled=navigation.cancelled;if(cancelled())return null;await navigation.wait(this.rootsReady);if(cancelled())return null;await navigation.wait(this.prefetchPath(ids,!!o.expandLast,navigation.signal));if(cancelled())return null;const release=this.holdLayout();let w;let rebuilt=false;try{w=await this.openPath(ids,o,navigation)}finally{rebuilt=release()}
|
|
@@ -451,9 +483,15 @@ await navigation.wait(this.source.page(parent===this.root?null:parent.h,cursor,t
|
|
|
451
483
|
if(page.positions)positions.push(...page.positions);items.push(...page.items);total=page.total;next=page.next||null;if(next&&(seen.has(next)||next===cursor))throw new Error("CanopyX: reload continuation did not advance.");if(next)seen.add(next);cursor=next}while(next&&items.length<target);if(navigation.cancelled()||parent._invalidated)return false;const current=new Map(this.keyMap);for(const key of this.descendants(parent).keys())current.delete(key);validateNodes(isInline?inline:items,current,inlineOnly);const anchor=this.captureScrollAnchor();this.retirePage(parent,controller);if(parent===this.root&&inlineOnly)parent.inlineItems=inline;this.reconcileList(parent,items,total==null?next?null:items.length:total,next,isInline?positions:null);this.restoreScrollAnchor(anchor);this.stateChanged();this.emit("reload",{node:parent.h,count:items.length,total:parent.total,ms:performance.now()- started});return true}catch(error){if(navigation.cancelled()||parent._invalidated)return false;this.emit("error",{node:parent.h,message:error.message||String(error)});throw error}finally{navigation.dispose();controller.abort();if(parent.reloadController===controller)parent.reloadController=null}},captureScrollAnchor(){const top=this.logicalTop();const index=this.rowAt(top);const w=this.rows[Math.min(index,this.rows.length-1)];return w&&!w.status?{id:w.key,offset:top-this.tops[this.indexOf(w)]}:null},restoreScrollAnchor(anchor){const w=anchor&&this.keyMap.get(anchor.id),index=this.indexOf(w);if(index>=0)this.setLogicalTop(Math.max(0,this.tops[index]+ anchor.offset))}});Object.assign(TreeGrid.prototype,{async setQuery(value,options,restoreOwner){if(this.destroyed)return false;if(options&&options.signal&&options.signal.aborted)return false;if(!this.queryAware)throw new Error("CanopyX: this source does not support view queries.");const query=readQuery(this,value),force=!!(options&&options.force);if(this.restoreController&&this.restoreController!==restoreOwner)this.restoreController.abort();if(this.queryController)this.queryController.abort();if(!force&&this.datasetRevision!=null&&JSON.stringify(query)===JSON.stringify(this.acceptedQuery)){this.pendingQuery=null;this.syncQueryControls();return true}
|
|
452
484
|
const controller=this.queryController=new AbortController();const navigation=navigationScope(this,[controller.signal,options&&options.signal]);this.pendingQuery=query;const started=performance.now(),target=force?Math.max(this.pageSize,(this.root.children||[]).length):1;try{const items=[],seen=new Set(),existing=new Map();let cursor=null,next=null,total=null,revision=null;do{if(navigation.cancelled())return false;const page=await navigation.wait(this.source.page(null,cursor,{signal:controller.signal,query,revision}));if(navigation.cancelled())return false;validatePage(page,existing,items.length);revision=snapshotRevision(page,revision);for(const node of page.items){const pending=[node];while(pending.length){const data=pending.pop();existing.set(data.id,true);for(const child of data.children||[])pending.push(child)}}
|
|
453
485
|
items.push(...page.items);total=page.total;next=page.next||null;if(next&&(next===cursor||seen.has(next)))throw new Error("CanopyX: query continuation did not advance.");if(next)seen.add(next);cursor=next}while(next&&items.length<target);if(navigation.cancelled())return false;const anchor=force?this.captureScrollAnchor():null;const filtersChanged=JSON.stringify(query.filters)!==JSON.stringify(this.acceptedQuery.filters);this.applyQuerySnapshot(query,revision,items,total,next,anchor);if(navigation.cancelled())return false;this.emit("query",{query:this.getQuery(),revision});if(filtersChanged&&!navigation.cancelled())this.emit("filter",{active:Array.from(this.activeFilters)});if(force&&!navigation.cancelled())this.emit("reload",{node:null,count:items.length,total:this.root.total,ms:performance.now()- started});if(!this.restoring&&!navigation.cancelled())await navigation.wait(this.expandInitial(this.root));return!navigation.cancelled()}catch(error){if(navigation.cancelled())return false;this.announce(fill(this.labels.queryFailed,{error:error.message||String(error)}));this.emit("error",{node:null,message:error.message||String(error)});throw error}finally{navigation.dispose();controller.abort();if(this.queryController===controller){this.queryController=null;this.pendingQuery=null;this.syncQueryControls()}}},applyQuerySnapshot(query,revision,items,total,next,anchor){this.dataLifetime.abort();this.dataLifetime=new AbortController();if(this.cancelSearch)this.cancelSearch();this.retirePage(this.root);for(const node of this.keyMap.values())this.retirePage(node);if(this.inactiveCache)this.inactiveCache.clear();this.acceptedQuery=query;this.datasetRevision=revision;this.sortState=query.sort;this.activeFilters=new Set(query.filters);this.filter=null;this.declaredFilter=null;this.buildHeader();this.syncQueryControls();this.reconcileList(this.root,items,total==null?next?null:items.length:total,next,null,false,!!this.restoring,true);this.rootsReady=Promise.resolve();if(anchor)this.restoreScrollAnchor(anchor);else this.setLogicalTop(0);const count=numberFormat(this.lang);this.announce(this.root.total!=null&&items.length<this.root.total?fill(this.labels.pageLoaded,{loaded:count.format(items.length),total:count.format(this.root.total)}):
|
|
454
|
-
fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChanged()}});
|
|
455
|
-
|
|
456
|
-
|
|
486
|
+
fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChanged()}});const EXPORT_FORMATS=new Map(),EXPORT_IMPL={},exportFormatId=id=>typeof id==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(id);
|
|
487
|
+
const exportFormatIds=list=>Array.isArray(list)&&new Set(list).size===list.length&&list.every(exportFormatId);function exportFormat(format){if(!format||!exportFormatId(format.id)||format.id==="csv"||
|
|
488
|
+
typeof format.label!=="string"||!format.label.trim()||typeof format.extension!=="string"||!/^[a-z\d]{1,16}$/i.test(format.extension) ||
|
|
489
|
+
format.mime!==undefined&&typeof format.mime!=="string"||typeof format.write!=="function")
|
|
490
|
+
throw new Error("CanopyX: invalid export format.");return Object.freeze({id:format.id,label:format.label,extension:format.extension,mime:format.mime||"application/octet-stream",write:format.write})}
|
|
491
|
+
function registerExportFormat(format){format=exportFormat(format);EXPORT_FORMATS.set(format.id,format);return()=>EXPORT_FORMATS.get(format.id)===format&&EXPORT_FORMATS.delete(format.id)}
|
|
492
|
+
EXPORT_IMPL.formatsOf=function(grid){const list=grid.opts.exportFormats??grid.view.exportFormats,known=new Map(EXPORT_FORMATS);for(const format of grid.plugins.filter(item=>(grid.opts.plugins||[]).some(local=>local.name===item.name))
|
|
493
|
+
.flatMap(item=>item.exportFormats||[]))known.set(format.id,exportFormat(format));if(list===undefined)return[...known.values()];const formats=Array.isArray(list)?list.map(item=>typeof item==="string"?known.get(item):exportFormat(item)).filter(Boolean):null;if(!formats||new Set(formats.map(format=>format.id)).size!==formats.length)throw new Error("CanopyX: invalid exportFormats.");return formats};function exportTimeout(grid,options){const timeout=options?.timeoutMs??grid.opts.exportTimeoutMs??600000;if(!Number.isInteger(timeout)||timeout<1||timeout>2147483647)throw new Error("CanopyX: invalid export timeout.");return timeout}
|
|
494
|
+
Object.assign(TreeGrid.prototype,{buildToolbar(){const bar=this.toolbar,tools=this.tools;if(this.view.search&&this.source.canSearch&&tools.search!==false)this.buildSearch(bar,this.view.search);if(tools.filters!==false)(this.view.filters||[]).forEach((filter)=>{if(filter.hidden)return;const label=h("label","cx-switch");const input=h("input");input.type="checkbox";input.setAttribute("data-cx-filter",filter.id);input.addEventListener("change",()=>{const pending=this.setFilterActive(filter.id,input.checked);if(pending&&pending.catch)pending.catch(()=>{})});label.appendChild(input);label.appendChild(document.createTextNode(filter.label));bar.appendChild(label)});this.syncToolbar();this.toolsReady=loadPart(TREE_TOOLS).then(()=>this.destroyed||this.installTools()).catch(error=>this.emit("error",{error}))},syncToolbar(){this.toolbar.hidden=!this.toolbar.firstChild||this.opts.toolbar===false},syncColumnControls(){},clearDeclaredFilters(){if(!this.activeFilters.size){return}
|
|
457
495
|
this.activeFilters.clear();this.toolbar.querySelectorAll("[data-cx-filter]").forEach((input)=>{input.checked=false});this.emit("filter",{active:[]});this.stateChanged()},buildSearch(bar,options){const box=h("div","cx-search");const input=h("input","cx-search-input");const list=h("ul","cx-results");const status=h("span","cx-sr-only");list.id=this.id+"-results";list.hidden=true;const labels=this.labels;const count=(value)=>numberFormat(this.lang).format(value);list.setAttribute("role","listbox");list.setAttribute("aria-label",labels.searchResults);input.type="search";input.placeholder=options.placeholder||labels.searchPlaceholder;input.setAttribute("aria-label",options.placeholder||labels.searchPlaceholder);input.setAttribute("role","combobox");input.setAttribute("aria-autocomplete","list");input.setAttribute("aria-expanded","false");input.setAttribute("aria-controls",list.id);status.setAttribute("aria-live","polite");let timer=0;let blurTimer=0;let sequence=0;let navigation=null;const cancelNavigation=()=>{if(navigation)navigation.abort()};let hits=[];let active=-1;const close=()=>{list.hidden=true;active=-1;input.setAttribute("aria-expanded","false");input.removeAttribute("aria-activedescendant")};const flyout=watchFlyout(box,list,()=>!list.hidden);this.disposeSearchFlyout=flyout.dispose;const choose=async(index)=>{const hit=hits[index];if(!hit){return}
|
|
458
496
|
cancelNavigation();const current=navigation=new AbortController();close();try{await this.expandPath(hit.path,{select:true,focus:true,signal:current.signal})}catch(err){if(!this.destroyed&&!current.signal.aborted)setText(status,fill(labels.searchFailed,{error:err.message}))}};const show=(result)=>{hits=result.hits||[];list.textContent="";hits.forEach((hit,index)=>{const item=h("li","cx-result");item.id=list.id+"-"+index;item.setAttribute("role","option");item.setAttribute("aria-selected","false");const label=h("span","cx-result-label");setText(label,hit.label);item.appendChild(label);if(hit.kind){const kind=h("span","cx-result-kind");setText(kind,hit.kind);item.appendChild(kind)}
|
|
459
497
|
if(hit.context){const context=h("span","cx-result-context");setText(context,hit.context);item.appendChild(context)}
|
|
@@ -464,19 +502,43 @@ fill(labels.searchFailed,{error:err&&err.message?err.message:err}))}}};const mov
|
|
|
464
502
|
if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
465
503
|
active=active<0?(delta<0?hits.length-1:0):(active+delta+hits.length)%hits.length;const item=list.children[active];item.setAttribute("aria-selected","true");input.setAttribute("aria-activedescendant",item.id);item.scrollIntoView({block:"nearest"})};input.addEventListener("input",()=>{cancelNavigation();sequence++;clearTimeout(timer);hits=[];close();timer=setTimeout(run,150)});input.addEventListener("keydown",(e)=>{if(e.defaultPrevented||e.isComposing)return;if(e.key==="ArrowDown"||e.key==="ArrowUp"){move(e.key==="ArrowDown"?1:-1)}else if(e.key==="Enter"){if(!list.hidden){choose(active>=0?active:0)}}else if(e.key==="Escape"){cancelNavigation();sequence++;clearTimeout(timer);if(list.hidden){input.value=""}
|
|
466
504
|
close()}else{return}
|
|
467
|
-
e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input}
|
|
468
|
-
|
|
505
|
+
e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input}});function watchFlyout(anchorElement,panel,isOpen){const position=()=>{if(!isOpen())return;const anchor=anchorElement.getBoundingClientRect(),fixed=getComputedStyle(panel).position==="fixed";const below=Math.max(0,global.innerHeight-anchor.bottom-8);const above=Math.max(0,anchor.top-8);const opensAbove=below<120&&above>below,room=opensAbove?above:below;panel.style.maxHeight=(panel.classList.contains("cx-fb")?room:Math.min(320,room))+ "px";panel.style.top=opensAbove?"auto":fixed?anchor.bottom+"px":"100%";panel.style.bottom=opensAbove?fixed?global.innerHeight-anchor.top+"px":"100%":"auto";const width=panel.getBoundingClientRect().width;panel.style.left=Math.max(8,Math.min(anchor.left,global.innerWidth-width-8))-(fixed?0:anchor.left)+ "px"};global.addEventListener("resize",position);global.addEventListener("scroll",position,true);return{position,dispose:()=>{global.removeEventListener("resize",position);global.removeEventListener("scroll",position,true)}}}
|
|
506
|
+
const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["ce442e100494535d","canopyx-tree-tools.149c371950d66a15.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"export":["5673e642f32c2f4e","canopyx-export.01512af72a298711.js"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
|
|
507
|
+
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
508
|
+
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
509
|
+
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
510
|
+
function loadPart(part){return part.ready||=Promise.resolve().then(async()=>{const[version,scriptFile,styleFile]=Object.hasOwn(PARTS,part.name)?PARTS[part.name]:[];part.version=version;if(!version)throw new Error("CanopyX: this runtime has no "+part.label+".");if(PART_CODE.get(part.name)?.version!==part.version||part.styleReady===null){const find=type=>[...document.querySelectorAll('script[type="application/x-canopyx-'+type+'"][data-cx-version="'+part.version+'"]')].find(item=>!pageNonce||item.nonce===pageNonce);const payload=find(part.name),script=h("script"),embeddedStyle=styleFile&&find(part.name+"-style");if(payload){if(embeddedStyle){const style=h("style");style.nonce=payload.nonce;setText(style,embeddedStyle.textContent);document.head.appendChild(style)}
|
|
511
|
+
script.textContent=trusted("Script",payload.textContent);script.nonce=payload.nonce;document.head.appendChild(script);script.remove()}else{const base=global.CanopyXFilterEditorBase,runtime=PART_URL||typeof base==="string"&&base||document.baseURI,hashed=PART_URL&&/\.[a-f\d]{16}\.js(?:[?#]|$)/.test(PART_URL);
|
|
512
|
+
const load=(element,attribute,file,kind)=>new Promise((resolve,reject)=>{const url=new URL(file,runtime).href;element[attribute]=attribute==="src"?trusted("ScriptURL",url):url;element.onload=resolve;element.onerror=()=>{element.remove();reject(new Error("CanopyX: "+part.label+" "+kind+" unavailable."))};document.head.appendChild(withNonce(element))});const link=h("link");link.rel="stylesheet";const style=styleFile&&(part.styleReady||=load(link,"href",hashed?styleFile:"canopyx-"+part.name+".css","stylesheet")
|
|
513
|
+
.catch(error=>{part.styleReady=null;throw error}));const code=PART_CODE.get(part.name)?.version===part.version||(part.scriptReady||=load(script,"src",hashed?scriptFile:"canopyx-"+part.name+".js","script")
|
|
514
|
+
.finally(()=>script.remove()).catch(error=>{part.scriptReady=null;throw error}));await Promise.all([style,code])}}
|
|
515
|
+
const code=PART_CODE.get(part.name);if(code?.version!==part.version)throw new Error("CanopyX: "+part.label+" and runtime must come from the same build.");for(const name of code.requires||[])await loadPart(part.parts[name]);code.install(part.context())}).catch(error=>{part.ready=null;throw error})}
|
|
516
|
+
const partQueue=global.CanopyXParts=global.CanopyXParts||[];partQueue.forEach(entry=>registerPart(...entry));partQueue.length=0;partQueue.push=(...entries)=>{entries.forEach(entry=>registerPart(...entry));return 0};const TREE_TOOLS=definePart(TreeGrid,"tree-tools","tree tools",()=>({TreeGrid,h,setText,fill,numberFormat,watchFlyout,columnWidth,prepareWidth,cellText,cellValue,isCellObject,foldText,formatting,sortKey,compareKeys,EXPORT_FORMATS,exportFormat,exportTimeout,exportFormatsOf:EXPORT_IMPL.formatsOf,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField}));const SEARCH_IMPL={},SEARCH_PART=definePart(TreeGrid,"search","sidecar search",()=>({SEARCH_IMPL,checkDocument,badIndex,isCount,isIdentifier,recentCache,packBytes,shardKey,foldText,trimSeparators,rankHits,validateSearchHit,PACK_CACHE,PACK_CACHE_BYTES,SHARD_CACHE,ENTRY_CACHE,ENTRY_BATCH,SEARCH_CANDIDATES}));async function requirePart(name){await this.toolsReady;const part=Object.hasOwn(this.parts,name)?this.parts[name]:null;if(!part)throw new Error("CanopyX: unknown part '"+name+"'.");return loadPart(part)}
|
|
517
|
+
TreeGrid.prototype.requirePart=requirePart;function readStateColumn(c,seen){requireIdentifier(c&&c.id,"state column ID");if(seen.has(c.id)||!Number.isFinite(c.width)||c.width<40||c.width>MAX_COLUMN_WIDTH||typeof c.hidden!=="boolean")
|
|
469
518
|
throw new Error("CanopyX: invalid state column.");seen.add(c.id);return{id:c.id,width:c.width,hidden:c.hidden}}
|
|
470
519
|
function statePaths(value){if(!Array.isArray(value))throw new Error("CanopyX: state paths must be arrays.");return value.map(path=>{if(!Array.isArray(path)||!path.length)throw new Error("CanopyX: state paths cannot be empty.");return path.map(id=>{requireIdentifier(id,"state path ID");return id})})}
|
|
471
|
-
Object.assign(TreeGrid.prototype,{getState(){const anchor=this.captureScrollAnchor(),anchorNode=anchor&&this.keyMap.get(anchor.id);return{version:1,expanded:this.getExpandedPaths(),selection:Array.from(this.selected,w=>this.pathOf(w)),columns:this.columns.map(c=>({id:c.id,width:c.width,hidden:!!c.hidden})),sort:this.sortState&&Object.assign({},this.sortState),filters:Array.from(this.activeFilters),timeZone:this.local?"local":"utc",pinTreeColumn:this.host.classList.contains("cx-pin-tree"),focus:this.focused&&!this.focused.status&&!this.focused._invalidated?this.pathOf(this.focused):null,anchor:anchorNode?{path:this.pathOf(anchorNode),offset:anchor.offset}:null}},readState(state){if(!state||typeof state!=="object")return null;try{const result={expanded:statePaths(state.expanded)};if(state.version===undefined)return result;if(state.version!==1)return null;result.version=1;result.selection=statePaths(state.selection);if(!this.multi&&result.selection.length>1)return null;if(!Array.isArray(state.columns)||!Array.isArray(state.filters))return null;const seen=new Set();result.columns=state.columns.map(c=>readStateColumn(c,seen));if(result.columns.some(c=>c.id===this.columns[0].id&&c.hidden))return null;result.filters=state.filters.map(id=>{requireIdentifier(id,"state filter ID");return id})
|
|
520
|
+
Object.assign(TreeGrid.prototype,{getState(){const anchor=this.captureScrollAnchor(),anchorNode=anchor&&this.keyMap.get(anchor.id);return{version:1,expanded:this.getExpandedPaths(),selection:Array.from(this.selected,w=>this.pathOf(w)),columns:this.columns.map(c=>({id:c.id,width:c.width,hidden:!!c.hidden})),sort:this.sortState&&Object.assign({},this.sortState),filters:Array.from(this.activeFilters),timeZone:this.local?"local":"utc",pinTreeColumn:this.host.classList.contains("cx-pin-tree"),focus:this.focused&&!this.focused.status&&!this.focused._invalidated?this.pathOf(this.focused):null,anchor:anchorNode?{path:this.pathOf(anchorNode),offset:anchor.offset}:null,...this.modeState({})}},readState(state){if(!state||typeof state!=="object")return null;try{const result={expanded:statePaths(state.expanded)};if(state.version===undefined)return result;if(state.version!==1)return null;result.version=1;result.selection=statePaths(state.selection);if(!this.multi&&result.selection.length>1)return null;if(!Array.isArray(state.columns)||!Array.isArray(state.filters))return null;const seen=new Set();result.columns=state.columns.map(c=>readStateColumn(c,seen));if(result.columns.some(c=>c.id===this.columns[0].id&&c.hidden))return null;result.filters=state.filters.map(id=>{requireIdentifier(id,"state filter ID");return id})
|
|
472
521
|
.filter(id=>(this.view.filters||[]).some(f=>f.id===id));result.sort=null;if(state.sort!=null){requireIdentifier(state.sort.column,"state sort column");if(state.sort.direction!=="asc"&&state.sort.direction!=="desc")return null;if(this.columns.some(c=>c.id===state.sort.column))result.sort={column:state.sort.column,direction:state.sort.direction}}
|
|
473
522
|
if(state.timeZone!=="utc"&&state.timeZone!=="local"||typeof state.pinTreeColumn!=="boolean")return null;result.timeZone=state.timeZone;result.pinTreeColumn=state.pinTreeColumn;result.focus=state.focus==null?null:statePaths([state.focus])[0];result.anchor=null;if(state.anchor!=null){if(!Number.isFinite(state.anchor.offset)||state.anchor.offset<0||state.anchor.offset>MAX_PROTOCOL_INTEGER)return null;result.anchor={path:statePaths([state.anchor.path])[0],offset:state.anchor.offset}}
|
|
474
|
-
return result}catch(error){return null}},applyStatePresentation(state){if(!state.version)return;if(this.cancelResize)this.cancelResize();for(const saved of state.columns){const column=this.columns.find(c=>c.id===saved.id);if(column){column.width=columnWidth(column,saved.width);column.hidden=saved.hidden}}
|
|
475
|
-
this.
|
|
523
|
+
Object.assign(result,readModeState(this,state));return result}catch(error){return null}},applyStatePresentation(state){if(!state.version)return;if(this.cancelResize)this.cancelResize();for(const saved of state.columns){const column=this.columns.find(c=>c.id===saved.id);if(column){column.width=columnWidth(column,saved.width);column.hidden=saved.hidden}}
|
|
524
|
+
this.syncColumnControls();this.sortState=state.sort;this.inlineOrderVersion=(this.inlineOrderVersion||0)+ 1;this.setTreeColumnPinned(state.pinTreeColumn);this.setTimeZone(state.timeZone);this.buildHeader();this.applySort(this.root,true);if(!this.queryAware)this.setActiveFilters(state.filters);this.applyModeState(state)},async loadStatePaths(state,current){const paths=state.selection.concat(state.focus?[state.focus]:[],state.anchor?[state.anchor.path]:[]);const attached=new Set([state.focus,state.anchor&&state.anchor.path].filter(Boolean).map(path=>JSON.stringify(path)));for(const path of paths){if(this.destroyed||current.signal.aborted)return;const existing=this.keyMap.get(path[path.length-1]);let cached=false;for(let node=existing;node;node=node.parent)if(node._cached)cached=true;if(existing&&JSON.stringify(this.pathOf(existing))===JSON.stringify(path)&&
|
|
476
525
|
(!attached.has(JSON.stringify(path))||!cached))continue;try{await this.expandPath(path,{select:false,focus:false,reveal:false,signal:current.signal})}
|
|
477
526
|
catch(error){/* Nodes removed since the snapshot are skipped. */ }
|
|
478
|
-
}},applyStatePosition(state,current){if(!state.version)return;const valid=path=>{const w=path&&this.keyMap.get(path[path.length-1]);return w&&JSON.stringify(this.pathOf(w))===JSON.stringify(path)?w:null};this.setSelection(state.selection.map(valid).filter(Boolean).map(w=>w.key));if(this.destroyed||current.signal.aborted)return;const focused=valid(state.focus);if(focused&&this.indexOf(focused)>=0)this.setFocus(focused,false);const anchor=state.anchor&&valid(state.anchor.path);if(anchor)this.restoreScrollAnchor({id:anchor.key,offset:state.anchor.offset})}});function
|
|
479
|
-
|
|
527
|
+
}},applyStatePosition(state,current){if(!state.version)return;const valid=path=>{const w=path&&this.keyMap.get(path[path.length-1]);return w&&JSON.stringify(this.pathOf(w))===JSON.stringify(path)?w:null};this.setSelection(state.selection.map(valid).filter(Boolean).map(w=>w.key));if(this.destroyed||current.signal.aborted)return;const focused=valid(state.focus);if(focused&&this.indexOf(focused)>=0)this.setFocus(focused,false);const anchor=state.anchor&&valid(state.anchor.path);if(anchor)this.restoreScrollAnchor({id:anchor.key,offset:state.anchor.offset})}});const PLUGINS=new Map();function plugin(value){const item=Object.freeze({...value}),fn=handler=>typeof handler==="function";const entry=menu=>action=>action&&typeof action.label==="string"&&
|
|
528
|
+
(fn(action.run)&&action.render===undefined||menu&&fn(action.render)&&action.run===undefined);if(!value||!partName(item.name)||Object.hasOwn(PARTS,item.name)||[item.init,item.destroy].some(hook=>hook!==undefined&&!fn(hook))||
|
|
529
|
+
["requires","views","toolbarItems","menuItems","exportFormats","recordActions"].some(key=>item[key]!==undefined&&!Array.isArray(item[key]))||
|
|
530
|
+
["cellRenderers","columnTypes","editors","events","aggregates","detailRenderers","drawerSections"].some(key=>item[key]!==undefined&&Object(item[key])!==item[key])||
|
|
531
|
+
!(item.requires||[]).every(partName)||!(item.views||[]).every(kind=>kind==="tree"||kind==="grid")||
|
|
532
|
+
![...Object.values(item.cellRenderers||{}),...Object.values(item.events||{}),...Object.values(item.detailRenderers||{}),...Object.values(item.drawerSections||{})].every(fn)||
|
|
533
|
+
!(item.toolbarItems||[]).every(entry())||!(item.recordActions||[]).every(entry())||!(item.menuItems||[]).every(entry(true)))
|
|
534
|
+
throw new Error("CanopyX: invalid plugin '"+item.name+"'.");item.exportFormats?.forEach(exportFormat);return item}
|
|
535
|
+
function use(value){const item=plugin(value);if(PLUGINS.has(item.name))throw new Error("CanopyX: plugin '"+item.name+"' is already registered.");PLUGINS.set(item.name,item);const formats=item.exportFormats?.map(registerExportFormat)||[];return()=>{if(PLUGINS.get(item.name)===item){PLUGINS.delete(item.name);formats.forEach(dispose=>dispose())}}}
|
|
536
|
+
function viewPlugins(options,kind){const byName=new Map(PLUGINS),ordered=[],visiting=new Set(),applies=item=>!item.views||item.views.includes(kind);for(const value of options.plugins||[]){const item=plugin(value);byName.delete(item.name);byName.set(item.name,item)}
|
|
537
|
+
const visit=item=>{if(ordered.includes(item)||!applies(item))return;if(visiting.has(item))throw new Error("CanopyX: plugin '"+item.name+"' requires itself.");visiting.add(item);for(const name of item.requires||[]){const required=byName.get(name);if(required&&applies(required))visit(required);else if(!Object.hasOwn(PARTS,name))throw new Error("CanopyX: plugin '"+item.name+"' requires '"+name+"', which is neither a plugin nor a part.")}
|
|
538
|
+
ordered.push(item)};byName.forEach(visit);return ordered}
|
|
539
|
+
function startPlugins(grid,api){if(!grid.plugins.length)return grid.ready;const signal=grid.lifetime.signal,failed=new Set(),byName=new Map(grid.plugins.map(item=>[item.name,item]));const blocked=item=>failed.has(item.name)||(item.requires||[]).some(name=>byName.has(name)&&blocked(byName.get(name)));const contexts=grid.plugins.map(item=>{const context=Object.freeze({get api(){return api()},element:grid.host,view:grid.view,kind:grid.kind,labels:grid.labels,signal,part:name=>grid.requirePart(name)});for(const[name,handler]of Object.entries(item.events||{}))grid.host.addEventListener("canopyx:"+name,event=>{if(!blocked(item))handler(event,context)},{signal});if(item.destroy)signal.addEventListener("abort",()=>item.destroy(context),{once:true});return context});const started=Promise.resolve().then(async()=>{for(const[index,item]of grid.plugins.entries()){if(signal.aborted)return;try{for(const name of item.requires||[]){if(failed.has(name))throw new Error("CanopyX: plugin '"+item.name+"' requires failed plugin '"+name+"'.");if(!grid.plugins.some(other=>other.name===name))await grid.requirePart(name)}
|
|
540
|
+
if(!signal.aborted){const context=contexts[index];await item.init?.(context)}}catch(error){failed.add(item.name);grid.emit("error",{error})}}});return Promise.all([grid.ready,started]).then(([result])=>result)}
|
|
541
|
+
function pluginItems(grid,key){return grid.plugins.flatMap(item=>item[key]||[])}
|
|
480
542
|
Object.assign(TreeGrid.prototype,{pathOf(w){const path=[];for(let node=w;node&&node!==this.root;node=node.parent){path.push(node.key)}
|
|
481
543
|
return path.reverse()},getExpandedPaths(){const out=[];const pending=this.modelChildren(this.root).slice().reverse();while(pending.length){const child=pending.pop();if(child.expanded&&this.expandable(child)){out.push(this.pathOf(child));const children=this.modelChildren(child);for(let i=children.length-1;i>=0;i--)pending.push(children[i])}}
|
|
482
544
|
return out},async setState(state){if(this.destroyed)return false;state=this.readState(state);if(!state)return false;if(this.restoreController)this.restoreController.abort();const current=this.restoreController=new AbortController();this.restoreGeneration=(this.restoreGeneration||0)+ 1;this.pendingRestorationToggles=null;this.restoring=true;clearTimeout(this.saveTimer);const before=new Map();this.keyMap.forEach((w)=>before.set(w,[w.expanded,w.autoExpand,w.filterCollapsed]));let restored=0;let fallback=false;const release=this.holdLayout();try{if(this.queryAware&&state.version&&!await this.setQuery({version:1,sort:state.sort,filters:state.filters},{signal:current.signal},current))return false;if(this.destroyed||current.signal.aborted)return false;this.applyStatePresentation(state);if(state.version)await this.loadStatePaths(state,current);if(this.destroyed||current.signal.aborted)return false;this.keyMap.forEach((w)=>{w.expanded=false;w.autoExpand=false});this.relayout();const paths=state.expanded.slice().sort((a,b)=>a.length-b.length);for(const path of paths){if(this.destroyed||current.signal.aborted)break;try{const node=await this.expandPath(path,{select:false,focus:false,reveal:false,expandLast:true,signal:current.signal});const wrapper=node&&this.keyMap.get(node.id);if(wrapper&&wrapper.expanded)restored++}catch(e){const last=this.keyMap.get(path[path.length-1]);if((last&&last.expanded)||path.some((id)=>{const node=this.keyMap.get(id);return node&&node.expanded&&node.error}))restored++}}
|
|
@@ -490,10 +552,7 @@ catch(error){if(!failure)failure=error}}
|
|
|
490
552
|
if(failure&&generation===(this.restoreGeneration||0))throw failure})();this.pendingToggleFlush=run;try{await run}finally{if(this.pendingToggleFlush===run)this.pendingToggleFlush=null;this.replayingToggles=false;this.cacheDirty=true;this.scheduleRender();if(this.pendingRestorationToggles===pending&&!pending.size){this.pendingRestorationToggles=null;if(!this.destroyed)this.stateChanged()}}},stateKey(){return this.view.stateKey==null?null:"canopyx:"+this.view.stateKey},stateChanged(){const key=this.stateKey();if(this.destroyed||!key||this.restoring||this.initializing||this.replayingToggles){return}
|
|
491
553
|
clearTimeout(this.saveTimer);this.saveTimer=setTimeout(()=>{if(this.destroyed||this.restoring||this.initializing||this.replayingToggles)return;try{sessionStorage.setItem(key,JSON.stringify(this.getState()))}catch(e){}},250)},async restoreState(){const key=this.stateKey();if(!key){return}
|
|
492
554
|
let state=null;try{state=JSON.parse(sessionStorage.getItem(key)||"null")}catch(e){state=null}
|
|
493
|
-
await this.setState(state)},visibleRows(){return this.rows.filter((w)=>!w.status).map((w)=>({node:w.h,level:w.level,path:this.pathOf(w)}))},toOutline(){
|
|
494
|
-
const pad=" style=\"padding-left:" +((w.level-1)*14+4)+ "px\"";if(w.h.detail){const detail=w.h.detail;const content=h("div");this.renderDetail(w.h,content,true);out.push("<tr><td"+pad+" colspan=\""+cols.length+"\">"+content.innerHTML+"</td></tr>");return}
|
|
495
|
-
out.push("<tr>");cols.forEach((column,i)=>{const index=this.columns.indexOf(column);const value=this.cellOf(w.h,column,index);const text=cellText(value,column,this.fmt);const content=h("span");const slot={};try{if(!column.render||column.render(w.h,content,new HostCellContext(slot,column.id,w.level,w.expanded,index))!==true){const badges=(i===0?w.h.badges||[]:[]).concat(isCellObject(value)?value.badges||[]:[]);content.appendChild(detailCell("span",Object.assign({},isCellObject(value)?value:{},{text,badges}),this.fmt))}
|
|
496
|
-
out.push("<td" +(i===0?pad:"")+ ">"+content.outerHTML+"</td>")}finally{disposeHostSlot(slot)}});out.push("</tr>")});out.push("</tbody></table>");return out.join("")},invalidateChildren(w){const pending=this.modelChildren(w).slice();let removedSelection=null;while(pending.length){const child=pending.pop();for(const descendant of this.modelChildren(child))pending.push(descendant);const removed=this.retireWrapper(child);if(!removedSelection)removedSelection=removed}
|
|
555
|
+
await this.setState(state)},visibleRows(){return this.rows.filter((w)=>!w.status).map((w)=>({node:w.h,level:w.level,path:this.pathOf(w)}))},toOutline(){return this.outlineElement()?.outerHTML||""},outlineElement(){return this.outlineTable?this.outlineTable():null},invalidateChildren(w){const pending=this.modelChildren(w).slice();let removedSelection=null;while(pending.length){const child=pending.pop();for(const descendant of this.modelChildren(child))pending.push(descendant);const removed=this.retireWrapper(child);if(!removedSelection)removedSelection=removed}
|
|
497
556
|
w.children=null;w.inlineCache=null;w.inlineOrder=null;if(this.inactiveCache)this.inactiveCache.delete(w);w.next=null;w.error=null;w.expanded=false;return removedSelection},applyCounts(doc,fromRefresh=false){if(this.destroyed)return;checkDocument(doc,"counts");if(this.queryAware)snapshotRevision(doc,this.datasetRevision);let removedSelection=null;let removed=false;doc.items.forEach((item)=>{const w=this.keyMap.get(item.id);if(w&&w.inline&&item.childCount!=null&&item.childCount!==w.h.children.length){throw new Error("CanopyX: inline childCount must equal children length.")}});if(!fromRefresh){for(const id of new Set(doc.items.map((item)=>item.id))){const w=this.keyMap.get(id);if(w)w.countRevision=(w.countRevision||0)+ 1}}
|
|
498
557
|
doc.items.forEach((item)=>{const w=this.keyMap.get(item.id);if(!w){return}
|
|
499
558
|
if(item.cells)w._v=0;if(typeof item.childCount==="number"){w.h.childCount=item.childCount;w.total=item.childCount;if(!w.inline){w.h.hasChildren=item.childCount>0;const loaded=w.children?w.children.length:0;if(w.loading)w.pendingChildCount=item.childCount;else if(item.childCount<loaded||(item.childCount>loaded&&w.children&&!w.next)){removedSelection=this.invalidateChildren(w)||removedSelection;removed=true}
|
|
@@ -505,23 +564,38 @@ w.content=nodeContent(w.h)});this.inlineOrderVersion=(this.inlineOrderVersion||0
|
|
|
505
564
|
const wanted=Array.from(new Set(ids||this.rows.filter((w)=>!w.status).map((w)=>w.key)));const mine=new Map();for(const id of wanted){const w=this.keyMap.get(id);if(w){w.countRevision=(w.countRevision||0)+ 1;mine.set(id,{w,revision:w.countRevision})}}
|
|
506
565
|
const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
|
|
507
566
|
finally{navigation.dispose()}
|
|
508
|
-
if(navigation.cancelled())return null;if(this.queryAware)snapshotRevision(doc,this.datasetRevision);checkDocument(doc,"counts");this.applyCounts(Object.assign({},doc,{items:doc.items.filter(item=>{const captured=mine.get(item.id);return captured&&this.keyMap.get(item.id)===captured.w&&captured.w.countRevision===captured.revision})}),true);return doc}});
|
|
509
|
-
},currentLink(){if(this.opts.parseLink){return(global.location.hash||"").replace(/^#/, "");
|
|
510
|
-
}
|
|
511
|
-
const prefix=this.deepLinkName()+ "=";const param=this.hashParams().find((p)=>p.startsWith(prefix));return param?param.slice(prefix.length):null},readLocation(){if(this.destroyed)return Promise.resolve();if(this.initializing){if(this.linkOpening&&this.currentLink()!==this.link){this.linkOpening.abort();this.link=null;// Deferred reopening must not reuse the canceled link if the address changes back.
|
|
567
|
+
if(navigation.cancelled())return null;if(this.queryAware)snapshotRevision(doc,this.datasetRevision);checkDocument(doc,"counts");this.applyCounts(Object.assign({},doc,{items:doc.items.filter(item=>{const captured=mine.get(item.id);return captured&&this.keyMap.get(item.id)===captured.w&&captured.w.countRevision===captured.revision})}),true);return doc}});const LINKS=definePart(TreeGrid,"links","deep links",()=>({TreeGrid,navigationScope}));for(const name of["openDeepLink","writeDeepLink"])TreeGrid.prototype[name]=function(...args){return this.view.deepLink?loadPart(LINKS).then(()=>this[name](...args),error=>this.emit("error",{error})):Promise.resolve()};TreeGrid.prototype.readLocation=function(){if(this.destroyed)return Promise.resolve();if(this.initializing){if(this.linkOpening&&this.currentLink()!==this.link){this.linkOpening.abort();this.link=null;// Deferred reopening must not reuse the canceled link if the address changes back.
|
|
512
568
|
}
|
|
513
569
|
this.linkChanged=this.dataLifetime.signal;return this.ready.then(()=>undefined,()=>undefined)}
|
|
514
|
-
return this.openDeepLink()},
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
570
|
+
return this.openDeepLink()};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{linkFilteredBy:"Filtered by {source}: {values}",linkRemoveFilter:"Remove the filter from {source}: {values}",linkFiltered:"Filtered by {source}: {count} rows",linkHighlighted:"{count} rows highlighted from {source}",linkHighlightedRows:"{count} rows highlighted",linkCleared:"Linked filters removed"}));const LINK_IMPL={TreeGrid,queryOwn:Symbol("own conditions")},LINK_PART=definePart(TreeGrid,"link","linked views",()=>({LINK_IMPL,h,setText,filterChipContent,fill,numberFormat,cellValue,setAttr,cellMatches}));const LINK_METHODS=["setExternalFilter","clearExternalFilters","setHighlight","clearHighlight","startLinkEvents"];function linkMethods(View){for(const name of LINK_METHODS)(View.prototype[name]=function(...args){return this.destroyed?Promise.resolve(/Highlight/.test(name) ? 0 : name === "startLinkEvents" ? undefined : false) : loadPart(LINK_PART).then(() => this[name](...args));
|
|
571
|
+
}).linkStub=true;View.prototype.getExternalFilters=function(){return(this.linkCommitted||[]).map(item=>JSON.parse(JSON.stringify(item)))};View.prototype.getHighlight=function(){return Array.from(this.linkHighlight||[])};View.prototype.linkStart=function(){if(this.opts.linkEvents===true)return loadPart(LINK_PART).then(()=>this.linkWatch()).catch(error=>{this.emit("error",{error});throw error})}}
|
|
572
|
+
linkMethods(TreeGrid);function linkGridPart(View){(Object.hasOwn(View.prototype,"parts")?View.prototype.parts:View.prototype.parts={}).link=LINK_PART}
|
|
573
|
+
function linkReady(grid,ready){return Promise.all([ready,grid.linkStart()]).then(([result])=>result)}
|
|
574
|
+
function linkApi(grid){const api={};for(const name of LINK_METHODS.concat("getExternalFilters","getHighlight"))api[name]=(...args)=>grid[name](...args);return api}
|
|
575
|
+
const RECORD_IMPL={},RECORD_PART=definePart(TreeGrid,"record-panel","record panel",()=>({RECORD_IMPL,TreeGrid,h,setText,fill,cellText,cellValue,isCellObject,formatValue,toneOf,applyTone,toneColors,badgeElement,loadSidecar,requireIdentifier,validateGridRows,PROTOCOL,foldText,createTreeGrid,svgIcon,gridExportField}));function loadRecordPanel(){return loadPart(RECORD_PART)}
|
|
576
|
+
const RECORD_STRUCTURE=["items","fields","records","events","changes","ref"];TreeGrid.prototype.recordNeeded=function(node){return!!this.view.record||!!node&&(!!node.fields||Object.values(node.cells||{}).some(cell=>isCellObject(cell)&&RECORD_STRUCTURE.some(name=>cell[name]!==undefined)))};TreeGrid.prototype.openRecord=function(id,options){return loadRecordPanel().then(()=>RECORD_IMPL.open(this,id,options))};function recordStart(grid){if(grid.view.record?.deepLink)loadRecordPanel().then(()=>RECORD_IMPL.start(grid),error=>grid.emit("error",{error}))}
|
|
577
|
+
DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{matchesHighlight:"Highlight matches",matchesCount:"[one]{count} match[other]{count} matches",matchesPosition:"{current} of {count}",matchesNone:"No matches",matchesLoaded:"[one]{count} match in loaded rows[other]{count} matches in loaded rows",matchesPrevious:"Previous match",matchesNext:"Next match",matchesAnnounce:"Match {current} of {count}, {column}",layoutMenu:"Layout",layoutFit:"Fit to width",layoutScroll:"All columns, scroll sideways",layoutCards:"Cards",layoutDensity:"Density",layoutCompact:"Compact",layoutNormal:"Normal",layoutComfortable:"Comfortable",layoutChanged:"Layout: {mode}, {density}",layoutSort:"Sort by"}));const LAYOUT_MODES=["fit","scroll","cards"],LAYOUT_DENSITIES=["compact","default","comfortable"];const DENSITY_STEP={tree:{compact:-4,default:0,comfortable:8},grid:{compact:-6,default:0,comfortable:8}};const VIEW_MODES={};const HIGHLIGHT_KEYS=["enabled","search","filters","matchCase","ignoreAccents","minLength","filterTones"];function readSearchHighlight(value,columns){if(value===undefined||value===false)return null;const options=value===true?{}:value,tone=name=>typeof name==="string"&&/^[a-z][a-z0-9-]{0,31}$/.test(name) && !name.endsWith("-bg");
|
|
578
|
+
if(!options||typeof options!=="object"||Array.isArray(options)||Object.keys(options).some(key=>!HIGHLIGHT_KEYS.includes(key))||
|
|
579
|
+
HIGHLIGHT_KEYS.slice(0,5).some(key=>options[key]!==undefined&&typeof options[key]!=="boolean")||
|
|
580
|
+
options.minLength!==undefined&&(!Number.isInteger(options.minLength)||options.minLength<1||options.minLength>32)||
|
|
581
|
+
options.filterTones!==undefined&&(!options.filterTones||typeof options.filterTones!=="object"||Array.isArray(options.filterTones)||
|
|
582
|
+
Object.entries(options.filterTones).some(([id,name])=>!columns.some(column=>column.id===id)||!tone(name))))
|
|
583
|
+
throw new Error("CanopyX: invalid search highlight options.");return Object.freeze({enabled:options.enabled!==false,search:options.search!==false,filters:options.filters!==false,matchCase:!!options.matchCase,ignoreAccents:!!options.ignoreAccents,minLength:options.minLength||1,filterTones:Object.freeze({...options.filterTones})})}
|
|
584
|
+
function readLayout(value,kind,density){if(value===undefined)return null;const all=kind==="tree"?LAYOUT_MODES.slice(0,2):LAYOUT_MODES;if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!["mode","modes","density"].includes(key))||
|
|
585
|
+
value.modes!==undefined&&(!Array.isArray(value.modes)||!value.modes.length||new Set(value.modes).size!==value.modes.length||
|
|
586
|
+
value.modes.some(mode=>!all.includes(mode)))||
|
|
587
|
+
value.mode!==undefined&&!(value.modes||all).includes(value.mode)||value.density!==undefined&&!LAYOUT_DENSITIES.includes(value.density))
|
|
588
|
+
throw new Error("CanopyX: invalid layout options.");const modes=Object.freeze((value.modes||all).slice());return Object.freeze({mode:value.mode||(modes.includes("fit")?"fit":modes[0]),modes,density:value.density||density||"default"})}
|
|
589
|
+
function readModeState(view,state){const result={};if(state.layout!==undefined){const layout=state.layout,options=view.layoutOptions;if(!layout||typeof layout!=="object"||!LAYOUT_MODES.includes(layout.mode)||!LAYOUT_DENSITIES.includes(layout.density))
|
|
590
|
+
throw new Error("CanopyX: invalid saved layout.");if(options)result.layout={mode:options.modes.includes(layout.mode)?layout.mode:options.mode,density:layout.density}}
|
|
591
|
+
if(state.highlight!==undefined){if(typeof state.highlight!=="boolean")throw new Error("CanopyX: invalid saved highlight choice.");if(view.highlightOptions)result.highlight=state.highlight}
|
|
592
|
+
return result}
|
|
593
|
+
function densityRowHeight(view,density){const steps=DENSITY_STEP[view.kind];return Math.max(20,Math.min(64,view.baseRowH+steps[density]- steps[view.declaredDensity]))}
|
|
594
|
+
const VIEW_MODE_METHODS={prepareViewModes(){const view=this.view,columns=this.columns||view.columns;this.highlightOptions=readSearchHighlight(view.searchHighlight,columns);this.declaredDensity=this.kind==="grid"?view.density||"default":"default";this.layoutOptions=readLayout(view.layout,this.kind,this.declaredDensity);this.baseRowH=this.rowH;this.adoptModes({layout:this.layoutOptions||{mode:"fit",density:this.declaredDensity},highlight:!!this.highlightOptions?.enabled})},adoptModes(choice){if(choice.layout){this.layoutMode=choice.layout.mode;this.density=choice.layout.density}
|
|
595
|
+
if(choice.highlight!==undefined)this.highlightOn=choice.highlight;this.rowH=densityRowHeight(this,this.density);if(this.layoutMode==="fit")this.host.removeAttribute("data-cx-layout");else this.host.setAttribute("data-cx-layout",this.layoutMode)},modeState(state){if(this.layoutOptions)state.layout={mode:this.layoutMode,density:this.density};if(this.highlightOptions)state.highlight=!!this.highlightOn;return state},applyModeState(state){if(state.layout&&(state.layout.mode!==this.layoutMode||state.layout.density!==this.density))
|
|
596
|
+
this.setLayout(state.layout).catch(error=>this.emit("error",{error}));if(state.highlight!==undefined&&state.highlight!==!!this.highlightOn)
|
|
597
|
+
this.setSearchHighlight(state.highlight).catch(error=>this.emit("error",{error}))},startViewModes(){const parts=[this.highlightOptions&&"highlight",this.layoutOptions&&"layouts"].filter(Boolean);return Promise.all(parts.map(name=>this.requirePart(name).then(()=>this.destroyed||VIEW_MODES[name+"Start"](this))
|
|
598
|
+
.catch(error=>this.emit("error",{error}))))},getLayout(){return Object.freeze({mode:this.layoutMode,density:this.density,modes:this.layoutOptions?this.layoutOptions.modes:Object.freeze(["fit"])})},setLayout(change){if(!this.layoutOptions)return Promise.reject(new Error("CanopyX: this view declares no layout modes."));return this.requirePart("layouts").then(()=>VIEW_MODES.setLayout(this,change))},getMatches(){return VIEW_MODES.matches?VIEW_MODES.matches(this):Object.freeze({enabled:!!this.highlightOn,total:0,current:0,complete:true})},setSearchHighlight(enabled){if(typeof enabled!=="boolean")return Promise.reject(new Error("CanopyX: the highlight switch is a boolean."));if(!this.highlightOptions)return Promise.reject(new Error("CanopyX: this view declares no search highlighting."));return this.requirePart("highlight").then(()=>VIEW_MODES.setHighlight(this,enabled))},nextMatch(options){return this.stepMatch(1,options)},previousMatch(options){return this.stepMatch(-1,options)},stepMatch(direction,options){if(!this.highlightOptions)return Promise.reject(new Error("CanopyX: this view declares no search highlighting."));return this.requirePart("highlight").then(()=>VIEW_MODES.step(this,direction,options?.focus!==false))}};Object.assign(TreeGrid.prototype,VIEW_MODE_METHODS);const viewModeContext=()=>({VIEW_MODES,TreeGrid,h,setText,fill,numberFormat,foldText,cellText,cellValue,isCellObject,setAttr,toneOf,watchFlyout,densityRowHeight,MAX_PROTOCOL_INTEGER,LAYOUT_DENSITIES,plural,svgIcon});definePart(TreeGrid,"highlight","search highlighting",viewModeContext);definePart(TreeGrid,"layouts","layout modes",viewModeContext);const MODES_TOOLBAR=TreeGrid.prototype.buildToolbar;TreeGrid.prototype.buildToolbar=function(){MODES_TOOLBAR.call(this);if(this.highlightOptions||this.layoutOptions)this.modesReady=Promise.resolve(this.toolsReady).then(()=>this.destroyed||this.startViewModes());this.lifetime.signal.addEventListener("abort",()=>this.host.removeAttribute("data-cx-layout"),{once:true})};Object.assign(TreeGrid.prototype,{destroy(){if(this.destroyed)return;this.destroyed=true;for(const row of[...this.rendered.values(),...this.pool])disposeRowHostCells(row);this.lifetime.abort();this.dataLifetime.abort();if(this.queryController)this.queryController.abort();if(this.restoreController)this.restoreController.abort();if(this.cancelResize)this.cancelResize();clearTimeout(this.saveTimer);clearTimeout(this.layoutTimer);if(this.cancelSearch)this.cancelSearch();if(this.disposeSearchFlyout)this.disposeSearchFlyout();cancelAnimationFrame(this.rafId);this.resizeObserver.disconnect();if(this.detailObserver)this.detailObserver.disconnect();this.grid.removeEventListener("scroll",this.onScroll);this.grid.removeEventListener("wheel",this.onScaledWheel);this.grid.removeEventListener("touchstart",this.onScaledTouchStart);this.grid.removeEventListener("touchmove",this.onScaledTouchMove);this.grid.removeEventListener("touchend",this.onScaledTouchEnd);this.grid.removeEventListener("touchcancel",this.onScaledTouchEnd);global.removeEventListener("beforeprint",this.onPrint);global.removeEventListener("hashchange",this.onHash);global.removeEventListener("popstate",this.onHash);if(this.linkOpening)this.linkOpening.abort();[this.toolbar,this.grid,this.live,this.outline].forEach(element=>element.remove());this.host.classList.remove("cx-pin-tree");this.host.removeAttribute("data-cx-ready");this.host.removeAttribute("data-canopyx-mounted");this.keyMap.clear();this.selected.clear();this.rendered.clear();this.pool.length=0;if(this.inactiveCache)this.inactiveCache.clear();if(this.inlineRetained)this.inlineRetained.clear();this.rows=[];this.tops=[0];this.total=0;this.root.children=null;this.root.inlineCache=null;this.root.inlineOrder=null;this.focused=null;this.anchor=null;this.source=null;this.touchScroll=null;this.opts={};for(const column of this.columns)column.render=null;this.filter=null;this.declaredFilter=null;this.pendingRestorationToggles=null;this.scrolledCell=null;this.focusHandoff=null;this.cancelResize=null;this.cancelSearch=null;this.disposeSearchFlyout=null;this.canvas.replaceChildren();this.outline.replaceChildren()}});function checkIntentPath(path){if(!Array.isArray(path)||!path.length)throw new Error("CanopyX: checking needs a complete node-ID path.");const seen=new Set();return path.map(id=>{requireIdentifier(id,"checking path ID");if(seen.has(id))throw new Error("CanopyX: a checking path cannot repeat an ID.");seen.add(id);return id})}
|
|
525
599
|
function checkBranch(){return{value:null,mask:0,children:new Map()}}
|
|
526
600
|
class CheckIntents{constructor(items,mode){if(!Array.isArray(items))throw new Error("CanopyX: checkIntents must be an array.");this.mode=mode;this.root=checkBranch();for(const item of items){if(!item||typeof item.checked!=="boolean")throw new Error("CanopyX: a checking intent needs a boolean checked value.");const path=checkIntentPath(item.path);let branch=this.root;for(const id of path){if(!branch.children.has(id))branch.children.set(id,checkBranch());branch=branch.children.get(id)}
|
|
527
601
|
if(branch.value!==null)throw new Error("CanopyX: a checking path cannot have duplicate intents.");branch.value=item.checked}
|
|
@@ -564,8 +638,8 @@ w&&!w.status&&!w.h.detail&&
|
|
|
564
638
|
super.onKey(event)}
|
|
565
639
|
getState(){const state=super.getState();if(this.checkMode){state.checkboxes=this.checkMode;state.checkIntents=this.checks.snapshot()}
|
|
566
640
|
return state}
|
|
567
|
-
|
|
568
|
-
({path:this.pathOf(w),label:w.h.label,detail:!!w.h.detail}));const
|
|
641
|
+
outlineElement(){const model=this.checks,items=this.rows.filter(w=>!w.status).map(w=>
|
|
642
|
+
({path:this.pathOf(w),label:w.h.label,detail:!!w.h.detail}));const table=super.outlineElement();if(!this.checkMode||!table)return table;const rows=table.tBodies[0].rows;items.forEach((item,index)=>{if(item.detail)return;const value=model.state(item.path),marker=h("span");marker.setAttribute("role","checkbox");marker.setAttribute("aria-readonly","true");marker.setAttribute("aria-label",item.label);marker.setAttribute("aria-checked",value==="mixed"?"mixed":String(value));marker.style.marginRight="6px";marker.textContent=value==="mixed"?"▣ ":value?"☑ ":"☐ ";rows[index].cells[0].prepend(marker)});return table}
|
|
569
643
|
readState(state){const result=super.readState(state);if(!result||!result.version)return result;if(state.checkboxes!==undefined&&state.checkboxes!==this.checkMode)return null;try{if(this.checkMode){result.checkboxes=this.checkMode;result.checkIntents=new CheckIntents(state.checkIntents===undefined?[]:state.checkIntents,this.checkMode).snapshot()}else if(state.checkIntents!==undefined&&(!Array.isArray(state.checkIntents)||state.checkIntents.length))return null;return result}catch(error){return null}}
|
|
570
644
|
async setState(state){const parsed=this.readState(state);if(!parsed||this.destroyed)return false;const revision=++this.checkRevision;if(parsed.checkIntents&&!this.voteCheck(new CheckIntents(parsed.checkIntents,this.checkMode),null,"restore",revision))return false;const operation=super.setState(parsed);this.paintCheckedRows();try{return await operation}
|
|
571
645
|
finally{this.paintCheckedRows()}}
|
|
@@ -601,10 +675,10 @@ cancelInteraction(){const operation=this.pendingInteraction;if(!operation)return
|
|
|
601
675
|
abortInteractionController(controller){if(!controller||controller.signal.aborted)return;if(this.interactionReplacementDepth){if(!this.interactionAbortQueue)this.interactionAbortQueue=new Set();this.interactionAbortQueue.add(controller)}else controller.abort()}
|
|
602
676
|
withInteractionReplacement(work){this.interactionReplacementDepth=(this.interactionReplacementDepth||0)+ 1;try{return work()}
|
|
603
677
|
finally{this.interactionReplacementDepth--;if(!this.interactionReplacementDepth){const controllers=this.interactionAbortQueue;this.interactionAbortQueue=null;if(controllers)for(const controller of controllers)controller.abort()}}}
|
|
604
|
-
destroy(){this.interactionClosing=true;this.cancelInteraction();super.destroy();this.interactions=null;if(this.interactionPins)this.interactionPins.clear()}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{actions:"Actions",editValue:"Edit {column}",saveEdit:"Save",cancelEdit:"Cancel",invalidEdit:"Enter a valid {column} value.",interactionPending:"Waiting for the host…",interactionRejected:"The host declined the operation.",interactionFailed:"Operation failed: {error}",markMove:"Move this node",moveHere:"Move here",moveBefore:"Move before",moveAfter:"Move after",cancelMove:"Cancel move"}));TreeGrid=class extends TreeGrid{build(){super.build();this.interactionUiLifetime=new AbortController();const signal=this.interactionUiLifetime.signal;this.grid.addEventListener("scroll",()=>this.closeInteractionPanel(false),{signal,passive:true});global.addEventListener("resize",()=>this.closeInteractionPanel(),{signal});document.addEventListener("pointerdown",event=>{if(this.interactionPanel&&!this.interactionPanel.contains(event.target))this.closeInteractionPanel(false)},{signal});this.grid.addEventListener("contextmenu",event=>{const row=event.target.closest(".cx-row");if(row&&row._w&&!hostControl(event.target)&&this.openActionMenu(row._w,row))event.preventDefault()},{signal})}
|
|
678
|
+
destroy(){this.interactionClosing=true;this.cancelInteraction();super.destroy();this.interactions=null;if(this.interactionPins)this.interactionPins.clear()}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{actions:"Actions",editValue:"Edit {column}",saveEdit:"Save",cancelEdit:"Cancel",invalidEdit:"Enter a valid {column} value.",interactionPending:"Waiting for the host…",interactionRejected:"The host declined the operation.",interactionFailed:"Operation failed: {error}",markMove:"Move this node",moveHere:"Move here",moveBefore:"Move before",moveAfter:"Move after",cancelMove:"Cancel move"}));TreeGrid=class extends TreeGrid{build(){super.build();this.interactionUiLifetime=new AbortController();const signal=this.interactionUiLifetime.signal;this.grid.addEventListener("scroll",()=>{const panel=this.interactionPanel;if(panel&&(panel._cxScrollTop!==this.grid.scrollTop||panel._cxScrollLeft!==this.grid.scrollLeft))this.closeInteractionPanel(false)},{signal,passive:true});global.addEventListener("resize",()=>this.closeInteractionPanel(),{signal});document.addEventListener("pointerdown",event=>{if(this.interactionPanel&&!this.interactionPanel.contains(event.target))this.closeInteractionPanel(false)},{signal});this.grid.addEventListener("contextmenu",event=>{const row=event.target.closest(".cx-row");if(row&&row._w&&!hostControl(event.target)&&this.openActionMenu(row._w,row))event.preventDefault()},{signal})}
|
|
605
679
|
interactionApi(){return Object.assign(super.interactionApi(),{beginEdit:(id,column)=>this.beginEdit(id,column),openActions:id=>{const w=this.interactionNode(id);return!!w&&this.openActionMenu(w,this.rendered.get(w))}})}
|
|
606
680
|
openInteractionPanel(w,anchor,role,label){const revision=(this.uiWorkRevision||0)+(this.interactionPanel?1:0);this.closeInteractionPanel(false);if(this.destroyed||this.interactionClosing||this.interactionPanel||(this.uiWorkRevision||0)!==revision)return null;this.render();anchor=this.rendered.get(w);if(this.destroyed||this.interactionClosing||this.interactionPanel||(this.uiWorkRevision||0)!==revision||!anchor||!anchor.isConnected)return null;const panel=this.interactionPanel=h("div","cx-interaction-panel");panel.setAttribute("role",role);panel.setAttribute("aria-label",label);this.panelNode=w;this.host.appendChild(panel);panel.addEventListener("pointerdown",()=>{panel._cxPointerFocus=panel.contains(document.activeElement)});return panel}
|
|
607
|
-
placeInteractionPanel(anchor){const panel=this.interactionPanel;if(!panel)return;const rect=anchor.getBoundingClientRect(),bounds=panel.getBoundingClientRect();panel.style.left=Math.max(8,Math.min(rect.left,global.innerWidth-bounds.width-8))+ "px";const below=global.innerHeight-rect.bottom-8,above=rect.top-8;const top=below>=Math.min(bounds.height,120)||below>=above?rect.bottom:rect.top-bounds.height;panel.style.top=Math.max(8,Math.min(top,global.innerHeight-bounds.height-8))+ "px"}
|
|
681
|
+
placeInteractionPanel(anchor){const panel=this.interactionPanel;if(!panel)return;const rect=anchor.getBoundingClientRect(),bounds=panel.getBoundingClientRect();panel.style.left=Math.max(8,Math.min(rect.left,global.innerWidth-bounds.width-8))+ "px";const below=global.innerHeight-rect.bottom-8,above=rect.top-8;const top=below>=Math.min(bounds.height,120)||below>=above?rect.bottom:rect.top-bounds.height;panel.style.top=Math.max(8,Math.min(top,global.innerHeight-bounds.height-8))+ "px";panel._cxScrollTop=this.grid.scrollTop;panel._cxScrollLeft=this.grid.scrollLeft}
|
|
608
682
|
closeInteractionPanel(restoreFocus=true){const panel=this.interactionPanel,controller=this.editorController,w=this.panelNode;if(!panel)return null;const active=document.activeElement;const owned=panel.contains(active)||panel._cxPointerFocus&&(active===document.body||active===document.documentElement);if(controller&&this.uiPendingOperation&&this.uiPendingOperation.controller===controller){this.uiPendingOperation=null;setText(this.live,"")}
|
|
609
683
|
this.uiWorkRevision=(this.uiWorkRevision||0)+ 1;const focus=owned?{node:w,revision:this.uiWorkRevision}:null;this.interactionPanel=null;this.panelNode=null;this.editorController=null;panel.remove();this.abortInteractionController(controller);if(restoreFocus)this.restoreInteractionFocus(focus);return focus}
|
|
610
684
|
restoreInteractionFocus(focus){if(!focus||this.destroyed||this.interactionClosing||this.interactionPanel||focus.revision!==this.uiWorkRevision)return;const active=document.activeElement;if(active&&active!==document.body&&active!==document.documentElement&&active.isConnected)return;const visible=w=>w&&!w._invalidated&&this.keyMap.get(w.key)===w&&this.indexOf(w)>=0;const target=visible(focus.node)?focus.node:visible(this.focused)?this.focused:this.rows.find(visible);if(target)this.setFocus(target,true);else{this.grid.tabIndex=0;this.grid.focus({preventScroll:true})}}
|
|
@@ -652,13 +726,18 @@ this.dropRow=null}
|
|
|
652
726
|
dropPlacement(event,row){const bounds=row.getBoundingClientRect(),fraction=(event.clientY-bounds.top)/ bounds.height;
|
|
653
727
|
return fraction<0.25?"before":fraction>0.75?"after":"inside"}
|
|
654
728
|
destroy(){this.clearDropRow();this.dragLease=null;super.destroy()}};function createTreeGrid(host,options){if(!host||!options||!options.view){throw new Error("CanopyX: createTreeGrid needs a host element and a view document.")}
|
|
655
|
-
const existing=activeGrids.get(host);if(existing)return existing;const grid=new TreeGrid(host,options);const api=Object.
|
|
729
|
+
const existing=activeGrids.get(host);if(existing)return existing;const grid=new TreeGrid(host,options);const api=Object.assign({element:host,view:grid.view,ready:startPlugins(grid,()=>api),expandPath:(ids,o)=>grid.expandPath(ids,o),setFilter:(filter,o)=>{if(!grid.queryAware)grid.clearDeclaredFilters();grid.setFilter(filter,o)},setFilterActive:(id,active)=>grid.setFilterActive(id,active),setActiveFilters:(ids)=>grid.setActiveFilters(ids),updateActiveFilters:(change)=>grid.updateActiveFilters(change),getActiveFilters:()=>Array.from(grid.activeFilters),sort:(columnId,direction)=>grid.sort(columnId,direction),getQuery:()=>grid.getQuery(),setQuery:(query,options)=>grid.setQuery(query,options),setColumnVisible:(columnId,visible)=>grid.setColumnVisible(columnId,visible),setColumnWidth:(columnId,width)=>grid.setColumnWidth(columnId,width),setTreeColumnPinned:(pinned)=>grid.setTreeColumnPinned(pinned),setTimeZone:(zone)=>grid.setTimeZone(zone),getTimeZone:()=>(grid.local?"local":"utc"),refresh:()=>{grid.version++;grid.scheduleRender()},reload:(id,options)=>grid.reload(id,options),readLocation:()=>grid.readLocation(),getSelected:()=>Array.from(grid.selected).map((w)=>w.h),setSelection:(ids)=>grid.setSelection(ids),clearSelection:()=>grid.setSelection([]),selectAll:(options)=>grid.selectAll(options),focus:()=>{if(grid.destroyed)return;const target=grid.indexOf(grid.focused)>=0?grid.focused:grid.rows[0];if(target)grid.setFocus(target,true);else grid.grid.focus({preventScroll:true})},visibleRows:()=>grid.visibleRows(),toOutline:()=>grid.toOutline(),getState:()=>grid.getState(),setState:(state)=>grid.setState(state),getLayout:()=>grid.getLayout(),setLayout:(change)=>grid.setLayout(change),getMatches:()=>grid.getMatches(),setSearchHighlight:(enabled)=>grid.setSearchHighlight(enabled),nextMatch:(options)=>grid.nextMatch(options),previousMatch:(options)=>grid.previousMatch(options),search:(query,limit)=>grid.search(query,limit||(grid.view.search&&grid.view.search.maxResults)||20),loadMore:(id)=>{const w=id==null?grid.root:grid.keyMap.get(id);return w?grid.loadMore(w):Promise.resolve()},applyCounts:(doc)=>grid.applyCounts(doc),refreshCounts:(ids)=>grid.refreshCounts(ids),closeMenus:()=>grid.closeMenus?.(),destroy:()=>{grid.destroy();if(activeGrids.get(host)===api)activeGrids.delete(host);if(host.canopyx===api)delete host.canopyx}},grid.interactionApi&&grid.interactionApi(),linkApi(grid));for(const name of["prepareExport","exportAs","getSavedViews","saveView","applySavedView","removeSavedView","resetView","setColumnOrder","resetColumns","setColumnPinned","openDetails","openRecord"])
|
|
730
|
+
api[name]=(...args)=>{if(grid[name])return grid[name](...args);const error=new Error("CanopyX: "+name+" needs the tree tools part.");if(/^(exportAs|applySavedView|resetView|openDetails|openRecord)$/.test(name)) return Promise.reject(error);
|
|
731
|
+
throw error};api.ready=linkReady(grid,api.ready);Object.freeze(api);activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;recordStart(grid);return api}
|
|
656
732
|
const configured=new Map();function configure(viewId,options){requireIdentifier(viewId,"view ID");if(!options||typeof options!=="object"){throw new Error("CanopyX: configure needs a view id and an options object.")}
|
|
657
733
|
if(Array.from(document.querySelectorAll("[data-canopyx-mounted]")).some((host)=>host.canopyx&&host.canopyx.view.id===viewId)){throw new Error("CanopyX: view '"+viewId+"' is already mounted; configure it before the page has loaded, or mark its fragment data-cx-mount=\"manual\" and call CanopyX.mount.")}
|
|
658
734
|
const registration=Object.assign({},configured.get(viewId),options,{view:undefined});configured.set(viewId,registration);return()=>{if(configured.get(viewId)===registration)configured.delete(viewId)}}
|
|
659
735
|
function unconfigure(viewId){requireIdentifier(viewId,"view ID");return configured.delete(viewId)}
|
|
736
|
+
const parsedViews=new WeakSet();let watcher=null;function mountWhenVisible(host){if(!watcher){watcher=new IntersectionObserver(entries=>entries.forEach(entry=>entry.isIntersecting&&mount(entry.target)),{rootMargin:Math.ceil(global.innerHeight / 2) + "px 0px" });
|
|
737
|
+
global.addEventListener("beforeprint",()=>document.querySelectorAll('[data-cx-mount="visible"]:not([data-canopyx-mounted])').forEach(host=>mount(host)))}
|
|
738
|
+
watcher.observe(host)}
|
|
660
739
|
function mount(root,automatic){const scope=root||document;const created=[];const showError=(host,err,api,view)=>{if(api){api.destroy();if(host.canopyx===api)delete host.canopyx}
|
|
661
740
|
const message=err&&err.message?err.message:String(err);let labels=DEFAULT_LABELS;for(const attempt of[()=>labelsFor(view&&view.labels,view&&configured.has(view.id)?configured.get(view.id).labels:null),()=>labelsFor(view&&view.labels)]){try{labels=attempt();break}catch(e){labels=DEFAULT_LABELS}}
|
|
662
|
-
const notice=document.createElement("div");notice.className="cx-mount-error";notice.setAttribute("role","alert");setText(notice,fill(labels.mountError,{error:message}));host.classList.add("cx-host");host.setAttribute("data-canopyx-mounted","true");host.setAttribute("data-cx-error",message);host.appendChild(notice);if(global.console)global.console.error(err)};
|
|
663
|
-
host.setAttribute("data-canopyx-mounted","true");let view=null;try{view=JSON.parse(config.textContent);const create=view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
|
|
664
|
-
global.CanopyX=Object.freeze(Object.assign({protocol:PROTOCOL,createTreeGrid,configure,unconfigure,mount:(root)=>mount(root,false),sources:Object.freeze({inline:inlineSource,sidecar:sidecarSource,remote:remoteSource}),badge:hostBadge,labels:DEFAULT_LABELS},typeof
|
|
741
|
+
const notice=document.createElement("div");notice.className="cx-mount-error";notice.setAttribute("role","alert");setText(notice,fill(labels.mountError,{error:message}));host.classList.add("cx-host");host.setAttribute("data-canopyx-mounted","true");host.setAttribute("data-cx-error",message);host.appendChild(notice);if(global.console)global.console.error(err)};const selector="[data-canopyx-host]:not([data-canopyx-mounted])";(scope.matches?.(selector)?[scope]:scope.querySelectorAll(selector)).forEach((host)=>{const config=host.querySelector("script.cx-config"),mode=host.getAttribute("data-cx-mount");if(!config||(automatic===true&&mode==="manual")){return}
|
|
742
|
+
if(mode==="visible"&&host!==scope&&global.IntersectionObserver)return mountWhenVisible(host);watcher?.unobserve(host);host.setAttribute("data-canopyx-mounted","true");let view=null;try{view=JSON.parse(config.textContent);if(view===Object(view))parsedViews.add(view);const create=view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
|
|
743
|
+
global.CanopyX=Object.freeze(Object.assign({protocol:PROTOCOL,createTreeGrid,configure,unconfigure,mount:(root)=>mount(root,false),sources:Object.freeze({inline:inlineSource,sidecar:sidecarSource,remote:remoteSource}),badge:hostBadge,labels:DEFAULT_LABELS,registerExportFormat,use,registerPart,setNonce},typeof GRID_API==="object"?GRID_API:{}));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);
|