@evotecit/canopyx 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +113 -0
  2. package/CanopyX.Html/Assets/canopyx-calendar-ics.js +87 -0
  3. package/CanopyX.Html/Assets/canopyx-calendar-recurrence.js +36 -0
  4. package/CanopyX.Html/Assets/canopyx-calendar.css +279 -0
  5. package/CanopyX.Html/Assets/canopyx-calendar.js +307 -0
  6. package/CanopyX.Html/Assets/canopyx-cell-range.css +38 -0
  7. package/CanopyX.Html/Assets/canopyx-cell-range.js +141 -0
  8. package/CanopyX.Html/Assets/canopyx-column-groups.css +18 -0
  9. package/CanopyX.Html/Assets/canopyx-column-groups.js +51 -0
  10. package/CanopyX.Html/Assets/canopyx-data.js +14 -0
  11. package/CanopyX.Html/Assets/canopyx-detail-rows.css +18 -0
  12. package/CanopyX.Html/Assets/canopyx-detail-rows.js +51 -0
  13. package/CanopyX.Html/Assets/canopyx-editing.css +44 -0
  14. package/CanopyX.Html/Assets/canopyx-editing.js +134 -0
  15. package/CanopyX.Html/Assets/canopyx-enhance.js +126 -0
  16. package/CanopyX.Html/Assets/canopyx-export.js +18 -0
  17. package/CanopyX.Html/Assets/canopyx-facets.css +86 -0
  18. package/CanopyX.Html/Assets/canopyx-facets.js +65 -0
  19. package/CanopyX.Html/Assets/canopyx-filter-editor.css +15 -18
  20. package/CanopyX.Html/Assets/canopyx-filter-editor.js +23 -17
  21. package/CanopyX.Html/Assets/canopyx-format-editor.css +77 -0
  22. package/CanopyX.Html/Assets/canopyx-format-editor.js +30 -0
  23. package/CanopyX.Html/Assets/canopyx-formatting.css +40 -0
  24. package/CanopyX.Html/Assets/canopyx-formatting.js +101 -0
  25. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +352 -246
  26. package/CanopyX.Html/Assets/canopyx-grid.css +139 -117
  27. package/CanopyX.Html/Assets/canopyx-grid.js +352 -246
  28. package/CanopyX.Html/Assets/canopyx-grouping.css +39 -0
  29. package/CanopyX.Html/Assets/canopyx-grouping.js +85 -0
  30. package/CanopyX.Html/Assets/canopyx-highlight.css +34 -0
  31. package/CanopyX.Html/Assets/canopyx-highlight.js +80 -0
  32. package/CanopyX.Html/Assets/canopyx-history.js +13 -0
  33. package/CanopyX.Html/Assets/canopyx-interactive.css +18 -6
  34. package/CanopyX.Html/Assets/canopyx-interactive.js +179 -100
  35. package/CanopyX.Html/Assets/canopyx-labels-de.js +304 -0
  36. package/CanopyX.Html/Assets/canopyx-labels-es.js +304 -0
  37. package/CanopyX.Html/Assets/canopyx-labels-fr.js +529 -0
  38. package/CanopyX.Html/Assets/canopyx-labels-it.js +529 -0
  39. package/CanopyX.Html/Assets/canopyx-labels-pl.js +304 -0
  40. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -0
  41. package/CanopyX.Html/Assets/canopyx-layouts.js +39 -0
  42. package/CanopyX.Html/Assets/canopyx-link.css +19 -0
  43. package/CanopyX.Html/Assets/canopyx-link.js +38 -0
  44. package/CanopyX.Html/Assets/canopyx-links.js +14 -0
  45. package/CanopyX.Html/Assets/canopyx-live.css +24 -0
  46. package/CanopyX.Html/Assets/canopyx-live.js +88 -0
  47. package/CanopyX.Html/Assets/canopyx-module.mjs +180 -100
  48. package/CanopyX.Html/Assets/canopyx-pager.css +10 -0
  49. package/CanopyX.Html/Assets/canopyx-pager.js +14 -0
  50. package/CanopyX.Html/Assets/canopyx-record-panel.css +111 -0
  51. package/CanopyX.Html/Assets/canopyx-record-panel.js +178 -0
  52. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +406 -342
  53. package/CanopyX.Html/Assets/canopyx-reporting.css +140 -135
  54. package/CanopyX.Html/Assets/canopyx-reporting.js +406 -342
  55. package/CanopyX.Html/Assets/canopyx-search.js +32 -0
  56. package/CanopyX.Html/Assets/canopyx-timeline.css +80 -0
  57. package/CanopyX.Html/Assets/canopyx-timeline.js +88 -0
  58. package/CanopyX.Html/Assets/canopyx-tree-tools.css +103 -0
  59. package/CanopyX.Html/Assets/canopyx-tree-tools.js +92 -0
  60. package/CanopyX.Html/Assets/canopyx.css +17 -5
  61. package/CanopyX.Html/Assets/canopyx.js +175 -96
  62. package/JavaScript/calendar.d.ts +362 -0
  63. package/JavaScript/cell-ranges.d.ts +76 -0
  64. package/JavaScript/column-groups.d.ts +32 -0
  65. package/JavaScript/editing.d.ts +257 -0
  66. package/JavaScript/enhance.d.ts +202 -0
  67. package/JavaScript/facets.d.ts +58 -0
  68. package/JavaScript/formatting.d.ts +88 -0
  69. package/JavaScript/grid.d.ts +409 -56
  70. package/JavaScript/hosts.d.ts +177 -2
  71. package/JavaScript/index.d.ts +53 -4
  72. package/JavaScript/live.d.ts +115 -0
  73. package/JavaScript/locales/de.mjs +304 -0
  74. package/JavaScript/locales/es.mjs +304 -0
  75. package/JavaScript/locales/fr.d.ts +6 -0
  76. package/JavaScript/locales/fr.mjs +527 -0
  77. package/JavaScript/locales/it.d.ts +6 -0
  78. package/JavaScript/locales/it.mjs +527 -0
  79. package/JavaScript/locales/pl.mjs +304 -0
  80. package/JavaScript/plugins.d.ts +113 -0
  81. package/JavaScript/protocol.d.ts +139 -6
  82. package/JavaScript/records.d.ts +136 -0
  83. package/README.md +61 -9
  84. package/README.nuget.md +1 -1
  85. package/package.json +99 -3
@@ -0,0 +1,307 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"2f44e057443bc649",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,plural,gridButton,gridIcon,gridFreeze,cellText,cellValue,isCellObject,gridQuery,gridQueryRows,gridResultShape,gridPresenterTone,validateGridRows,validateGridPage,validateGridCursorPage,validStructuralCount,checkDocument,numberFormat,HostCellContext,disposeHostSlot,loadPart,PROTOCOL}=context;const CALENDAR_LABELS={calendarToday:"Today",calendarPrevious:"Previous period",calendarNext:"Next period",calendarViews:"Calendar view",calendarMonth:"Month",calendarWeek:"Week",calendarDay:"Day",calendarDays:"{count} days",calendarAgenda:"Agenda",calendarYear:"Year",calendarTimeline:"Timeline",calendarGoTo:"Go to date",calendarAllDay:"All day",calendarMore:"+{count} more",calendarMoreLabel:"Show {count} more events on {date}",calendarDayEvents:"Events on {date}",calendarNoEvents:"No events in this period",calendarEvents:"[one]{count} event[other]{count} events",calendarShowing:"{view}, {range}",calendarRepeats:"Repeats",calendarTruncated:"Showing the first {count} events. Narrow the period or the filters to see the rest.",calendarLoading:"Loading events…",calendarLoadError:"Could not load events: {error}",calendarNow:"Now",calendarZoom:"Zoom",calendarZoomHour:"Hours",calendarZoomDay:"Days",calendarZoomWeek:"Weeks",calendarZoomMonth:"Months",calendarZoomQuarter:"Quarters",calendarResources:"Resources",calendarCapacity:"Concurrent events",calendarCapacityAt:"{count} at once",calendarBusy:"{hours} h busy",calendarOverlaps:"[one]{count} overlap[other]{count} overlaps",calendarUnavailable:"Unavailable",calendarOutsideHours:"Outside working hours",calendarConflict:"Overlaps {title}",calendarNotAllowed:"{resource} cannot take this event",calendarMoved:"{title} moved to {time}",calendarResized:"{title} now ends {time}",calendarSeriesMoved:"Every occurrence of {title} moved",calendarSeriesTime:"Only the time of a whole series can be moved here.",calendarMoveHelp:"Arrow keys move, Shift with arrow keys changes the end, Alt with Up or Down changes the resource, Enter saves, Escape cancels.",calendarEdit:"Move or resize {title}",calendarSaveFailed:"{title} was not saved: {error}",calendarExport:"iCalendar (.ics)",calendarTimeRange:"{start} – {end}",calendarSelected:"Selected {range}",calendarLane:"{resource}, {events}",calendarHighlighted:"[one]{count} event highlighted[other]{count} events highlighted",calendarMini:"Mini month",calendarWeekList:"Week list",calendarDayList:"Day list",calendarMonths:"Months",calendarWeekNumber:"Week {number}",calendarWeekShort:"W{number}",calendarNewEvent:"New event",calendarEventTitle:"Title",calendarCreate:"Add",calendarCancel:"Cancel",calendarCreated:"{title} added on {time}",calendarReceived:"{title} scheduled on {time}",calendarOccurrenceTitle:"Change a repeating event",calendarOccurrenceReadOnly:"This part of the series cannot be saved because some values are read-only.",calendarThisEvent:"This event",calendarThisAndFollowing:"This and following events",calendarAllEvents:"All events",calendarSources:"Calendars",calendarImportFailed:"Could not read {source}: {error}",calendarActions:"Actions for {title}",calendarOpen:"Open",calendarMove:"Move",calendarNewHere:"New event here",calendarOutsideRange:"Outside the dates this calendar shows",calendarClose:"Close",calendarDetailsOf:"Details of {title}"};function calLabelsFor(labels){const out={...CALENDAR_LABELS};for(const name of Object.keys(CALENDAR_LABELS))if(Object.hasOwn(labels,name))out[name]=labels[name];return Object.freeze(out)}
2
+ function calCount(grid,template,count,values){return fill(plural(template,count,grid.lang),{...values,count:numberFormat(grid.lang).format(count)})}
3
+ const CAL_DAY=86400000,CAL_MINUTE=60000,CAL_HOUR=3600000;const calZones=new Map();function calDayNumber(y,m,d){return Math.floor(Date.UTC(y,m-1,d)/ CAL_DAY); }
4
+ function calDayParts(day){const date=new Date(day*CAL_DAY);return{y:date.getUTCFullYear(),m:date.getUTCMonth()+ 1,d:date.getUTCDate(),dow:date.getUTCDay()}}
5
+ function calDow(day){return((day+4)%7+7)%7}
6
+ function calParseDay(text){const match=typeof text==="string"&&/^(\d{4})-(\d\d)-(\d\d)$/.exec(text);
7
+ if(!match)return NaN;const day=calDayNumber(+match[1],+match[2],+match[3]),parts=calDayParts(day);return parts.m===+match[2]&&parts.d===+match[3]?day:NaN}
8
+ function calDayText(day){const parts=calDayParts(day);return String(parts.y).padStart(4,"0")+ "-"+String(parts.m).padStart(2,"0")+ "-"+String(parts.d).padStart(2,"0")}
9
+ function calMonthStart(day){const parts=calDayParts(day);return calDayNumber(parts.y,parts.m,1)}
10
+ function calAddMonths(day,months){const parts=calDayParts(day),index=parts.y*12+parts.m-1+months,y=Math.floor(index / 12), m = index - y * 12 + 1;
11
+ return calDayNumber(y,m,Math.min(parts.d,calMonthDays(y,m)))}
12
+ function calMonthDays(y,m){return new Date(Date.UTC(y,m,0)).getUTCDate()}
13
+ function calWeekStart(day,firstDay){return day -(calDow(day)- firstDay+7)%7}
14
+ function calMinutes(text){const match=typeof text==="string"&&/^(\d\d):(\d\d)$/.exec(text);
15
+ return match&&+match[1]<=24&&+match[2]<60&&+match[1]*60 + +match[2]<=1440?+match[1]*60 + +match[2]:NaN}
16
+ function calZone(name){if(calZones.has(name))return calZones.get(name);let offset;if(name==="utc")offset=()=>0;else if(name==="local")offset=time=>-new Date(time).getTimezoneOffset()*CAL_MINUTE;else{let format;try{format=new Intl.DateTimeFormat("en-US",{timeZone:name,hourCycle:"h23",year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}
17
+ catch(_){return null}
18
+ const cache=new Map();offset=time=>{const bucket=Math.floor(time / CAL_MINUTE);
19
+ let value=cache.get(bucket);if(value===undefined){const parts={};for(const part of format.formatToParts(new Date(bucket*CAL_MINUTE)))parts[part.type]=part.value;value=Date.UTC(+parts.year,+parts.month-1,+parts.day,+parts.hour%24,+parts.minute,+parts.second)- bucket*CAL_MINUTE;if(cache.size>4096)cache.clear();cache.set(bucket,value)}
20
+ return value}}
21
+ const zone={name,offset,parts(time){const date=new Date(time+offset(time)),h=date.getUTCHours(),mi=date.getUTCMinutes();const y=date.getUTCFullYear(),m=date.getUTCMonth()+ 1,d=date.getUTCDate();return{y,m,d,h,mi,day:calDayNumber(y,m,d),dow:date.getUTCDay(),minutes:h*60+mi}},day(time){return Math.floor((time+offset(time))/ CAL_DAY); },
22
+ instant(day,minutes){const wall=day*CAL_DAY+minutes*CAL_MINUTE,before=offset(wall-12*CAL_HOUR),after=offset(wall+12*CAL_HOUR);if(before===after)return wall-before;const early=wall-before,late=wall-after,earlyValid=offset(early)===before,lateValid=offset(late)===after;if(earlyValid&&lateValid)return Math.min(early,late);return lateValid&&!earlyValid?late:early},start(day){return zone.instant(day,0)}};calZones.set(name,zone);return zone}
23
+ function calInstant(value){if(typeof value!=="string")return NaN;if(value.length===20&&value.charCodeAt(19)===90&&value.charCodeAt(10)===84){const digit=at=>{const code=value.charCodeAt(at)- 48;return code>=0&&code<=9?code:NaN},two=at=>digit(at)*10+digit(at+1);const year=two(0)*100+two(2),month=two(5),day=two(8),hour=two(11),minute=two(14),second=two(17);if(year>=1000&&month>=1&&month<=12&&day>=1&&day<=28&&hour<24&&minute<60&&second<60&&
24
+ value.charCodeAt(4)===45&&value.charCodeAt(7)===45&&value.charCodeAt(13)===58&&value.charCodeAt(16)===58)
25
+ return Date.UTC(year,month-1,day,hour,minute,second)}
26
+ return /^\d{4}-\d\d-\d\dT.*(?:Z|[+-]\d\d:\d\d)$/.test(value) ? Date.parse(value) : NaN;
27
+ }
28
+ function calIso(time){const text=new Date(time).toISOString();return text.endsWith(".000Z")?text.slice(0,-5)+ "Z":text}
29
+ const CAL_SUNDAY_REGIONS=/^(?:US|CA|MX|BR|JP|KR|TW|HK|IL|PH|IN|ZA|SA|AR|CO|PE|VE|GT|HN|SV|NI|PA|DO|PR|KE|TH|ID|PK|BD)$/;
30
+ function calFirstDay(lang){try{const locale=new Intl.Locale(lang||"en-US"),info=typeof locale.getWeekInfo==="function"?locale.getWeekInfo():locale.weekInfo;if(info&&Number.isInteger(info.firstDay))return info.firstDay%7;const region=locale.maximize().region;return CAL_SUNDAY_REGIONS.test(region||"")?0:region?1:0}catch(_){return 1}}
31
+ function calFormats(lang,zone){const cache=new Map(),timeZone=zone.name==="utc"?"UTC":zone.name==="local"?undefined:zone.name;const make=(options,dayFormat)=>{const key=JSON.stringify(options)+(dayFormat?"d":"t");let format=cache.get(key);if(!format){const settings={...options,timeZone:dayFormat?"UTC":timeZone};try{format=new Intl.DateTimeFormat(lang||undefined,settings)}catch(_){format=new Intl.DateTimeFormat(undefined,settings)}
32
+ cache.set(key,format)}
33
+ return format};const day=(options,value)=>make(options,true).format(new Date(value*CAL_DAY));const range=(options,from,to)=>{if(from===to)return day(options,from);const format=make(options,true);try{return format.formatRange(new Date(from*CAL_DAY),new Date(to*CAL_DAY))}catch(_){return format.format(new Date(from*CAL_DAY))+ " – "+format.format(new Date(to*CAL_DAY))}};return{lang,day,range,monthTitle:value=>day({month:"long",year:"numeric"},value),monthName:value=>day({month:"long"},value),monthShort:value=>day({month:"short"},value),weekday:(value,width)=>day({weekday:width||"short"},value),dayNumber:value=>day({day:"numeric"},value),dayHeader:value=>day({weekday:"short",day:"numeric"},value),longDay:value=>day({weekday:"long",day:"numeric",month:"long",year:"numeric"},value),shortDay:value=>day({day:"numeric",month:"short"},value),date:value=>day({day:"numeric",month:"short",year:"numeric"},value),year:value=>day({year:"numeric"},value),dayRange:(from,to)=>range({day:"numeric",month:"short",year:"numeric"},from,to),monthRange:(from,to)=>range({month:"short",year:"numeric"},from,to),time:value=>make({hour:"numeric",minute:"2-digit"}).format(new Date(value)),hour:minutes=>make({hour:"numeric"},true).format(new Date(minutes*CAL_MINUTE)),clock:minutes=>make({hour:"numeric",minute:"2-digit"},true).format(new Date(minutes*CAL_MINUTE)),number:value=>numberFormat(lang).format(value)}}
34
+ const CAL_VIEWS=["month","week","day","days","agenda","year","timeline","mini","weekList","dayList","months"],CAL_ZOOMS=["hour","day","week","month","quarter"];const CAL_KEYS=new Set(["start","end","title","subtitle","allDay","resource","status","recurrence","exceptions","series","recurrenceId","display","seriesEnd","view","views","date","days","firstDay","hiddenDays","slotMinutes","minTime","maxTime","scrollTime","workingHours","nowIndicator","eventLimit","eventStyle","timeZone","editable","resources","zoom","zooms","capacity","ranges","availability","limit","phone","toolbar","details","description","tone","source","sources","create","drop","occurrence","weekNumbers","navLinks","validRange","fixedWeeks","outsideDays","span","menu","hover","yearEvents","agendaSpan","months","print"]);const CAL_TONE=/^[a-z][a-z0-9-]{0,31}$/;
35
+ function calInvalid(message){throw new Error("CanopyX: "+message)}
36
+ function calDays(list,name){if(!Array.isArray(list)||list.length>7||new Set(list).size!==list.length||list.some(day=>!Number.isInteger(day)||day<0||day>6))calInvalid(name+" lists weekdays 0 to 6.");return new Set(list)}
37
+ function calTimeRange(value,name){const start=calMinutes(value?.start),end=calMinutes(value?.end);if(!(start<end))calInvalid(name+" needs a start before its end (HH:mm).");return{start,end}}
38
+ function calSettings(grid){const input=grid.view.calendar,columns=new Map(grid.columns.map(column=>[column.id,column]));if(!input||typeof input!=="object"||Array.isArray(input))calInvalid("calendar settings are an object.");for(const key of Object.keys(input))if(!CAL_KEYS.has(key))calInvalid("calendar settings have no '"+key+"'.");const column=(name,kinds,required)=>{const id=input[name];if(id===undefined&&!required)return null;const found=columns.get(id);if(!found||kinds&&!kinds.includes(found.kind))calInvalid("calendar "+name+" names a " +(kinds?kinds.join(" or ")+ " ":"")+ "column.");return found};const settings={start:column("start",["datetime"],true),end:column("end",["datetime"]),allDay:column("allDay",["text"]),title:column("title")||grid.columns.find(item=>item.rowHeader)||grid.columns.find(item=>!item.hidden),subtitle:column("subtitle"),resource:column("resource",["text"]),status:column("status"),recurrence:column("recurrence",["text"]),exceptions:column("exceptions",["text"]),series:column("series",["text"]),recurrenceId:column("recurrenceId",["datetime"]),display:column("display",["text"]),seriesEnd:column("seriesEnd",["datetime"])};if(!settings.series!==!settings.recurrenceId)calInvalid("calendar series and recurrenceId come together.");const views=input.views===undefined?["month","week","day","agenda"].concat(input.resources?["timeline"]:[]):input.views;if(!Array.isArray(views)||!views.length||new Set(views).size!==views.length||views.some(view=>!CAL_VIEWS.includes(view)))calInvalid("calendar views list unique views.");const view=input.view===undefined?views[0]:input.view;if(!views.includes(view))calInvalid("the calendar's first view must be one of its views.");settings.views=views;settings.view=view;settings.date=input.date===undefined?null:calParseDay(input.date);if(Number.isNaN(settings.date))calInvalid("calendar date is yyyy-MM-dd.");const integer=(name,min,max,fallback)=>{const value=input[name]===undefined?fallback:input[name];if(!Number.isInteger(value)||value<min||value>max)calInvalid("calendar "+name+" is an integer from "+min+" to "+max+".");return value};settings.days=integer("days",2,14,3);settings.firstDay=input.firstDay===undefined?calFirstDay(grid.lang):integer("firstDay",0,6,0);settings.hidden=input.hiddenDays===undefined?new Set():calDays(input.hiddenDays,"calendar hiddenDays");if(settings.hidden.size===7)calInvalid("calendar hiddenDays must leave a day.");settings.slotMinutes=input.slotMinutes===undefined?30:input.slotMinutes;if(![5,10,15,20,30,60].includes(settings.slotMinutes))calInvalid("calendar slotMinutes is 5, 10, 15, 20, 30 or 60.");const time=(name,fallback)=>{const value=input[name]===undefined?fallback:calMinutes(input[name]);if(Number.isNaN(value))calInvalid("calendar "+name+" is HH:mm.");return value};settings.minMinutes=time("minTime",0);settings.maxMinutes=time("maxTime",1440);settings.scrollMinutes=time("scrollTime",480);if(settings.minMinutes>=settings.maxMinutes||settings.minMinutes%settings.slotMinutes||settings.maxMinutes%settings.slotMinutes)
39
+ calInvalid("calendar minTime comes before maxTime, both on slot boundaries.");if(input.workingHours!==undefined){const hours=input.workingHours;if(!hours||typeof hours!=="object"||Object.keys(hours).some(key=>!["days","start","end"].includes(key)))calInvalid("calendar workingHours takes days, start and end.");settings.working={...calTimeRange(hours,"calendar workingHours"),days:calDays(hours.days===undefined?[1,2,3,4,5]:hours.days,"calendar workingHours days")}}else settings.working=null;for(const name of["nowIndicator"])if(input[name]!==undefined&&typeof input[name]!=="boolean")calInvalid("calendar "+name+" is a boolean.");settings.now=input.nowIndicator!==false;settings.eventLimit=input.eventLimit===undefined||input.eventLimit==="auto"?"auto":integer("eventLimit",1,20,3);settings.eventStyle=input.eventStyle===undefined?"quiet":input.eventStyle;if(!["quiet","strong"].includes(settings.eventStyle))calInvalid("calendar eventStyle is quiet or strong.");settings.zoneName=input.timeZone===undefined?grid.local?"local":"utc":input.timeZone;if(typeof settings.zoneName!=="string"||!calZone(settings.zoneName))calInvalid("calendar timeZone is utc, local or a time zone the browser knows.");settings.zoneFixed=input.timeZone!==undefined;settings.limit=integer("limit",1,100000,20000);settings.phone=input.phone===undefined?null:input.phone;if(settings.phone!==null&&!views.includes(settings.phone))calInvalid("calendar phone names one of the calendar's views.");settings.editable=calEditable(input.editable,columns,settings);settings.zooms=input.zooms===undefined?CAL_ZOOMS.slice():input.zooms;if(!Array.isArray(settings.zooms)||!settings.zooms.length||new Set(settings.zooms).size!==settings.zooms.length||settings.zooms.some(zoom=>!CAL_ZOOMS.includes(zoom)))
40
+ calInvalid("calendar zooms list unique zoom levels.");settings.zoom=input.zoom===undefined?settings.zooms.includes("day")?"day":settings.zooms[0]:input.zoom;if(!settings.zooms.includes(settings.zoom))calInvalid("calendar zoom must be one of its zooms.");settings.resources=calResources(input.resources);settings.capacity=calCapacity(input.capacity);settings.ranges=calRanges(input.ranges);settings.availability=calAvailability(input.availability);calSettingsMore(input,column,settings);return settings}
41
+ function calEditable(value,columns,settings){if(value===undefined||value===false)return null;const input=value===true?{}:value;if(!input||typeof input!=="object"||Array.isArray(input)||
42
+ Object.keys(input).some(key=>!["move","resize","resources","overlap","workingHours","allowedResources","snapMinutes","commit"].includes(key)))
43
+ calInvalid("calendar editable takes move, resize, resources, overlap, workingHours, allowedResources, snapMinutes and commit.");for(const name of["move","resize","resources","overlap","workingHours"])if(input[name]!==undefined&&typeof input[name]!=="boolean")calInvalid("calendar editable "+name+" is a boolean.");const allowed=input.allowedResources===undefined?null:columns.get(input.allowedResources);if(input.allowedResources!==undefined&&(!allowed||allowed.kind!=="text"))calInvalid("calendar editable allowedResources names a text column.");const snap=input.snapMinutes===undefined?settings.slotMinutes:input.snapMinutes;if(![1,5,10,15,20,30,60].includes(snap))calInvalid("calendar editable snapMinutes is 1, 5, 10, 15, 20, 30 or 60.");if(input.commit!==undefined&&!["auto","manual"].includes(input.commit))calInvalid("calendar editable commit is auto or manual.");return{move:input.move!==false,resize:input.resize!==false&&!!settings.end,resources:input.resources!==false&&!!settings.resource,overlap:input.overlap!==false,working:!!input.workingHours,allowed,snap,commit:input.commit||"auto"}}
44
+ function calResources(value){if(value===undefined)return null;if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!["view","width","label","aggregates"].includes(key))||
45
+ !value.view||value.view.type!=="view")calInvalid("calendar resources hold a tree view and optional width, label and aggregates.");if(value.width!==undefined&&(!Number.isInteger(value.width)||value.width<120||value.width>800))calInvalid("calendar resources width is 120 to 800 pixels.");if(value.label!==undefined&&(typeof value.label!=="string"||!value.label.trim()||value.label.length>256))calInvalid("calendar resources label is text.");const aggregates=value.aggregates||{};if(typeof aggregates!=="object"||Array.isArray(aggregates)||Object.entries(aggregates).some(([id,kind])=>
46
+ !(value.view.columns||[]).some(column=>column.id===id)||!["count","busy","overlap"].includes(kind)))calInvalid("calendar resource aggregates map tree columns to count, busy or overlap.");return{view:value.view,width:value.width||280,label:value.label||null,aggregates}}
47
+ function calCapacity(value){if(value===undefined)return null;const thresholds=value&&value.thresholds;if(!value||typeof value!=="object"||Object.keys(value).some(key=>!["thresholds","label"].includes(key))||!Array.isArray(thresholds)||thresholds.length>8||
48
+ thresholds.some((item,index)=>!item||!Number.isInteger(item.at)||item.at<1||!CAL_TONE.test(item.tone||"")||index&&thresholds[index-1].at>=item.at)||
49
+ value.label!==undefined&&(typeof value.label!=="string"||!value.label.trim()))
50
+ calInvalid("calendar capacity takes rising thresholds { at, tone } and a label.");return{thresholds,label:value.label||null}}
51
+ function calRanges(list){if(list===undefined)return[];if(!Array.isArray(list)||list.length>1000)calInvalid("calendar ranges are a list of at most 1000.");return list.map(item=>{const start=calInstant(item?.start),end=calInstant(item?.end);if(!item||typeof item!=="object"||Object.keys(item).some(key=>!["start","end","label","tone","resource","kind","recurrence","inverse"].includes(key))||item.inverse!==undefined&&typeof item.inverse!=="boolean"||
52
+ !(start<end)||typeof item.label!=="string"||!item.label.trim()||item.label.length>256||item.tone!==undefined&&!CAL_TONE.test(item.tone)||
53
+ item.resource!==undefined&&typeof item.resource!=="string"||item.kind!==undefined&&!["background","unavailable"].includes(item.kind))
54
+ calInvalid("a calendar range has start before end, a label, and optional tone, resource, kind and recurrence.");if(item.recurrence!==undefined)calRec().calRule(item.recurrence);return{start,end,label:item.label,tone:calSafeTone(item.tone)||(item.kind==="unavailable"?"neutral":"info"),resource:item.resource??null,kind:item.kind||"background",recurrence:item.recurrence||null,inverse:item.inverse===true}})}
55
+ function calAvailability(list){if(list===undefined)return[];if(!Array.isArray(list)||list.length>10000)calInvalid("calendar availability is a list of at most 10000.");return list.map(item=>{if(!item||typeof item!=="object"||Object.keys(item).some(key=>!["resource","days","start","end"].includes(key))||item.resource!==undefined&&typeof item.resource!=="string")
56
+ calInvalid("calendar availability entries take resource, days, start and end.");return{resource:item.resource??null,days:calDays(item.days===undefined?[1,2,3,4,5]:item.days,"calendar availability days"),...calTimeRange(item,"calendar availability")}})}
57
+ const CAL_TOOLBAR=["search","filters","columns","views","export","more"],CAL_ID=/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
58
+ function calSettingsMore(input,column,settings){const flag=(name,fallback)=>{if(input[name]===undefined)return fallback;if(typeof input[name]!=="boolean")calInvalid("calendar "+name+" is a boolean.");return input[name]};const choice=(name,values,fallback)=>{if(input[name]===undefined)return fallback;if(!values.includes(input[name]))calInvalid("calendar "+name+" is "+values.join(", ")+ ".");return input[name]};const toolbar=input.toolbar;if(toolbar!==undefined&&typeof toolbar!=="boolean"&&!(Array.isArray(toolbar)&&new Set(toolbar).size===toolbar.length&&toolbar.every(item=>CAL_TOOLBAR.includes(item))))
59
+ calInvalid("calendar toolbar is true, false or a list of "+CAL_TOOLBAR.join(", ")+ ".");settings.toolbar=toolbar===undefined||toolbar===true?true:toolbar===false?false:new Set(toolbar);settings.details=choice("details",["drawer","popover"],"drawer");settings.description=column("description",["text"]);settings.tone=column("tone",["text"]);settings.source=column("source",["text"]);settings.sources=calSources(input.sources,settings.source);settings.create=calCreate(input.create,settings);settings.drop=flag("drop",false);if((settings.create||settings.drop)&&(!settings.start.editable||!settings.title?.editable))calInvalid("calendar create and drop need editable start and title columns.");const overrides=!!(settings.series?.editable&&settings.recurrenceId?.editable);settings.occurrence=choice("occurrence",["ask","this","following","all"],overrides?"ask":"all");if(!overrides&&settings.occurrence!=="all")calInvalid("calendar occurrence "+settings.occurrence+" needs editable series and recurrenceId columns.");settings.weekNumbers=input.weekNumbers===undefined||input.weekNumbers===false?false:choice("weekNumbers",["iso","locale"],false);settings.navLinks=flag("navLinks",true);settings.validRange=calValidRange(input.validRange);settings.fixedWeeks=flag("fixedWeeks",false);settings.outsideDays=flag("outsideDays",true);settings.span=calSpan(input.span);settings.menu=flag("menu",true);settings.hover=flag("hover",true);settings.yearEvents=flag("yearEvents",false);settings.print=flag("print",false);settings.agendaSpan=choice("agendaSpan",["day","week","month","year"],"month");if(input.months!==undefined&&(!Number.isInteger(input.months)||input.months<2||input.months>24))calInvalid("calendar months is an integer from 2 to 24.");settings.months=input.months??6}
60
+ function calSources(list,sourceColumn){if(list===undefined)return[];if(!Array.isArray(list)||!list.length||list.length>50)calInvalid("calendar sources are a list of 1 to 50.");const ids=new Set();return list.map(item=>{if(!item||typeof item!=="object"||Array.isArray(item)||Object.keys(item).some(key=>!["id","label","tone","ics","hidden"].includes(key))||
61
+ typeof item.id!=="string"||!CAL_ID.test(item.id)||ids.has(item.id)||typeof item.label!=="string"||!item.label.trim()||item.label.length>256||
62
+ item.tone!==undefined&&!CAL_TONE.test(item.tone)||item.ics!==undefined&&!calSameOrigin(item.ics)||item.hidden!==undefined&&typeof item.hidden!=="boolean")
63
+ calInvalid("a calendar source has a unique id, a label, and optional tone, ics (a same-origin address) and hidden.");if(item.ics===undefined&&!sourceColumn)calInvalid("calendar sources without ics need the source column.");ids.add(item.id);return{id:item.id,label:item.label,tone:calSafeTone(item.tone),ics:item.ics??null,hidden:item.hidden===true}})}
64
+ function calSameOrigin(value){if(typeof value!=="string"||!value||value.length>2048)return false;try{const base=typeof location!=="undefined"?location.href:"https://localhost/",url=new URL(value,base);if(!/^[a-z][a-z0-9+.-]*:|^\/\//i.test(value)) return true;
65
+ return(url.protocol==="http:"||url.protocol==="https:")&&url.origin!=="null"&&url.origin===new URL(base).origin}catch{return false}}
66
+ function calCreate(value,settings){if(value===undefined||value===false)return null;const input=value===true?{}:value;if(!input||typeof input!=="object"||Array.isArray(input)||Object.keys(input).some(key=>!["minutes","allDay"].includes(key))||
67
+ input.minutes!==undefined&&(!Number.isInteger(input.minutes)||input.minutes<5||input.minutes>1440)||input.allDay!==undefined&&typeof input.allDay!=="boolean")
68
+ calInvalid("calendar create is true or { minutes (5 to 1440), allDay }.");return{minutes:input.minutes??60,allDay:input.allDay!==false&&!!settings.allDay?.editable}}
69
+ function calValidRange(value){if(value===undefined)return null;const first=value?.start===undefined?-Infinity:calParseDay(value.start),end=value?.end===undefined?Infinity:calParseDay(value.end);if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!["start","end"].includes(key))||Number.isNaN(first)||Number.isNaN(end)||!(first<end))
70
+ calInvalid("calendar validRange has a start before its end (yyyy-MM-dd).");return{first,last:end-1}}
71
+ function calSpan(value){if(value===undefined)return null;if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!["count","unit"].includes(key))||
72
+ !Number.isInteger(value.count)||value.count<1||value.count>366||!["day","week","month"].includes(value.unit))
73
+ calInvalid("calendar span is { count (1 to 366), unit (day, week or month) }.");return{count:value.count,unit:value.unit}}
74
+ Object.assign(DataGrid.prototype,{calAllowsSpan(start,end,allDay){if(!Number.isFinite(start)||!Number.isFinite(end)||end<start)return false;const valid=this.cal.validRange;if(!valid)return true;const last=Math.max(start,end-1),firstDay=allDay?Math.floor(start / CAL_DAY) : this.calZone.day(start);
75
+ const lastDay=allDay?Math.floor(last / CAL_DAY) : this.calZone.day(last);
76
+ return firstDay>=valid.first&&lastDay<=valid.last},calDisplaySpan(event){return event.allDay?{start:this.calZone.start(Math.floor(event.start / CAL_DAY)), end: this.calZone.start(Math.floor(event.end / CAL_DAY)) } : event;
77
+ }});const CAL_BASES=new WeakMap(),CAL_UTC=()=>calZone("utc"),CAL_TONE_NAME=/^[a-z][a-z0-9-]{0,31}$/;
78
+ const calRec=()=>DataGrid.prototype.calKit.calRule?DataGrid.prototype.calKit:null;function calSafeTone(tone){return typeof tone==="string"&&CAL_TONE_NAME.test(tone)&&!tone.endsWith("-bg")?tone:null}
79
+ function calDateOf(value){return typeof value==="string"?calParseDay(value.slice(0,10)):NaN}
80
+ function calBase(view,row){const cached=CAL_BASES.get(row);if(cached&&cached.view===view&&cached.version===view.calVersion)return cached.base;const cal=view.cal,cells=row.cells,fields=row.external?row.fields:null;const read=column=>column&&!fields?cellValue(cells[column.id]):null,value=name=>fields?fields[name]??null:read(cal[name]);let base=null;const start=calInstant(value("start"));if(Number.isFinite(start)){const allDay=value("allDay")===true||value("allDay")==="true";let end=calInstant(value("end"));if(allDay){const first=calDateOf(value("start")),endText=value("end"),last=calDateOf(endText);const endDay=Number.isFinite(end)&&Number.isFinite(last)?(/T00:00(?::00(?:\.0+)?)?(?:Z|[+-]|$)/.test(endText) ? last : last + 1) : NaN;
81
+ base={start:first*CAL_DAY,end:(endDay>first?endDay:first+1)*CAL_DAY}}else base={start,end:Number.isFinite(end)&&end>=start?end:start};const ownTone=cal.tone&&!fields?calSafeTone(String(read(cal.tone)??"").trim()):null;Object.assign(base,{key:row.id,row,allDay,marker:!allDay&&base.end===base.start,title:(fields?fields.title:cellText(cells[cal.title.id],cal.title,view.fmt))||row.id,subtitle:cal.subtitle&&!fields?cellText(cells[cal.subtitle.id],cal.subtitle,view.fmt):"",tone:ownTone||(fields?null:calTone(cal.status,cells))||calSourceTone(cal,fields?row.external:read(cal.source)),source:fields?row.external:cal.source?calResourceOf(read(cal.source)):null,external:!!fields,toneFromStatus:!ownTone&&!fields,resource:cal.resource?calResourceOf(read(cal.resource)):null,background:cal.display?String(read(cal.display)||"").toLowerCase()==="background":false,rule:null,ruleError:null,seriesZone:allDay?CAL_UTC():fields?.seriesZone?calZone(fields.seriesZone):view.calSeriesZone,series:fields?fields.series||null:cal.series?read(cal.series)||null:null,recurrenceId:calInstant(fields?fields.recurrenceId:read(cal.recurrenceId))});const text=value("recurrence");if(typeof text==="string"&&text.trim()&&!base.series){try{const rec=calRec();if(!rec)throw new Error("CanopyX: the calendar recurrence part is not loaded.");base.rule=rec.calRule(text);Object.assign(base,rec.calExceptions(value("exceptions"),base.seriesZone))}
82
+ catch(error){base.ruleError=error}}}
83
+ CAL_BASES.set(row,{view,version:view.calVersion,base});return base}
84
+ function calSourceTone(cal,id){return id!=null&&cal.sources?.length?cal.sources.find(source=>source.id===String(id).trim())?.tone??null:null}
85
+ function calResourceOf(value){return typeof value==="string"&&value.trim()?value.trim():null}
86
+ function calTone(column,cells){if(!column)return null;const cell=cells[column.id],raw=cellValue(cell),own=isCellObject(cell)&&typeof cell.tone==="string"?cell.tone:null;if(raw==null)return calSafeTone(own);return calSafeTone(column.presenter?gridPresenterTone(column.presenter,raw,own):own)}
87
+ function calOccurrences(view,rows,from,to,metadata){const out=[],overrides=new Map([...(metadata||[])].map(([id,starts])=>[id,new Set(starts)])),bases=[],limit=view.cal.limit;for(const row of rows){const base=calBase(view,row);if(!base)continue;if(base.series&&Number.isFinite(base.recurrenceId)){if(!overrides.has(base.series))overrides.set(base.series,new Set());overrides.get(base.series).add(base.recurrenceId)}
88
+ bases.push(base)}
89
+ for(const base of bases){if(base.ruleError&&!view.calRuleErrors.has(base.key)){view.calRuleErrors.add(base.key);view.emit("error",{error:base.ruleError,id:base.key})}
90
+ if(!base.rule){const span=view.calDisplaySpan(base);if(calOverlaps(span.start,span.end,from,to))out.push(calOccurrence(view,base,base.start,false));continue}
91
+ const duration=base.end-base.start,moved=overrides.get(base.key),excluded=moved?new Set([...base.excluded,...moved]):base.excluded;base.overridden=!!moved;const first=base.allDay?view.calZone.day(from)*CAL_DAY:from,last=base.allDay?(view.calZone.day(to-1)+ 1)*CAL_DAY:to;const starts=calRec().calExpand(base.rule,base.start,duration,base.seriesZone,first,last,excluded,base.excludedDays,limit+1);for(const start of starts){const event=calOccurrence(view,base,start,true),span=view.calDisplaySpan(event);if(calOverlaps(span.start,span.end,from,to))out.push(event)}}
92
+ out.sort((a,b)=>a.start-b.start||(b.end-b.start)-(a.end-a.start)||(a.id<b.id?-1:a.id>b.id?1:0));if(out.length>limit){out.length=limit;out.truncated=true}
93
+ return out}
94
+ function calOverlaps(start,end,from,to){return end>start?start<to&&end>from:start>=from&&start<to}
95
+ function calOccurrence(view,base,start,recurring){const end=start +(base.end-base.start),zone=base.allDay?CAL_UTC():view.calZone;const sd=zone.day(start),ed=end>start?zone.day(end-1):sd;return{id:recurring?base.key+"@"+calIso(start):base.key,key:base.key,base,row:base.row,start,end,allDay:base.allDay,marker:base.marker,sd,ed,recurring,occurrence:recurring?start:null,background:base.background,resource:base.resource,title:base.title,subtitle:base.subtitle,tone:base.tone}}
96
+ function calStaticRanges(view,from,to){const out=[],inverse=new Map();view.cal.ranges.forEach((range,index)=>{const starts=range.recurrence?calRec().calExpand(calRec().calRule(range.recurrence),range.start,range.end-range.start,view.calSeriesZone,from,to,new Set(),new Set(),range.inverse?20000:1000):
97
+ calOverlaps(range.start,range.end,from,to)?[range.start]:[];if(range.inverse&&!inverse.has(range.resource??""))inverse.set(range.resource??"",{range,index,spans:[]});for(const start of starts){const end=start+range.end-range.start;if(range.inverse){inverse.get(range.resource??"").spans.push([Math.max(start,from),Math.min(end,to)]);continue}
98
+ out.push({id:"range-"+index+"-"+start,start,end,sd:view.calZone.day(start),ed:view.calZone.day(end-1),label:range.label,tone:range.tone,resource:range.resource,kind:range.kind})}});for(const{range,index,spans}of inverse.values()){const add=(start,end)=>{if(end>start)out.push({id:"range-"+index+"-out-"+start,start,end,sd:view.calZone.day(start),ed:view.calZone.day(end-1),label:range.label,tone:range.tone,resource:range.resource,kind:range.kind,inverse:true})};let at=from;for(const[start,end]of spans.sort((a,b)=>a[0]- b[0])){add(at,start);at=Math.max(at,end)}
99
+ add(at,to)}
100
+ return out}
101
+ function calLanes(events,first,last,dayIndex){const busy=[],placed=[];const sorted=events.slice().sort((a,b)=>a.sd-b.sd||(b.ed-b.sd)-(a.ed-a.sd)||(a.allDay===b.allDay?0:a.allDay?-1:1)||a.start-b.start||(a.id<b.id?-1:1));for(const event of sorted){const from=Math.max(event.sd,first),to=Math.min(event.ed,last);const cells=[];for(let day=from;day<=to;day++)if(dayIndex(day)>=0)cells.push(dayIndex(day));if(!cells.length)continue;let lane=0;while(cells.some(cell=>busy[cell]?.[lane]))lane++;for(const cell of cells)(busy[cell]||=[])[lane]=true;placed.push({event,lane,from:cells[0],to:cells[cells.length-1],days:cells.length})}
102
+ return placed}
103
+ function calColumns(segments){segments.sort((a,b)=>a.top-b.top||(b.bottom-b.top)-(a.bottom-a.top)||(a.event.id<b.event.id?-1:1));let cluster=[],columns=[],clusterEnd=-Infinity;const finish=()=>{for(const item of cluster){item.cols=columns.length;let span=1;for(let next=item.col+1;next<columns.length;next++){if(columns[next].some(other=>other.top<item.bottom&&other.bottom>item.top))break;span++}
104
+ item.span=span}
105
+ cluster=[];columns=[];clusterEnd=-Infinity};for(const item of segments){const end=Math.max(item.bottom,item.top+item.min);if(item.top>=clusterEnd)finish();let col=columns.findIndex(list=>list[list.length-1].visualEnd<=item.top);if(col<0){col=columns.length;columns.push([])}
106
+ item.col=col;item.visualEnd=end;columns[col].push(item);cluster.push(item);clusterEnd=Math.max(clusterEnd,end)}
107
+ finish();return segments}
108
+ function calSegments(view,events,day,minMinutes,maxMinutes){const zone=view.calZone,start=zone.start(day),out=[];for(const event of events){if(event.background||calInAllDayRow(event)||event.sd>day||event.ed<day)continue;const top=event.sd<day?0:zone.parts(event.start).minutes,bottom=event.ed>day?1440:event.end>event.start?zone.parts(event.end).minutes||1440:top;if(bottom<minMinutes||top>=maxMinutes&&!(top===maxMinutes&&event.marker))continue;out.push({event,top:Math.max(top,minMinutes),bottom:Math.min(Math.max(bottom,top),maxMinutes),min:view.calMinSegment,clippedTop:top<minMinutes||event.sd<day,clippedBottom:bottom>maxMinutes||event.ed>day,dayStart:start})}
109
+ return calColumns(out)}
110
+ function calInAllDayRow(event){return event.allDay||event.end-event.start>=CAL_DAY}
111
+ const CAL_ICONS={previous:"M10 3 5 8l5 5",next:"m6 3 5 5-5 5",calendar:"M3 4h10v9H3zM3 7h10M6 2v3M10 2v3",repeat:"M3 7a4 4 0 0 1 7-3l1 1M13 9a4 4 0 0 1-7 3l-1-1M11 2v3H8M5 14v-3h3",print:"M4 6V2h8v4M4 11H2V6h12v5h-2M4 9h8v5H4z"};function calIcon(name){const svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),path=document.createElementNS("http://www.w3.org/2000/svg","path");svg.setAttribute("viewBox","0 0 16 16");svg.setAttribute("aria-hidden","true");svg.setAttribute("class","cx-cal-ico");path.setAttribute("d",CAL_ICONS[name]);svg.appendChild(path);return svg}
112
+ Object.assign(DataGrid.prototype,{calBuildBar(){const labels=this.calLabels,bar=this.calBar=h("div","cx-cal-bar"),nav=h("div","cx-cal-nav");nav.setAttribute("role","group");nav.setAttribute("aria-label",this.view.label);const button=(text,icon,run,cls)=>{const item=gridButton(icon?"":text,run);item.classList.add("cx-btn","cx-cal-"+cls);if(icon){item.classList.add("icon","ghost");item.appendChild(calIcon(icon));item.setAttribute("aria-label",text);item.title=text}
113
+ return item};this.calPrev=button(labels.calendarPrevious,"previous",()=>this.calendarStep(-1),"prev");this.calTodayButton=button(labels.calendarToday,null,()=>{if(this.calTodayButton.getAttribute("aria-disabled")!=="true")this.calendarToday()},"gotoday");this.calNextButton=button(labels.calendarNext,"next",()=>this.calendarStep(1),"next");nav.append(this.calTodayButton,this.calPrev,this.calNextButton);this.calTitle=h("h2","cx-cal-title");this.calTitle.id=this.id+"-cal-title";const jump=this.calJump=h("input","cx-cal-jump");jump.type="date";jump.setAttribute("aria-label",labels.calendarGoTo);jump.title=labels.calendarGoTo;jump.addEventListener("change",()=>{const day=calParseDay(jump.value);if(Number.isFinite(day))this.setCalendarDate(jump.value)});const jumpBox=h("span","cx-cal-jumpbox");if(typeof HTMLInputElement==="function"&&"showPicker" in HTMLInputElement.prototype){const open=this.calJumpButton=h("button","cx-cal-jumpbtn");open.type="button";jump.tabIndex=-1;jump.setAttribute("aria-hidden","true");jump.classList.add("cx-cal-jump-behind");open.addEventListener("click",()=>{try{jump.showPicker()}
114
+ catch{jump.classList.remove("cx-cal-jump-behind");jump.removeAttribute("aria-hidden");jump.removeAttribute("tabindex");open.remove();this.calJumpButton=null;jump.focus()}});jumpBox.append(jump,open)}else jumpBox.append(jump);const views=this.calViewGroup=h("div","cx-cal-views");views.setAttribute("role","group");views.setAttribute("aria-label",labels.calendarViews);this.calViewButtons=new Map();for(const view of this.cal.views){const item=button(this.calViewName(view),null,()=>this.setCalendarView(view),"view");item.setAttribute("aria-pressed","false");item.dataset.cxCalView=view;this.calViewButtons.set(view,item);views.appendChild(item)}
115
+ const select=this.calViewSelect=h("select","cx-cal-view-select");select.setAttribute("aria-label",labels.calendarViews);for(const view of this.cal.views){const option=h("option");option.value=view;setText(option,this.calViewName(view));select.appendChild(option)}
116
+ select.addEventListener("change",()=>this.setCalendarView(select.value));const zoom=this.calZoomSelect=h("select","cx-cal-zoom");zoom.setAttribute("aria-label",labels.calendarZoom);zoom.title=labels.calendarZoom;for(const level of this.cal.zooms){const option=h("option");option.value=level;setText(option,labels["calendarZoom"+level[0].toUpperCase()+ level.slice(1)]);zoom.appendChild(option)}
117
+ zoom.addEventListener("change",()=>this.setCalendarZoom(zoom.value));if(this.cal.views.length<2){views.hidden=true;select.hidden=true}
118
+ const print=this.cal.print?button(this.labels.gridPrint||"Print","print",()=>global.print(),"print"):null;bar.append(nav,this.calTitle,h("span","cx-spacer"),jumpBox,zoom,views,select,...(print?[print]:[]));return bar},calViewName(view){const labels=this.calLabels;return view==="days"?fill(labels.calendarDays,{count:this.calDaysCount()}):labels["calendar"+view[0].toUpperCase()+ view.slice(1)]},calDaysCount(){return this.calPhone&&this.cal.days>3?3:this.cal.days},calSyncBar(announce){const view=this.calView,range=this.calRange;for(const[name,item]of this.calViewButtons){item.setAttribute("aria-pressed",String(name===view));item.classList.toggle("active",name===view)}
119
+ this.calViewSelect.value=view;this.calZoomSelect.hidden=view!=="timeline"||this.cal.zooms.length<2;this.calZoomSelect.value=this.calZoom;const title=this.calRangeTitle();setText(this.calTitle,title);this.calJump.value=calDayText(this.calDate);if(this.calJumpButton){setText(this.calJumpButton,this.calFmt.date(this.calDate));this.calJumpButton.setAttribute("aria-label",this.calLabels.calendarGoTo+", "+this.calFmt.date(this.calDate));this.calJumpButton.title=this.calLabels.calendarGoTo}
120
+ const today=this.calZone.day(Date.now());this.calTodayButton.setAttribute("aria-disabled",String(today>=range.firstDay&&today<=range.lastDay||!this.calInValid({firstDay:today,lastDay:today})));if(this.cal.validRange)for(const[button,direction]of[[this.calPrev,-1],[this.calNextButton,1]])button.setAttribute("aria-disabled",String(!this.calInValid(this.calRangeOf(view,this.calStepDate(direction)))));if(announce)this.calAnnounce(fill(this.calLabels.calendarShowing,{view:this.calViewName(view),range:title}))},calRangeTitle(){const fmt=this.calFmt,range=this.calRange;switch(this.calView){case "month":case "mini":return fmt.monthTitle(this.calDate);case "agenda":{const span=this.cal.agendaSpan;return span==="month"?fmt.monthTitle(this.calDate):span==="year"?fmt.year(this.calDate):span==="day"?fmt.longDay(range.firstDay):fmt.dayRange(range.firstDay,range.lastDay)}
121
+ case "months":return fmt.monthRange(range.firstDay,range.lastDay);case "year":return fmt.year(this.calDate);case "day":case "dayList":return this.calPhone?fmt.shortDay(range.firstDay)+ ", "+fmt.year(range.firstDay):fmt.longDay(range.firstDay);case "timeline":return this.calTimelineTitle?this.calTimelineTitle():fmt.dayRange(range.firstDay,range.lastDay);default:return fmt.dayRange(range.firstDay,range.lastDay)}},calAnnounce(text){clearTimeout(this.calAnnounceTimer);this.calAnnounceTimer=setTimeout(()=>{if(!this.destroyed)setText(this.calLive,text)},150)}});const CAL_ROW_COPIES=new WeakMap();Object.assign(DataGrid.prototype,{calTimeText(event){if(event.allDay)return this.calLabels.calendarAllDay;const fmt=this.calFmt,labels=this.calLabels;if(event.marker)return fmt.time(event.start);if(event.ed>event.sd)return fill(labels.calendarTimeRange,{start:fmt.shortDay(event.sd)+ " "+fmt.time(event.start),end:fmt.shortDay(event.ed)+ " "+fmt.time(event.end)});return fill(labels.calendarTimeRange,{start:fmt.time(event.start),end:fmt.time(event.end)})},calStatusText(event){const column=this.cal.status;return column?cellText(event.row.cells[column.id],column,this.fmt):""},calEventName(event){const parts=[event.title,event.allDay&&event.ed>event.sd?this.calFmt.dayRange(event.sd,event.ed):this.calTimeText(event)];const status=this.calStatusText(event);if(status)parts.push(status);if(event.subtitle)parts.push(event.subtitle);if(event.resource&&this.calResourceName)parts.push(this.calResourceName(event.resource));if(event.recurring)parts.push(this.calLabels.calendarRepeats);return parts.filter(Boolean).join(", ")},calDisposeEventRenderers(container){const slots=this.calHostSlots;if(!container)this.calHostSlots=null;for(const slot of slots||[])if(!container||container.contains(slot.content)){slots.delete(slot);disposeHostSlot(slot)}},calEventElement(event,kind){const element=h("button","cx-cal-ev cx-cal-ev-"+kind +(event.marker?" cx-cal-marker":"")+(event.allDay?" cx-cal-allday":""));element.type="button";element.tabIndex=-1;element.dataset.cxEvent=event.id;element.dataset.cxRecord=event.key;element._calEvent=event;if(event.tone){element.dataset.cxTone=event.tone;element.style.setProperty("--cx-ev",event.tone==="neutral"?"var(--cx-tone-neutral,var(--cx-muted))":"var(--cx-tone-"+event.tone+",var(--cx-accent))");element.style.setProperty("--cx-ev-bg","var(--cx-tone-"+event.tone+"-bg,color-mix(in srgb,var(--cx-ev) 14%,var(--cx-surface)))")}
122
+ element.setAttribute("aria-label",this.calEventName(event));if(this.linkHighlight?.has(event.key))element.dataset.cxLinked="true";const template=this.opts.calendar?.render;if(typeof template==="function"){let copy=CAL_ROW_COPIES.get(event.row);if(copy?.version!==this.calVersion){copy={version:this.calVersion,row:gridFreeze(JSON.parse(JSON.stringify(event.row)))};CAL_ROW_COPIES.set(event.row,copy)}
123
+ const row=copy.row;try{if(template({event:this.calPublicEvent(event),row,element,kind,labels:this.calLabels})===true)return element}
124
+ catch(error){this.emit("error",{error});element.textContent=""}}
125
+ const time=h("span","cx-cal-ev-time"),title=h("span","cx-cal-ev-title");if(!event.allDay&&kind!=="bar"&&kind!=="lane"){setText(time,kind==="item"?this.calTimeText(event):this.calFmt.time(event.start));element.appendChild(time)}
126
+ setText(title,event.title);element.appendChild(title);if(kind==="block"||kind==="item"||kind==="lane"){if(event.subtitle){const subtitle=h("span","cx-cal-ev-sub");setText(subtitle,event.subtitle);element.appendChild(subtitle)}
127
+ const column=this.cal.status;if(column&&(kind!=="lane")&&cellValue(event.row.cells[column.id])!=null){const statusClass="cx-cal-ev-status" +(event.base?.toneFromStatus?" cx-cal-status-tone":"");let status=h("span",statusClass);let drawn=false;if(column.render){const slot={content:status},context=new HostCellContext(slot,column.id,undefined,undefined,this.columns.indexOf(column));(this.calHostSlots||=new Set()).add(slot);try{drawn=column.render(event.row,status,context)===true}
128
+ catch(error){disposeHostSlot(slot);this.calHostSlots.delete(slot);status=h("span",statusClass);this.emit("error",{error})}}
129
+ if(!drawn){const text=h("span");setText(text,this.calStatusText(event));status.appendChild(text)}
130
+ element.appendChild(status)}}
131
+ if(event.recurring)element.appendChild(calIcon("repeat"));return element},calPublicEvent(event){return gridFreeze({id:event.id,record:event.key,start:calIso(event.start),end:calIso(event.end),allDay:event.allDay,title:event.title,resource:event.resource,recurring:event.recurring,source:event.base?.source??null,external:!!event.base?.external})},async calOpenEvent(event,keyboard,returnFocus){if(event.base?.external)return this.calShowDetails(event,this.calBody.querySelector('[data-cx-event="'+CSS.escape(event.id)+ '"]')||returnFocus);const row=gridFreeze(JSON.parse(JSON.stringify(event.row)));if(!this.emit("rowActivate",{id:event.key,row,keyboard:!!keyboard,occurrence:event.recurring?calIso(event.start):null}))return;if(typeof this.opts.onDetails==="function")
132
+ return this.runGridHook(signal=>this.opts.onDetails({row,query:this.acceptedQuery,signal,returnFocus,occurrence:event.recurring?calIso(event.start):null}),this.dataLifetime.signal);if(this.cal.details==="popover")return this.calShowDetails(event,this.calBody.querySelector('[data-cx-event="'+CSS.escape(event.id)+ '"]')||returnFocus);await this.requirePart("filter-editor");if(this.recordNeeded(event.row))await this.loadRecordPanel();if(this.destroyed)return;const dialog=this.openGridDrawer(event.row,null,returnFocus);const id=event.id;dialog?.addEventListener("close",()=>requestAnimationFrame(()=>{if(this.destroyed||this.calRoot.contains(document.activeElement)||document.activeElement&&document.activeElement!==document.body&&document.activeElement!==this.grid)return;(this.calBody.querySelector('[data-cx-event="'+CSS.escape(id)+ '"]')||this.calFocusTarget())?.focus({preventScroll:true})}),{once:true});return dialog},async openCalendarEvent(id){const event=this.calEvents.find(item=>item.id===id||item.key===id);if(!event)throw new Error("CanopyX: the calendar does not show event "+id+".");return this.calOpenEvent(event,false,document.activeElement)},calOpenMore(day,events,anchor){this.calCloseMore();const labels=this.calLabels,panel=this.calMore=h("div","cx-cal-more"),title=h("h3","cx-cal-more-h"),list=h("div","cx-cal-more-list");panel.setAttribute("role","dialog");panel.setAttribute("aria-modal","false");title.id=this.id+"-cal-more";panel.setAttribute("aria-labelledby",title.id);setText(title,fill(labels.calendarDayEvents,{date:this.calFmt.longDay(day)}));const close=gridButton("",()=>this.calCloseMore(true));close.classList.add("cx-btn","icon","ghost","cx-cal-more-close");close.setAttribute("aria-label",this.labels.gridCloseRecord);close.appendChild(gridIcon("close"));const head=h("div","cx-cal-more-top");head.append(title,close);for(const event of events)list.appendChild(this.calEventElement(event,"chip"));panel.append(head,list);panel._calAnchor=anchor;this.calRoot.appendChild(panel);const box=anchor.getBoundingClientRect(),frame=this.calRoot.getBoundingClientRect();const width=Math.min(300,frame.width-16);panel.style.width=width+"px";panel.style.left=Math.max(8,Math.min(box.left-frame.left-8,frame.width-width-8))+ "px";panel.style.top=Math.max(8,Math.min(box.top-frame.top-4,frame.height-panel.offsetHeight-8))+ "px";panel.addEventListener("keydown",event=>{const items=[...list.children],at=items.indexOf(document.activeElement);if(event.key==="Escape"){event.preventDefault();event.stopPropagation();this.calCloseMore(true)}
133
+ else if(event.key==="ArrowDown"||event.key==="ArrowUp"){event.preventDefault();items[Math.max(0,Math.min(items.length-1,at +(event.key==="ArrowDown"?1:-1)))]?.focus()}
134
+ else if(event.key==="Tab"){event.preventDefault();(document.activeElement===close?items[0]:close).focus()}});panel.addEventListener("click",event=>{const element=event.target.closest(".cx-cal-ev");if(element)this.calOpenEvent(element._calEvent,event.detail===0,element).catch(error=>this.emit("error",{error}))});this.calMoreOutside=event=>{if(!panel.contains(event.target)&&event.target!==anchor)this.calCloseMore(false)};document.addEventListener("pointerdown",this.calMoreOutside,true);(list.firstChild||close).focus();list.firstChild&&(list.firstChild.tabIndex=0)},calCloseMore(restore){const panel=this.calMore;if(!panel)return;this.calMore=null;document.removeEventListener("pointerdown",this.calMoreOutside,true);panel.remove();if(restore&&panel._calAnchor?.isConnected)panel._calAnchor.focus({preventScroll:true})},calMarkHighlights(){const keys=this.linkHighlight;let count=0;for(const element of this.calBody.querySelectorAll(".cx-cal-ev")){const on=!!keys?.has(element.dataset.cxRecord);if(on)count++;if(on)element.dataset.cxLinked="true";else delete element.dataset.cxLinked}
135
+ this.host.classList.toggle("cx-cal-has-linked",!!keys?.size);if(keys?.size)this.calAnnounce(calCount(this,this.calLabels.calendarHighlighted,count))}});const CAL_HOVER_DELAY=600;Object.assign(DataGrid.prototype,{calDetailsContent(event,titleId){const title=h("h3","cx-cal-det-title"),when=h("p","cx-cal-det-when"),parts=[title,when];if(titleId)title.id=titleId;setText(title,event.title);setText(when,this.calTimeText(event));const status=this.calStatusText(event),resource=event.resource!=null?(this.calResourceName?this.calResourceName(event.resource):event.resource):"";const fields=event.base?.external?event.row.fields:null,sourceName=fields?this.cal.sources.find(source=>source.id===event.base.source)?.label:"";const facts=[status,resource,event.subtitle,fields?.location,sourceName].filter(Boolean);if(facts.length){const line=h("p","cx-cal-det-facts");setText(line,facts.join(" · "));parts.push(line)}
136
+ const description=fields?fields.description||"":this.cal.description?cellText(event.row.cells[this.cal.description.id],this.cal.description,this.fmt):"";if(description){const text=h("p","cx-cal-det-desc");setText(text,description);parts.push(text)}
137
+ return parts},calPlace(panel,anchor){this.calRoot.appendChild(panel);const box=anchor.getBoundingClientRect(),frame=this.calRoot.getBoundingClientRect(),width=Math.min(320,frame.width-16);panel.style.width=width+"px";const rtl=getComputedStyle(this.calRoot).direction==="rtl";const right=box.right-frame.left+6,left=box.left-frame.left-width-6;let x=rtl?(left>=8?left:right):(right+width<=frame.width-8?right:left);x=Math.max(8,Math.min(x,frame.width-width-8));panel.style.left=x+"px";panel.style.top=Math.max(8,Math.min(box.top-frame.top,frame.height-panel.offsetHeight-8))+ "px"},calShowDetails(event,anchor){this.calCloseDetails(false);this.calHideHover();const panel=this.calDetails=h("div","cx-cal-details"),close=gridButton("",()=>this.calCloseDetails(true)),head=h("div","cx-cal-det-top");const titleId=this.id+"-cal-det";panel.setAttribute("role","dialog");panel.setAttribute("aria-modal","false");panel.setAttribute("aria-labelledby",titleId);close.classList.add("cx-btn","icon","ghost","cx-cal-det-close");close.setAttribute("aria-label",this.calLabels.calendarClose);close.appendChild(gridIcon("close"));const[title,...rest]=this.calDetailsContent(event,titleId);head.append(title,close);panel.append(head,...rest);panel._calAnchor=anchor;this.calPlace(panel,anchor||this.calBody);const description=panel.querySelector(".cx-cal-det-desc");if(description&&description.scrollHeight>description.clientHeight+1)description.tabIndex=0;panel.addEventListener("keydown",key=>{if(key.key==="Escape"){key.preventDefault();key.stopPropagation();this.calCloseDetails(true)}
138
+ else if(key.key==="Tab"){key.preventDefault();const stops=[close,...panel.querySelectorAll(".cx-cal-det-desc[tabindex]")],at=stops.indexOf(document.activeElement);stops[(at +(key.shiftKey?stops.length-1:1))%stops.length].focus()}});this.calDetailsOutside=press=>{if(!panel.contains(press.target)&&press.target!==anchor)this.calCloseDetails(false)};document.addEventListener("pointerdown",this.calDetailsOutside,true);close.focus({preventScroll:true});return panel},calCloseDetails(restore){const panel=this.calDetails;if(!panel)return;this.calDetails=null;document.removeEventListener("pointerdown",this.calDetailsOutside,true);panel.remove();if(restore&&panel._calAnchor?.isConnected)panel._calAnchor.focus({preventScroll:true})},calHoverOver(pointer){if(!this.cal.hover||pointer.pointerType==="touch"||this.calDrag||this.calMoving||this.calDetails||this.calMenu)return;const element=pointer.target.closest?.(".cx-cal-ev");if(!element?._calEvent||element===this.calHoverAnchor)return;this.calHideHover();this.calHoverAnchor=element;this.calHoverTimer=setTimeout(()=>{if(this.destroyed||!element.isConnected||this.calDrag||this.calDetails)return;const card=this.calHover=h("div","cx-cal-hover");card.setAttribute("aria-hidden","true");card.append(...this.calDetailsContent(element._calEvent));this.calPlace(card,element)},CAL_HOVER_DELAY)},calHoverOut(pointer){if(this.calHoverAnchor&&!this.calHoverAnchor.contains(pointer.relatedTarget))this.calHideHover()},calHideHover(){clearTimeout(this.calHoverTimer);this.calHoverAnchor=null;this.calHover?.remove();this.calHover=null}});Object.assign(DataGrid.prototype,{calMenuContext(target,x){const element=target.closest?.(".cx-cal-ev"),lane=!element&&x!=null&&target.closest?.(".cx-tl-lane");const cell=lane?[...lane.querySelectorAll(".cx-tl-cell")].find(item=>{const box=item.getBoundingClientRect();return x>=box.left&&x<box.right}):target.closest?.("[data-cx-day]");if(element?._calEvent)return{kind:"event",target:element,event:this.calPublicEvent(element._calEvent),raw:element._calEvent};if(!cell)return null;const minutes=cell.dataset.cxMinutes!==undefined?Number(cell.dataset.cxMinutes):null;return{kind:cell.classList.contains("cx-tl-cell")?"resource":minutes===null?"day":"slot",target:cell,day:calDayText(Number(cell.dataset.cxDay)),minutes,resource:this.calCellResource(cell)}},calMenuItems(context){const labels=this.calLabels,items=[];if(context.kind==="event"){items.push({id:"open",label:labels.calendarOpen,run:()=>this.calOpenEvent(context.raw,true,context.target)});if(this.calStartMove&&this.cal.editable&&(this.calCanEdit(context.raw,false)||this.calCanEdit(context.raw,true)))items.push({id:"move",label:labels.calendarMove,run:()=>this.calStartMove(context.target)})}else if(this.cal.create&&this.calCreateFromClick){items.push({id:"new",label:labels.calendarNewHere,run:()=>this.calCreateFromClick(context.target)})}
139
+ const hook=this.opts.calendar?.menu;if(typeof hook!=="function")return items;const own={kind:context.kind,event:context.event,day:context.day,minutes:context.minutes,resource:context.resource};let result;try{result=hook(Object.freeze(own),items.map(({id,label})=>Object.freeze({id,label})))}catch(error){this.emit("error",{error});return items}
140
+ if(result===null||result===false)return[];if(!Array.isArray(result))return items;return result.slice(0,30).map(item=>{const kept=item&&items.find(entry=>entry.id===item.id&&!item.run);if(kept)return kept;return item&&typeof item.label==="string"&&item.label.trim()&&typeof item.run==="function"?{id:item.id??null,label:item.label.slice(0,200),run:()=>item.run(Object.freeze(own)),disabled:item.disabled===true}:null}).filter(Boolean)},calOpenMenu(target,x,y){if(!this.cal.menu)return false;const context=this.calMenuContext(target,x);if(context?.target.classList.contains("cx-cal-invalid"))return false;if(!context)return false;const items=this.calMenuItems(context);if(!items.length)return false;this.calCloseMenu(false);this.calHideHover();const menu=this.calMenu=h("div","cx-cal-menu"),name=context.kind==="event"?context.raw.title:context.kind==="resource"?this.calResourceName?.(context.resource)??"":this.calFmt.longDay(calParseDay(context.day));menu.setAttribute("role","menu");menu.setAttribute("aria-label",fill(this.calLabels.calendarActions,{title:name}));const buttons=items.map(item=>{const button=h("button","cx-cal-menu-item");button.type="button";button.setAttribute("role","menuitem");button.tabIndex=-1;setText(button,item.label);if(item.disabled)button.setAttribute("aria-disabled","true");button.addEventListener("click",()=>{if(item.disabled)return;this.calCloseMenu(true);Promise.resolve().then(()=>item.run()).catch(error=>this.emit("error",{error}))});menu.appendChild(button);return button});menu._calReturn=target;menu.addEventListener("contextmenu",event=>event.preventDefault());this.calRoot.appendChild(menu);const frame=this.calRoot.getBoundingClientRect(),box=target.getBoundingClientRect(),width=menu.offsetWidth,height=menu.offsetHeight;const left=x!=null?x-frame.left:box.left-frame.left+Math.min(box.width,24),top=y!=null?y-frame.top:box.top-frame.top+Math.min(box.height,24);menu.style.left=Math.max(4,Math.min(left,frame.width-width-4))+ "px";menu.style.top=Math.max(4,Math.min(top,frame.height-height-4))+ "px";menu.addEventListener("keydown",key=>{const at=buttons.indexOf(document.activeElement);if(key.key==="Escape"||key.key==="Tab"){key.preventDefault();key.stopPropagation();this.calCloseMenu(true)}
141
+ else if(key.key==="ArrowDown"){key.preventDefault();buttons[(at+1)%buttons.length].focus()}
142
+ else if(key.key==="ArrowUp"){key.preventDefault();buttons[(at+buttons.length-1)%buttons.length].focus()}
143
+ else if(key.key==="Home"){key.preventDefault();buttons[0].focus()}
144
+ else if(key.key==="End"){key.preventDefault();buttons[buttons.length-1].focus()}});this.calMenuOutside=press=>{if(!menu.contains(press.target))this.calCloseMenu(false)};document.addEventListener("pointerdown",this.calMenuOutside,true);buttons[0].focus({preventScroll:true});return true},calCloseMenu(restore){const menu=this.calMenu;if(!menu)return;this.calMenu=null;document.removeEventListener("pointerdown",this.calMenuOutside,true);menu.remove();if(restore&&menu._calReturn?.isConnected)menu._calReturn.focus({preventScroll:true})},calMenuStart(){if(!this.cal.menu)return;const body=this.calBody,signal=this.lifetime.signal;let press=null;const cancel=()=>{if(press){clearTimeout(press.timer);press=null;this.calPressTimer=0}};body.addEventListener("contextmenu",event=>{cancel();if(this.calMenu&&Date.now()-(this.calMenuOpenedAt||0)<800){event.preventDefault();return}
145
+ if(event.shiftKey||this.calDrag?.started||this.calMoving)return;const keyboard=event.button!==2&&event.clientX===0&&event.clientY===0;if(this.calOpenMenu(event.target,keyboard?null:event.clientX,keyboard?null:event.clientY)){event.preventDefault();this.calMenuOpenedAt=Date.now()}},{signal});body.addEventListener("pointerdown",event=>{cancel();if(this.calPressSuppress){this.calPressSuppress=false;this.calSuppressClick=false}
146
+ if(event.pointerType!=="touch")return;press={x:event.clientX,y:event.clientY,target:event.target,timer:setTimeout(()=>{const at=press;press=null;if(at&&!this.calDrag?.started&&Date.now()-(this.calMenuOpenedAt||0)>800&&this.calOpenMenu(at.target,at.x,at.y)){this.calMenuOpenedAt=Date.now();this.calSuppressClick=true;this.calPressSuppress=true}},600)};this.calPressTimer=press.timer},{signal,passive:true});body.addEventListener("pointermove",event=>{if(press&&Math.hypot(event.clientX-press.x,event.clientY-press.y)>8)cancel()},{signal,passive:true});for(const type of["pointerup","pointercancel","scroll"])body.addEventListener(type,cancel,{signal,passive:true,capture:type==="scroll"})},calMenuKey(event){if(!this.cal.menu||!(event.key==="ContextMenu"||event.key==="F10"&&event.shiftKey))return false;if(!this.calOpenMenu(event.target))return false;event.preventDefault();this.calMenuOpenedAt=Date.now();return true}});Object.assign(DataGrid.prototype,{calBuildLegend(){const sources=this.cal.sources;this.calHiddenSources=new Set(sources.filter(source=>source.hidden).map(source=>source.id));if(!sources.length)return null;const legend=this.calLegend=h("div","cx-cal-legend");legend.setAttribute("role","group");legend.setAttribute("aria-label",this.calLabels.calendarSources);for(const source of sources){const button=h("button","cx-cal-src"),swatch=h("i","cx-cal-src-swatch"),text=h("span");button.type="button";button.dataset.cxSource=source.id;button.setAttribute("aria-pressed",String(!this.calHiddenSources.has(source.id)));if(source.tone)button.style.setProperty("--cx-ev","var(--cx-tone-"+source.tone+",var(--cx-accent))");swatch.setAttribute("aria-hidden","true");setText(text,source.label);button.append(swatch,text);button.addEventListener("click",()=>this.calToggleSource(source.id));legend.appendChild(button)}
147
+ return legend},setCalendarSource(id,visible){return this.calToggleSource(id,visible)},calToggleSource(id,show){const hidden=this.calHiddenSources,source=this.cal.sources.find(item=>item.id===id);if(!source)throw new Error("CanopyX: the calendar has no source "+id+".");const visible=show===undefined?hidden.has(id):!!show;if(visible)hidden.delete(id);else hidden.add(id);this.calLegend?.querySelector('[data-cx-source="'+CSS.escape(id)+ '"]')?.setAttribute("aria-pressed",String(visible));this.calSourceVersion=(this.calSourceVersion||0)+ 1;if(this.calAllEvents){this.calEvents=this.calShownEvents(this.calAllEvents);this.calPaint({keep:true})}
148
+ this.emit("calendarSource",{id,visible});return visible},calShownEvents(events){const hidden=this.calHiddenSources;return hidden?.size?events.filter(event=>!event.base.source||!hidden.has(event.base.source)):events}});const CAL_LANE=22,CAL_NUMBER=26;const calDayOrder=(a,b)=>(b.allDay-a.allDay)||a.start-b.start||(a.id<b.id?-1:1);Object.assign(DataGrid.prototype,{calWeekdays(){const days=[];for(let i=0;i<7;i++){const dow=(this.cal.firstDay+i)%7;if(!this.cal.hidden.has(dow))days.push(dow)}
149
+ return days},calWeekdayRow(className,corner){const row=h("div",className),fmt=this.calFmt;row.setAttribute("role","row");if(corner){const cell=h("div","cx-cal-corner");cell.setAttribute("role","columnheader");setText(cell,corner===true?"":corner);row.appendChild(cell)}
150
+ for(const dow of this.calWeekdays()){const cell=h("div","cx-cal-dowc"),day=calDayNumber(2023,1,1)+ dow;cell.setAttribute("role","columnheader");cell.setAttribute("aria-label",fmt.weekday(day,"long"));setText(cell,fmt.weekday(day,this.calPhone?"narrow":"short"));row.appendChild(cell)}
151
+ return row},calDayName(day,count,ranges){const parts=[this.calFmt.longDay(day)];if(day===this.calZone.day(Date.now()))parts.push(this.calLabels.calendarToday);for(const range of ranges||[])parts.push(range.label);parts.push(count?calCount(this,this.calLabels.calendarEvents,count):this.calLabels.calendarNoEvents);return parts.join(", ")},calBackgroundsOn(day,resource,partial){const out=[];for(const range of this.calRanges)if(range.sd<=day&&range.ed>=day&&(resource===undefined?!range.inverse||range.resource==null:range.resource==null||range.resource===resource)&&
152
+ (partial||!range.inverse||range.start<=this.calZone.start(day)&&range.end>=this.calZone.start(day+1)))out.push(range);for(const event of this.calEvents)if(event.background&&event.sd<=day&&event.ed>=day&&(resource===undefined||event.resource==null||event.resource===resource))
153
+ out.push({id:event.id,label:event.title,tone:event.tone||"info",kind:"background",sd:event.sd,ed:event.ed,start:event.start,end:event.end,resource:event.resource});return out},calPaintMonth(){const range=this.calRange,body=this.calBody,weekCount=Math.round((range.lastDay-range.firstDay+1)/ 7);
154
+ const available=body.clientHeight-CAL_NUMBER-4,rowHeight=this.calPhone?44:Math.max(96,Math.floor(available / weekCount));
155
+ this.calCells=new Map();const grid=this.calMonthGrid(range.firstDay,range.lastDay,range.month,{rowHeight,dots:this.calPhone,outside:this.cal.outsideDays,labelledBy:this.calTitle.id});body.replaceChildren(grid);this.calGrid=grid;if(this.calPhone){const day=this.calFocus?.day??this.calDate;body.appendChild(this.calDayList(day));this.calMarkListedDay(day)}},calMonthGrid(first,last,monthStart,options){const weekdays=this.calWeekdays(),today=this.calZone.day(Date.now()),month=calDayParts(monthStart).m,monthEnd=calAddMonths(monthStart,1)- 1;const events=this.calEvents.filter(event=>!event.background),rowHeight=options.rowHeight,valid=this.cal.validRange;const grid=h("div","cx-cal-month" +(options.className?" "+options.className:""));grid.setAttribute("role","grid");if(options.labelledBy)grid.setAttribute("aria-labelledby",options.labelledBy);grid.setAttribute("aria-readonly",String(!this.cal.editable&&!this.cal.create));grid.style.setProperty("--cx-cal-cols",String(weekdays.length));grid.appendChild(this.calWeekdayRow("cx-cal-dow"));const weeks=h("div","cx-cal-weeks");weeks.setAttribute("role","rowgroup");grid.appendChild(weeks);const fit=Math.max(1,Math.floor((rowHeight-CAL_NUMBER-4)/ CAL_LANE));
156
+ const limit=this.calPrinting?Infinity:options.dots?0:this.cal.eventLimit==="auto"?fit:Math.min(this.cal.eventLimit,Math.max(fit,1));for(let week=first;week<=last;week +=7){const row=h("div","cx-cal-week"),columns=new Map();row.setAttribute("role","row");row.style.minHeight=rowHeight+"px";if(this.cal.weekNumbers){const number=this.calWeekNumber(week+3);row.setAttribute("aria-label",fill(this.calLabels.calendarWeekNumber,{number}));row.dataset.cxWeek=String(number)}
157
+ weekdays.forEach((dow,index)=>columns.set(week +(dow-this.cal.firstDay+7)%7,index));const shown=[...columns.keys()].sort((a,b)=>a-b);const outside=day=>day<monthStart||day>monthEnd,pad=day=>!options.outside&&outside(day);const order=new Map(shown.map((day,index)=>[day,index])),cells=[];for(const day of shown){if(pad(day)){const empty=h("div","cx-cal-pad");empty.setAttribute("role","gridcell");empty.style.gridColumn=String(order.get(day)+ 1);cells.push(empty);row.appendChild(empty);continue}
158
+ const invalid=valid&&(day<valid.first||day>valid.last);const cell=h("div","cx-cal-day" +(calDayParts(day).m!==month?" cx-cal-out":"")+(day===today?" cx-cal-today":"")+(invalid?" cx-cal-invalid":""));cell.setAttribute("role","gridcell");cell.tabIndex=-1;cell.dataset.cxDay=String(day);if(invalid)cell.setAttribute("aria-disabled","true");if(day===today)cell.setAttribute("aria-current","date");const number=h("span","cx-cal-num");setText(number,this.calFmt.dayNumber(day));number.setAttribute("aria-hidden","true");cell.appendChild(number);if(this.cal.weekNumbers&&cells.every(item=>item.classList.contains("cx-cal-pad"))){const wk=h("span","cx-cal-wknum");setText(wk,fill(this.calLabels.calendarWeekShort,{number:row.dataset.cxWeek}));wk.setAttribute("aria-hidden","true");cell.appendChild(wk)}
159
+ cell.style.gridColumn=String(order.get(day)+ 1);cells.push(cell);this.calCells.set(day,cell);row.appendChild(cell)}
160
+ const weekEvents=events.filter(event=>event.sd<=week+6&&event.ed>=week);const placed=calLanes(weekEvents,week,week+6,day=>order.has(day)&&!pad(day)?order.get(day):-1);if(this.calPrinting)row.style.minHeight=Math.max(rowHeight,CAL_NUMBER+6 +(Math.max(-1,...placed.map(item=>item.lane))+ 1)*CAL_LANE)+ "px";const totals=new Array(shown.length).fill(0);for(const item of placed)for(let col=item.from;col<=item.to;col++)totals[col]++;const dayLimit=totals.map(total=>total>limit?Math.max(0,limit-1):limit),visible=new Array(shown.length).fill(0);for(const item of placed){let run=null;const flush=()=>{if(!run)return;const event=item.event,bar=calInAllDayRow(event)||event.ed>event.sd,element=this.calEventElement(event,bar?"bar":"chip");element.style.setProperty("--c",String(run.from));element.style.setProperty("--s",String(run.to-run.from+1));element.style.setProperty("--l",String(item.lane));if(event.sd<shown[run.from])element.classList.add("cx-cal-cont-start");if(event.ed>shown[run.to])element.classList.add("cx-cal-cont-end");cells[run.from].appendChild(element);run=null};for(let col=item.from;col<=item.to;col++){if(item.lane<dayLimit[col]){visible[col]++;if(run)run.to=col;else run={from:col,to:col}}
161
+ else flush()}
162
+ flush()}
163
+ cells.forEach((cell,col)=>{if(cell.classList.contains("cx-cal-pad"))return;const day=shown[col],backgrounds=this.calBackgroundsOn(day);if(backgrounds.length){cell.classList.add("cx-cal-bgday");cell.style.setProperty("--cx-bg-tone","var(--cx-tone-"+backgrounds[0].tone+",var(--cx-tone-info))");if(backgrounds[0].sd===day||col===0){const label=h("span","cx-cal-bglabel");setText(label,backgrounds[0].label);label.setAttribute("aria-hidden","true");cell.appendChild(label)}}
164
+ const hidden=totals[col]- visible[col];cell.setAttribute("aria-label",this.calDayName(day,totals[col],backgrounds));if(options.dots&&totals[col]){const dots=h("span","cx-cal-dots");dots.setAttribute("aria-hidden","true");for(const item of placed.filter(entry=>entry.from<=col&&entry.to>=col).slice(0,3)){const dot=h("i");if(item.event.tone)dot.style.setProperty("--cx-ev","var(--cx-tone-"+item.event.tone+",var(--cx-accent))");dots.appendChild(dot)}
165
+ cell.appendChild(dots)}else if(hidden>0){const more=gridButton(fill(this.calLabels.calendarMore,{count:numberFormat(this.lang).format(hidden)}),()=>{});more.className="cx-cal-more-btn";more.tabIndex=-1;more.dataset.cxDay=String(day);more.setAttribute("aria-label",fill(this.calLabels.calendarMoreLabel,{count:numberFormat(this.lang).format(hidden),date:this.calFmt.longDay(day)}));more.style.setProperty("--l",String(dayLimit[col]));more.style.setProperty("--c",String(col));cell.appendChild(more)}});weeks.appendChild(row)}
166
+ return grid},calWeekNumber(day){if(this.cal.weekNumbers!=="locale"){const thursday=day -(calDow(day)+ 6)%7+3,year=calDayParts(thursday).y;return Math.floor((thursday-calDayNumber(year,1,1))/ 7) + 1;
167
+ }
168
+ let minimal=1;try{const locale=new Intl.Locale(this.lang);minimal=(locale.getWeekInfo?.()||locale.weekInfo)?.minimalDays||1}catch{minimal=1}
169
+ const start=calWeekStart(day,this.cal.firstDay),firstWeek=year=>{const january=calDayNumber(year,1,1);let week=calWeekStart(january,this.cal.firstDay);if(week+7-january<minimal)week +=7;return week};let year=calDayParts(start+6).y,base=firstWeek(year);if(start<base){year--;base=firstWeek(year)}
170
+ return Math.floor((start-base)/ 7) + 1;
171
+ },calMarkListedDay(day){for(const cell of this.calBody.querySelectorAll(".cx-cal-day[data-cx-listed]"))cell.removeAttribute("data-cx-listed");this.calBody.querySelector('.cx-cal-day[data-cx-day="'+day+'"]')?.setAttribute("data-cx-listed","")},calDayList(day){const list=h("div","cx-cal-daylist"),title=h("h3","cx-cal-daylist-h"),events=this.calEventsOn(day);setText(title,this.calFmt.longDay(day));list.appendChild(title);if(!events.length){const empty=h("p","cx-cal-empty");setText(empty,this.calLabels.calendarNoEvents);list.appendChild(empty)}
172
+ for(const event of events)list.appendChild(this.calEventElement(event,"item"));return list},calEventsOn(day){const buckets=this.calDayBuckets;if(buckets?.events===this.calEvents&&day>=buckets.first&&day<=buckets.last)return buckets.days.get(day)||[];return this.calEvents.filter(event=>!event.background&&event.sd<=day&&event.ed>=day).sort(calDayOrder)},calBucketDays(first,last){const days=new Map();for(const event of this.calEvents)if(!event.background)for(let day=Math.max(first,event.sd);day<=Math.min(last,event.ed);day++)(days.get(day)||days.set(day,[]).get(day)).push(event);for(const list of days.values())list.sort(calDayOrder);this.calDayBuckets={events:this.calEvents,days,first,last}},calKeyMonth(event,cell){const day=Number(cell.dataset.cxDay),key=event.key;let next=null;if(key==="ArrowLeft"||key==="ArrowRight")next=this.calShownDay(day +(key==="ArrowLeft"?-1:1),key==="ArrowLeft"?-1:1);else if(key==="ArrowUp"||key==="ArrowDown")next=day +(key==="ArrowUp"?-7:7);else if(key==="Home")next=event.ctrlKey?this.calShownDay(this.calRange.month,1):this.calShownDay(calWeekStart(day,this.cal.firstDay),1);else if(key==="End")next=event.ctrlKey?this.calShownDay(calAddMonths(this.calRange.month,1)- 1,-1):this.calShownDay(calWeekStart(day,this.cal.firstDay)+ 6,-1);else if(key==="PageUp"||key==="PageDown"){const sign=key==="PageUp"?-1:1,view=this.calView;next=view==="weekList"?day+7*sign:view==="dayList"?this.calShownDay(day+sign,sign):calAddMonths(day,sign*(event.shiftKey?12:1))}
173
+ else if(key==="Enter"||key===" "||key==="F2"){event.preventDefault();const first=cell.querySelector(".cx-cal-ev,.cx-cal-more-btn");if(first&&key!==" "){first.tabIndex=0;cell.tabIndex=-1;first.focus()}
174
+ else this.calSelectDays(day,day);return true}
175
+ if(next===null)return false;event.preventDefault();this.calFocusDay(next);return true},calKeyInDay(event,item){const cell=item.closest(".cx-cal-day"),items=[...cell.querySelectorAll(".cx-cal-ev,.cx-cal-more-btn")],at=items.indexOf(item),key=event.key;if(key==="ArrowDown"||key==="ArrowUp"){event.preventDefault();const next=items[at +(key==="ArrowDown"?1:-1)];if(next){item.tabIndex=-1;next.tabIndex=0;next.focus()}return true}
176
+ if(key==="Escape"||key==="ArrowLeft"||key==="ArrowRight"){event.preventDefault();item.tabIndex=-1;const day=Number(cell.dataset.cxDay);if(key==="Escape"){cell.tabIndex=0;cell.focus()}else this.calFocusDay(this.calShownDay(day +(key==="ArrowLeft"?-1:1),key==="ArrowLeft"?-1:1));return true}
177
+ return false},calFocusDay(day){const range=this.calRange,view=this.calView;const monthly=view==="month"||view==="mini",inside=monthly?calMonthStart(day)===range.month:day>=range.firstDay&&day<=range.lastDay;const valid=this.cal.validRange;if(valid&&(day<valid.first||day>valid.last)){this.calAnnounce(this.calLabels.calendarOutsideRange);return}
178
+ this.calFocus={day,minutes:this.calFocus?.minutes??null,event:null};if(!inside){this.calGo(monthly||view==="agenda"||view==="months"?calMonthStart(day):day,view,day).then(()=>this.calRestoreFocus(true));return}
179
+ if(this.calPhone&&view==="month"){const list=this.calBody.querySelector(".cx-cal-daylist");if(list){this.calDisposeEventRenderers(list);list.replaceWith(this.calDayList(day))}
180
+ this.calMarkListedDay(day);const count=this.calEventsOn(day).length;this.calAnnounce(this.calFmt.longDay(day)+ ", " +(count?calCount(this,this.calLabels.calendarEvents,count):this.calLabels.calendarNoEvents))}
181
+ this.calRestoreFocus(true)}});const CAL_DAY_GRIDS=["month","mini","months","weekList","dayList"];function calDayGrid(view){return CAL_DAY_GRIDS.includes(view)}
182
+ Object.assign(DataGrid.prototype,{calPaintMini(){const range=this.calRange;this.calCells=new Map();const grid=this.calMonthGrid(range.firstDay,range.lastDay,range.month,{rowHeight:34,dots:true,outside:this.cal.outsideDays,labelledBy:this.calTitle.id,className:"cx-cal-mini"});this.calBody.replaceChildren(grid);this.calGrid=grid},calPaintMonths(){const range=this.calRange,stack=h("div","cx-cal-months");this.calCells=new Map();for(let month=range.month;month<=range.lastDay;month=calAddMonths(month,1)){const section=h("section","cx-cal-mblock"),title=h("h3","cx-cal-mtitle"),last=calAddMonths(month,1)- 1;title.id=this.id+"-cal-m"+month;setText(title,this.calFmt.monthTitle(month));const first=calWeekStart(month,this.cal.firstDay),end=calWeekStart(last,this.cal.firstDay)+ 6;section.append(title,this.calMonthGrid(first,end,month,{rowHeight:92,dots:this.calPhone,outside:false,labelledBy:title.id}));stack.appendChild(section)}
183
+ this.calBody.replaceChildren(stack);this.calGrid=stack},calPaintList(){const range=this.calRange,days=this.calShownDays(range.firstDay,range.lastDay),today=this.calZone.day(Date.now()),valid=this.cal.validRange,fmt=this.calFmt;const grid=h("div","cx-cal-list" +(days.length===1?" cx-cal-list-day":"")),row=h("div","cx-cal-lrow");grid.setAttribute("role","grid");grid.setAttribute("aria-labelledby",this.calTitle.id);grid.setAttribute("aria-readonly",String(!this.cal.editable&&!this.cal.create));row.setAttribute("role","row");grid.style.setProperty("--cx-cal-cols",String(days.length));this.calCells=new Map();for(const day of days){const events=this.calEventsOn(day),invalid=valid&&(day<valid.first||day>valid.last);const cell=h("div","cx-cal-day cx-cal-lday" +(day===today?" cx-cal-today":"")+(invalid?" cx-cal-invalid":""));cell.setAttribute("role","gridcell");cell.tabIndex=-1;cell.dataset.cxDay=String(day);if(invalid)cell.setAttribute("aria-disabled","true");if(day===today)cell.setAttribute("aria-current","date");cell.setAttribute("aria-label",this.calDayName(day,events.length,this.calBackgroundsOn(day)));const head=h("div","cx-cal-lhead"),name=h("span","cx-cal-dayname"),number=h("span","cx-cal-num");head.setAttribute("aria-hidden","true");setText(name,fmt.weekday(day,days.length===1?"long":"short"));setText(number,fmt.dayNumber(day));head.append(name,number);if(this.cal.weekNumbers&&day===days[0]){const wk=h("span","cx-cal-wknum");setText(wk,fill(this.calLabels.calendarWeekShort,{number:this.calWeekNumber(day)}));head.appendChild(wk)}
184
+ cell.appendChild(head);if(!events.length){const empty=h("p","cx-cal-empty");setText(empty,this.calLabels.calendarNoEvents);empty.setAttribute("aria-hidden","true");cell.appendChild(empty)}
185
+ for(const event of events)cell.appendChild(this.calEventElement(event,"item"));const backgrounds=this.calBackgroundsOn(day);if(backgrounds.length){cell.classList.add("cx-cal-bgday");cell.style.setProperty("--cx-bg-tone","var(--cx-tone-"+backgrounds[0].tone+",var(--cx-tone-info))")}
186
+ this.calCells.set(day,cell);row.appendChild(cell)}
187
+ grid.appendChild(row);this.calBody.replaceChildren(grid);this.calGrid=grid}});const CAL_SLOT_PX=24,CAL_GUTTER=64,CAL_ALLDAY_LANES=3;Object.assign(DataGrid.prototype,{calTimeColumns(days){const all=this.calResourceColumns?.()||null;this.calResourcePages=null;if(!all)return days.map(day=>({day,resource:null}));const size=this.calPhone&&!this.calPrinting&&all.length>2?(days.length===1?2:1):all.length,pages=Math.ceil(all.length / size);
188
+ const page=Math.min(Math.floor((this.calResourceFirst||0)/ size), pages - 1);
189
+ if(pages>1)this.calResourcePages={all,size,pages,page};const resources=all.slice(page*size,page*size+size);const out=[];for(const day of days)for(const resource of resources)out.push({day,resource:resource.id,label:resource.label,group:resource.group});return out},calResourcePicker(){const{all,size,pages,page}=this.calResourcePages,bar=h("div","cx-cal-rpick"),select=h("select","cx-cal-rpick-select");select.setAttribute("aria-label",this.calLabels.calendarResources);for(let at=0;at<pages;at++){const option=h("option");option.value=String(at);setText(option,all.slice(at*size,at*size+size).map(item=>item.label).join(", "));select.appendChild(option)}
190
+ select.value=String(page);select.addEventListener("change",()=>{this.calResourceFirst=Number(select.value)*size;this.calPaint({keep:true});this.calBody.querySelector(".cx-cal-rpick-select")?.focus()});bar.appendChild(select);return bar},calPaintTime(){const cal=this.cal,range=this.calRange,days=this.calShownDays(range.firstDay,range.lastDay),columns=this.calTimeColumns(days);const today=this.calZone.day(Date.now()),slot=cal.slotMinutes,ppm=CAL_SLOT_PX / slot, labels = this.calLabels, fmt = this.calFmt;
191
+ const grid=h("div","cx-cal-time");grid.setAttribute("role","grid");grid.setAttribute("aria-labelledby",this.calTitle.id);grid.setAttribute("aria-readonly",String(!cal.editable));grid.style.setProperty("--cx-cal-n",String(columns.length));grid.style.setProperty("--cx-cal-ppm",ppm+"px");grid.style.setProperty("--cx-cal-gutter",CAL_GUTTER+"px");const head=h("div","cx-cal-thead");head.setAttribute("role","rowgroup");const dayRow=h("div","cx-cal-trow cx-cal-heads");dayRow.setAttribute("role","row");const corner=h("div","cx-cal-corner");corner.setAttribute("role","columnheader");dayRow.appendChild(corner);const perDay=columns.length / days.length;
192
+ for(const day of days){const cell=h("div","cx-cal-dayhead" +(day===today?" cx-cal-today":""));cell.setAttribute("role","columnheader");cell.dataset.cxDay=String(day);cell.style.flexGrow=String(perDay);cell.setAttribute("aria-label",fmt.longDay(day));if(perDay>1)cell.setAttribute("aria-colspan",String(perDay));if(!this.calAllowsSpan(day*CAL_DAY,(day+1)*CAL_DAY,true)){cell.classList.add("cx-cal-invalid");cell.setAttribute("aria-disabled","true")}
193
+ const name=h("span","cx-cal-dayname"),number=h("span","cx-cal-daynum");setText(name,fmt.weekday(day,this.calPhone?"narrow":"short"));setText(number,fmt.dayNumber(day));cell.append(name,number);if(day===today)cell.setAttribute("aria-current","date");dayRow.appendChild(cell)}
194
+ head.appendChild(dayRow);if(perDay>1&&columns.some(column=>column.group)){const groupRow=h("div","cx-cal-trow cx-cal-gheads");groupRow.setAttribute("role","row");const blank=h("div","cx-cal-corner");blank.setAttribute("role","columnheader");groupRow.appendChild(blank);for(let at=0;at<columns.length;){let end=at;while(end+1<columns.length&&columns[end+1].group===columns[at].group&&columns[end+1].day===columns[at].day)end++;const cell=h("div","cx-cal-ghead");cell.setAttribute("role","columnheader");cell.setAttribute("aria-colspan",String(end-at+1));cell.style.flexGrow=String(end-at+1);setText(cell,columns[at].group||"");cell.title=columns[at].group||"";groupRow.appendChild(cell);at=end+1}
195
+ head.appendChild(groupRow)}
196
+ if(perDay>1){const resourceRow=h("div","cx-cal-trow cx-cal-rheads");resourceRow.setAttribute("role","row");const blank=h("div","cx-cal-corner");blank.setAttribute("role","columnheader");resourceRow.appendChild(blank);for(const column of columns){const cell=h("div","cx-cal-rhead");cell.setAttribute("role","columnheader");setText(cell,column.label);cell.title=column.label;resourceRow.appendChild(cell)}
197
+ head.appendChild(resourceRow)}
198
+ head.appendChild(this.calAllDayRow(columns,days));grid.appendChild(head);const scroller=this.calScroller=h("div","cx-cal-scroll"),slots=h("div","cx-cal-slots");slots.setAttribute("role","rowgroup");const visible=cal.maxMinutes-cal.minMinutes;slots.style.height=visible*ppm+"px";this.calSlotCells=new Map();const segmentsByColumn=columns.map(column=>calSegments(this,this.calEvents.filter(event=>column.resource==null||event.resource===column.resource),column.day,cal.minMinutes,cal.maxMinutes));for(let minutes=cal.minMinutes;minutes<cal.maxMinutes;minutes +=slot){const row=h("div","cx-cal-srow" +(minutes%60===0?" cx-cal-hour":""));row.setAttribute("role","row");const label=h("div","cx-cal-stime");label.setAttribute("role","rowheader");if(minutes%60===0||slot>=60)setText(label,fmt.clock(minutes));else{label.setAttribute("aria-label",fmt.clock(minutes))}
199
+ row.appendChild(label);columns.forEach((column,index)=>{const cell=h("div","cx-cal-slot");cell.setAttribute("role","gridcell");cell.tabIndex=-1;cell.dataset.cxDay=String(column.day);cell.dataset.cxMinutes=String(minutes);if(column.resource!=null)cell.dataset.cxResource=column.resource;if(!this.calAllowsSpan(column.day*CAL_DAY,(column.day+1)*CAL_DAY,true)){cell.classList.add("cx-cal-invalid");cell.setAttribute("aria-disabled","true")}
200
+ cell.setAttribute("aria-label",fmt.longDay(column.day)+ ", "+fmt.clock(minutes)+(column.label?", "+column.label:""));if(column.day===today)cell.classList.add("cx-cal-today");this.calSlotCells.set(index+":"+minutes,cell);row.appendChild(cell)});slots.appendChild(row)}
201
+ columns.forEach((column,index)=>{this.calShade(slots,column,index,ppm);for(const segment of segmentsByColumn[index]){const event=segment.event,element=this.calEventElement(event,"block");element.style.setProperty("--d",String(index));element.style.setProperty("--c",String(segment.col));element.style.setProperty("--k",String(segment.cols));element.style.setProperty("--sp",String(segment.span));element.style.setProperty("--t",String(segment.top-cal.minMinutes));element.style.setProperty("--h",String(Math.max(segment.bottom-segment.top,event.marker?0:this.calMinSegment)));if(segment.clippedTop)element.classList.add("cx-cal-cont-start");if(segment.clippedBottom)element.classList.add("cx-cal-cont-end");if((segment.bottom-segment.top)*ppm<40)element.classList.add("cx-cal-short");const start=Math.min(cal.maxMinutes-slot,Math.floor(segment.top / slot) * slot);
202
+ (this.calSlotCells.get(index+":"+start)||slots).appendChild(element)}
203
+ const count=segmentsByColumn[index].length;if(count)for(let minutes=cal.minMinutes;minutes<cal.maxMinutes;minutes +=slot){const cell=this.calSlotCells.get(index+":"+minutes),starting=cell.querySelectorAll(".cx-cal-ev").length;if(starting)cell.setAttribute("aria-label",cell.getAttribute("aria-label")+ ", "+calCount(this,labels.calendarEvents,starting))}});if(cal.now){this.calNowLine=h("div","cx-cal-now");this.calNowLine.setAttribute("aria-hidden","true");slots.appendChild(this.calNowLine);this.calNowColumns=columns}
204
+ scroller.appendChild(slots);grid.appendChild(scroller);this.calBody.replaceChildren(...(this.calResourcePages?[this.calResourcePicker()]:[]),grid);this.calGrid=grid;if(cal.now)this.calTick();head.style.paddingInlineEnd=Math.max(0,scroller.offsetWidth-scroller.clientWidth)+ "px";if(!this.calScrolled||this.calScrolledView!==this.calView){this.calScrolled=true;this.calScrolledView=this.calView;const now=this.calZone.parts(Date.now()).minutes,shown=scroller.clientHeight / ppm;
205
+ const target=days.includes(today)&&now>cal.scrollMinutes+shown-60?now-60:cal.scrollMinutes;scroller.scrollTop=Math.max(0,(target-cal.minMinutes)*ppm-10)}},calAllDayRow(columns,days){const row=h("div","cx-cal-trow cx-cal-allrow");row.setAttribute("role","row");const label=h("div","cx-cal-stime cx-cal-alllabel");label.setAttribute("role","rowheader");setText(label,this.calLabels.calendarAllDay);row.appendChild(label);const index=new Map(),events=this.calEvents.filter(event=>!event.background&&calInAllDayRow(event));const perDay=columns.length / days.length, cells = [];
206
+ columns.forEach((column,at)=>{const cell=h("div","cx-cal-allcell");cell.setAttribute("role","gridcell");cell.tabIndex=-1;cell.dataset.cxDay=String(column.day);if(!this.calAllowsSpan(column.day*CAL_DAY,(column.day+1)*CAL_DAY,true)){cell.classList.add("cx-cal-invalid");cell.setAttribute("aria-disabled","true")}
207
+ if(column.resource!=null)cell.dataset.cxResource=column.resource;cells.push(cell);row.appendChild(cell);if(perDay===1)index.set(column.day,at)});const placed=[];if(perDay===1)placed.push(...calLanes(events,days[0],days[days.length-1],day=>index.has(day)?index.get(day):-1));else columns.forEach((column,at)=>{for(const event of events)if(event.sd<=column.day&&event.ed>=column.day&&event.resource===column.resource)placed.push({event,lane:0,from:at,to:at})});if(perDay>1){const lanes=new Map();for(const item of placed){item.lane=lanes.get(item.from)||0;lanes.set(item.from,item.lane+1)}}
208
+ const totals=new Array(columns.length).fill(0),shown=new Array(columns.length).fill(0);for(const item of placed)for(let at=item.from;at<=item.to;at++)totals[at]++;const lanes=Math.min(CAL_ALLDAY_LANES,Math.max(0,...placed.map(item=>item.lane+1))),limit=totals.map(total=>total>lanes?lanes-1:lanes);for(const item of placed){let from=null;const flush=to=>{if(from===null)return;const element=this.calEventElement(item.event,"bar");element.style.setProperty("--c",String(from));element.style.setProperty("--s",String(to-from+1));element.style.setProperty("--l",String(item.lane));if(item.event.sd<columns[from].day)element.classList.add("cx-cal-cont-start");if(item.event.ed>columns[to].day)element.classList.add("cx-cal-cont-end");cells[from].appendChild(element);from=null};for(let at=item.from;at<=item.to;at++){if(item.lane<limit[at]){shown[at]++;if(from===null)from=at}else flush(at-1)}
209
+ flush(item.to)}
210
+ cells.forEach((cell,at)=>{const hidden=totals[at]- shown[at],day=columns[at].day;cell.setAttribute("aria-label",this.calLabels.calendarAllDay+", "+this.calDayName(day,totals[at],this.calBackgroundsOn(day,columns[at].resource??undefined)));if(hidden>0){const more=gridButton(fill(this.calLabels.calendarMore,{count:numberFormat(this.lang).format(hidden)}),()=>{});more.className="cx-cal-more-btn";more.tabIndex=-1;more.dataset.cxDay=String(day);more.style.setProperty("--l",String(limit[at]));more.style.setProperty("--c",String(at));more.setAttribute("aria-label",fill(this.calLabels.calendarMoreLabel,{count:numberFormat(this.lang).format(hidden),date:this.calFmt.longDay(day)}));cell.appendChild(more)}});row.style.setProperty("--cx-cal-lanes",String(Math.max(1,lanes)));return row},calShade(slots,column,index,ppm){const cal=this.cal,dow=calDow(column.day),add=(from,to,cls,label,tone)=>{const top=Math.max(from,cal.minMinutes),bottom=Math.min(to,cal.maxMinutes);if(bottom<=top)return;const block=h("div","cx-cal-shade "+cls);block.setAttribute("aria-hidden","true");block.style.setProperty("--d",String(index));block.style.setProperty("--t",String(top-cal.minMinutes));block.style.setProperty("--h",String(bottom-top));if(tone)block.style.setProperty("--cx-bg-tone","var(--cx-tone-"+tone+",var(--cx-tone-info))");if(label&&(bottom-top)*ppm>=18){const text=h("span");setText(text,label);block.appendChild(text)}
211
+ slots.appendChild(block)};const hours=this.calHoursFor?.(column.resource,dow)??(cal.working&&cal.working.days.has(dow)?[cal.working]:cal.working?[]:null);if(hours){let at=0;for(const span of hours.slice().sort((a,b)=>a.start-b.start)){add(at,span.start,"cx-cal-off");at=Math.max(at,span.end)}
212
+ add(at,1440,"cx-cal-off")}
213
+ const zone=this.calZone,dayStart=zone.start(column.day),dayEnd=zone.start(column.day+1);for(const range of this.calBackgroundsOn(column.day,column.resource??undefined,true)){const from=range.start<=dayStart?0:zone.parts(range.start).minutes,to=range.end>=dayEnd?1440:zone.parts(range.end).minutes;add(from,to,range.kind==="unavailable"?"cx-cal-unavailable":"cx-cal-bgrange",range.label,range.kind==="unavailable"?null:range.tone)}}});Object.assign(DataGrid.prototype,{calKeyTime(event,target,eventElement){const key=event.key,cal=this.cal,slot=cal.slotMinutes;const cell=eventElement?this.calSlotOf(eventElement):target.closest(".cx-cal-slot,.cx-cal-allcell");if(!cell)return;const columns=[...this.calGrid.querySelectorAll(".cx-cal-allcell")],resource=cell.dataset.cxResource;const column=columns.findIndex(item=>item.dataset.cxDay===cell.dataset.cxDay&&item.dataset.cxResource===resource);const minutes=cell.dataset.cxMinutes===undefined?null:Number(cell.dataset.cxMinutes);const go=(index,at)=>{const fresh=[...this.calGrid.querySelectorAll(".cx-cal-allcell")],target=fresh[index==="last"?fresh.length-1:Math.max(0,Math.min(fresh.length-1,index))];if(!target)return;const day=Number(target.dataset.cxDay);if(!this.calAllowsSpan(day*CAL_DAY,(day+1)*CAL_DAY,true))return;this.calFocus={day,minutes:at,event:null,resource:target.dataset.cxResource};const element=at===null?target:this.calGrid.querySelector('.cx-cal-slot[data-cx-day="'+day+'"][data-cx-minutes="'+at+'"]' +(target.dataset.cxResource!==undefined?'[data-cx-resource="'+CSS.escape(target.dataset.cxResource)+ '"]':""));if(element){for(const other of this.calBody.querySelectorAll('[tabindex="0"]'))other.tabIndex=-1;element.tabIndex=0;element.focus()}};if(eventElement){if(key==="Escape"||key==="ArrowLeft"||key==="ArrowRight"){event.preventDefault();go(column +(key==="ArrowLeft"?-1:key==="ArrowRight"?1:0),minutes);return}
214
+ if(key==="ArrowUp"||key==="ArrowDown"){event.preventDefault();const list=[...this.calGrid.querySelectorAll(".cx-cal-ev")].filter(item=>{const owner=this.calSlotOf(item);return owner&&owner.dataset.cxDay===cell.dataset.cxDay&&owner.dataset.cxResource===resource});const next=list[list.indexOf(eventElement)+(key==="ArrowDown"?1:-1)];if(next){eventElement.tabIndex=-1;next.tabIndex=0;next.focus()}}
215
+ return}
216
+ const first=cal.minMinutes,last=cal.maxMinutes-slot;if(key==="ArrowLeft"||key==="ArrowRight"){event.preventDefault();const next=column +(key==="ArrowLeft"?-1:1),pages=this.calResourcePages;if(pages&&(next<0?pages.page>0:next>=columns.length&&pages.page<pages.pages-1)){this.calResourceFirst=(pages.page +(next<0?-1:1))*pages.size;this.calPaint({keep:true});go(next<0?"last":0,minutes);return}
217
+ if(next<0||next>=columns.length){this.calendarStep(next<0?-1:1).then(()=>go(next<0?"last":0,minutes));return}
218
+ go(next,minutes)}else if(key==="ArrowUp"||key==="ArrowDown"){event.preventDefault();const next=minutes===null?(key==="ArrowDown"?first:null):minutes +(key==="ArrowUp"?-slot:slot);if(minutes!==null&&next<first){go(column,null);return}
219
+ if(next===null||next>last)return;if(event.shiftKey&&minutes!==null){const anchor=this.calAnchorMinutes??minutes;this.calAnchorMinutes=anchor;this.calSelectSlots(Number(cell.dataset.cxDay),Math.min(anchor,next),Math.max(anchor,next)+ slot,resource)}else this.calAnchorMinutes=null;go(column,next)}else if(key==="Home"||key==="End"){event.preventDefault();go(column,key==="Home"?first:last)}
220
+ else if(key==="PageUp"||key==="PageDown"){event.preventDefault();this.calendarStep(key==="PageUp"?-1:1).then(()=>go(column,minutes))}
221
+ else if(key===" "){event.preventDefault();if(minutes===null)this.calSelectDays(Number(cell.dataset.cxDay),Number(cell.dataset.cxDay),resource);else this.calSelectSlots(Number(cell.dataset.cxDay),minutes,minutes+slot,resource)}
222
+ else if(key==="Enter"||key==="F2"){event.preventDefault();const inside=cell.querySelector(".cx-cal-ev,.cx-cal-more-btn");if(inside){cell.tabIndex=-1;inside.tabIndex=0;inside.focus()}
223
+ else if(minutes===null)this.calSelectDays(Number(cell.dataset.cxDay),Number(cell.dataset.cxDay),resource);else this.calSelectSlots(Number(cell.dataset.cxDay),minutes,minutes+slot,resource)}},calSlotOf(element){return element.closest(".cx-cal-slot,.cx-cal-allcell")},calTick(){const line=this.calNowLine;if(line?.isConnected&&this.calNowColumns){const now=Date.now(),today=this.calZone.day(now),minutes=this.calZone.parts(now).minutes,cal=this.cal;const first=this.calNowColumns.findIndex(column=>column.day===today),count=this.calNowColumns.filter(column=>column.day===today).length;line.hidden=first<0||minutes<cal.minMinutes||minutes>=cal.maxMinutes;line.style.setProperty("--d",String(Math.max(0,first)));line.style.setProperty("--sp",String(count||1));line.style.setProperty("--t",String(minutes-cal.minMinutes));line.title=this.calLabels.calendarNow+" "+this.calFmt.time(now)}
224
+ this.calTickTimeline?.()},calScrollState(){if(this.calView==="timeline")return this.calTimelineScroll?.()||null;const scroller=this.calScroller?.isConnected&&this.calBody.contains(this.calScroller)?this.calScroller:null;return{top:scroller?scroller.scrollTop:this.calBody.scrollTop,body:!scroller,view:this.calView,date:this.calDate}},calRestoreScroll(state){if(state.view!==this.calView||state.date!==this.calDate&&this.calView!=="week"&&this.calView!=="day"&&this.calView!=="days")return false;if(state.timeline)return this.calRestoreTimelineScroll?.(state);if(state.body)this.calBody.scrollTop=state.top;else if(this.calScroller?.isConnected)this.calScroller.scrollTop=state.top;return true}});Object.assign(DataGrid.prototype,{calPaintAgenda(){const range=this.calRange,fmt=this.calFmt,today=this.calZone.day(Date.now());const grid=h("div","cx-cal-agenda");grid.setAttribute("role","grid");grid.setAttribute("aria-labelledby",this.calTitle.id);grid.setAttribute("aria-readonly","true");let rows=0;if(range.lastDay-range.firstDay>31)this.calBucketDays(range.firstDay,range.lastDay);for(let day=range.firstDay;day<=range.lastDay;day++){if(this.cal.hidden.has(calDow(day)))continue;const events=this.calEventsOn(day);if(!events.length)continue;const group=h("div","cx-cal-agroup" +(day===today?" cx-cal-today":""));group.setAttribute("role","rowgroup");const heading=h("div","cx-cal-ahead");heading.setAttribute("role","row");const headingCell=h("div","cx-cal-ahead-c");headingCell.setAttribute("role","columnheader");headingCell.setAttribute("aria-colspan","2");const weekday=h("span","cx-cal-aweekday"),date=h("span","cx-cal-adate");setText(weekday,fmt.weekday(day,"long"));setText(date,fmt.shortDay(day));headingCell.append(date,weekday);if(day===today){const mark=h("span","cx-cal-atoday");setText(mark,this.calLabels.calendarToday);headingCell.appendChild(mark);headingCell.setAttribute("aria-current","date")}
225
+ heading.appendChild(headingCell);group.appendChild(heading);for(const event of events){const row=h("div","cx-cal-arow");row.setAttribute("role","row");row.tabIndex=-1;row.dataset.cxDay=String(day);row.dataset.cxEventRow=event.id;const time=h("div","cx-cal-atime"),title=h("div","cx-cal-atitle");time.setAttribute("role","gridcell");title.setAttribute("role","gridcell");setText(time,event.allDay?this.calLabels.calendarAllDay:event.marker?fmt.time(event.start):event.sd<day||event.ed>day?this.calTimeText(event):fill(this.calLabels.calendarTimeRange,{start:fmt.time(event.start),end:fmt.time(event.end)}));const element=this.calEventElement(event,"item");element.querySelector(".cx-cal-ev-time")?.remove();title.appendChild(element);row.setAttribute("aria-label",fmt.longDay(day)+ ", "+this.calEventName(event));row.append(time,title);group.appendChild(row);rows++}
226
+ grid.appendChild(group)}
227
+ if(!rows){const empty=h("p","cx-cal-empty");setText(empty,this.calLabels.calendarNoEvents);this.calBody.replaceChildren(empty);this.calGrid=null;return}
228
+ this.calBody.replaceChildren(grid);this.calGrid=grid;if(!this.calFocus?.event){const rowsList=[...grid.querySelectorAll(".cx-cal-arow")],focusDay=this.calFocus?.day??this.calDate;const row=rowsList.find(item=>Number(item.dataset.cxDay)>=focusDay)||rowsList[rowsList.length-1];if(row)this.calFocus={day:Number(row.dataset.cxDay),minutes:null,event:null,agendaRow:row.dataset.cxEventRow}}},calScrollAgenda(){const row=this.calFocusTarget(),group=row?.closest?.(".cx-cal-agroup");if(group)this.calBody.scrollTop=Math.max(0,group.offsetTop)},calKeyAgenda(event,target){const row=target.closest(".cx-cal-arow");if(!row)return;const rows=[...this.calGrid.querySelectorAll(".cx-cal-arow")],at=rows.indexOf(row),key=event.key;let next=null;if(key==="ArrowDown"||key==="ArrowUp")next=rows[at +(key==="ArrowDown"?1:-1)];else if(key==="Home")next=rows[0];else if(key==="End")next=rows[rows.length-1];else if(key==="PageUp"||key==="PageDown"){event.preventDefault();this.calendarStep(key==="PageUp"?-1:1).then(()=>this.calRestoreFocus(true));return}
229
+ else if(key==="Enter"||key===" "){event.preventDefault();const element=row.querySelector(".cx-cal-ev");this.calOpenEvent(element._calEvent,true,row).catch(error=>this.emit("error",{error}));return}
230
+ if(!next)return;event.preventDefault();row.tabIndex=-1;next.tabIndex=0;next.focus();this.calFocus={day:Number(next.dataset.cxDay),minutes:null,event:null,agendaRow:next.dataset.cxEventRow}},calPaintYear(){const range=this.calRange,fmt=this.calFmt,today=this.calZone.day(Date.now()),counts=new Map();for(const event of this.calEvents)if(!event.background)for(let day=Math.max(event.sd,range.firstDay);day<=Math.min(event.ed,range.lastDay);day++)counts.set(day,(counts.get(day)||0)+ 1);const most=Math.max(1,...counts.values()),year=h("div","cx-cal-year");for(let month=0;month<12;month++){const first=calAddMonths(range.firstDay,month),last=calAddMonths(first,1)- 1,box=h("section","cx-cal-ymonth");const title=h("h3","cx-cal-ymonth-h");title.id=this.id+"-cal-y"+month;setText(title,fmt.monthName(first));box.appendChild(title);const grid=h("div","cx-cal-ygrid");grid.setAttribute("role","grid");grid.setAttribute("aria-labelledby",title.id);grid.setAttribute("aria-readonly","true");grid.style.setProperty("--cx-cal-cols",String(this.calWeekdays().length));grid.appendChild(this.calWeekdayRow("cx-cal-ydow"));for(let week=calWeekStart(first,this.cal.firstDay);week<=last;week +=7){const row=h("div","cx-cal-yweek");row.setAttribute("role","row");for(const dow of this.calWeekdays()){const day=week +(dow-this.cal.firstDay+7)%7,cell=h("div",day<first||day>last?"cx-cal-ypad":"cx-cal-yday");cell.setAttribute("role","gridcell");if(day>=first&&day<=last){const count=counts.get(day)||0;cell.tabIndex=-1;cell.dataset.cxDay=String(day);setText(cell,fmt.dayNumber(day));if(!this.calAllowsSpan(day*CAL_DAY,(day+1)*CAL_DAY,true)){cell.classList.add("cx-cal-invalid");cell.setAttribute("aria-disabled","true")}
231
+ if(count)cell.dataset.cxHeat=String(Math.min(4,Math.ceil(count / most * 4)));
232
+ if(day===today){cell.classList.add("cx-cal-today");cell.setAttribute("aria-current","date")}
233
+ cell.setAttribute("aria-label",this.calDayName(day,count,this.calBackgroundsOn(day)));cell.title=fmt.longDay(day)+ " · "+calCount(this,this.calLabels.calendarEvents,count)}
234
+ row.appendChild(cell)}
235
+ grid.appendChild(row)}
236
+ box.appendChild(grid);if(this.cal.yearEvents)box.appendChild(this.calYearList(first,last));year.appendChild(box)}
237
+ if(!counts.size){this.calBody.replaceChildren(year);this.calGrid=year;return}
238
+ const legend=h("div","cx-cal-ylegend"),low=h("span"),high=h("span");legend.setAttribute("aria-hidden","true");setText(low,calCount(this,this.calLabels.calendarEvents,1));setText(high,calCount(this,this.calLabels.calendarEvents,most));legend.appendChild(low);for(let heat=1;heat<=4;heat++){const swatch=h("i","cx-cal-yswatch");swatch.dataset.cxHeat=String(heat);legend.appendChild(swatch)}
239
+ legend.appendChild(high);this.calBody.replaceChildren(year,legend);this.calGrid=year},calYearList(first,last){const list=h("ul","cx-cal-ylist"),events=this.calEvents.filter(event=>!event.background&&event.sd<=last&&event.ed>=first).sort((a,b)=>a.start-b.start),shown=events.slice(0,5);list.setAttribute("aria-label",this.calFmt.monthName(first)+ ", "+calCount(this,this.calLabels.calendarEvents,events.length));for(const event of shown){const item=h("li"),day=h("span","cx-cal-yl-day"),element=this.calEventElement(event,"chip");setText(day,this.calFmt.dayNumber(Math.max(event.sd,first)));day.setAttribute("aria-hidden","true");element.tabIndex=0;item.append(day,element);list.appendChild(item)}
240
+ if(events.length>shown.length){const item=h("li","cx-cal-yl-more"),more=gridButton(fill(this.calLabels.calendarMore,{count:numberFormat(this.lang).format(events.length-shown.length)}),()=>{if(this.cal.views.includes("month"))this.calGo(first,"month",true);else if(this.cal.views.includes("agenda"))this.calGo(first,"agenda",true)});more.className="cx-cal-yl-morebtn";item.appendChild(more);list.appendChild(item)}
241
+ return list},calKeyYear(event,cell){const day=Number(cell.dataset.cxDay),key=event.key,range=this.calRange;if(!this.calAllowsSpan(day*CAL_DAY,(day+1)*CAL_DAY,true))return;let next=null;if(key==="ArrowLeft"||key==="ArrowRight")next=day +(key==="ArrowLeft"?-1:1);else if(key==="ArrowUp"||key==="ArrowDown")next=day +(key==="ArrowUp"?-7:7);else if(key==="Home")next=calMonthStart(day);else if(key==="End")next=calAddMonths(calMonthStart(day),1)- 1;else if(key==="PageUp"||key==="PageDown")next=calAddMonths(day,key==="PageUp"?-12:12);else if(key==="Enter"){event.preventDefault();if(this.cal.views.includes("day"))this.calGo(day,"day",true).then(()=>this.calRestoreFocus(true));else if(this.cal.views.includes("week"))this.calGo(day,"week",true).then(()=>this.calRestoreFocus(true));return}
242
+ if(next===null)return;event.preventDefault();next=this.calClampDay(next);if(next<range.firstDay||next>range.lastDay){this.calGo(next,"year",next).then(()=>this.calRestoreFocus(true));return}
243
+ this.calFocus={day:next,minutes:null,event:null};this.calRestoreFocus(true)}});Object.assign(DataGrid.prototype,{calPaint(options={}){if(!this.calBody||this.destroyed)return;this.calDisposeEventRenderers();const owned=this.calBody.contains(document.activeElement)&&!this.calMore;this.calCloseMore(false);this.calHideHover();const scroll=options.keep?this.calScrollState?.():null;const view=this.calView;this.host.setAttribute("data-cx-calendar-view",view);if(view==="month")this.calPaintMonth();else if(view==="mini")this.calPaintMini();else if(view==="months")this.calPaintMonths();else if(view==="weekList"||view==="dayList")this.calPaintList();else if(view==="week"||view==="day"||view==="days")this.calPaintTime();else if(view==="agenda")this.calPaintAgenda();else if(view==="year")this.calPaintYear();else this.calPaintTimeline(options);const restored=scroll&&!options.resize&&this.calRestoreScroll(scroll);if(!restored&&view==="agenda")this.calScrollAgenda();this.calMarkSelection();this.calRestoreFocus(owned)},calFocusTarget(){const focus=this.calFocus,body=this.calBody;if(focus?.event){const element=body.querySelector('[data-cx-event="'+CSS.escape(focus.event)+ '"]');if(element)return element}
244
+ if(focus?.unit!=null&&focus.resource!=null){const element=body.querySelector('.cx-tl-cell[data-cx-unit="'+focus.unit+'"][data-cx-resource="'+CSS.escape(focus.resource)+ '"]');if(element)return element}
245
+ if(focus?.resource!=null&&this.calView==="timeline"&&this.tlAxis){const unit=this.tlAxis.units.findIndex(item=>item.first<=focus.day&&focus.day<=item.last);const element=body.querySelector('.cx-tl-cell[data-cx-unit="'+Math.max(0,unit)+ '"][data-cx-resource="'+CSS.escape(focus.resource)+ '"]');if(element)return element}
246
+ if(focus?.agendaRow){const element=body.querySelector('[data-cx-event-row="'+CSS.escape(focus.agendaRow)+ '"]');if(element)return element}
247
+ const resource=focus?.resource!=null?'[data-cx-resource="'+CSS.escape(focus.resource)+ '"]':"";if(focus&&focus.minutes!=null){const slot=body.querySelector('.cx-cal-slot[data-cx-day="'+focus.day+'"][data-cx-minutes="'+focus.minutes+'"]'+resource);if(slot)return slot}
248
+ if(focus&&focus.minutes==null){const cell=body.querySelector('.cx-cal-allcell[data-cx-day="'+focus.day+'"]'+resource);if(cell)return cell}
249
+ if(focus){const day=body.querySelector('.cx-cal-day[data-cx-day="'+focus.day+'"],.cx-cal-yday[data-cx-day="'+focus.day+'"],[data-cx-day="'+focus.day+'"][data-cx-minutes],.cx-cal-arow[data-cx-day="'+focus.day+'"]');if(day)return day}
250
+ return body.querySelector('.cx-cal-day:not(.cx-cal-out):not(.cx-cal-invalid),.cx-cal-yday:not(.cx-cal-invalid),[data-cx-minutes]:not(.cx-cal-invalid),.cx-cal-arow,.cx-tl-cell:not(.cx-cal-invalid)')||null},calRestoreFocus(force){const target=this.calFocusTarget();for(const element of this.calBody.querySelectorAll('[tabindex="0"]'))if(element!==target&&!element.closest(".cx-cal-ylist"))element.tabIndex=-1;if(!target)return;target.tabIndex=0;if(force)target.focus({preventScroll:false})},calFocusIn(event){const element=event.target,eventElement=element.closest(".cx-cal-ev"),cell=element.closest("[data-cx-day]");if(!eventElement&&!cell||element.closest(".cx-cal-ylist"))return;if(eventElement?._calEvent)this.calFocus={day:eventElement._calEvent.sd,minutes:null,event:eventElement._calEvent.id};else if(cell)this.calFocus={day:Number(cell.dataset.cxDay),minutes:cell.dataset.cxMinutes!==undefined?Number(cell.dataset.cxMinutes):null,event:null,resource:cell.dataset.cxResource,agendaRow:cell.dataset.cxEventRow,unit:cell.dataset.cxUnit!==undefined?Number(cell.dataset.cxUnit):null};for(const other of this.calBody.querySelectorAll('[tabindex="0"]'))if(other!==element&&!other.closest(".cx-cal-ylist"))other.tabIndex=-1;if(element.tabIndex<0&&(eventElement||cell))element.tabIndex=0},calKey(event){if(event.defaultPrevented||event.altKey&&!this.calMoving)return;if(this.calRtl&&(event.key==="ArrowLeft"||event.key==="ArrowRight")){const real=event,key=event.key==="ArrowLeft"?"ArrowRight":"ArrowLeft";event=new Proxy(real,{get:(target,name)=>name==="key"?key:typeof target[name]==="function"?target[name].bind(target):target[name]})}
251
+ if(this.calEditKey?.(event)||this.calMenuKey(event))return;const target=event.target,eventElement=target.closest(".cx-cal-ev"),more=target.closest(".cx-cal-more-btn");if((event.ctrlKey||event.metaKey)&&(event.key==="z"||event.key==="y"||event.key==="Z")){if(!this.cal.editable)return;event.preventDefault();const done=event.key==="y"||event.shiftKey?this.redo():this.undo();if(done===true&&this.cal.editable.commit==="auto"&&this.hasChanges())this.calCommit(this.getChanges().map(change=>change.id),"");return}
252
+ if(eventElement&&(event.key==="Enter"||event.key===" ")){event.preventDefault();this.calOpenEvent(eventElement._calEvent,true,eventElement).catch(error=>this.emit("error",{error}));return}
253
+ if(more&&(event.key==="Enter"||event.key===" ")){event.preventDefault();this.calOpenMore(Number(more.dataset.cxDay),this.calEventsOn(Number(more.dataset.cxDay)),more);return}
254
+ const view=this.calView,dayCell=!eventElement&&!more&&target.closest("[data-cx-day]");if(this.cal.drop&&dayCell&&this.calDropFromKey?.(event,dayCell))return;if(this.cal.create&&dayCell&&this.calCreateFromKey?.(event,dayCell))return;if(calDayGrid(view)&&(eventElement||more)&&this.calKeyInDay(event,eventElement||more))return;if(calDayGrid(view)){const cell=target.closest(".cx-cal-day");if(cell)this.calKeyMonth(event,cell);return}
255
+ if(view==="week"||view==="day"||view==="days"){this.calKeyTime(event,target,eventElement);return}
256
+ if(view==="agenda"){this.calKeyAgenda(event,target);return}
257
+ if(view==="year"){const cell=target.closest(".cx-cal-yday");if(cell)this.calKeyYear(event,cell);return}
258
+ this.calKeyTimeline?.(event,target,eventElement)},calClick(event){if(this.calSuppressClick){this.calSuppressClick=false;return}
259
+ const target=event.target,eventElement=target.closest(".cx-cal-ev"),more=target.closest(".cx-cal-more-btn");if(eventElement){this.calOpenEvent(eventElement._calEvent,false,eventElement).catch(error=>this.emit("error",{error}));return}
260
+ if(more){this.calOpenMore(Number(more.dataset.cxDay),this.calEventsOn(Number(more.dataset.cxDay)),more);return}
261
+ if(this.calView==="timeline"){this.calTimelineClick?.(event);return}
262
+ if(target.closest(".cx-cal-invalid"))return;const number=target.closest(".cx-cal-num,.cx-cal-dayhead,.cx-cal-yday");if(number&&this.cal.navLinks&&this.cal.views.includes("day")&&!this.calPhone&&this.calView!=="mini"&&target.closest(".cx-cal-num,.cx-cal-dayhead")){const day=Number(number.closest("[data-cx-day]")?.dataset.cxDay??number.dataset.cxDay);if(Number.isFinite(day)){this.calGo(day,"day",true);return}}
263
+ const cell=target.closest("[data-cx-day]");if(!cell)return;const day=Number(cell.dataset.cxDay);if(!this.calAllowsSpan(day*CAL_DAY,(day+1)*CAL_DAY,true))return;if(this.calView==="year"){if(this.cal.views.includes("day"))this.calGo(day,"day",true);else this.calFocusDay(day);return}
264
+ if(cell.dataset.cxMinutes!==undefined){const minutes=Number(cell.dataset.cxMinutes);this.calSelectSlots(day,minutes,minutes+this.cal.slotMinutes,cell.dataset.cxResource)}
265
+ else this.calSelectDays(day,day,this.calCellResource(cell));if(this.calPhone&&this.calView==="month"||this.calView==="mini")this.calFocusDay(day);else if(this.cal.create&&!cell.classList.contains("cx-cal-invalid"))this.calCreateFromClick?.(cell)?.catch?.(error=>this.emit("error",{error}))},calCellResource(cell){return this.tlSolo&&cell.classList.contains("cx-tl-cell")?null:cell.dataset.cxResource??null},calSelectDays(first,last,resource){const zone=this.calZone;if(!this.calAllowsSpan(first*CAL_DAY,(last+1)*CAL_DAY,true))return false;this.calSelection={first,last,from:zone.start(first),to:zone.start(last+1),resource:resource??null};this.calReportSelection(this.calFmt.dayRange(first,last))},calSelectSlots(day,fromMinutes,toMinutes,resource){const zone=this.calZone,from=zone.instant(day,fromMinutes),to=zone.instant(day,toMinutes);if(!this.calAllowsSpan(from,to,false))return false;this.calSelection={first:day,last:day,from,to,fromMinutes,toMinutes,resource:resource??null};this.calReportSelection(this.calFmt.longDay(day)+ " "+fill(this.calLabels.calendarTimeRange,{start:this.calFmt.time(from),end:this.calFmt.time(to)}))},calReportSelection(label){const selection=this.calSelection,events=this.calEvents.filter(event=>{const span=this.calDisplaySpan(event);return!event.background&&calOverlaps(span.start,span.end,selection.from,selection.to)&&(selection.resource==null||event.resource===selection.resource)});this.calMarkSelection();this.calAnnounce(fill(this.calLabels.calendarSelected,{range:label}));this.calEmitLink("select",events,label,selection)},calMarkSelection(){const selection=this.calSelection;for(const cell of this.calBody.querySelectorAll('[aria-selected="true"]'))cell.setAttribute("aria-selected","false");if(!selection)return;for(const cell of this.calBody.querySelectorAll("[data-cx-day]")){const day=Number(cell.dataset.cxDay),minutes=cell.dataset.cxMinutes;const inside=day>=selection.first&&day<=selection.last&&(minutes===undefined||selection.fromMinutes===undefined||+minutes>=selection.fromMinutes&&+minutes<selection.toMinutes)&&
266
+ (selection.resource==null||cell.dataset.cxResource===undefined||cell.dataset.cxResource===selection.resource);if(inside)cell.setAttribute("aria-selected","true")}},clearCalendarSelection(){if(!this.calSelection)return;this.calSelection=null;this.calMarkSelection();this.calEmitLink("clear",[],"",null)},calEmitLink(action,events,label,selection){if(this.opts.linkEvents!==true&&!this.linkWatching)return;const keys=[...new Set(events.map(event=>event.key))].slice(0,10000),values=Object.create(null);if(selection?.resource!=null&&this.cal.resource)values[this.cal.resource.id]=[selection.resource];this.emit("linkchange",{view:this.view.id,action,scope:"select",keys,values,label,range:selection?{column:this.cal.start.id,from:calIso(selection.from),to:calIso(selection.to)}:null})}});const CAL_CACHE=12,CAL_FALLBACK_PAGES=10;Object.assign(DataGrid.prototype,{async calRecords(from,to,signal){const query=this.acceptedQuery||gridQuery(this.view,this.view.query||{});if(this.view.source.kind==="inline"&&!this.opts.source){if(this.view.rowData){if(!this.source.decoded)await this.source.query({...query,offset:0,limit:1},{signal});await this.source.decoded}
267
+ const rows=this.view.rows,key=gridResultShape(query);if(this.calRowsCache?.rows!==rows||this.calRowsCache.key!==key||this.calRowsCache.source!==this.source||this.calRowsCache.shape!==rows.length+":"+this.calVersion){const plain=!query.search&&!query.filters.length&&!query.filterGroup;this.calRowsCache={rows,key,source:this.source,shape:rows.length+":"+this.calVersion,matched:plain?rows:gridQueryRows(this.view,query,new Map(this.view.columns.map(column=>[column.id,column])),null)}}
268
+ const matched=this.calEdited(this.calRowsCache.matched);return{rows:matched,overrides:this.calInlineOverrides(matched,from,to),truncated:false}}
269
+ if(this.cal.allDay){from -=CAL_DAY;to +=CAL_DAY}
270
+ if(this.calResourcesReady)await this.calResourcesReady();const resources=this.calResourceWindow?.()||null,key=JSON.stringify([gridResultShape(query),query.revision,this.calVersion,from,to,resources]);this.calWindowKey=JSON.stringify(resources);const cache=this.calCache||=new Map();if(cache.has(key)){const hit=cache.get(key);cache.delete(key);cache.set(key,hit);return{...hit,rows:this.calEdited(hit.rows,true)}}
271
+ let answer;if(!this.cursor&&typeof this.source.range==="function"&&!this.calNoRange){try{answer=await this.calRangeRequest(query,from,to,resources,signal)}
272
+ catch(error){if(error?.code!=="unsupported")throw error;this.calNoRange=true}}
273
+ answer||=await this.calFallbackRequest(query,from,to,resources,signal);answer=await this.calHostedOverrides(answer,from,to,signal);cache.set(key,answer);while(cache.size>CAL_CACHE)cache.delete(cache.keys().next().value);return{...answer,rows:this.calEdited(answer.rows,true)}},calEdited(rows,hosted){const changes=this.edit?.changes;if(!changes?.size)return rows;const out=rows.map(row=>changes.get(row.id)?.row||row);if(hosted&&this.edit.inserted?.size){const ids=new Set(rows.map(row=>row.id));for(const[id,row]of this.edit.inserted)if(!ids.has(id))out.push(changes.get(id)?.row??row)}
274
+ return out},calRangeRequest(query,from,to,resources,signal){const cal=this.cal,request={protocol:PROTOCOL,type:"gridRangeQuery",view:this.view.id,query:{...query,offset:0,limit:Math.min(10000,cal.limit)},from:calIso(from),to:calIso(to),start:cal.start.id,calendarView:this.calView,limit:Math.min(10000,cal.limit)};for(const name of["end","recurrence","seriesEnd","resource"])if(cal[name])request[name]=cal[name].id;if(resources&&cal.resource)request.resources=resources;return this.runGridHook(hook=>this.source.range(gridFreeze(request),{signal:hook}),signal).then(page=>{checkDocument(page,"gridRangePage");if(page.view!==this.view.id||typeof page.revision!=="string"||!page.revision||!Array.isArray(page.items)||!validStructuralCount(page.matched)||page.items.length>request.limit||page.items.length>page.matched||
275
+ typeof page.truncated!=="boolean"||page.truncated!==page.items.length<page.matched)
276
+ throw new Error("CanopyX: the range page does not answer its query.");if(query.revision!==undefined&&page.revision!==query.revision)throw new Error("CanopyX: grid page snapshot changed.");validateGridRows(page.items,this.view.columns);return{rows:page.items,truncated:page.truncated,matched:page.matched,revision:page.revision}})},async calFallbackRequest(query,from,to,resources,signal){const cal=this.cal,iso=value=>calIso(value),groups=[];const plain={operator:"or",filters:[{column:cal.start.id,operator:"ge",values:[iso(from)]}],groups:[]};if(cal.end)plain.filters.push({column:cal.end.id,operator:"gt",values:[iso(from)]});if(cal.recurrence){const series={operator:"and",filters:[{column:cal.recurrence.id,operator:"isNotNull",values:[]},{column:cal.recurrence.id,operator:"ne",values:[""]}],groups:[]};if(cal.seriesEnd)series.groups.push({operator:"or",filters:[{column:cal.seriesEnd.id,operator:"isNull",values:[]},{column:cal.seriesEnd.id,operator:"gt",values:[iso(from)]}],groups:[]});plain.groups.push(series)}
277
+ groups.push(plain);if(query.filterGroup)groups.push(query.filterGroup);const filters=[{column:cal.start.id,operator:"lt",values:[iso(to)]}];if(resources&&cal.resource)filters.push({column:cal.resource.id,operator:"in",values:resources});const input={...query,cursor:undefined,offset:undefined,sort:cal.start.sortable?[{column:cal.start.id,direction:"asc"}]:[],filterGroup:{operator:"and",filters,groups}};return this.calQueryPages(input,signal,{pages:CAL_FALLBACK_PAGES,rows:cal.limit})},async calReload(options={}){if(this.destroyed||!this.calBody)return;const epoch=++this.calEpoch,range=this.calRange=this.calRangeOf(this.calView,this.calDate);this.calAnswer=null;this.calSyncBar(!!options.announce);const from=this.calZone.start(range.firstDay),to=this.calZone.start(range.lastDay+1);const lifetime=this.calLifetime=new AbortController(),signal=AbortSignal.any?AbortSignal.any([lifetime.signal,this.dataLifetime.signal]):lifetime.signal;this.calPrevLifetime?.abort();this.calPrevLifetime=lifetime;let answer;const slow=setTimeout(()=>{if(epoch===this.calEpoch)this.calNotice(this.calLabels.calendarLoading,false)},200);if(this.cal.sources.some(source=>source.ics)&&!this.calIcsLoad)this.calIcsLoad=this.calLoadPart("calendar-ics").then(()=>this.calLoadIcs()).catch(error=>{this.calIcsLoad=null;throw error});try{answer=await this.calRecords(from,to,signal);if(this.calIcsLoad)await this.calIcsLoad}
278
+ catch(error){clearTimeout(slow);if(epoch!==this.calEpoch||error.name==="AbortError"||this.destroyed)return;this.calNotice(fill(this.calLabels.calendarLoadError,{error:error.message}),true);this.emit("error",{error});return}
279
+ clearTimeout(slow);if(epoch!==this.calEpoch||signal.aborted||this.destroyed)return;const rows=this.calIcsRows?.length?[...answer.rows,...this.calIcsRows]:answer.rows;this.calAnswer={rows,query:this.acceptedQuery,source:this.source,signal,epoch};const events=calOccurrences(this,rows,from,to,answer.overrides);this.calAllEvents=events;this.calEvents=this.calShownEvents(events);this.calRanges=calStaticRanges(this,from,to);const sticky=this.calSticky?.at===this.calStickyKey()?this.calSticky.text:"";const truncated=answer.truncated||events.truncated?fill(this.calLabels.calendarTruncated,{count:numberFormat(this.lang).format(events.length)}):"";const imported=this.calIcsErrors?.join(" ")||"";setText(this.calNoticeElement,[sticky,truncated,imported].filter(Boolean).join(" "));this.calNoticeElement.hidden=!sticky&&!truncated&&!imported;this.calNoticeElement.classList.toggle("cx-cal-error",!!sticky||!!imported);this.calPaint(options);this.host.setAttribute("data-cx-calendar-ready","true");this.emit("calendar",this.calSnapshot())},calNotice(text,error,sticky){this.calSticky=sticky&&text?{text,at:this.calStickyKey()}:text?this.calSticky:null;setText(this.calNoticeElement,text);this.calNoticeElement.hidden=!text;this.calNoticeElement.classList.toggle("cx-cal-error",!!error)},calStickyKey(){return[this.calView,this.calRange?.firstDay,this.calRange?.lastDay,this.calZoom].join("|")}});Object.assign(DataGrid.prototype,{async calQueryPages(query,signal,budget){const rows=[],ids=new Set(),tokens=new Set(),cursor=this.cursor;let next,revision=query.revision,snapshot=null,matched=0,complete=false;while(budget.pages>0&&budget.rows>0){let input=gridQuery(this.view,{...query,limit:Math.min(10000,this.pageSize,budget.rows),revision,offset:cursor?undefined:rows.length,cursor:cursor?next:undefined});if(input.groupBy?.length)input=gridQuery(this.view,{...input,groupBy:[],sort:query.sort||[]});const raw=await this.runGridHook(hook=>this.source.query(input,{signal:hook}),signal);const page=cursor?validateGridCursorPage(raw,this.view,input,snapshot):validateGridPage(raw,this.view,input,snapshot);if(page.offset!==rows.length)throw new Error("CanopyX: calendar pages do not form one result.");for(const row of page.items){if(ids.has(row.id))throw new Error("CanopyX: calendar pages repeat a record.");ids.add(row.id);rows.push(row)}
280
+ budget.pages--;budget.rows -=page.items.length;revision=page.revision;matched=page.matched;snapshot||={revision,total:page.total,matched:page.matched};complete=cursor?page.nextCursor===undefined:rows.length===matched;if(complete)break;if(cursor){next=page.nextCursor;if(tokens.has(next))throw new Error("CanopyX: calendar pages repeat a cursor.");tokens.add(next)}}
281
+ return{rows,matched,revision,truncated:!complete}}});const CAL_FIRST_INSTANT=Date.parse("0001-01-01T00:00:00Z");function calSeriesMasters(view,rows,from,to){if(!view.cal.series||!view.cal.recurrenceId)return[];const out=[];for(const row of rows){const base=calBase(view,row);if(!base?.rule)continue;const duration=Math.max(0,base.end-base.start);out.push({id:base.key,from:Math.max(CAL_FIRST_INSTANT,base.start,from-duration),to})}
282
+ return out}
283
+ function calOverrideMetadata(view,masters,rows){const wanted=new Map(masters.map(master=>[master.id,master])),overrides=new Map();for(const row of rows){const base=calBase(view,row),master=base?.series&&wanted.get(base.series);if(!master||!Number.isFinite(base.recurrenceId)||base.recurrenceId<master.from||base.recurrenceId>=master.to)continue;if(!overrides.has(base.series))overrides.set(base.series,new Set());overrides.get(base.series).add(base.recurrenceId)}
284
+ return overrides}
285
+ Object.assign(DataGrid.prototype,{calInlineOverrides(rows,from,to){const masters=calSeriesMasters(this,rows,from,to);return masters.length?calOverrideMetadata(this,masters,this.calEdited(this.view.rows)):new Map()},async calHostedOverrides(answer,from,to,signal){const rows=this.calEdited(answer.rows,true),masters=calSeriesMasters(this,rows,from,to);if(!masters.length)return{...answer,rows,overrides:new Map()};if(typeof answer.revision!=="string"||!answer.revision)throw new Error("CanopyX: calendar replacement metadata needs the range revision.");const budget={pages:CAL_FALLBACK_PAGES,rows:this.cal.limit},overrides=new Map(),withheld=new Set();for(let at=0;at<masters.length;at +=1000){const batch=masters.slice(at,at+1000);if(!budget.pages||!budget.rows){for(const master of batch)withheld.add(master.id);continue}
286
+ const lower=Math.min(...batch.map(master=>master.from));const query={search:"",revision:answer.revision,sort:[],filters:[{column:this.cal.series.id,operator:"in",values:batch.map(master=>master.id)},{column:this.cal.recurrenceId.id,operator:"ge",values:[calIso(lower)]},{column:this.cal.recurrenceId.id,operator:"lt",values:[calIso(to)]}]};const support=await this.calQueryPages(query,signal,budget);if(support.truncated){for(const master of batch)withheld.add(master.id);continue}
287
+ for(const[id,starts]of calOverrideMetadata(this,batch,this.calEdited(support.rows,true)))overrides.set(id,starts)}
288
+ return{...answer,rows:withheld.size?rows.filter(row=>!withheld.has(row.id)):rows,overrides,truncated:answer.truncated||withheld.size>0}}});Object.assign(DataGrid.prototype,{calParts(){const input=this.view.calendar||{},ranges=Array.isArray(input.ranges)?input.ranges:[],sources=Array.isArray(input.sources)?input.sources:[];const ics=sources.some(source=>source&&source.ics!==undefined),needs=[];if(input.recurrence!==undefined||ics||ranges.some(range=>range&&range.recurrence!==undefined))needs.push("calendar-recurrence");if(input.editable||input.create||input.drop)needs.push("calendar-edit");if(ics)needs.push("calendar-ics");return Promise.all(needs.map(name=>this.calLoadPart(name)))},calLoadPart(name){return loadPart(DataGrid.prototype.parts[name])},calExportFormat(){const grid=this;const write=this.calIcsFormatWriter=async(capture,context)=>{await grid.calLoadPart("calendar-ics");return grid.calIcsWrite(capture,context)};return{id:"ics",label:this.calLabels.calendarExport,extension:"ics",mime:"text/calendar",write}},calIcsExportColumns(explicit){const mapped=new Set(Object.values(this.cal)),columns=this.columns.filter(column=>mapped.has(column));if(columns.some(column=>column.exportable===false))throw new Error("CanopyX: iCalendar export needs every mapped calendar column to allow export.");const ids=columns.map(column=>column.id);if(explicit!==undefined&&(!Array.isArray(explicit)||ids.some(id=>!explicit.includes(id))))
289
+ throw new Error("CanopyX: iCalendar export needs all mapped calendar columns; include their ids in the export columns.");return ids},calPrepareIcsExport(options){const columns=this.calIcsExportColumns(options.columns);return this.prepareExport({...options,columns:options.columns===undefined?columns:options.columns,values:"raw"})},async exportCalendar(options={}){await this.calLoadPart("calendar-ics");return this.calIcsExport(options)}});Object.assign(DataGrid.prototype,{startCalendar(){if(this.cal)return;this.cal=calSettings(this);this.calVersion=1;this.calEpoch=0;this.calRuleErrors=new Set();this.calEvents=[];this.calRanges=[];this.calLabels=calLabelsFor(this.labels);this.calZone=calZone(this.cal.zoneName);this.calFmt=calFormats(this.lang,this.calZone);this.calSeriesZone=calZone(this.cal.zoneFixed&&this.cal.zoneName!=="local"?this.cal.zoneName:"utc");this.calMinSegment=Math.max(15,this.cal.slotMinutes*0.75);this.calView=this.cal.view;this.calZoom=this.cal.zoom;this.calDate=this.calClampDay(this.cal.date??this.calZone.day(Date.now()));this.calCollapsed=new Set();this.defaultState.calendar={view:this.cal.view,date:null,zoom:this.cal.zoom};const signal=this.lifetime.signal,host=this.host;host.classList.add("cx-calendar");if(this.cal.toolbar===false)host.classList.add("cx-cal-bare");else if(this.cal.toolbar!==true){for(const item of CAL_TOOLBAR)if(!this.cal.toolbar.has(item))host.classList.add("cx-cal-no-"+item);if(!this.cal.toolbar.has("more")&&this.overflow){this.placeMenus?.(false);this.overflow.details.remove();this.overflow=null}}
290
+ host.setAttribute("data-cx-event-style",this.cal.eventStyle);const root=this.calRoot=h("div","cx-cal");root.setAttribute("role","region");root.setAttribute("aria-label",this.view.label);this.calNoticeElement=h("div","cx-cal-notice");this.calNoticeElement.hidden=true;this.calNoticeElement.setAttribute("role","status");this.calBody=h("div","cx-cal-body");this.calLive=h("div","cx-live cx-sr-only");this.calLive.setAttribute("aria-live","polite");root.append(...[this.calBuildBar(),this.calBuildLegend(),this.calNoticeElement,this.calBody,this.calLive].filter(Boolean));this.grid.before(root);this.calRtl=getComputedStyle(host).direction==="rtl";host.classList.toggle("cx-cal-rtl",this.calRtl);this.calPhone=host.clientWidth>0&&host.clientWidth<=640;if(this.calPhone&&!this.initialState?.calendar)this.calView=this.cal.phone||(this.cal.views.includes("agenda")?"agenda":this.calView);this.calBody.addEventListener("keydown",event=>this.calKey(event),{signal});this.calBody.addEventListener("click",event=>this.calClick(event),{signal});this.calBody.addEventListener("focusin",event=>this.calFocusIn(event),{signal});this.calBody.addEventListener("pointerover",event=>this.calHoverOver(event),{signal});this.calBody.addEventListener("pointerout",event=>this.calHoverOut(event),{signal});for(const type of["pointerdown","keydown","wheel"])this.calBody.addEventListener(type,()=>this.calHideHover(),{signal,passive:true});this.calMenuStart();host.addEventListener("canopyx:query",()=>{const shape=gridResultShape(this.acceptedQuery||{}),same=this.calQueryShape===shape;this.calQueryShape=shape;this.calCache?.clear();this.calReload(same?{keep:true}:{})},{signal});host.addEventListener("canopyx:rowsChanged",()=>{this.calCache?.clear();this.calVersion++;this.calReload({keep:true})},{signal});host.addEventListener("canopyx:commit",()=>{this.calCache?.clear();this.calVersion++;this.calReload({keep:true})},{signal});host.addEventListener("canopyx:cellValueChanged",()=>{this.calVersion++;this.calReload({keep:true})},{signal});if(this.cal.editable||this.cal.create||this.cal.drop){host.setAttribute("data-cx-calendar-editable","true");this.calEditStart?.();this.calBody.addEventListener("pointerdown",event=>this.calDragStart(event),{signal});this.calBody.addEventListener("pointermove",event=>this.calDragMove(event),{signal});this.calBody.addEventListener("pointerup",event=>this.calDragEnd(event,false),{signal});this.calBody.addEventListener("pointercancel",event=>this.calDragEnd(event,true),{signal});this.calBody.addEventListener("lostpointercapture",event=>{if(this.calDrag?.id===event.pointerId&&!this.calDrag.started)this.calDrag=null},{signal})}
291
+ let width=0,height=0;const observer=new ResizeObserver(()=>requestAnimationFrame(()=>{if(this.destroyed||!root.clientWidth)return;this.updateGridPhone();const phone=host.clientWidth<=640,changed=phone!==this.calPhone||Math.abs(root.clientWidth-width)>1||Math.abs(this.calBody.clientHeight-height)>1;width=root.clientWidth;height=this.calBody.clientHeight;const switched=phone!==this.calPhone;if(switched){this.calPhone=phone;host.classList.toggle("cx-cal-phone",phone);this.calSyncBar(false)}
292
+ if(!changed||!this.calRange)return;if(switched||this.calView==="month")this.calPaint({keep:true,resize:true});else if(this.calView==="timeline")this.tlPaintLanes?.();else if(this.calScroller?.isConnected)this.calGrid.querySelector(".cx-cal-thead").style.paddingInlineEnd=Math.max(0,this.calScroller.offsetWidth-this.calScroller.clientWidth)+ "px"}));observer.observe(root);host.classList.toggle("cx-cal-phone",this.calPhone);const timer=setInterval(()=>{if(!this.destroyed&&this.cal.now)this.calTick?.()},60000);const print=on=>{if(this.destroyed||!this.calRange)return;this.calPrinting=on;host.classList.toggle("cx-cal-printing",on);this.tlPrint?.(on);if(this.calView!=="timeline")this.calPaint({keep:true,resize:true})};global.addEventListener("beforeprint",()=>print(true),{signal});global.addEventListener("afterprint",()=>print(false),{signal});signal.addEventListener("abort",()=>{observer.disconnect();clearInterval(timer);clearTimeout(this.calAnnounceTimer);this.calPrevLifetime?.abort();this.calCancelGesture?.();this.calDragEnd?.(null,true);this.calStopMoving?.();if(this.calDropDrag)this.calDropClear?.();this.calDisposeEventRenderers();root.remove();host.classList.remove("cx-calendar","cx-cal-phone","cx-cal-bare","cx-cal-rtl",...CAL_TOOLBAR.map(item=>"cx-cal-no-"+item));this.calHideHover();this.calCloseDetails(false);this.calCloseMenu(false);this.calCloseCreate?.(false);this.calCloseAsk?.(null);clearTimeout(this.calPressTimer);host.removeAttribute("data-cx-event-style");host.removeAttribute("data-cx-calendar-ready")},{once:true});calWrapHighlight();const plugin={name:"calendar-export",exportFormats:[this.calExportFormat()]};this.plugins=[...this.plugins,plugin];this.opts={...this.opts,plugins:[...this.opts.plugins||[],plugin]};this.calRange=this.calRangeOf(this.calView,this.calDate);this.calSyncBar(false);if(this.cal.resources||this.calView==="timeline")return this.calLoadTimeline()},calRangeOf(view,date){const cal=this.cal;if(view==="month"||view==="mini"){const month=calMonthStart(date),end=calAddMonths(month,1)- 1,first=calWeekStart(month,cal.firstDay);return{firstDay:first,lastDay:view==="mini"||cal.fixedWeeks?first+41:calWeekStart(end,cal.firstDay)+ 6,month}}
293
+ if(view==="months"){const month=calMonthStart(date);return{firstDay:month,lastDay:calAddMonths(month,cal.months)- 1,month}}
294
+ if(view==="week"||view==="weekList"){const first=calWeekStart(date,cal.firstDay);return{firstDay:first,lastDay:first+6}}
295
+ if(view==="day"||view==="dayList")return{firstDay:date,lastDay:date};if(view==="days"){let last=date;for(let shown=0;shown<this.calDaysCount();last++)if(!cal.hidden.has(calDow(last)))shown++;return{firstDay:date,lastDay:last-1}}
296
+ if(view==="agenda"){const span=cal.agendaSpan;if(span==="day")return{firstDay:date,lastDay:date};if(span==="week"){const first=calWeekStart(date,cal.firstDay);return{firstDay:first,lastDay:first+6}}
297
+ if(span==="year"){const parts=calDayParts(date);return{firstDay:calDayNumber(parts.y,1,1),lastDay:calDayNumber(parts.y+1,1,1)- 1}}
298
+ const month=calMonthStart(date);return{firstDay:month,lastDay:calAddMonths(month,1)- 1,month}}
299
+ if(view==="year"){const parts=calDayParts(date);return{firstDay:calDayNumber(parts.y,1,1),lastDay:calDayNumber(parts.y+1,1,1)- 1}}
300
+ return this.calTimelineRange(date)},calShownDays(first,last){const days=[];for(let day=first;day<=last;day++)if(!this.cal.hidden.has(calDow(day)))days.push(day);return days},calShownDay(day,direction){for(let i=0;i<7&&this.cal.hidden.has(calDow(day));i++)day +=direction||1;return day},calendarStep(direction){if(direction!==1&&direction!==-1)throw new Error("CanopyX: calendarStep takes 1 or -1.");const next=this.calStepDate(direction);if(!this.calInValid(this.calRangeOf(this.calView,next)))return Promise.resolve(this.getCalendar());return this.calGo(next,this.calView)},calInValid(range){const valid=this.cal.validRange;return!valid||range.lastDay>=valid.first&&range.firstDay<=valid.last},calClampDay(day){const valid=this.cal.validRange;return valid?Math.min(Math.max(day,valid.first),valid.last):day},calStepDate(direction){const date=this.calDate,view=this.calView;let next;const span=view==="agenda"?this.cal.agendaSpan:null;if(view==="month"||view==="mini"||span==="month")next=calAddMonths(date,direction);else if(view==="months")next=calAddMonths(date,this.cal.months*direction);else if(view==="year"||span==="year")next=calAddMonths(date,12*direction);else if(view==="week"||view==="weekList"||span==="week")next=date+7*direction;else if(view==="day"||view==="dayList"||span==="day")next=this.calShownDay(date+direction,direction);else if(view==="days"){const range=this.calRangeOf("days",date);next=direction>0?range.lastDay+1:this.calStepBack(date)}
301
+ else next=this.calTimelineStep(date,direction);return next},calStepBack(date){let day=date;for(let shown=0;shown<this.calDaysCount();){day--;if(!this.cal.hidden.has(calDow(day)))shown++}
302
+ return day},calendarToday(){return this.calGo(this.calClampDay(this.calZone.day(Date.now())),this.calView,true)},setCalendarDate(date){const day=typeof date==="number"?date:calParseDay(String(date).slice(0,10));if(!Number.isFinite(day))throw new Error("CanopyX: a calendar date is yyyy-MM-dd.");return this.calGo(this.calClampDay(day),this.calView,true)},setCalendarView(view){if(!this.cal.views.includes(view))throw new Error("CanopyX: the calendar has no view '"+view+"'.");const date=this.calFocus?.day??this.calDate;return this.calGo(view==="day"||view==="days"?this.calShownDay(date,1):date,view,true)},setCalendarZoom(zoom){if(!this.cal.zooms.includes(zoom))throw new Error("CanopyX: the calendar has no zoom '"+zoom+"'.");this.calZoom=zoom;return this.calGo(this.calDate,this.calView,true)},async calGo(date,view,focus){date=this.calClampDay(date);if(typeof focus==="number")focus=this.calClampDay(focus);const changed=view!==this.calView||date!==this.calDate,shift=date-this.calDate;const day=typeof focus==="number"?focus:!focus&&this.calFocus&&view===this.calView?this.calFocus.day+shift:date;this.calView=view;this.calDate=date;this.calFocus={day,minutes:this.calFocus?.minutes??null,event:null,resource:this.calFocus?.resource??null};if(view==="timeline")await this.calLoadTimeline();await this.calReload({announce:changed,keep:true});if(changed)this.stateChanged();return this.getCalendar()},getCalendar(){calNeed(this);const range=this.calRange||this.calRangeOf(this.calView,this.calDate);return gridFreeze({view:this.calView,date:calDayText(this.calDate),zoom:this.calZoom,from:calDayText(range.firstDay),to:calDayText(range.lastDay),timeZone:this.calZone.name,events:this.calEvents.length,collapsed:[...this.calCollapsed]})},calSnapshot(){return this.getCalendar()},getCalendarEvents(){return gridFreeze(this.calEvents.map(event=>gridFreeze({id:event.id,record:event.key,start:calIso(event.start),end:calIso(event.end),allDay:event.allDay,title:event.title,resource:event.resource,recurring:event.recurring,background:event.background})))},calApplyState(input){const state=input&&typeof input==="object"&&!Array.isArray(input)?input:{};const view=this.cal.views.includes(state.view)?state.view:this.cal.view,zoom=this.cal.zooms.includes(state.zoom)?state.zoom:this.cal.zoom;const saved=typeof state.date==="string"?calParseDay(state.date):NaN,date=this.calClampDay(Number.isFinite(saved)?saved:this.cal.date??this.calZone.day(Date.now()));const collapsed=Array.isArray(state.collapsed)&&state.collapsed.length<=10000?state.collapsed.filter(id=>typeof id==="string"):[];this.calZoom=zoom;this.calCollapsed=new Set(collapsed);this.calApplyCollapsed?.();return this.calGo(date,view,true)}});const CAL_STATE={get:DataGrid.prototype.getState,apply:DataGrid.prototype.applyStatePresentation,zone:DataGrid.prototype.setTimeZone};Object.assign(DataGrid.prototype,{getState(){const state=CAL_STATE.get.call(this);if(this.cal)state.calendar={view:this.calView,date:calDayText(this.calDate),zoom:this.calZoom,...this.calCollapsed.size?{collapsed:[...this.calCollapsed]}:{}};return state},applyStatePresentation(state){const applied=CAL_STATE.apply.call(this,state);if(!this.cal||state.calendar===undefined)return applied;const calendar=this.calApplyState(state.calendar);return Promise.all([applied,calendar]).then(([result])=>result)},setTimeZone(zone){const result=CAL_STATE.zone.call(this,zone);if(this.cal&&!this.cal.zoneFixed&&this.calZone.name!==(this.local?"local":"utc")){this.calZone=calZone(this.local?"local":"utc");this.calFmt=calFormats(this.lang,this.calZone);this.calVersion++;this.calReload({keep:true})}
303
+ return result}});function calNeed(grid){if(!grid.cal)throw new Error("CanopyX: this grid has no calendar.")}
304
+ for(const name of["setCalendarView","setCalendarDate","setCalendarZoom","calendarStep","calendarToday","getCalendarEvents","exportCalendar","openCalendarEvent","clearCalendarSelection","setCalendarSource"]){const method=DataGrid.prototype[name];if(method)DataGrid.prototype[name]=function(...args){calNeed(this);return method.apply(this,args)}}
305
+ function calWrapHighlight(){for(const name of["setHighlight","clearHighlight"]){const current=DataGrid.prototype[name];if(current._calendar)continue;const wrapped=function(...args){const result=current.apply(this,args);if(this.cal)Promise.resolve(result).then(()=>{calWrapHighlight();this.calMarkHighlights()},()=>{});return result};wrapped._calendar=true;wrapped.linkStub=current.linkStub;DataGrid.prototype[name]=wrapped}}
306
+ Object.assign(DataGrid.prototype,{calLoadTimeline(){if(!this.cal.resources&&!this.cal.views.includes("timeline"))return Promise.resolve();return this.calTimelineLoad||=loadPart(DataGrid.prototype.parts.timeline).then(()=>this.destroyed||this.startTimeline())
307
+ .catch(error=>{this.calTimelineLoad=null;this.emit("error",{error})})},calTimelineRange(date){const first=calWeekStart(date,this.cal.firstDay);return{firstDay:first,lastDay:first+6}},calTimelineStep(date,direction){return date+7*direction},calPaintTimeline(){this.calBody.replaceChildren()}});const CAL_FOCUS_RESULT=DataGrid.prototype.focusResult;DataGrid.prototype.focusResult=function(...args){return this.cal?Promise.resolve():CAL_FOCUS_RESULT.apply(this,args)};DataGrid.prototype.calKit={CAL_DAY,CAL_HOUR,CAL_MINUTE,CAL_GUTTER,CAL_SLOT_PX,calAddMonths,calCount,calDayNumber,calDayParts,calDow,calMonthDays,calMonthStart,calWeekStart,calZone,calIso,calParseDay,calDayText,calLanes,calColumns,calBase,calOverlaps,calInAllDayRow,calDayGrid};installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["calendar",part])})(typeof window!=="undefined"?window:globalThis);