@evotecit/canopyx 0.3.15 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CanopyX.Html/Assets/canopyx-board.css +50 -0
  2. package/CanopyX.Html/Assets/canopyx-board.js +39 -0
  3. package/CanopyX.Html/Assets/canopyx-buckets.js +15 -0
  4. package/CanopyX.Html/Assets/canopyx-calculations.css +7 -0
  5. package/CanopyX.Html/Assets/canopyx-calculations.js +52 -0
  6. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +3 -3
  7. package/CanopyX.Html/Assets/canopyx-calendar.css +5 -24
  8. package/CanopyX.Html/Assets/canopyx-calendar.js +14 -19
  9. package/CanopyX.Html/Assets/canopyx-cell-range.js +16 -14
  10. package/CanopyX.Html/Assets/canopyx-changes.css +6 -24
  11. package/CanopyX.Html/Assets/canopyx-changes.js +18 -15
  12. package/CanopyX.Html/Assets/canopyx-compare.css +37 -0
  13. package/CanopyX.Html/Assets/canopyx-compare.js +12 -0
  14. package/CanopyX.Html/Assets/canopyx-data.js +3 -3
  15. package/CanopyX.Html/Assets/canopyx-editing.css +2 -2
  16. package/CanopyX.Html/Assets/canopyx-editing.js +2 -2
  17. package/CanopyX.Html/Assets/canopyx-enhance.js +4 -4
  18. package/CanopyX.Html/Assets/canopyx-export.js +54 -13
  19. package/CanopyX.Html/Assets/canopyx-facets.css +14 -5
  20. package/CanopyX.Html/Assets/canopyx-facets.js +19 -15
  21. package/CanopyX.Html/Assets/canopyx-filter-editor.css +71 -85
  22. package/CanopyX.Html/Assets/canopyx-filter-editor.js +20 -16
  23. package/CanopyX.Html/Assets/canopyx-flow-checks.css +2 -0
  24. package/CanopyX.Html/Assets/canopyx-flow-checks.js +5 -4
  25. package/CanopyX.Html/Assets/canopyx-flow-dialog.css +2 -0
  26. package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
  27. package/CanopyX.Html/Assets/canopyx-flow-entry.css +136 -12
  28. package/CanopyX.Html/Assets/canopyx-flow-entry.js +5 -5
  29. package/CanopyX.Html/Assets/canopyx-flow-facts.js +2 -0
  30. package/CanopyX.Html/Assets/canopyx-flow-module.mjs +5 -5
  31. package/CanopyX.Html/Assets/canopyx-flow.js +8 -7
  32. package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
  33. package/CanopyX.Html/Assets/canopyx-formatting.js +10 -9
  34. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +185 -131
  35. package/CanopyX.Html/Assets/canopyx-grid.css +228 -96
  36. package/CanopyX.Html/Assets/canopyx-grid.js +185 -131
  37. package/CanopyX.Html/Assets/canopyx-grouping.css +22 -15
  38. package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
  39. package/CanopyX.Html/Assets/canopyx-interactive.css +197 -74
  40. package/CanopyX.Html/Assets/canopyx-interactive.js +38 -45
  41. package/CanopyX.Html/Assets/canopyx-labels-de.js +177 -9
  42. package/CanopyX.Html/Assets/canopyx-labels-es.js +177 -9
  43. package/CanopyX.Html/Assets/canopyx-labels-fr.js +176 -8
  44. package/CanopyX.Html/Assets/canopyx-labels-it.js +176 -8
  45. package/CanopyX.Html/Assets/canopyx-labels-pl.js +176 -8
  46. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -20
  47. package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
  48. package/CanopyX.Html/Assets/canopyx-link.js +2 -2
  49. package/CanopyX.Html/Assets/canopyx-live.css +1 -3
  50. package/CanopyX.Html/Assets/canopyx-live.js +20 -15
  51. package/CanopyX.Html/Assets/canopyx-matrix.css +1 -8
  52. package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
  53. package/CanopyX.Html/Assets/canopyx-module.mjs +38 -45
  54. package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
  55. package/CanopyX.Html/Assets/canopyx-path.css +45 -0
  56. package/CanopyX.Html/Assets/canopyx-path.js +28 -0
  57. package/CanopyX.Html/Assets/canopyx-pivot.css +75 -0
  58. package/CanopyX.Html/Assets/canopyx-pivot.js +72 -0
  59. package/CanopyX.Html/Assets/canopyx-plan.css +56 -0
  60. package/CanopyX.Html/Assets/canopyx-plan.js +67 -0
  61. package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
  62. package/CanopyX.Html/Assets/canopyx-record-panel.css +11 -2
  63. package/CanopyX.Html/Assets/canopyx-record-panel.js +18 -12
  64. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +225 -164
  65. package/CanopyX.Html/Assets/canopyx-reporting.css +240 -107
  66. package/CanopyX.Html/Assets/canopyx-reporting.js +225 -164
  67. package/CanopyX.Html/Assets/canopyx-stack.css +35 -0
  68. package/CanopyX.Html/Assets/canopyx-stack.js +24 -0
  69. package/CanopyX.Html/Assets/canopyx-time-slider.css +18 -0
  70. package/CanopyX.Html/Assets/canopyx-time-slider.js +27 -0
  71. package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
  72. package/CanopyX.Html/Assets/canopyx-tree-tools.css +36 -35
  73. package/CanopyX.Html/Assets/canopyx-tree-tools.js +15 -12
  74. package/CanopyX.Html/Assets/canopyx-ui-menu.css +41 -0
  75. package/CanopyX.Html/Assets/canopyx-ui-menu.js +64 -0
  76. package/CanopyX.Html/Assets/canopyx-ui-navigation.css +73 -0
  77. package/CanopyX.Html/Assets/canopyx-ui-navigation.js +39 -0
  78. package/CanopyX.Html/Assets/canopyx-ui-overlays.css +79 -0
  79. package/CanopyX.Html/Assets/canopyx-ui-overlays.js +42 -0
  80. package/CanopyX.Html/Assets/canopyx-ui-picker.css +12 -0
  81. package/CanopyX.Html/Assets/canopyx-ui-picker.js +6 -0
  82. package/CanopyX.Html/Assets/canopyx-ui-stepper.css +174 -0
  83. package/CanopyX.Html/Assets/canopyx-ui-stepper.js +48 -0
  84. package/CanopyX.Html/Assets/canopyx-ui-tabs.css +153 -0
  85. package/CanopyX.Html/Assets/canopyx-ui-tabs.js +47 -0
  86. package/CanopyX.Html/Assets/canopyx-values.css +5 -0
  87. package/CanopyX.Html/Assets/canopyx-values.js +13 -0
  88. package/CanopyX.Html/Assets/canopyx-view-intents.css +12 -0
  89. package/CanopyX.Html/Assets/canopyx-view-intents.js +56 -0
  90. package/CanopyX.Html/Assets/canopyx.css +197 -71
  91. package/CanopyX.Html/Assets/canopyx.js +29 -19
  92. package/JavaScript/board.d.ts +30 -0
  93. package/JavaScript/changes.d.ts +4 -1
  94. package/JavaScript/facets.d.ts +5 -3
  95. package/JavaScript/flow.d.ts +6 -0
  96. package/JavaScript/grid.d.ts +153 -17
  97. package/JavaScript/hosts.d.ts +14 -1
  98. package/JavaScript/index.d.ts +4 -2
  99. package/JavaScript/live.d.ts +2 -0
  100. package/JavaScript/locales/de.mjs +177 -9
  101. package/JavaScript/locales/es.mjs +177 -9
  102. package/JavaScript/locales/fr.mjs +176 -8
  103. package/JavaScript/locales/it.mjs +176 -8
  104. package/JavaScript/locales/pl.mjs +176 -8
  105. package/JavaScript/matrix.d.ts +4 -1
  106. package/JavaScript/path.d.ts +33 -0
  107. package/JavaScript/pivot.d.ts +135 -0
  108. package/JavaScript/plan.d.ts +23 -0
  109. package/JavaScript/protocol.d.ts +16 -3
  110. package/JavaScript/records.d.ts +20 -4
  111. package/JavaScript/stack.d.ts +28 -0
  112. package/JavaScript/time-slider.d.ts +58 -0
  113. package/JavaScript/ui.d.ts +261 -0
  114. package/JavaScript/view-intents.d.ts +45 -0
  115. package/README.md +22 -3
  116. package/ThirdParty/OfficeIMO/LICENSE +21 -0
  117. package/ThirdParty/OfficeIMO/README.md +13 -0
  118. package/package.json +76 -3
@@ -1,4 +1,7 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"2810fb06a78b85e3",install(context){if(installed.has(context.CHANGES_IMPL))return;const{CHANGES_IMPL,h,setText,fill,plural,numberFormat,gridFreeze,toneOf,applyTone,svgIcon,PROTOCOL}=context;const CHANGE_LABELS={changeInline:"Inline",changeSplit:"Side by side",changeLayout:"Layout",changeCopyDiff:"Copy diff",changeCopied:"Copied",changeCopyFailed:"Could not copy",changeNext:"Next change",changePrevious:"Previous change",changePosition:"Change {index} of {count}",changeShowLines:"[one]Show {count} more line[other]Show {count} more lines",changeHiddenLines:"[one]{count} unchanged line not included[other]{count} unchanged lines not included",changeLineCounts:"Lines added: {added}, removed: {removed}",changeLineAdded:"Added line {line}",changeLineRemoved:"Removed line {line}",changeLineContext:"Line {line}",changeBefore:"Before",changeAfter:"After",changeBinary:"Binary file: compared by size and hash, not by lines.",changeUnavailable:"The content of this file is not available.",changeIdentical:"The text did not change.",changeTruncated:"Some changes are not shown: the comparison was too large.",changeTooLarge:"These texts are too long to compare here.",changeDiffOf:"Changes in {name}",changeAdded:"Added",changeRemoved:"Removed",changeModified:"Modified",changeRenamed:"Renamed",changeFlaggedOnly:"Flagged only",changeFlaggedCount:"{count} flagged",changeNoItems:"No changes to show.",changeSize:"Size",changeHash:"Hash",changeTime:"Time",changeCopyHash:"Copy the full hash",changeDetails:"Details of {name}",changeFileCount:"[one]{count} file[other]{count} files",changeFrom:"from {path}",changeShowMore:"Show more",changeFlags:"Flags",changeFacts:"Details",changeRoot:"Top level",changeShown:"{shown} of {total} shown",changeFilter:"Show changes",changeActions:"Actions for {name}",changeConverged:"Converged",changePropagating:"Propagating",changeFault:"Fault",changeUnknown:"Unknown",changeReplica:"Replica",changeState:"State",changeFirstSeen:"First seen",changeLastSeen:"Last seen",changeTimeline:"Delay",changeNotSeen:"Not seen yet",changeDelay:"+{duration}",changeSource:"Source",changeProgress:"{converged} of {total} have the change",changeSeconds:"{count} s",changeMinutes:"{count} min",changeHours:"{count} h",changeDays:"{count} d",changeUtcTime:"{time} UTC",changeExportCsv:"Export CSV",changeExportJson:"Export JSON",changeReason:"Reason",changeReasonHint:"{action}: say why. The reason is kept with the action.",changeReasonRequired:"Enter a reason first.",changeCancel:"Cancel",changeUnclaimed:"This page cannot carry out “{action}” itself. Copy the request to pass it on.",changeCopyRequest:"Copy request",changeFiltered:"Filtered: {filters}",changePrintLimit:"[one]{count} more line is not printed.[other]{count} more lines are not printed."};function changeLabels(...sources){const labels={...CHANGE_LABELS};for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))changeFail("labels must be an object of strings");for(const[name,text]of Object.entries(source)){if(!Object.hasOwn(labels,name)){if(/^change[A-Z]/.test(name)) changeFail("there is no change label '" + name + "'"); continue; }
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"419bb03b4580fd5c",install(context){if(installed.has(context.CHANGES_IMPL))return;const{CHANGES_IMPL,h,setText,fill,plural,numberFormat,gridFreeze,toneOf,applyTone,svgIcon,PROTOCOL}=context;function humanTimeFormat(lang,local,utcTemplate){const formatter=new Intl.DateTimeFormat(lang||undefined,{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hourCycle:"h23",timeZone:local?undefined:"UTC",...(local?{timeZoneName:"short"}:{})});return{format(value){const parts=formatter.formatToParts(new Date(value));const text=parts.map((part,index)=>part.type==="literal"&&parts[index+1]?.type==="timeZoneName"?part.value.replace(/\s/g,"\u00a0"):part.value).join("");
2
+ return local?text:fill(utcTemplate||"{time} UTC",{time:text}).replace(/ (\S+)$/,"\u00a0$1");
3
+ }}}
4
+ const CHANGE_LABELS={changeInline:"Inline",changeSplit:"Side by side",changeLayout:"Layout",changeCopyDiff:"Copy diff",changeCopied:"Copied",changeCopyFailed:"Could not copy",changeNext:"Next change",changePrevious:"Previous change",changePosition:"Change {index} of {count}",changeShowLines:"[one]Show {count} more line[other]Show {count} more lines",changeHiddenLines:"[one]{count} unchanged line not included[other]{count} unchanged lines not included",changeLineCounts:"Lines added: {added}, removed: {removed}",changeLineAdded:"Added line {line}",changeLineRemoved:"Removed line {line}",changeLineContext:"Line {line}",changeBefore:"Before",changeAfter:"After",changeBinary:"Binary file: compared by size and hash, not by lines.",changeUnavailable:"The content of this file is not available.",changeIdentical:"The text did not change.",changeTruncated:"Some changes are not shown: the comparison was too large.",changeTooLarge:"These texts are too long to compare here.",changeDiffOf:"Changes in {name}",changeAdded:"Added",changeRemoved:"Removed",changeModified:"Modified",changeRenamed:"Renamed",changeFlaggedOnly:"Flagged only",changeFlaggedCount:"{count} flagged",changeNoItems:"No changes to show.",changeSize:"Size",changeHash:"Hash",changeTime:"Time",changeCopyHash:"Copy the full hash",changeDetails:"Details of {name}",changeFileCount:"[one]{count} file[other]{count} files",changeFrom:"from {path}",changeShowMore:"Show more",changeFlags:"Flags",changeFacts:"Details",changeRoot:"Top level",changeShown:"{shown} of {total} shown",changeFilter:"Show changes",changeActions:"Actions for {name}",changeConverged:"Converged",changePropagating:"Propagating",changeFault:"Fault",changeUnknown:"Unknown",changeReplica:"Replica",changeState:"State",changeFirstSeen:"First seen",changeLastSeen:"Last seen",changeTimeline:"Delay",changeNotSeen:"Not seen yet",changeDelay:"+{duration}",changeSource:"Source",changeProgress:"{converged} of {total} have the change",changeSeconds:"{count} s",changeMinutes:"{count} min",changeHours:"{count} h",changeDays:"{count} d",changeUtcTime:"{time} UTC",changeExportCsv:"Export CSV",changeExportJson:"Export JSON",changeReason:"Reason",changeReasonHint:"{action}: say why. The reason is kept with the action.",changeReasonRequired:"Enter a reason first.",changeCancel:"Cancel",changeUnclaimed:"This page cannot carry out “{action}” itself. Copy the request to pass it on.",changeCopyRequest:"Copy request",changeFiltered:"Filtered: {filters}",changePrintLimit:"[one]{count} more line is not printed.[other]{count} more lines are not printed."};function changeLabels(...sources){const labels={...CHANGE_LABELS};for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))changeFail("labels must be an object of strings");for(const[name,text]of Object.entries(source)){if(!Object.hasOwn(labels,name)){if(/^change[A-Z]/.test(name)) changeFail("there is no change label '" + name + "'"); continue; }
2
5
  if(typeof text!=="string"||!/\S/.test(text)) changeFail("label '" + name + "' must be a string with visible text");
3
6
  labels[name]=text}}
4
7
  return Object.freeze(labels)}
@@ -37,7 +40,7 @@ return Object.freeze(values)}
37
40
  function changeDiffChecked(diff){changeLabels(diff.labels);return diff}
38
41
  function changesDocument(input){const doc=changeEnvelope(input,"changes",["id","label","actions","toolbar","values","exports","items","groupByFolder","flaggedOnly","expandFlagged","hashLength","pageSize","timeZone","labels"]);changeLabels(doc.labels);const actionIds=new Set(),actions=changeList(doc.actions,"actions",8).map(entry=>changeAction(entry,actionIds));const barIds=new Set(actionIds),listValues=changeValues(doc.values,"the change list"),toolbar=changeList(doc.toolbar,"toolbar",8).map(entry=>{const action=changeAction(entry,barIds),key=action.copy??action.link;if(action.reveal!=="none")changeFail("the bar action '"+action.id+"' has no item to reveal");if(key!==null&&!Object.hasOwn(listValues,key))changeFail("the bar action '"+action.id+"' uses the list value '"+key+"', which the list does not have");return action});const exports=changeList(doc.exports,"exports",2);if(exports.some(name=>name!=="csv"&&name!=="json")||new Set(exports).size!==exports.length)changeFail("exports are csv and json, each once");const ids=new Set(),items=changeList(doc.items,"items",10000).map((entry,index)=>{const item=changeKeys(entry,["id","kind","path","previousPath","kindLabel","secondary","before","after","facts","flags","diff","actions","values"],"an item");const id=changeId(item.id,"an item id"),what="the item '"+id+"'";if(ids.has(id))changeFail(what+" appears twice");ids.add(id);const kind=changeEnum(item.kind,CHANGE_KINDS,what+" kind"),path=changeLine(item.path,what+" path",2048),previousPath=changeLine(item.previousPath,what+" previous path",2048);if(kind===undefined||path===null||!/\S/.test(path)) changeFail(what + " needs a kind and a path");
39
42
  if(kind==="renamed"&&previousPath===null||(kind==="added"||kind==="removed")&&previousPath!==null)changeFail(what+" has a previous path only when it is renamed or modified, and always when renamed");const facts=changeList(item.facts,what+" facts",32).map(value=>{const fact=changeKeys(value,["label","before","after","tone","note"],"a fact");if(fact.before===undefined&&fact.after===undefined)changeFail(what+" has a fact without a value");return Object.freeze({label:changeText(fact.label,"a fact label",200),before:changeText(fact.before,"a fact value",2000,true),after:changeText(fact.after,"a fact value",2000,true),tone:changeTone(fact.tone,"a fact"),note:changeText(fact.note,"a fact note",500,true)})});const flagIds=new Set(),flags=changeList(item.flags,what+" flags",16).map(value=>{const flag=changeKeys(value,["id","label","reason","tone"],"a flag"),flagId=changeId(flag.id,"a flag id",64);if(flagIds.has(flagId))changeFail(what+" has the flag '"+flagId+"' twice");flagIds.add(flagId);return Object.freeze({id:flagId,label:changeText(flag.label,"a flag label",64),reason:changeText(flag.reason,"a flag reason",500),tone:changeTone(flag.tone,"a flag")||"warning"})});const offered=changeList(item.actions,what+" actions",8);for(const action of offered)if(!actionIds.has(action))changeFail(what+" offers the undeclared action '"+action+"'");const values=changeValues(item.values,what);return Object.freeze({id,index,kind,path,previousPath,kindLabel:changeText(item.kindLabel,what+" kind label",64,true),secondary:changeText(item.secondary,what+" secondary text",200,true),before:changeVersion(item.before,what+" before"),after:changeVersion(item.after,what+" after"),facts:Object.freeze(facts),flags:Object.freeze(flags),diff:item.diff===undefined?null:changeDiffChecked(diffBody(item.diff,what+" diff")),actions:Object.freeze([...offered]),values})});return Object.freeze({id:changeId(doc.id,"the change list id"),label:changeText(doc.label,"the label",200),actions:Object.freeze(actions),toolbar:Object.freeze(toolbar),values:listValues,exports:Object.freeze([...exports]),items:Object.freeze(items),groupByFolder:doc.groupByFolder!==false&&changeFlag(doc.groupByFolder??true,"groupByFolder"),flaggedOnly:changeFlag(doc.flaggedOnly,"flaggedOnly"),expandFlagged:changeFlag(doc.expandFlagged,"expandFlagged"),hashLength:changeInteger(doc.hashLength,"hashLength",4,64,8),pageSize:changeInteger(doc.pageSize,"pageSize",10,1000,200),local:changeEnum(doc.timeZone,["utc","local"],"timeZone","utc")==="local",labels:doc.labels})}
40
- function propagationDocument(input){const doc=changeEnvelope(input,"propagation",["id","label","start","asOf","state","summary","title","groupCount","witnesses","timeZone","labels"]);const start=changeTime(doc.start,"start"),asOf=changeTime(doc.asOf,"asOf");if(start!==null&&asOf!==null&&start>asOf)changeFail("a propagation view starts before its evidence time");const groupCount=changeText(doc.groupCount,"the group count",200,true);if(groupCount!==null&&!changePluralText(groupCount))changeFail("the group count needs {count} in every form, and plural forms that end with [other]");const ids=new Set(),witnesses=changeList(doc.witnesses,"witnesses",2000).map(entry=>{const witness=changeKeys(entry,["id","label","secondary","state","firstSeen","lastSeen","note","source","group"],"a replica"),id=changeId(witness.id,"a replica id");if(ids.has(id))changeFail("the replica '"+id+"' appears twice");ids.add(id);const firstSeen=changeTime(witness.firstSeen,"firstSeen"),lastSeen=changeTime(witness.lastSeen,"lastSeen");if(firstSeen!==null&&lastSeen!==null&&firstSeen>lastSeen)changeFail("the replica '"+id+"' was first seen after it last reported");const state=changeEnum(witness.state,PROPAGATION_STATES,"a replica state");if(state===undefined)changeFail("the replica '"+id+"' needs a state");return Object.freeze({id,label:changeText(witness.label,"a replica label",200),secondary:changeText(witness.secondary,"a replica's secondary text",200,true),state,firstSeen,lastSeen,note:changeText(witness.note,"a replica note",500,true),source:changeFlag(witness.source,"source"),group:changeText(witness.group,"a replica group",200,true)})});if(!witnesses.length)changeFail("a propagation view has 1 to 2000 replicas");return Object.freeze({id:changeId(doc.id,"the propagation id"),label:changeText(doc.label,"the label",200),start,asOf,state:changeEnum(doc.state,PROPAGATION_STATES,"state",null),summary:changeText(doc.summary,"the summary",500,true),title:changeText(doc.title,"the title",200,true),groupCount,witnesses:Object.freeze(witnesses),local:changeEnum(doc.timeZone,["utc","local"],"timeZone","utc")==="local",labels:doc.labels})}
43
+ function propagationDocument(input){const doc=changeEnvelope(input,"propagation",["id","label","start","asOf","state","summary","title","groupCount","witnesses","timeZone","labels","timeSlider"]);const start=changeTime(doc.start,"start"),asOf=changeTime(doc.asOf,"asOf");if(start!==null&&asOf!==null&&start>asOf)changeFail("a propagation view starts before its evidence time");const groupCount=changeText(doc.groupCount,"the group count",200,true);if(groupCount!==null&&!changePluralText(groupCount))changeFail("the group count needs {count} in every form, and plural forms that end with [other]");const ids=new Set(),witnesses=changeList(doc.witnesses,"witnesses",2000).map(entry=>{const witness=changeKeys(entry,["id","label","secondary","state","firstSeen","lastSeen","note","source","group"],"a replica"),id=changeId(witness.id,"a replica id");if(ids.has(id))changeFail("the replica '"+id+"' appears twice");ids.add(id);const firstSeen=changeTime(witness.firstSeen,"firstSeen"),lastSeen=changeTime(witness.lastSeen,"lastSeen");if(firstSeen!==null&&lastSeen!==null&&firstSeen>lastSeen)changeFail("the replica '"+id+"' was first seen after it last reported");const state=changeEnum(witness.state,PROPAGATION_STATES,"a replica state");if(state===undefined)changeFail("the replica '"+id+"' needs a state");return Object.freeze({id,label:changeText(witness.label,"a replica label",200),secondary:changeText(witness.secondary,"a replica's secondary text",200,true),state,firstSeen,lastSeen,note:changeText(witness.note,"a replica note",500,true),source:changeFlag(witness.source,"source"),group:changeText(witness.group,"a replica group",200,true)})});if(!witnesses.length)changeFail("a propagation view has 1 to 2000 replicas");return Object.freeze({id:changeId(doc.id,"the propagation id"),label:changeText(doc.label,"the label",200),start,asOf,state:changeEnum(doc.state,PROPAGATION_STATES,"state",null),summary:changeText(doc.summary,"the summary",500,true),title:changeText(doc.title,"the title",200,true),groupCount,witnesses:Object.freeze(witnesses),local:changeEnum(doc.timeZone,["utc","local"],"timeZone","utc")==="local",labels:doc.labels})}
41
44
  const CHANGE_UNITS=["B","KB","MB","GB","TB"];function changeSize(bytes,lang){if(bytes<1024)return numberFormat(lang,0,0).format(bytes)+ " B";let value=bytes,unit=0;while(value>=1024&&unit<CHANGE_UNITS.length-1){value /= 1024; unit++; }
42
45
  return numberFormat(lang,0,1).format(value)+ " "+CHANGE_UNITS[unit]}
43
46
  const changePad=value=>(value<10?"0":"")+ value;function changeTimeText(ms,local,labels){const d=new Date(ms),get=local?["getFullYear","getMonth","getDate","getHours","getMinutes"]:["getUTCFullYear","getUTCMonth","getUTCDate","getUTCHours","getUTCMinutes"];const[year,month,day,hours,minutes]=get.map(name=>d[name]());const text=String(year).padStart(4,"0")+ "-"+changePad(month+1)+ "-"+changePad(day)+ " "+changePad(hours)+ ":"+changePad(minutes);return local?text:fill(labels.changeUtcTime,{time:text})}
@@ -97,7 +100,7 @@ class DiffView{constructor(host,options,doc){this.host=host;this.options=options
97
100
  emit(name,detail,callback){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true});this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:callback},bubbles:true}))}}}
98
101
  prepare(){const doc=this.doc;this.tooLarge=doc.content==="text"&&!doc.hunks.length&&(doc.left.length>DIFF_CLIENT_LINES||doc.right.length>DIFF_CLIENT_LINES);this.hunks=doc.content!=="text"||this.tooLarge?[]:doc.hunks.length?doc.hunks:diffHunks(doc.left,doc.right,doc.context);const built=diffRows(doc,this.hunks);this.rows=built.rows;this.added=built.added;this.removed=built.removed}
99
102
  build(){const doc=this.doc,labels=this.labels,signal=this.lifetime.signal;const root=this.root=h("div","cx-diff");const head=h("div","cx-diff-head"),title=this.titleBox=h("div","cx-diff-title"),tools=this.tools=h("div","cx-diff-tools");head.append(title,tools);this.notes=h("div","cx-diff-notes");this.state=h("div","cx-diff-state");this.state.hidden=true;const scroll=this.scroll=h("div","cx-diff-scroll");scroll.tabIndex=0;scroll.setAttribute("role","table");this.columns=h("div","cx-diff-columns");this.columns.setAttribute("role","row");this.columns.setAttribute("aria-rowindex","1");this.canvas=h("div","cx-diff-canvas");this.canvas.setAttribute("role","rowgroup");scroll.append(this.columns,this.canvas);this.live=h("div","cx-sr-only");this.live.setAttribute("aria-live","polite");this.print=h("div","cx-diff-print");const frame=this.frameBox=h("div","cx-diff-frame");this.edgeStart=h("div","cx-diff-edge cx-diff-edge-start");this.edgeEnd=h("div","cx-diff-edge cx-diff-edge-end");this.edgeStart.hidden=this.edgeEnd.hidden=true;frame.append(scroll,this.edgeStart,this.edgeEnd);root.append(head,this.notes,this.state,frame,this.live,this.print);this.host.appendChild(root);this.probe=h("span","cx-diff-probe");setText(this.probe,"0000000000");root.appendChild(this.probe);this.buildTools();changeTips(root,signal);scroll.addEventListener("scroll",()=>{this.schedule();this.edges()},{passive:true,signal});scroll.addEventListener("keydown",event=>this.onKey(event),{signal});scroll.addEventListener("click",event=>this.onClick(event),{signal});global.addEventListener("beforeprint",()=>this.renderPrint(),{signal});this.resize=new ResizeObserver(()=>{if(!this.destroyed)this.layout()});this.resize.observe(root);this.refresh();if(changesInPrint())this.renderPrint()}
100
- buildTools(){const labels=this.labels,tools=this.tools,signal=this.lifetime.signal;const group=this.layoutGroup=h("div","cx-diff-seg");group.setAttribute("role","radiogroup");group.setAttribute("aria-label",labels.changeLayout);this.layoutButtons=["inline","split"].map(mode=>{const button=h("button","cx-diff-segbtn");button.type="button";button.setAttribute("role","radio");button._mode=mode;setText(button,mode==="inline"?labels.changeInline:labels.changeSplit);button.addEventListener("click",()=>this.setLayout(mode),{signal});button.addEventListener("keydown",event=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(event.key))return;event.preventDefault();const next=mode==="inline"?"split":"inline";this.setLayout(next);this.layoutButtons.find(item=>item._mode===next).focus()},{signal});group.appendChild(button);return button});const button=(label,icon,action,text)=>{const element=h("button","cx-diff-btn");element.type="button";element.appendChild(changeIcon(icon));if(text){const name=h("span");setText(name,label);element.appendChild(name)}else{element.setAttribute("aria-label",label);element.setAttribute("data-cx-tip",label)}
103
+ buildTools(){const labels=this.labels,tools=this.tools,signal=this.lifetime.signal;const group=this.layoutGroup=h("div","cx-segmented cx-diff-seg");group.dataset.size="sm";group.setAttribute("role","radiogroup");group.setAttribute("aria-label",labels.changeLayout);this.layoutButtons=["inline","split"].map(mode=>{const button=h("button","cx-diff-segbtn");button.type="button";button.setAttribute("role","radio");button._mode=mode;setText(button,mode==="inline"?labels.changeInline:labels.changeSplit);button.addEventListener("click",()=>this.setLayout(mode),{signal});button.addEventListener("keydown",event=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(event.key))return;event.preventDefault();const next=mode==="inline"?"split":"inline";this.setLayout(next);this.layoutButtons.find(item=>item._mode===next).focus()},{signal});group.appendChild(button);return button});const button=(label,icon,action,text)=>{const element=h("button","cx-btn cx-diff-btn");element.type="button";element.dataset.size="sm";element.appendChild(changeIcon(icon));if(text){const name=h("span");setText(name,label);element.appendChild(name)}else{element.dataset.icon="";element.setAttribute("aria-label",label);element.setAttribute("data-cx-tip",label)}
101
104
  element.addEventListener("click",action,{signal});return element};this.prevButton=button(labels.changePrevious,"up",()=>this.move(-1));this.nextButton=button(labels.changeNext,"down",()=>this.move(1));this.copyButton=button(labels.changeCopyDiff,"copy",()=>this.copy(),true);tools.append(group,this.prevButton,this.nextButton,this.copyButton)}
102
105
  refresh(){const doc=this.doc,labels=this.labels,title=this.titleBox;title.replaceChildren();const name=h("span","cx-diff-path");setText(name,doc.path?(doc.previousPath&&doc.previousPath!==doc.path?doc.previousPath+" → "+doc.path:doc.path):doc.label);title.appendChild(name);if(doc.content==="text"&&!this.tooLarge){const stats=h("span","cx-diff-stats"),plus=h("span","cx-diff-plus"),minus=h("span","cx-diff-minus");setText(plus,"+"+this.format.format(this.added));setText(minus,"−"+this.format.format(this.removed));const spoken=h("span","cx-sr-only");setText(spoken,fill(labels.changeLineCounts,{added:this.format.format(this.added),removed:this.format.format(this.removed)}));plus.setAttribute("aria-hidden","true");minus.setAttribute("aria-hidden","true");stats.append(plus,minus,spoken);title.appendChild(stats)}
103
106
  const notes=[];for(const note of doc.notes)notes.push([note.text,note.tone||"info"]);if(doc.truncated)notes.push([labels.changeTruncated,"warning"]);if(doc.message&&doc.content==="text")notes.push([doc.message,"info"]);this.notes.replaceChildren(...notes.map(([text,tone])=>{const note=h("div","cx-diff-note");applyTone(note,"cx-diff-note",toneOf(tone));setText(note,text);return note}));this.notes.hidden=!notes.length;const empty=doc.content!=="text"?doc.message||(doc.content==="binary"?labels.changeBinary:doc.content==="identical"?labels.changeIdentical:labels.changeUnavailable)
@@ -134,13 +137,13 @@ if(written>5000){const rest=this.rows.slice(5000).reduce((sum,row)=>sum +(row.t=
134
137
  table.appendChild(body);this.print.replaceChildren(table)},getHunks(){return this.hunks.map(hunk=>({before:hunk.before,after:hunk.after,lines:hunk.lines.map(line=>(line.kind==="="?" ":line.kind)+ line.text)}))},update(view){this.doc=diffDocument(view);this.labels=changeLabels(this.options.baseLabels,this.doc.labels,this.options.labels);this.source=view;this.current=-1;this.prepare();this.refresh()}});function diffCreate(host,options,doc){const view=new DiffView(host,options,doc);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("diffReady",{diff:view.doc.id},"onReady");return impl}),document:()=>view.source||options.view,getLayout:()=>view.shown,setLayout:mode=>view.setLayout(mode),next:()=>view.move(1),previous:()=>view.move(-1),expandAll:()=>view.expandAll(),copy:()=>view.copy(),getHunks:()=>view.getHunks(),update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.resize.disconnect();cancelAnimationFrame(view.frame);clearTimeout(view.copyTimer);view.root.remove();host.removeAttribute("data-cx-ready")}};return impl}
135
138
  class ChangeListView{constructor(host,options){this.host=host;this.options=options;this.lifetime=new AbortController();this.doc=changesDocument(options.view);this.labels=changeLabels(this.doc.labels,options.labels);this.lang=host.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||"en";this.format=numberFormat(this.lang);this.kinds=new Set();this.flaggedOnly=this.doc.flaggedOnly;this.limit=this.doc.pageSize;this.diffs=new Map();this.open=new Set(this.doc.expandFlagged?this.doc.items.filter(item=>item.flags.length).map(item=>item.id):[]);if(options.state)this.applyState(options.state,false);this.build()}
136
139
  emit(name,detail,callback){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true});this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:callback},bubbles:true}))}}}
137
- build(){const signal=this.lifetime.signal;const root=this.root=h("div","cx-changes");this.bar=h("div","cx-chg-bar");this.list=h("ul","cx-chg-list");this.list.setAttribute("aria-label",this.doc.label);this.empty=h("div","cx-chg-empty");this.empty.hidden=true;this.more=h("button","cx-chg-more");this.more.type="button";this.more.hidden=true;this.live=h("div","cx-sr-only");this.live.setAttribute("aria-live","polite");this.barPanel=h("div","cx-chg-barpanel");root.append(this.bar,this.barPanel,this.list,this.empty,this.more,this.live);this.host.appendChild(root);root.addEventListener("click",event=>this.onClick(event),{signal});root.addEventListener("keydown",event=>this.onKey(event),{signal});changeTips(root,signal);root.addEventListener("change",event=>{if(event.target===this.flagSwitch){this.applyState({flaggedOnly:event.target.checked});this.flagSwitch?.focus()}},{signal});this.more.addEventListener("click",()=>{const shown=new Set([...this.list.querySelectorAll("[data-cx-item]")].map(item=>item.getAttribute("data-cx-item")));this.limit +=this.doc.pageSize;this.render();if(this.more.hidden)[...this.list.querySelectorAll("[data-cx-item]")].find(item=>!shown.has(item.getAttribute("data-cx-item")))?.querySelector(".cx-chg-toggle")?.focus()},{signal});this.printing=changesInPrint();global.addEventListener("beforeprint",()=>{this.printing=true;this.render()},{signal});global.addEventListener("afterprint",()=>{this.printing=false;this.render()},{signal});this.render()}
140
+ build(){const signal=this.lifetime.signal;const root=this.root=h("div","cx-changes");this.bar=h("div","cx-chg-bar");this.list=h("ul","cx-chg-list");this.list.setAttribute("aria-label",this.doc.label);this.empty=h("div","cx-chg-empty");this.empty.hidden=true;this.more=h("button","cx-btn cx-chg-more");this.more.dataset.size="sm";this.more.type="button";this.more.hidden=true;this.live=h("div","cx-sr-only");this.live.setAttribute("aria-live","polite");this.barPanel=h("div","cx-chg-barpanel");root.append(this.bar,this.barPanel,this.list,this.empty,this.more,this.live);this.host.appendChild(root);root.addEventListener("click",event=>this.onClick(event),{signal});root.addEventListener("keydown",event=>this.onKey(event),{signal});changeTips(root,signal);root.addEventListener("change",event=>{if(event.target===this.flagSwitch){this.applyState({flaggedOnly:event.target.checked});this.flagSwitch?.focus()}},{signal});this.more.addEventListener("click",()=>{const shown=new Set([...this.list.querySelectorAll("[data-cx-item]")].map(item=>item.getAttribute("data-cx-item")));this.limit +=this.doc.pageSize;this.render();if(this.more.hidden)[...this.list.querySelectorAll("[data-cx-item]")].find(item=>!shown.has(item.getAttribute("data-cx-item")))?.querySelector(".cx-chg-toggle")?.focus()},{signal});this.printing=changesInPrint();global.addEventListener("beforeprint",()=>{this.printing=true;this.render()},{signal});global.addEventListener("afterprint",()=>{this.printing=false;this.render()},{signal});this.render()}
138
141
  visible(){return this.doc.items.filter(item=>(!this.kinds.size||this.kinds.has(item.kind))&&(!this.flaggedOnly||item.flags.length))}
139
142
  folders(items){if(!this.doc.groupByFolder)return[["","",items]];const groups=new Map();for(const item of items){const folder=changePath(item.path)[0],key=folder.replace(/\\/g, "/");
140
143
  if(!groups.has(key))groups.set(key,[key,folder,[]]);groups.get(key)[2].push(item)}
141
144
  return[...groups.values()].sort((a,b)=>a[0]<b[0]?-1:a[0]>b[0]?1:0)}
142
- renderBar(){const labels=this.labels,items=this.doc.items,kinds=h("div","cx-chg-kinds");kinds.setAttribute("role","group");kinds.setAttribute("aria-label",labels.changeFilter);for(const kind of CHANGE_KINDS){const count=items.filter(item=>item.kind===kind).length;if(!count)continue;const button=h("button","cx-chg-kind"),badge=h("span"),number=h("span","cx-chg-count");button.type="button";button._kind=kind;button.setAttribute("aria-pressed",String(this.kinds.has(kind)));applyTone(badge,"cx-chg-badge",toneOf(CHANGE_TONES[kind]));setText(badge,labels["change"+kind[0].toUpperCase()+ kind.slice(1)]);setText(number,this.format.format(count));button.append(badge,number);kinds.appendChild(button)}
143
- const children=[kinds],flagged=items.filter(item=>item.flags.length).length;if(flagged){const label=h("label","cx-chg-switch"),box=this.flagSwitch=h("input"),text=h("span");box.type="checkbox";box.checked=this.flaggedOnly;setText(text,labels.changeFlaggedOnly+" · "+changeCount(labels.changeFlaggedCount,flagged,this.lang,{count:this.format.format(flagged)}));label.append(box,text);children.push(label)}
145
+ renderBar(){const labels=this.labels,items=this.doc.items,kinds=h("div","cx-chg-kinds");kinds.setAttribute("role","group");kinds.setAttribute("aria-label",labels.changeFilter);for(const kind of CHANGE_KINDS){const count=items.filter(item=>item.kind===kind).length;if(!count)continue;const button=h("button","cx-chip cx-chg-kind"),badge=h("span"),number=h("span","cx-chg-count");button.type="button";button._kind=kind;button.setAttribute("aria-pressed",String(this.kinds.has(kind)));applyTone(badge,"cx-pill cx-chg-badge",toneOf(CHANGE_TONES[kind]));setText(badge,labels["change"+kind[0].toUpperCase()+ kind.slice(1)]);setText(number,this.format.format(count));button.append(badge,number);kinds.appendChild(button)}
146
+ const children=[kinds],flagged=items.filter(item=>item.flags.length).length;if(flagged){const label=h("label","cx-chg-switch"),box=this.flagSwitch=h("input","cx-check"),text=h("span");box.type="checkbox";box.checked=this.flaggedOnly;setText(text,labels.changeFlaggedOnly+" · "+changeCount(labels.changeFlaggedCount,flagged,this.lang,{count:this.format.format(flagged)}));label.append(box,text);children.push(label)}
144
147
  const summary=h("span","cx-chg-summary"),shown=Math.min(this.visible().length,this.shownLimit());setText(summary,shown===items.length?"":fill(labels.changeShown,{shown:this.format.format(shown),total:this.format.format(items.length)}));const filters=[...CHANGE_KINDS.filter(kind=>this.kinds.has(kind)).map(kind=>labels["change"+kind[0].toUpperCase()+ kind.slice(1)]),...(this.flaggedOnly?[labels.changeFlaggedOnly]:[])];const printed=h("span","cx-chg-printnote");setText(printed,filters.length?fill(labels.changeFiltered,{filters:filters.join(", ")}):"");children.push(summary,printed);const tools=this.tools();if(tools)children.push(tools);this.bar.replaceChildren(...children)}
145
148
  shownLimit(){return this.printing?Math.max(this.limit,2000):this.limit}
146
149
  render(){const focused=document.activeElement&&this.root.contains(document.activeElement)?document.activeElement.closest("[data-cx-item]")?.getAttribute("data-cx-item"):null;this.renderBar();const items=this.visible(),shown=items.slice(0,this.shownLimit()),labels=this.labels,rows=[];for(const[key,folder,members]of this.folders(shown)){if(this.doc.groupByFolder){const group=h("li","cx-chg-group"),title=h("div","cx-chg-folder"),name=h("span","cx-chg-foldername"),count=h("span","cx-chg-foldercount"),list=h("ul","cx-chg-sublist");setText(name,folder||labels.changeRoot);setText(count,changeCount(labels.changeFileCount,members.length,this.lang,{count:this.format.format(members.length)}));title.append(name,count);list.setAttribute("aria-label",folder||labels.changeRoot);for(const item of members)list.appendChild(this.row(item));group.append(title,list);rows.push(group)}else{for(const item of members)rows.push(this.row(item))}}
@@ -153,7 +156,7 @@ getState(){return gridFreeze({kinds:[...this.kinds],flaggedOnly:this.flaggedOnly
153
156
  toggle(id,open){const item=this.doc.items.find(entry=>entry.id===id);if(!item)return false;const next=open===undefined?!this.open.has(id):!!open;if(next===this.open.has(id))return true;if(next)this.open.add(id);else this.open.delete(id);this.render();this.emit("changeToggle",{list:this.doc.id,item:id,open:next},"onToggle");return true}
154
157
  focusItem(id){const toggle=[...this.list.querySelectorAll(".cx-chg-toggle")].find(button=>button.closest("[data-cx-item]").getAttribute("data-cx-item")===id);if(!toggle)return false;toggle.focus();return true}
155
158
  update(view){this.doc=changesDocument(view);this.labels=changeLabels(this.doc.labels,this.options.labels);this.source=view;this.open=new Set([...this.open].filter(id=>this.doc.items.some(item=>item.id===id)));for(const diff of this.diffs.values())diff.destroy();this.diffs.clear();this.limit=this.doc.pageSize;this.pending=null;this.list.setAttribute("aria-label",this.doc.label);this.render()}}
156
- Object.assign(ChangeListView.prototype,{row(item){const labels=this.labels,doc=this.doc,open=this.open.has(item.id),[folder,name]=changePath(item.path);const li=h("li","cx-chg-item" +(item.flags.length?" cx-chg-flagged":"")),row=h("div","cx-chg-row");li.setAttribute("data-cx-item",item.id);const toggle=h("button","cx-chg-toggle"),detailId=(this.host.id||"cx-changes")+ "-"+this.uid()+ "-detail";toggle.type="button";toggle.setAttribute("aria-expanded",String(open));toggle.setAttribute("aria-label",fill(labels.changeDetails,{name:doc.groupByFolder?name:item.path}));toggle.appendChild(changeIcon("chevron"));const badge=h("span");applyTone(badge,"cx-chg-badge",toneOf(CHANGE_TONES[item.kind]));setText(badge,item.kindLabel||labels["change"+item.kind[0].toUpperCase()+ item.kind.slice(1)]);const main=h("div","cx-chg-main"),title=h("div","cx-chg-title"),nameText=h("span","cx-chg-name");setText(nameText,doc.groupByFolder?name:item.path);nameText.title=item.path;title.appendChild(nameText);if(item.previousPath){const from=h("span","cx-chg-from");setText(from,fill(labels.changeFrom,{path:item.previousPath}));title.appendChild(from)}
159
+ Object.assign(ChangeListView.prototype,{row(item){const labels=this.labels,doc=this.doc,open=this.open.has(item.id),[folder,name]=changePath(item.path);const li=h("li","cx-chg-item" +(item.flags.length?" cx-chg-flagged":"")),row=h("div","cx-chg-row");li.setAttribute("data-cx-item",item.id);const toggle=h("button","cx-chg-toggle"),detailId=(this.host.id||"cx-changes")+ "-"+this.uid()+ "-detail";toggle.type="button";toggle.setAttribute("aria-expanded",String(open));toggle.setAttribute("aria-label",fill(labels.changeDetails,{name:doc.groupByFolder?name:item.path}));toggle.appendChild(changeIcon("chevron"));const badge=h("span");applyTone(badge,"cx-pill cx-chg-badge",toneOf(CHANGE_TONES[item.kind]));setText(badge,item.kindLabel||labels["change"+item.kind[0].toUpperCase()+ item.kind.slice(1)]);const main=h("div","cx-chg-main"),title=h("div","cx-chg-title"),nameText=h("span","cx-chg-name");setText(nameText,doc.groupByFolder?name:item.path);nameText.title=item.path;title.appendChild(nameText);if(item.previousPath){const from=h("span","cx-chg-from");setText(from,fill(labels.changeFrom,{path:item.previousPath}));title.appendChild(from)}
157
160
  main.appendChild(title);if(item.flags.length){const flags=h("div","cx-chg-flags");for(const flag of item.flags){const chip=h("span");applyTone(chip,"cx-chg-flag",toneOf(flag.tone));chip.title=flag.reason;chip.append(changeIcon("flag"),document.createTextNode(flag.label));flags.appendChild(chip)}
158
161
  main.appendChild(flags)}
159
162
  const second=[item.secondary,(item.after||item.before)?.time!=null?changeTimeText((item.after||item.before).time,doc.local,labels):null].filter(Boolean);if(second.length){const line=h("div","cx-chg-sub");setText(line,second.join(" · "));main.appendChild(line)}
@@ -186,17 +189,17 @@ function changeExportDiff(diff){if(!diff)return null;let hunks=diff.hunks;const
186
189
  function changeExportVersion(version){return version?{size:version.size,hash:version.hash,time:changeIso(version.time)}:null}
187
190
  function changeExportItem(item){return{id:item.id,kind:item.kind,kindLabel:item.kindLabel,path:item.path,previousPath:item.previousPath,secondary:item.secondary,before:changeExportVersion(item.before),after:changeExportVersion(item.after),flags:item.flags.map(flag=>({id:flag.id,label:flag.label,reason:flag.reason,tone:flag.tone})),facts:item.facts.map(fact=>({label:fact.label,before:fact.before,after:fact.after,note:fact.note})),values:{...item.values},diff:changeExportDiff(item.diff)}}
188
191
  function changeCsv(items){const keys=[...new Set(items.flatMap(item=>Object.keys(item.values)))].sort();const head=["kind","kindLabel","path","previousPath","secondary","sizeBefore","sizeAfter","hashBefore","hashAfter","timeBefore","timeAfter","flags","facts"].concat(keys.map(key=>"values."+key));const rows=[head].concat(items.map(item=>[item.kind,item.kindLabel,item.path,item.previousPath,item.secondary,item.before?.size,item.after?.size,item.before?.hash,item.after?.hash,changeIso(item.before?.time),changeIso(item.after?.time),item.flags.map(flag=>flag.label+": "+flag.reason).join(" | "),item.facts.map(fact=>fact.label+": " +(fact.before??"")+(fact.before!=null&&fact.after!=null?" → ":"")+(fact.after??"")+(fact.note?" ("+fact.note+")":"")).join(" | ")].concat(keys.map(key=>item.values[key]))));return rows.map(row=>row.map(changeCsvField).join(",")).join("\r\n")+ "\r\n"}
189
- Object.assign(ChangeListView.prototype,{actionControl(action,item){if(item&&item.actions.length&&!item.actions.includes(action.id))return null;const values=item?item.values:this.doc.values;const value=key=>key===null?null:item&&key==="path"?item.path:item&&key==="previousPath"?item.previousPath:values[key]??null;if(action.copy!==null&&value(action.copy)===null)return null;const href=action.link===null?null:changeHref(value(action.link));if(action.link!==null&&href===null)return null;if(action.reveal==="diff"&&!item.diff)return null;const control=h(href?"a":"button","cx-chg-action" +(action.icon==="none"||!item?" cx-chg-textaction":""));if(href)control.href=href;else control.type="button";control._action=action;control._item=item;control.setAttribute("data-cx-action",action.id);if(action.icon!=="none")control.appendChild(changeIcon(action.icon));if(action.icon==="none"||!item)control.appendChild(document.createTextNode(action.label));else{control.setAttribute("aria-label",action.label);control.setAttribute("data-cx-tip",action.label)}
192
+ Object.assign(ChangeListView.prototype,{actionControl(action,item){if(item&&item.actions.length&&!item.actions.includes(action.id))return null;const values=item?item.values:this.doc.values;const value=key=>key===null?null:item&&key==="path"?item.path:item&&key==="previousPath"?item.previousPath:values[key]??null;if(action.copy!==null&&value(action.copy)===null)return null;const href=action.link===null?null:changeHref(value(action.link));if(action.link!==null&&href===null)return null;if(action.reveal==="diff"&&!item.diff)return null;const control=h(href?"a":"button","cx-btn cx-chg-action" +(action.icon==="none"||!item?" cx-chg-textaction":""));control.dataset.size="sm";if(href)control.href=href;else control.type="button";control._action=action;control._item=item;control.setAttribute("data-cx-action",action.id);if(action.icon!=="none")control.appendChild(changeIcon(action.icon));if(action.icon==="none"||!item)control.appendChild(document.createTextNode(action.label));else{control.setAttribute("aria-label",action.label);control.setAttribute("data-cx-tip",action.label)}
190
193
  if(action.reason)control.setAttribute("aria-expanded",String(!!(this.pending&&this.pending.mode==="ask"&&this.pending.action===action&&this.pending.item===item)));return control},tools(){const tools=h("div","cx-chg-tools");tools.setAttribute("role","group");tools.setAttribute("aria-label",fill(this.labels.changeActions,{name:this.doc.label}));for(const action of this.doc.toolbar){const control=this.actionControl(action,null);if(control)tools.appendChild(control)}
191
- for(const format of this.doc.exports){const button=h("button","cx-chg-action cx-chg-textaction cx-chg-export");button.type="button";button._export=format;button.append(changeIcon("download"),document.createTextNode(format==="csv"?this.labels.changeExportCsv:this.labels.changeExportJson));tools.appendChild(button)}
194
+ for(const format of this.doc.exports){const button=h("button","cx-btn cx-chg-action cx-chg-textaction cx-chg-export");button.type="button";button.dataset.size="sm";button._export=format;button.append(changeIcon("download"),document.createTextNode(format==="csv"?this.labels.changeExportCsv:this.labels.changeExportJson));tools.appendChild(button)}
192
195
  return tools.childNodes.length?tools:null},detailOf(action,item,extra){return Object.assign({list:this.doc.id,action:action.id,item:item?item.id:null,kind:item?item.kind:null,path:item?item.path:null,previousPath:item?item.previousPath:null,values:{...(item?item.values:this.doc.values)},copied:null},extra)},request(detail){const event=new CustomEvent("canopyx:changeAction",{detail,bubbles:true,cancelable:true});this.host.dispatchEvent(event);let claimed=event.defaultPrevented;if(typeof this.options.onAction==="function"){try{this.options.onAction(detail,event);claimed=true}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:"onAction"},bubbles:true}))}}
193
196
  return claimed},async act(action,item,control){if(action.copy!==null){const key=action.copy,copied=await this.copyText(item&&key==="path"?item.path:item&&key==="previousPath"?item.previousPath:(item?item.values:this.doc.values)[key]);if(!this.destroyed)this.emit("changeAction",this.detailOf(action,item,{copied}),"onAction");return}
194
197
  if(action.link!==null){this.emit("changeAction",this.detailOf(action,item,{href:control.getAttribute("href")}),"onAction");return}
195
198
  if(action.reveal!=="none"){this.reveal(item.id,action.reveal);this.emit("changeAction",this.detailOf(action,item,{reveal:action.reveal}),"onAction");return}
196
199
  if(action.reason){this.pending={mode:"ask",action,item,text:"",error:false};this.render();this.focusPanel();return}
197
200
  this.send(action,item,null)},send(action,item,reason){const detail=this.detailOf(action,item,reason===null?{}:{reason});const claimed=this.request(detail);if(this.destroyed)return;if(item?!this.doc.items.includes(item):!this.doc.toolbar.includes(action)){this.pending=null;this.render();return}
198
- this.pending=claimed?null:{mode:"note",action,item,detail};this.render();if(this.pending)this.focusPanel();else this.focusAction(action,item)},reveal(id,what){this.toggle(id,true);const box=this.list.querySelector("[data-cx-item=\""+CSS.escape(id)+ "\"] > .cx-chg-detail");if(!box)return;const target=what==="diff"?box.querySelector(".cx-chg-diff .cx-diff-scroll")||box:box;if(target===box)box.tabIndex=-1;target.scrollIntoView?.({block:"nearest"});target.focus({preventScroll:true})},panel(){this.panelLife?.abort();this.panelLife=new AbortController();const pending=this.pending,labels=this.labels,signal=this.panelLife.signal,box=h("div","cx-chg-panel");box.setAttribute("role","group");box.setAttribute("aria-label",pending.action.label);const cancel=h("button","cx-chg-action cx-chg-textaction");cancel.type="button";setText(cancel,labels.changeCancel);cancel.addEventListener("click",()=>{const{action,item}=pending;this.pending=null;this.render();this.focusAction(action,item)},{signal});if(pending.mode==="note"){const text=h("p","cx-chg-note"),copy=h("button","cx-chg-action cx-chg-textaction");copy.type="button";setText(text,fill(labels.changeUnclaimed,{action:pending.action.label}));copy.append(changeIcon("copy"),document.createTextNode(labels.changeCopyRequest));copy.addEventListener("click",()=>this.copyText(JSON.stringify(pending.detail,null,2)),{signal});const buttons=h("div","cx-chg-askbtns");buttons.append(copy,cancel);box.append(text,buttons);return box}
199
- const id=(this.host.id||"cx-changes")+ "-reason-"+this.uid(),field=h("textarea","cx-chg-reasonbox"),label=h("label","cx-chg-asklabel"),hint=h("div","cx-chg-hint");field.id=id;field.rows=2;field.maxLength=1000;field.value=pending.text;field.setAttribute("aria-describedby",id+"-hint");label.htmlFor=id;setText(label,labels.changeReason);hint.id=id+"-hint";setText(hint,fill(labels.changeReasonHint,{action:pending.action.label}));const error=h("div","cx-chg-askerror");error.setAttribute("role","alert");setText(error,pending.error?labels.changeReasonRequired:"");if(pending.error)field.setAttribute("aria-invalid","true");const confirm=h("button","cx-chg-action cx-chg-textaction cx-chg-primary");confirm.type="button";setText(confirm,pending.action.label);const submit=()=>{const reason=field.value.trim();if(!reason){pending.error=true;pending.text=field.value;this.render();this.focusPanel();return}
201
+ this.pending=claimed?null:{mode:"note",action,item,detail};this.render();if(this.pending)this.focusPanel();else this.focusAction(action,item)},reveal(id,what){this.toggle(id,true);const box=this.list.querySelector("[data-cx-item=\""+CSS.escape(id)+ "\"] > .cx-chg-detail");if(!box)return;const target=what==="diff"?box.querySelector(".cx-chg-diff .cx-diff-scroll")||box:box;if(target===box)box.tabIndex=-1;target.scrollIntoView?.({block:"nearest"});target.focus({preventScroll:true})},panel(){this.panelLife?.abort();this.panelLife=new AbortController();const pending=this.pending,labels=this.labels,signal=this.panelLife.signal,box=h("div","cx-chg-panel");box.setAttribute("role","group");box.setAttribute("aria-label",pending.action.label);const cancel=h("button","cx-btn cx-chg-action cx-chg-textaction");cancel.type="button";cancel.dataset.size="sm";setText(cancel,labels.changeCancel);cancel.addEventListener("click",()=>{const{action,item}=pending;this.pending=null;this.render();this.focusAction(action,item)},{signal});if(pending.mode==="note"){const text=h("p","cx-chg-note"),copy=h("button","cx-btn cx-chg-action cx-chg-textaction");copy.type="button";copy.dataset.size="sm";setText(text,fill(labels.changeUnclaimed,{action:pending.action.label}));copy.append(changeIcon("copy"),document.createTextNode(labels.changeCopyRequest));copy.addEventListener("click",()=>this.copyText(JSON.stringify(pending.detail,null,2)),{signal});const buttons=h("div","cx-chg-askbtns");buttons.append(copy,cancel);box.append(text,buttons);return box}
202
+ const id=(this.host.id||"cx-changes")+ "-reason-"+this.uid(),field=h("textarea","cx-textarea cx-chg-reasonbox"),label=h("label","cx-chg-asklabel"),hint=h("div","cx-chg-hint");field.id=id;field.rows=2;field.maxLength=1000;field.value=pending.text;field.setAttribute("aria-describedby",id+"-hint");label.htmlFor=id;setText(label,labels.changeReason);hint.id=id+"-hint";setText(hint,fill(labels.changeReasonHint,{action:pending.action.label}));const error=h("div","cx-chg-askerror");error.setAttribute("role","alert");setText(error,pending.error?labels.changeReasonRequired:"");if(pending.error)field.setAttribute("aria-invalid","true");const confirm=h("button","cx-btn cx-chg-action cx-chg-textaction cx-chg-primary");confirm.dataset.variant="primary";confirm.dataset.size="sm";confirm.type="button";setText(confirm,pending.action.label);const submit=()=>{const reason=field.value.trim();if(!reason){pending.error=true;pending.text=field.value;this.render();this.focusPanel();return}
200
203
  this.send(pending.action,pending.item,reason)};confirm.addEventListener("click",submit,{signal});field.addEventListener("input",()=>{pending.text=field.value},{signal});field.addEventListener("keydown",event=>{if(event.key==="Escape"){event.preventDefault();event.stopPropagation();cancel.click()}
201
204
  else if(event.key==="Enter"&&(event.ctrlKey||event.metaKey)){event.preventDefault();submit()}},{signal});const buttons=h("div","cx-chg-askbtns");buttons.append(confirm,cancel);box.append(label,field,hint,error,buttons);return box},focusPanel(){const target=this.root.querySelector(".cx-chg-panel :is(textarea, button)");if(target)target.focus()},focusAction(action,item){const scope=item?this.list.querySelector("[data-cx-item=\""+CSS.escape(item.id)+ "\"] > .cx-chg-row"):this.bar;scope?.querySelector("[data-cx-action=\""+CSS.escape(action.id)+ "\"]")?.focus()},exportItems(format){const items=this.folders(this.visible()).flatMap(group=>group[2]),name=(this.doc.id.replace(/[^\w.-]+/g, "-") || "changes") + "." + format;
202
205
  const text=format==="csv"?""+changeCsv(items)
@@ -204,7 +207,7 @@ const text=format==="csv"?""+changeCsv(items)
204
207
  emit(name,detail,callback){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true});this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:callback},bubbles:true}))}}}
205
208
  stateText(state){return this.labels["change"+state[0].toUpperCase()+ state.slice(1)]}
206
209
  badge(state,className){const badge=h("span"),[icon,tone]=PROPAGATION_LOOK[state];applyTone(badge,className +(state==="fault"||state==="propagating"?" cx-prop-attn":""),toneOf(tone));const text=h("span");setText(text,this.stateText(state));badge.append(changeIcon(icon),text);return badge}
207
- render(){const doc=this.doc,labels=this.labels,witnesses=doc.witnesses;const seen=witnesses.map(witness=>witness.firstSeen).filter(time=>time!==null),reports=witnesses.flatMap(witness=>[witness.firstSeen,witness.lastSeen]).filter(time=>time!==null);const start=doc.start??(seen.length?Math.min(...seen):null),end=doc.asOf??(reports.length?Math.max(...reports):null),span=start!==null&&end!==null?Math.max(1,end-start):0;const overall=doc.state||witnesses.reduce((worst,witness)=>PROPAGATION_RANK[witness.state]>PROPAGATION_RANK[worst]?witness.state:worst,"converged");const converged=witnesses.filter(witness=>witness.state==="converged").length;const head=h("div","cx-prop-head"),summary=h("span","cx-prop-summary"),counts=h("ul","cx-prop-counts");setText(summary,doc.summary||fill(labels.changeProgress,{converged:this.format.format(converged),total:this.format.format(witnesses.length)}));head.append(this.badge(overall,"cx-prop-state"),summary);for(const state of PROPAGATION_STATES){const count=witnesses.filter(witness=>witness.state===state).length;if(!count)continue;const item=h("li","cx-prop-count"),badge=this.badge(state,"cx-prop-key"),number=h("span","cx-prop-number");setText(number,this.format.format(count));item.append(badge,number);counts.appendChild(item)}
210
+ render(){const doc=this.doc,labels=this.labels,witnesses=doc.witnesses;this.timeFormat=humanTimeFormat(this.lang,doc.local,labels.changeUtcTime);const seen=witnesses.map(witness=>witness.firstSeen).filter(time=>time!==null),reports=witnesses.flatMap(witness=>[witness.firstSeen,witness.lastSeen]).filter(time=>time!==null);const start=doc.start??(seen.length?Math.min(...seen):null),end=doc.asOf??(reports.length?Math.max(...reports):null),span=start!==null&&end!==null?Math.max(1,end-start):0;const overall=doc.state||witnesses.reduce((worst,witness)=>PROPAGATION_RANK[witness.state]>PROPAGATION_RANK[worst]?witness.state:worst,"converged");const converged=witnesses.filter(witness=>witness.state==="converged").length;const head=h("div","cx-prop-head"),summary=h("span","cx-prop-summary"),counts=h("ul","cx-prop-counts");setText(summary,doc.summary||fill(labels.changeProgress,{converged:this.format.format(converged),total:this.format.format(witnesses.length)}));head.append(this.badge(overall,"cx-prop-state"),summary);for(const state of PROPAGATION_STATES){const count=witnesses.filter(witness=>witness.state===state).length;if(!count)continue;const item=h("li","cx-prop-count"),badge=this.badge(state,"cx-prop-key"),number=h("span","cx-prop-number");setText(number,this.format.format(count));item.append(badge,number);counts.appendChild(item)}
208
211
  counts.setAttribute("aria-label",labels.changeState);const table=h("table","cx-prop-table"),thead=h("thead"),heads=h("tr");table.setAttribute("aria-label",doc.label);for(const text of[doc.title||labels.changeReplica,labels.changeState,labels.changeFirstSeen,labels.changeLastSeen,labels.changeTimeline]){const th=h("th");th.scope="col";setText(th,text);heads.appendChild(th)}
209
212
  thead.appendChild(heads);table.appendChild(thead);const groups=new Map();for(const witness of witnesses){if(!groups.has(witness.group))groups.set(witness.group,[]);groups.get(witness.group).push(witness)}
210
213
  for(const[group,members]of groups){const body=h("tbody");if(group!==null){const tr=h("tr","cx-prop-group"),th=h("th"),count=h("span","cx-prop-groupcount");th.scope="rowgroup";th.colSpan=5;setText(th,group);setText(count," · "+changeCount(doc.groupCount||"{count}",members.length,this.lang,{count:this.format.format(members.length)}));th.appendChild(count);tr.appendChild(th);body.appendChild(tr)}
@@ -213,9 +216,9 @@ this.root.replaceChildren(head,counts,table)}
213
216
  row(witness,start,span){const labels=this.labels,tr=h("tr","cx-prop-row cx-prop-"+witness.state),name=h("th"),state=h("td","cx-prop-statecell"),first=h("td","cx-prop-first"),last=h("td","cx-prop-last"),lane=h("td","cx-prop-lanecell");name.scope="row";const label=h("span","cx-prop-name");setText(label,witness.label);name.appendChild(label);if(witness.source){const mark=h("span","cx-prop-source");setText(mark,labels.changeSource);name.appendChild(mark)}
214
217
  if(witness.secondary){const sub=h("span","cx-prop-sub");setText(sub,witness.secondary);name.appendChild(sub)}
215
218
  state.appendChild(this.badge(witness.state,"cx-prop-badge"));if(witness.note){const note=h("span","cx-prop-sub");setText(note,witness.note);state.appendChild(note)}
216
- let delay=null;if(witness.firstSeen!==null){const time=h("span");setText(time,changeTimeText(witness.firstSeen,this.doc.local,labels));first.appendChild(time);if(start!==null&&!witness.source){delay=fill(labels.changeDelay,{duration:changeDuration(witness.firstSeen-start,labels,this.format)});const sub=h("span","cx-prop-delay");setText(sub,delay);first.appendChild(sub)}}else{const none=h("span","cx-prop-none");setText(none,labels.changeNotSeen);first.appendChild(none)}
217
- setText(last,witness.lastSeen!==null?changeTimeText(witness.lastSeen,this.doc.local,labels):"");const track=h("div","cx-prop-lane"),fillBar=h("div","cx-prop-fill");track.setAttribute("aria-hidden","true");const reached=witness.firstSeen!==null&&span?Math.min(1,Math.max(0,(witness.firstSeen-start)/ span)) : witness.state === "unknown" || !span ? 0 : 1;
219
+ let delay=null;if(witness.firstSeen!==null){const time=h("span");setText(time,this.timeFormat.format(witness.firstSeen));first.appendChild(time);if(start!==null&&!witness.source){delay=fill(labels.changeDelay,{duration:changeDuration(witness.firstSeen-start,labels,this.format)});const sub=h("span","cx-prop-delay");setText(sub,delay);first.appendChild(sub)}}else{const none=h("span","cx-prop-none");setText(none,labels.changeNotSeen);first.appendChild(none)}
220
+ setText(last,witness.lastSeen!==null?this.timeFormat.format(witness.lastSeen):"");const track=h("div","cx-prop-lane"),fillBar=h("div","cx-prop-fill");track.setAttribute("aria-hidden","true");const reached=witness.firstSeen!==null&&span?Math.min(1,Math.max(0,(witness.firstSeen-start)/ span)) : witness.state === "unknown" || !span ? 0 : 1;
218
221
  fillBar.style.width=(reached*100).toFixed(1)+ "%";applyTone(fillBar,"cx-prop-fill" +(witness.firstSeen===null&&witness.state!=="unknown"?" cx-prop-open":""),toneOf(PROPAGATION_LOOK[witness.state][1]));track.appendChild(fillBar);if(delay)track.title=delay;lane.appendChild(track);tr.append(name,state,first,last,lane);return tr}
219
222
  update(view){this.doc=propagationDocument(view);this.labels=changeLabels(this.doc.labels,this.options.labels);this.source=view;this.render()}}
220
223
  function propagationCreate(host,options){const view=new PropagationView(host,options);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("propagationReady",{propagation:view.doc.id},"onReady");return impl}),document:()=>view.source||options.view,update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.root.remove();host.removeAttribute("data-cx-ready")}};return impl}
221
- Object.assign(CHANGES_IMPL,{diff:diffCreate,changes:changeListCreate,propagation:propagationCreate});installed.add(context.CHANGES_IMPL)}};(global.CanopyXParts||=[]).push(["changes",part])})(typeof window!=="undefined"?window:globalThis);
224
+ Object.assign(CHANGES_IMPL,{diff:diffCreate,changes:changeListCreate,propagation:propagationCreate,validatePropagation:propagationDocument});installed.add(context.CHANGES_IMPL)}};(global.CanopyXParts||=[]).push(["changes",part])})(typeof window!=="undefined"?window:globalThis);
@@ -0,0 +1,37 @@
1
+ .cx-compare-summary{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--hfx-space-3,0.75rem) var(--hfx-space-6,2rem);width:100%;box-sizing:border-box;padding:calc(var(--hfx-space-1,0.25rem)*3.5) var(--hfx-space-4,1rem);border-block-end:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font:inherit;}
2
+ .cx-compare-counts{display:flex;flex-wrap:wrap;gap:calc(var(--hfx-space-1,0.25rem)*2.5) var(--hfx-space-6,2rem);}
3
+ .cx-compare-count{display:inline-flex;align-items:baseline;gap:var(--hfx-space-1-5,6px);white-space:nowrap;}
4
+ .cx-compare-count strong{font-size:var(--hfx-type-title-size,1.25rem);font-variant-numeric:tabular-nums;}
5
+ .cx-compare-toggle{display:inline-flex;align-items:center;gap:var(--hfx-space-2,0.5rem);white-space:nowrap;cursor:pointer;}
6
+ .cx-compare-toggle input{accent-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));width:calc(var(--hfx-space-1,0.25rem)*4.5);height:calc(var(--hfx-space-1,0.25rem)*4.5);margin:0;}
7
+ .cx-compare-toggle input:focus-visible{outline:var(--hfx-focus-width,2px) solid var(--hfx-color-focus-ring,light-dark(#02726a,#37c9b8));outline-offset:var(--hfx-focus-offset,2px);}
8
+ .cx-compare-status{font-size:var(--hfx-type-small-size,0.8125rem);font-weight:var(--hfx-weight-semibold,600);}
9
+ .cx-compare-added{color:var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));}
10
+ .cx-compare-removed{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
11
+ .cx-compare-changed{color:var(--hfx-color-status-warn-fg,light-dark(#8c5c05,#eeb640));}
12
+ .cx-compare-unchanged{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
13
+ .cx-before-after{display:inline-flex;align-items:baseline;gap:var(--hfx-space-1-5,6px);max-width:100%;line-height:inherit;}
14
+ .cx-wrap .cx-before-after{flex-wrap:wrap;}
15
+ .cx-before-after del{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));text-decoration-thickness:var(--hfx-border-width,1px);overflow-wrap:anywhere;}
16
+ .cx-before-after ins{color:var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));text-decoration:none;overflow-wrap:anywhere;}
17
+ @media(max-width:480px){.cx-compare-summary{padding:var(--hfx-space-3,0.75rem);gap:calc(var(--hfx-space-1,0.25rem)*3.5);}.cx-compare-counts{gap:var(--hfx-space-2,0.5rem) var(--hfx-space-4,1rem);}.cx-compare-toggle{min-height:calc(var(--hfx-space-1,0.25rem)*7);}}
18
+ @media print{.cx-compare-toggle{display:none;}.cx-compare-summary{break-inside:avoid;}}
19
+ .cx-compare-column-counts{display:flex;flex-wrap:wrap;gap:var(--hfx-space-1,0.25rem) var(--hfx-space-4,1rem);flex-basis:100%;color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));font-size:var(--hfx-type-caption-size,0.75rem);}
20
+ .cx-compare-column-counts > span{white-space:nowrap;}
21
+ .cx-compare-column-counts[hidden]{display:none;}
22
+ .cx-compare-view.cx-phone .cx-phone-fields{gap:var(--hfx-space-1,0.25rem) var(--hfx-space-2,0.5rem);}
23
+ .cx-compare-view.cx-phone .cx-phone-field{overflow:visible;}
24
+ .cx-compare-view.cx-phone .cx-phone-value{white-space:normal;overflow:visible;}
25
+ body .cx-host.cx-compare-drawer{height:var(--cx-ui-viewport-block);min-height:0;padding:0;border-radius:var(--hfx-radius-none,0);}
26
+ .cx-compare-drawer .cx-drawer-body{padding-block:var(--hfx-space-3,0.75rem);}
27
+ .cx-compare-drawer .cx-record-section{margin:0;}
28
+ .cx-compare-drawer .cx-drawer-body .cx-dl{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,2fr);gap:var(--hfx-space-3,0.75rem) var(--hfx-space-4,1rem);margin:0;padding:var(--hfx-space-3,0.75rem) 0;}
29
+ .cx-compare-drawer .cx-dl dt{font-size:var(--hfx-type-small-size,0.8125rem);color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));}
30
+ .cx-compare-drawer .cx-dl dd{margin:0;}
31
+ .cx-compare-record-changes{margin:0;padding:0 0 var(--hfx-space-3,0.75rem);border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
32
+ .cx-compare-record-changes h3,.cx-compare-drawer .cx-drawer-body .cx-record-toggle{margin:0;font-size:var(--hfx-type-small-size,0.8125rem);font-weight:var(--hfx-weight-semibold,600);letter-spacing:normal;text-transform:none;color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));}
33
+ .cx-compare-drawer .cx-drawer-body .cx-record-toggle{padding:var(--hfx-space-3,0.75rem) 0 var(--hfx-space-1,0.25rem);justify-content:space-between;}
34
+ .cx-compare-drawer[data-phone="true"] .cx-dl{grid-template-columns:minmax(0,1fr);gap:var(--hfx-space-1,0.25rem);}
35
+ .cx-compare-drawer[data-phone="true"] .cx-dl dd{margin-block-end:var(--hfx-space-2,0.5rem);}
36
+ @media (forced-colors:active){.cx-before-after del{color:CanvasText;}.cx-before-after ins{color:CanvasText;}}
37
+ .cx-compare-removed-row .cx-before-after del{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
@@ -0,0 +1,12 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"2df5f137f630380f",requires:["presenters"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,plural,cellValue,numberFormat,gridQueryStart,gridQuery}=context;const COMPARE_LABELS={compareSummary:"Dataset comparison",compareAdded:"Added",compareRemoved:"Removed",compareChanged:"Changed",compareUnchanged:"Unchanged",compareOnlyChanges:"Changed only",compareColumnChanges:"[one]{column}: {count} changed cell[other]{column}: {count} changed cells",compareChanges:"Changes"};function compareDefinition(grid){const value=grid.view.compare,columns=new Set(grid.columns.map(column=>column.id));if(!value||typeof value!=="object"||!columns.has(value.statusColumn)||
2
+ typeof value.onlyChanges!=="boolean"||!value.counts||!value.columns||
3
+ ["added","removed","changed","unchanged"].some(name=>!Number.isSafeInteger(value.counts[name])||value.counts[name]<0)||
4
+ Object.entries(value.columns).some(([id,count])=>!columns.has(id)||id===value.statusColumn||!Number.isSafeInteger(count)||count<0))
5
+ throw new Error("CanopyX: invalid dataset comparison.");return value}
6
+ function compareOnlyFilter(grid,filter){return filter.column===grid.view.compare.statusColumn&&filter.operator==="ne"&&filter.values?.length===1&&filter.values[0]==="unchanged"}
7
+ Object.assign(DataGrid.prototype,{startDatasetCompare(){if(this.compareStrip)return;this.labels=Object.freeze({...COMPARE_LABELS,...this.labels});const info=compareDefinition(this),strip=this.compareStrip=h("div","cx-compare-summary");strip.setAttribute("role","region");strip.setAttribute("aria-label",this.labels.compareSummary);const counts=h("div","cx-compare-counts"),number=numberFormat(this.lang);for(const name of["added","removed","changed"]){const item=h("span","cx-compare-count cx-compare-"+name),count=h("strong"),label=h("span");setText(count,number.format(info.counts[name]));setText(label,this.labels["compare"+name[0].toUpperCase()+ name.slice(1)]);item.append(count,label);counts.append(item)}
8
+ const toggle=h("label","cx-compare-toggle"),input=this.compareToggle=h("input"),caption=h("span");input.type="checkbox";input.setAttribute("role","switch");setText(caption,this.labels.compareOnlyChanges);input.checked=info.onlyChanges;toggle.append(input,caption);strip.append(counts,toggle);this.host.insertBefore(strip,this.grid);input.addEventListener("change",()=>{input.disabled=true;this.setOnlyChanges(input.checked).catch(error=>this.emit("error",{error})).finally(()=>{if(!this.destroyed){input.disabled=false;this.syncDatasetCompare()}})},{signal:this.lifetime.signal});this.host.addEventListener("canopyx:state",()=>this.syncDatasetCompare(),{signal:this.lifetime.signal});const status=this.columns.find(column=>column.id===info.statusColumn);if(!status.render)status.render=(row,element)=>{const state=cellValue(row.cells[status.id]),label=this.labels["compare"+String(state)[0]?.toUpperCase()+ String(state).slice(1)];element.closest(".cx-row")?.classList.toggle("cx-compare-removed-row",state==="removed");const badge=h("span","cx-compare-status cx-compare-"+state);setText(badge,label||state);element.append(badge);return true};const details=h("div","cx-compare-column-counts");for(const column of this.columns){const count=Object.hasOwn(info.columns,column.id)?info.columns[column.id]:0;if(!count)continue;const caption=fill(plural(this.labels.compareColumnChanges,count,this.lang),{column:column.title,count:number.format(count)});column.headerDescription=[column.headerDescription,caption].filter(Boolean).join(". ");const item=h("span");setText(item,caption);details.append(item)}
9
+ details.hidden=!details.firstChild;strip.append(details);this.host.classList.add("cx-compare-view");this.buildHeader();if(info.onlyChanges){const query=this.view.query||{};this.view.query={...query,filters:[...(query.filters||[]).filter(filter=>!compareOnlyFilter(this,filter)),{column:info.statusColumn,operator:"ne",values:["unchanged"]}]};this.defaultState.query=gridQuery(this.view,gridQueryStart(this.view.query))}},getOnlyChanges(){return!!this.view.compare&&!!this.getQuery()?.filters?.some(filter=>compareOnlyFilter(this,filter))},setOnlyChanges(enabled){if(typeof enabled!=="boolean"||!this.view.compare)throw new Error("CanopyX: only-changes requires a comparison and a boolean.");const query=this.queryForChange(true),filters=(query.filters||[]).filter(filter=>!compareOnlyFilter(this,filter));if(enabled)filters.push({column:this.view.compare.statusColumn,operator:"ne",values:["unchanged"]});return this.setQuery(gridQueryStart(query,{filters})).then(result=>{this.syncDatasetCompare();return result})},syncDatasetCompare(){if(this.compareToggle)this.compareToggle.checked=this.getOnlyChanges()}});const COMPARE_RECORD_BASE={phoneLineColumns:DataGrid.prototype.phoneLineColumns,filterCaption:DataGrid.prototype.filterCaption};Object.assign(DataGrid.prototype,{filterCaption(filter){return this.view.compare&&compareOnlyFilter(this,filter)?this.labels.compareOnlyChanges:COMPARE_RECORD_BASE.filterCaption?.call(this,filter)},phoneLineColumns(row){if(!this.view.compare||!row)return COMPARE_RECORD_BASE.phoneLineColumns.call(this,row);if(this.host.clientWidth>640||(this.layoutMode||"fit")!=="fit")return[];return cellValue(row.cells[this.view.compare.statusColumn])==="changed"?
10
+ this.hiddenViewColumns().filter(column=>row.cells[column.id]?.changes?.length):[]}});DataGrid.drawerHook(5,function(dialog){if(!this.view.compare)return;dialog.classList.add("cx-compare-drawer");dialog.dataset.modal="true";dialog._cxOverlay.setModal(true);let paints;dialog.addEventListener("close",()=>paints?.abort(),{once:true});dialog.addEventListener("cx-drawer-paint",()=>{paints?.abort();paints=new AbortController();const row=dialog._cxRecord,body=dialog.querySelector(".cx-drawer-body");body.querySelector(".cx-compare-record-changes")?.remove();const changed=this.columns.filter(column=>column.drawer!==false&&row.cells[column.id]?.changes?.length);const status=cellValue(row.cells[this.view.compare.statusColumn]);if(status==="removed")for(const column of changed){for(const block of body.querySelectorAll('.cx-record-block[data-cx-field="'+CSS.escape(column.id)+ '"]')){const list=h("dl","cx-dl"),term=h("dt"),value=h("dd"),old=h("del");setText(term,column.title);term.dataset.cxField=value.dataset.cxField=column.id;const earlier={...row,cells:{...row.cells,[column.id]:row.cells[column.id].changes[0].before}};this.paintDrawerValue(column,earlier,old,{signal:paints.signal});value.append(old);list.append(term,value);block.replaceWith(list)}}
11
+ if(!changed.length||status!=="changed")return;const section=h("section","cx-compare-record-changes"),heading=h("h3"),list=h("dl","cx-dl");setText(heading,this.labels.compareChanges);section.append(heading,list);section.setAttribute("aria-label",this.labels.compareChanges);for(const column of changed){const term=h("dt"),value=h("dd");setText(term,column.title);term.dataset.cxField=value.dataset.cxField=column.id;this.paintDrawerValue(column,row,value,{signal:paints.signal});list.append(term,value);for(const field of body.querySelectorAll('[data-cx-field="'+CSS.escape(column.id)+ '"]')){const block=field.closest(".cx-record-block");if(block)block.remove();else field.remove()}}
12
+ body.prepend(section)})});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["compare",part])})(typeof window!=="undefined"?window:globalThis);
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"d74a01c46f1c40cc",install(context){if(installed.has(context.GRID_DATA_IMPL))return;const{GRID_DATA_IMPL,PROTOCOL,validateGridRows,inlineGridSource,loadSidecar,gridIdOrder}=context;const ROW_SCRIPTS=':scope>script[type="application/x-canopyx-rows"]';const nextTask=()=>globalThis.scheduler?.yield?scheduler.yield():new Promise(resolve=>setTimeout(resolve));async function inflateRows(text){if(typeof DecompressionStream!=="function")throw new Error("CanopyX: this browser cannot read compressed row data.");let bytes;if(Uint8Array.fromBase64)bytes=Uint8Array.fromBase64(text);else{const binary=atob(text);bytes=new Uint8Array(binary.length);for(let i=0;i<binary.length;i++)bytes[i]=binary.charCodeAt(i)}
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"bab770170bd2cfdd",install(context){if(installed.has(context.GRID_DATA_IMPL))return;const{GRID_DATA_IMPL,PROTOCOL,validateGridRows,repairGridCell,inlineGridSource,loadSidecar,gridIdOrder,gridInlineRevision}=context;const ROW_SCRIPTS=':scope>script[type="application/x-canopyx-rows"]';const nextTask=()=>globalThis.scheduler?.yield?scheduler.yield():new Promise(resolve=>setTimeout(resolve));async function inflateRows(text){if(typeof DecompressionStream!=="function")throw new Error("CanopyX: this browser cannot read compressed row data.");let bytes;if(Uint8Array.fromBase64)bytes=Uint8Array.fromBase64(text);else{const binary=atob(text);bytes=new Uint8Array(binary.length);for(let i=0;i<binary.length;i++)bytes[i]=binary.charCodeAt(i)}
2
2
  return new Response(new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"))).text()}
3
3
  function rowDataSource(grid,outer){const view=grid.view,data=view.rowData,rows=view.rows,ids=new Set(),declared=new Set(view.columns.map(column=>column.id));const columns=data.columns,chunks=data.chunks,sidecar=data.base!==undefined;if(data.version!==1||!Array.isArray(columns)||!columns.length||new Set(columns).size!==columns.length||columns.some(id=>!declared.has(id))||
4
4
  ![undefined,"gzip"].includes(data.encoding)||![undefined,true,false].includes(data.ordered)||
@@ -7,9 +7,9 @@ sidecar&&typeof data.base!=="string"||
7
7
  throw new Error("CanopyX: invalid grid rowData.");const scripts=!sidecar&&!Array.isArray(chunks)?grid.host.querySelectorAll(ROW_SCRIPTS):null;const total=Array.isArray(chunks)?chunks.length:chunks;if(scripts&&scripts.length!==total)throw new Error("CanopyX: the grid has "+scripts.length+" row data scripts, not "+total+".");const files=[];rows.length=0;const read=index=>Array.isArray(chunks)?chunks[index]:sidecar?files[index]||=loadSidecar(data.base,view.id,"rows/"+index):scripts[index].textContent;const expand=row=>{if(!Array.isArray(row)||row.length>columns.length+1)throw new Error("CanopyX: invalid compact grid row.");const key=row[0],cells={};for(let c=1;c<row.length;c++){if(row[c]===undefined)throw new Error("CanopyX: compact grid cells cannot be undefined; use null for an empty cell.");if(row[c]!=null)columns[c-1]==="__proto__"?Object.defineProperty(cells,"__proto__",{value:row[c],enumerable:true,writable:true,configurable:true}):cells[columns[c-1]]=row[c]}
8
8
  if(key===null||typeof key!=="object")return{id:key,cells};const record={id:key.id,cells};if(key.tone!==undefined)record.tone=key.tone;if(key.className!==undefined)record.className=key.className;return record};let previous=null,finished=false,ordered=data.ordered===true,legacyOrder=false,advance,progress;const retirePrefix=()=>{grid.pages.clear();grid.rowCache.clear();grid.placeholders.clear();grid.focused=null;if(grid.snapshot&&!grid.pendingQuery)grid.setQuery(grid.queryForChange(true),{pageLength:grid.pageLength,allRows:grid.pageAll}).catch(()=>{});grid.version++;grid.scheduleRender()};const waitForChunk=()=>progress=new Promise(resolve=>advance=resolve);waitForChunk();const done=(async()=>{const active=()=>{if(grid.destroyed)throw new DOMException("Grid was destroyed.","AbortError")};for(let index=0;index<total;index++){active();let chunk=await read(index);active();if(sidecar&&index+1<total)read(index+1).catch(()=>{});if(typeof chunk==="string"){if(data.encoding){chunk=await inflateRows(chunk);active()}
9
9
  chunk=JSON.parse(chunk)}
10
- if(!Array.isArray(chunk))throw new Error("CanopyX: a row data chunk is not an array of rows.");const decoded=chunk.map(expand);validateGridRows(decoded,view.columns,ids);if(ordered)for(const row of decoded){if(previous!==null&&gridIdOrder(previous,row.id)>=0){if(!(previous<row.id))throw new Error("CanopyX: row data is not in its declared identity order.");ordered=false;legacyOrder=true;retirePrefix();break}
10
+ if(!Array.isArray(chunk))throw new Error("CanopyX: a row data chunk is not an array of rows.");const decoded=validateGridRows(chunk.map(expand),view.columns,ids,repairGridCell);if(ordered)for(const row of decoded){if(previous!==null&&gridIdOrder(previous,row.id)>=0){if(!(previous<row.id))throw new Error("CanopyX: row data is not in its declared identity order.");ordered=false;legacyOrder=true;retirePrefix();break}
11
11
  previous=row.id}
12
12
  for(const row of decoded)rows.push(row);if(rows.length>data.count)break;files[index]=null;advance();waitForChunk();await nextTask()}
13
- active();if(rows.length!==data.count)throw new Error("CanopyX: row data holds "+rows.length+" rows, not "+data.count+".");finished=true;ids.clear();if(legacyOrder)retirePrefix();if(Array.isArray(chunks))chunks.fill(null);scripts?.forEach(script=>{script.textContent=""});grid.version++;grid.scheduleRender()})();done.catch(()=>outer.reset());done.then(()=>{if(!grid.destroyed){grid.version++;grid.scheduleRender?.()}},()=>{});let complete=null;outer.invalidate=()=>{complete=null};const all=async()=>{await done;return complete||=inlineGridSource(view)};outer.decoded=done;if(!Object.hasOwn(grid,"columnFacetItems"))grid.columnFacetItems=async function(...args){await outer.decoded;return Object.getPrototypeOf(this).columnFacetItems.apply(this,args)};if(view.reporting?.facetColumns?.length)outer.facet=async(request,options)=>(await all()).facet(request,options);return{async query(query,options){if(ordered&&!view.reporting&&!view.footers?.length&&!query.search&&!query.filters.length&&!query.filterGroup&&!query.sort.length){const end=Math.min(data.count,query.offset+query.limit);while(rows.length<end&&!finished)await Promise.race([progress,done]);if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");if(!finished&&ordered)return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:"inline",offset:query.offset,total:data.count,matched:data.count,items:rows.slice(query.offset,end)}}
13
+ active();if(rows.length!==data.count)throw new Error("CanopyX: row data holds "+rows.length+" rows, not "+data.count+".");finished=true;ids.clear();if(legacyOrder)retirePrefix();if(Array.isArray(chunks))chunks.fill(null);scripts?.forEach(script=>{script.textContent=""});grid.version++;grid.scheduleRender()})();done.catch(()=>outer.reset());done.then(()=>{if(!grid.destroyed){grid.version++;grid.scheduleRender?.()}},()=>{});let complete=null;outer.invalidate=()=>{complete=null};const all=async()=>{await done;return complete||=inlineGridSource(view)};outer.decoded=done;if(!Object.hasOwn(grid,"columnFacetItems"))grid.columnFacetItems=async function(...args){await outer.decoded;return Object.getPrototypeOf(this).columnFacetItems.apply(this,args)};if(view.reporting?.facetColumns?.length)outer.facet=async(request,options)=>(await all()).facet(request,options);return{async query(query,options){if(ordered&&!view.reporting&&!view.footers?.length&&!view.columns.some(column=>column.calculation!==undefined)&&!query.search&&!query.filters.length&&!query.filterGroup&&!query.tupleFilters?.length&&!query.sort.length){const end=Math.min(data.count,query.offset+query.limit);while(rows.length<end&&!finished)await Promise.race([progress,done]);if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");if(!finished&&ordered)return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:gridInlineRevision(view),offset:query.offset,total:data.count,matched:data.count,items:rows.slice(query.offset,end)}}
14
14
  return(await all()).query(query,options)}}}
15
15
  GRID_DATA_IMPL.source=rowDataSource;installed.add(context.GRID_DATA_IMPL)}};(global.CanopyXParts||=[]).push(["data",part])})(typeof window!=="undefined"?window:globalThis);
@@ -18,8 +18,8 @@
18
18
  .cx-editbar[hidden]{display:none;}
19
19
  .cx-editbar-text{margin-right:var(--cx-size-4px);white-space:nowrap;}
20
20
  .cx-editbar-errors{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
21
- .cx-data-grid .cx-editbar .cx-btn{height:var(--cx-size-28px);min-height:var(--cx-size-28px);font-size:var(--cx-size-13px);padding:0 var(--cx-size-9px);}
22
- .cx-data-grid .cx-editbar .icon{width:var(--cx-size-28px);padding:0;}
21
+ .cx-data-grid .cx-editbar :is(.cx-btn,.cx-button){block-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));min-block-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));padding:0 var(--hfx-space-2,0.5rem);}
22
+ .cx-data-grid .cx-editbar .icon{inline-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));padding:0;}
23
23
  .cx-host .cx-edit-save,.cx-host .cx-edit-save:hover{background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));border-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-accent-contrast,light-dark(#ffffff,#0c0d10));}
24
24
  .cx-host .cx-edit-save:disabled{opacity:.45;}
25
25
  .cx-phone .cx-editbar{order:10;flex:1 1 100%;flex-wrap:wrap;height:auto;min-height:var(--cx-size-44px);padding-block:var(--cx-size-4px);}
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"579a74b205923fd5",requires:["filter-editor","forms"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,FORMS_IMPL,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}=context;const{EDIT_EDITORS,editOptions,formRuleSpec,formCheck,FORM_LABELS}=FORMS_IMPL;const EDIT_LABELS={...FORM_LABELS,gridEditCell:"Edit {column}",gridEditSave:"Save",gridEditCancel:"Cancel",gridEditDiscard:"Discard",gridEditUndo:"Undo",gridEditRedo:"Redo",gridEditEdited:"Edited",gridEditChanges:"Unsaved changes: {count}",gridEditErrors:"Errors: {count}",gridEditSaving:"Saving…",gridEditSaved:"Saved rows: {count}",gridEditFailed:"Rows not saved: {count}",gridEditConflict:"Someone else changed this row. Reload it and try again.",gridEditRejected:"Not saved: {error}",gridEditNotSaved:"Not saved.",gridEditUndone:"Undone: {column}",gridEditRedone:"Redone: {column}"};const editCopy=value=>gridFreeze(JSON.parse(JSON.stringify(value===undefined?null:value)));const editSame=(left,right)=>left===right||left==null&&right==null;const editScalar=value=>value===null||typeof value==="string"||typeof value==="boolean"||Number.isFinite(value);const editBad=(id,problem)=>{throw new Error("CanopyX: column "+id+" "+problem+".")};function editSpec(column,editors,columns){const id=column.id,raw=column.editor===undefined?{}:typeof column.editor==="string"?{type:column.editor}:column.editor;const rules=column.validation===undefined?{}:column.validation,object=value=>value&&typeof value==="object"&&!Array.isArray(value);if(!object(raw)||!object(rules))editBad(id,"has an invalid editor or validation");const type=raw.type??column.kind;if(typeof type!=="string"||!Object.hasOwn(editors,type))editBad(id,"names an editor nobody registered");const fits={number:["number"],text:["text"],toggle:["text"],date:["datetime","text"],datetime:["datetime","text"],time:["datetime","text"]}[type];if(fits&&!fits.includes(column.kind))editBad(id,"cannot use a "+type+" editor for its kind");const{pattern,rules:checks}=formRuleSpec(raw,rules,problem=>editBad(id,problem));const lookup=raw.lookup??(type==="lookup"?{}:null);if(lookup&&(!object(lookup)||lookup.column!==undefined&&!columns.some(item=>item.id===lookup.column)||
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"25fd83789bef4fcb",requires:["filter-editor","forms"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,FORMS_IMPL,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}=context;const{EDIT_EDITORS,editOptions,formRuleSpec,formCheck,FORM_LABELS}=FORMS_IMPL;const EDIT_LABELS={...FORM_LABELS,gridEditCell:"Edit {column}",gridEditSave:"Save",gridEditCancel:"Cancel",gridEditDiscard:"Discard",gridEditUndo:"Undo",gridEditRedo:"Redo",gridEditEdited:"Edited",gridEditChanges:"Unsaved changes: {count}",gridEditErrors:"Errors: {count}",gridEditSaving:"Saving…",gridEditSaved:"Saved rows: {count}",gridEditFailed:"Rows not saved: {count}",gridEditConflict:"Someone else changed this row. Reload it and try again.",gridEditRejected:"Not saved: {error}",gridEditNotSaved:"Not saved.",gridEditUndone:"Undone: {column}",gridEditRedone:"Redone: {column}"};const editCopy=value=>gridFreeze(JSON.parse(JSON.stringify(value===undefined?null:value)));const editSame=(left,right)=>left===right||left==null&&right==null;const editScalar=value=>value===null||typeof value==="string"||typeof value==="boolean"||Number.isFinite(value);const editBad=(id,problem)=>{throw new Error("CanopyX: column "+id+" "+problem+".")};function editSpec(column,editors,columns){const id=column.id,raw=column.editor===undefined?{}:typeof column.editor==="string"?{type:column.editor}:column.editor;const rules=column.validation===undefined?{}:column.validation,object=value=>value&&typeof value==="object"&&!Array.isArray(value);if(!object(raw)||!object(rules))editBad(id,"has an invalid editor or validation");const type=raw.type??column.kind;if(typeof type!=="string"||!Object.hasOwn(editors,type))editBad(id,"names an editor nobody registered");const fits={number:["number"],text:["text"],toggle:["text"],date:["datetime","text"],datetime:["datetime","text"],time:["datetime","text"]}[type];if(fits&&!fits.includes(column.kind))editBad(id,"cannot use a "+type+" editor for its kind");const{pattern,rules:checks}=formRuleSpec(raw,rules,problem=>editBad(id,problem));const lookup=raw.lookup??(type==="lookup"?{}:null);if(lookup&&(!object(lookup)||lookup.column!==undefined&&!columns.some(item=>item.id===lookup.column)||
2
2
  lookup.limit!==undefined&&!(Number.isInteger(lookup.limit)&&lookup.limit>=1&&lookup.limit<=50)||
3
3
  lookup.minLength!==undefined&&!(Number.isInteger(lookup.minLength)&&lookup.minLength>=0&&lookup.minLength<=16)))editBad(id,"has invalid lookup settings");return Object.freeze({id,column,type,editor:gridFreeze({...raw,type}),options:editOptions(raw.options,id),lookup:lookup&&Object.freeze({column:lookup.column??id,limit:lookup.limit??20,minLength:lookup.minLength??1}),pattern,rules:checks})}
4
4
  function editCell(original,value,text){if(!isCellObject(original)&&text===undefined)return value;const cell=isCellObject(original)?{...original}:{};delete cell.text;delete cell.searchText;delete cell.sort;delete cell.secondary;cell.value=value;if(text!==undefined)cell.text=text;return cell}
@@ -84,7 +84,7 @@ const options=popup?.matches(":popover-open")?popup.getBoundingClientRect():box;
84
84
  if(!state.barEl)return;const entries=this.editEntries(),count=entries.length,errors=entries.filter(editInvalid).length,saving=[...state.changes.values()].some(entry=>entry.status==="saving");state.barEl.hidden=!count&&!state.undo.length&&!state.redo.length&&!saving;setText(state.barText,saving?state.labels.gridEditSaving:fill(state.labels.gridEditChanges,{count:numberFormat(this.lang).format(count)}));setText(state.barErrors,errors&&!saving?" · "+fill(state.labels.gridEditErrors,{count:numberFormat(this.lang).format(errors)}):"");state.undoButton.disabled=!state.undo.length;state.redoButton.disabled=!state.redo.length;state.discardButton.disabled=state.saveButton.disabled=!count||saving},editRemember(column,option){const seen=(this.edit.seen||=new Map()),list=seen.get(column)||[];if(!list.some(item=>item.value===option.value)){list.push(option);if(list.length>1000)list.shift()}
85
85
  seen.set(column,list)},async editLookup(spec,search,record,signal){const limit=spec.lookup.limit,lookups=this.edit.options.lookups,own=lookups&&Object.hasOwn(lookups,spec.id)?lookups[spec.id]:null,text=foldText(search.trim());const matching=list=>list.filter(option=>foldText(option.label).includes(text)).slice(0,limit);if(typeof own==="function")return editOptions(await own({column:spec.id,search,limit,row:editCopy(record),signal}),spec.id).slice(0,limit);if(spec.options)return matching(spec.options);const column=this.columns.find(item=>item.id===spec.lookup.column);if(this.view.source.kind==="inline"){await this.source.decoded;const values=new Map();for(const row of this.view.rows){const value=cellValue(row.cells[column.id]);if(value!=null&&!values.has(value))values.set(value,{value,label:cellText(row.cells[column.id],column,this.fmt)})}
86
86
  return matching([...values.values()].sort((left,right)=>left.label.localeCompare(right.label,this.lang)))}
87
- if(typeof this.source.facet!=="function"||!this.acceptedQuery)return[];const query=gridQuery(this.view,{...this.acceptedQuery,search:"",filters:[],filterGroup:undefined});const request=editCopy({protocol:PROTOCOL,type:"gridFacetQuery",query,column:column.id,search,limit,includeCurrentFilter:false});const response=await this.runGridHook(hook=>this.source.facet(request,{signal:hook}),signal);const page=validateGridFacetPage(response,this.view,request,{total:this.snapshot.total,matched:this.snapshot.total});return page.items.filter(item=>item.value!=null).map(item=>({value:item.value,label:cellText(item.value,column,this.fmt)}))}});const EDIT_FILL_ROW=DataGrid.prototype.fillRow;DataGrid.prototype.fillRow=function(element,row){EDIT_FILL_ROW.call(this,element,row);if(this.edit&&!row.status)this.paintEdits(element,row)};const editDrawerOn=grid=>{const state=grid.edit;return!!state&&state.specs.size>0&&state.drawer!=="never"&&(state.drawer!=="phone"||grid.phone)};DataGrid.drawerHook(10,function(dialog){dialog.addEventListener("cx-drawer-paint",()=>{const body=dialog.querySelector(".cx-drawer-body");if(body&&dialog.isConnected&&editDrawerOn(this))this.editForm(dialog,body,dialog._cxRecord?.id)});dialog.addEventListener("close",()=>{if(this.edit?.form?.dialog===dialog)this.editFormDispose()},{once:true})});Object.assign(DataGrid.prototype,{editFormDispose(){const form=this.edit?.form;if(form){form.fields.forEach(field=>this.editDispose(field));this.edit.form=null}},recordKeepField(id,record){return editDrawerOn(this)&&!this.editReadOnly&&this.edit.specs.has(id)&&!!record&&!!this.editRecord(record.id)&&this.canEditCell(this.editRecord(record.id),id)},editForm(dialog,body,id){const state=this.edit,record=this.editRecord(id),labels=state.labels;const kept=state.form&&state.form.dialog===dialog&&state.form.record.id===id&&record?state.form:null;if(!kept)this.editFormDispose();if(!record||this.editReadOnly||this.destroyed){this.editFormDispose();return}
87
+ if(typeof this.source.facet!=="function"||!this.acceptedQuery)return[];const query=gridQuery(this.view,{...this.acceptedQuery,search:"",filters:[],filterGroup:undefined,tupleFilters:undefined});const request=editCopy({protocol:PROTOCOL,type:"gridFacetQuery",query,column:column.id,search,limit,includeCurrentFilter:false});const response=await this.runGridHook(hook=>this.source.facet(request,{signal:hook}),signal);const page=validateGridFacetPage(response,this.view,request,{total:this.snapshot.total,matched:this.snapshot.total});return page.items.filter(item=>item.value!=null).map(item=>({value:item.value,label:cellText(item.value,column,this.fmt)}))}});const EDIT_FILL_ROW=DataGrid.prototype.fillRow;DataGrid.prototype.fillRow=function(element,row){EDIT_FILL_ROW.call(this,element,row);if(this.edit&&!row.status)this.paintEdits(element,row)};const editDrawerOn=grid=>{const state=grid.edit;return!!state&&state.specs.size>0&&state.drawer!=="never"&&(state.drawer!=="phone"||grid.phone)};DataGrid.drawerHook(10,function(dialog){dialog.addEventListener("cx-drawer-paint",()=>{const body=dialog.querySelector(".cx-drawer-body");if(body&&dialog.isConnected&&editDrawerOn(this))this.editForm(dialog,body,dialog._cxRecord?.id)});dialog.addEventListener("close",()=>{if(this.edit?.form?.dialog===dialog)this.editFormDispose()},{once:true})});Object.assign(DataGrid.prototype,{editFormDispose(){const form=this.edit?.form;if(form){form.fields.forEach(field=>this.editDispose(field));this.edit.form=null}},recordKeepField(id,record){return editDrawerOn(this)&&!this.editReadOnly&&this.edit.specs.has(id)&&!!record&&!!this.editRecord(record.id)&&this.canEditCell(this.editRecord(record.id),id)},editForm(dialog,body,id){const state=this.edit,record=this.editRecord(id),labels=state.labels;const kept=state.form&&state.form.dialog===dialog&&state.form.record.id===id&&record?state.form:null;if(!kept)this.editFormDispose();if(!record||this.editReadOnly||this.destroyed){this.editFormDispose();return}
88
88
  const form=kept||(state.form={dialog,record,fields:[]}),existing=new Map(form.fields.map(field=>[field.spec.id,field]));for(const{column,term,value}of this.drawerFields(dialog)){if(!column||value.tagName!=="DD"||!state.specs.has(column.id)||!this.canEditCell(record,column.id))continue;let field=existing.get(column.id);if(!field){field=this.editCreate(record,state.specs.get(column.id),"form");field.message=h("div","cx-edit-message");field.message.id=this.id+"-em" + ++state.sequence;field.message.hidden=true;form.fields.push(field)}
89
89
  field.label=term;if(field.host.parentNode!==value||field.message.parentNode!==value)value.replaceChildren(field.host,field.message)}
90
90
  dialog.classList.toggle("cx-edit-form",form.fields.length>0);let foot=dialog.querySelector(".cx-drawer-f");if(!form.fields.length){foot?.remove();return}
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"a101e32b0f8a1644",install(context){if(installed.has(context.GRID_ENHANCE_IMPL))return;const{DataGrid,GRID_ENHANCE_IMPL,PROTOCOL,createDataGrid,h,setText,gridQueryRows,gridCompare,gridResultShape,isIdentifier,GRID_REPORTING,loadPart,GRID_PAGER}=context;const ENHANCE_KINDS={text:"text",number:"number",datetime:"datetime",date:"datetime"};const ENHANCE_DATE=/^(\d{4}-\d\d-\d\d)(?:[T ](\d\d:\d\d(?::\d\d(?:\.\d{1,9})?)?)(Z|[+-]\d\d:?\d\d)?)?$/;
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"c0f4773cf3b59919",install(context){if(installed.has(context.GRID_ENHANCE_IMPL))return;const{DataGrid,GRID_ENHANCE_IMPL,PROTOCOL,createDataGrid,h,setText,gridQueryRows,gridCompare,gridResultShape,isIdentifier,GRID_REPORTING,loadPart,GRID_PAGER}=context;const ENHANCE_KINDS={text:"text",number:"number",datetime:"datetime",date:"datetime"};const ENHANCE_DATE=/^(\d{4}-\d\d-\d\d)(?:[T ](\d\d:\d\d(?::\d\d(?:\.\d{1,9})?)?)(Z|[+-]\d\d:?\d\d)?)?$/;
2
2
  const ENHANCE_NUMBER=/^([^\p{L}\d\s+\-\u2212.,()]{0,3})\s?([+\-\u2212]?)(\(?)(\d[\d\s'\u2019.,\u00a0\u202f]*?)(\)?)(?:\s?[^\p{L}\d\s()]{1,3}|\s\p{L}{1,3})?$/u;
3
3
  let enhanceDecimal=null;function enhanceDecimalMark(){if(enhanceDecimal===null){try{enhanceDecimal=new Intl.NumberFormat(document.documentElement.lang||undefined).formatToParts(1.5).find(part=>part.type==="decimal")?.value||"."}
4
4
  catch(_){enhanceDecimal="."}}
@@ -94,10 +94,10 @@ return{url,params:settings.url==="hash"?new URLSearchParams(fragment):url.search
94
94
  async function enhanceLoadState(settings){let envelope=null;try{const pending=ENHANCE_STATE_WRITES.get(settings.key);if(pending)await pending.done;envelope=settings.load?await settings.load({key:settings.key,view:settings.view}):JSON.parse(enhanceStorage(settings)?.getItem(settings.key)||"null")}catch(_){envelope=null}
95
95
  if(!envelope||typeof envelope!=="object"||settings.duration&&!(Number.isFinite(envelope.time)&&Date.now()- envelope.time<=settings.duration*1000))envelope=null;let shared=null;if(settings.url){try{shared=JSON.parse(enhanceAddress(settings)?.params.get(settings.parameter)||"null")}catch(_){shared=null}}
96
96
  if(shared&&typeof shared==="object"&&!Array.isArray(shared)){const base=envelope&&envelope.current&&typeof envelope.current==="object"?envelope.current:
97
- {version:1,view:settings.view,columns:[],timeZone:settings.timeZone,selection:[],scrollTop:0,query:{}};const sort=Array.isArray(shared.s)?shared.s.filter(Array.isArray).map(([column,direction])=>({column,direction})):[];const query={...base.query,sort,search:typeof shared.q==="string"?shared.q:"",filters:Array.isArray(shared.f)?shared.f:[]};if(shared.g&&typeof shared.g==="object")query.filterGroup=shared.g;else delete query.filterGroup;const paging={pageLength:Number.isInteger(shared.l)?shared.l:base.paging?.pageLength||settings.pageLength||50,offset:Number.isInteger(shared.o)?shared.o:0};if(shared.a===true)paging.allRows=true;envelope={...envelope,current:{...base,query,paging,scrollTop:0}}}
97
+ {version:1,view:settings.view,columns:[],timeZone:settings.timeZone,selection:[],scrollTop:0,query:{}};const sort=Array.isArray(shared.s)?shared.s.filter(Array.isArray).map(([column,direction])=>({column,direction})):[];const query={...base.query,sort,search:typeof shared.q==="string"?shared.q:"",filters:Array.isArray(shared.f)?shared.f:[]};if(shared.g&&typeof shared.g==="object")query.filterGroup=shared.g;else delete query.filterGroup;if(Array.isArray(shared.t)&&shared.t.length)query.tupleFilters=shared.t;else delete query.tupleFilters;const paging={pageLength:Number.isInteger(shared.l)?shared.l:base.paging?.pageLength||settings.pageLength||50,offset:Number.isInteger(shared.o)?shared.o:0};if(shared.a===true)paging.allRows=true;envelope={...envelope,current:{...base,query,paging,scrollTop:0}}}
98
98
  return envelope}
99
99
  function enhanceSaveState(settings,envelope){envelope.time=Date.now();try{if(settings.save)enhanceWriteState(settings,envelope);else enhanceStorage(settings)?.setItem(settings.key,JSON.stringify(envelope))}catch(_){/* A full or blocked store leaves the grid usable. */ }
100
- if(!settings.url)return;const query=envelope.current.query,paging=envelope.current.paging||{},shared={};if(query.sort.length)shared.s=query.sort.map(sort=>[sort.column,sort.direction]);if(query.search)shared.q=query.search;if(query.filters.length)shared.f=query.filters;if(query.filterGroup)shared.g=query.filterGroup;if(paging.offset)shared.o=paging.offset;if(paging.pageLength&&paging.pageLength!==settings.pageLength)shared.l=paging.pageLength;if(paging.allRows)shared.a=true;const address=enhanceAddress(settings);if(!address)return;const{url,params}=address,text=Object.keys(shared).length?JSON.stringify(shared):null;if(params.get(settings.parameter)===text)return;if(text===null)params.delete(settings.parameter);else params.set(settings.parameter,text);if(settings.url==="hash")url.hash=params.toString();try{global.history.replaceState(global.history.state,"",url.href)}catch(_){/* Sandboxed documents cannot change their address. */ }
100
+ if(!settings.url)return;const query=envelope.current.query,paging=envelope.current.paging||{},shared={};if(query.sort.length)shared.s=query.sort.map(sort=>[sort.column,sort.direction]);if(query.search)shared.q=query.search;if(query.filters.length)shared.f=query.filters;if(query.filterGroup)shared.g=query.filterGroup;if(query.tupleFilters?.length)shared.t=query.tupleFilters;if(paging.offset)shared.o=paging.offset;if(paging.pageLength&&paging.pageLength!==settings.pageLength)shared.l=paging.pageLength;if(paging.allRows)shared.a=true;const address=enhanceAddress(settings);if(!address)return;const{url,params}=address,text=Object.keys(shared).length?JSON.stringify(shared):null;if(params.get(settings.parameter)===text)return;if(text===null)params.delete(settings.parameter);else params.set(settings.parameter,text);if(settings.url==="hash")url.hash=params.toString();try{global.history.replaceState(global.history.state,"",url.href)}catch(_){/* Sandboxed documents cannot change their address. */ }
101
101
  }
102
102
  function enhanceStateStore(grid,settings,envelope){grid.readStoredState=()=>envelope;grid.writeStoredState=function(state){if(this.destroyed)return;enhanceSaveState(settings,{current:state,views:[...this.savedViews].map(([name,value])=>({name,state:value}))})}}
103
103
  const ENHANCE_OPTIONS=["id","label","noun","columns","data","url","serverSide","rowId","paging","pageLength","pageLengths","lengthMenu","info","pageNumbers","pageInput","firstLast","search","searchText","searchMode","order","sortable","selection","stateSave","texts","markup","footer","height","theme","density","zebra","view","on"];const ENHANCE_TEXTS={search:"searchPlaceholder",info:"gridPageInfo",infoFiltered:"gridPageInfoFiltered",lengthMenu:"gridPageLength",first:"gridFirstPage",previous:"gridPreviousPage",next:"gridNextPage",last:"gridLastPage",page:"gridPageButton",all:"gridAll",empty:"gridEmpty",emptyReason:"gridEmptyReason",noMatches:"gridNoMatches",loading:"loading",loadFailed:"gridLoadFailed",retry:"gridRetry"};const ENHANCE_DERIVED=["init","draw","order","search","page","length"];const ENHANCE_SETUP=new WeakMap(),ENHANCE_CONTROLLERS=new WeakMap(),ENHANCE_PENDING=new WeakMap();let enhanceSeq=0;const ENHANCE_PREPARE=DataGrid.prototype.prepareState;DataGrid.prototype.prepareState=function(){const setup=ENHANCE_SETUP.get(this.opts);if(setup)setup(this);return ENHANCE_PREPARE.call(this)};function enhanceOrder(order,columns){if(order===undefined||order===null)return[];if(!Array.isArray(order))throw new Error("CanopyX: order is a list of [column, direction] pairs.");return order.map(entry=>{const[reference,direction="asc"]=Array.isArray(entry)?entry:[entry&&entry.column,entry&&entry.direction];const column=typeof reference==="number"?columns[reference]:columns.find(item=>item.grid.id===reference);if(!column||!column.grid.sortable||!["asc","desc"].includes(direction))throw new Error("CanopyX: order names an unknown or unsortable column.");return{column:column.grid.id,direction}})}
@@ -118,7 +118,7 @@ const footer=read&&settings.footer!==false&&!(settings.view&&settings.view.repor
118
118
  function enhanceEvents(host,initial){const handlers=new Map(),listening=new Set(),lifetime=new AbortController();let controller=null,api=null,previous=null,initialized=false;const call=(handler,detail,event)=>{try{Promise.resolve(handler.call(controller,detail,controller,event)).catch(error=>global.console?.error(error))}
119
119
  catch(error){global.console?.error(error)}};const emit=(name,detail,event)=>{for(const handler of[...(handlers.get(name)||[])])call(handler,detail,event)};const on=(name,handler)=>{if(typeof name!=="string"||!/^[A-Za-z][\w-]*$/.test(name) || typeof handler !== "function") throw new Error("CanopyX: on takes an event name and a function.");
120
120
  (handlers.get(name)||handlers.set(name,new Set()).get(name)).add(handler);if(!ENHANCE_DERIVED.includes(name)&&!listening.has(name)){listening.add(name);host.addEventListener("canopyx:"+name,event=>emit(name,event.detail,event),{signal:lifetime.signal})}
121
- if(name==="init"&&initialized)Promise.resolve().then(()=>{if(!lifetime.signal.aborted&&handlers.get(name)?.has(handler))call(handler,{query:api.getQuery()})})};host.addEventListener("canopyx:query",event=>{if(!api)return;const query=event.detail.query,paging=api.getPaging(),before=previous,detail={query,paging};const same=(left,right)=>JSON.stringify(left===undefined?null:left)===JSON.stringify(right===undefined?null:right);previous=detail;if(before){if(!same(before.query.sort,query.sort))emit("order",detail,event);if(before.query.search!==query.search||!same([before.query.filters,before.query.filterGroup],[query.filters,query.filterGroup]))emit("search",detail,event);if(before.paging.pageLength!==paging.pageLength)emit("length",detail,event);if(before.paging.pageIndex!==paging.pageIndex)emit("page",detail,event)}
121
+ if(name==="init"&&initialized)Promise.resolve().then(()=>{if(!lifetime.signal.aborted&&handlers.get(name)?.has(handler))call(handler,{query:api.getQuery()})})};host.addEventListener("canopyx:query",event=>{if(!api)return;const query=event.detail.query,paging=api.getPaging(),before=previous,detail={query,paging};const same=(left,right)=>JSON.stringify(left===undefined?null:left)===JSON.stringify(right===undefined?null:right);previous=detail;if(before){if(!same(before.query.sort,query.sort))emit("order",detail,event);if(before.query.search!==query.search||!same([before.query.filters,before.query.filterGroup,before.query.tupleFilters],[query.filters,query.filterGroup,query.tupleFilters]))emit("search",detail,event);if(before.paging.pageLength!==paging.pageLength)emit("length",detail,event);if(before.paging.pageIndex!==paging.pageIndex)emit("page",detail,event)}
122
122
  emit("draw",detail,event)},{signal:lifetime.signal});try{if(initial!==undefined){if(initial===null||typeof initial!=="object")throw new Error("CanopyX: on is an object of handlers by event name.");for(const[name,handler]of Object.entries(initial))on(name,handler)}}catch(error){lifetime.abort();throw error}
123
123
  return{on,off:(name,handler)=>{handlers.get(name)?.delete(handler)},abort:()=>lifetime.abort(),start(created,grid){controller=created;api=grid;api.ready.then(query=>{if(lifetime.signal.aborted)return;initialized=true;emit("init",{query})},()=>{})}}}
124
124
  function enhanceController({api,element,host,isTable,read,columns,store,rowSource,server,events,settings,caption,footer,restore}){let destroyed=false;const recordOf=row=>store.records&&store.records.has(row.id)?store.records.get(row.id):store.tableRecord?store.tableRecord(row.id):undefined;const local=name=>{if(server)throw new Error("CanopyX: "+name+" changes inline data; a server source changes on the server, then reload().")};const refresh=()=>{store.version++;if(store.grid)store.grid.view.rows=store.rows;const query=api.getQuery();if(!query)return api.reload();const request={...query,revision:undefined};return store.grid&&store.grid.pageAll?store.grid.setQuery({...request,offset:0},{pageLength:Math.max(1,store.rows.length),allRows:true}):api.setQuery(request)};const own={table:isTable?element:null,headerGroups:read?enhanceHeaderGroups(read,columns.map(column=>column.grid.id)):[],on(name,handler){events.on(name,handler);return controller},off(name,handler){events.off(name,handler);return controller},getRows(scope="all"){if(!["all","filtered","page"].includes(scope))throw new Error("CanopyX: getRows takes all, filtered or page.");const query=api.getQuery();let rows=store.rows;if(!server&&scope!=="all"){rows=query?rowSource.order(query):[];if(scope==="page"){const paging=api.getPaging();rows=rows.slice(Math.max(0,paging.first-1),paging.last)}}