"use strict"; var KimaiHeatmap = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // assets/src/heatmap.ts var heatmap_exports = {}; __export(heatmap_exports, { init: () => init }); // assets/src/state.ts function createInitialState(weekStart) { return { mode: "year", metric: "hours", filters: { projectId: null, customerId: null, activityId: null }, weekStart, data: null }; } // assets/src/renderers/registry.ts var renderers = /* @__PURE__ */ new Map(); function registerRenderer(renderer) { renderers.set(renderer.mode, renderer); } function getRenderer(mode) { const r = renderers.get(mode); if (!r) throw new Error(`Unknown heatmap mode: ${mode}`); return r; } // ../../../node_modules/d3-selection/src/namespaces.js var xhtml = "http://www.w3.org/1999/xhtml"; var namespaces_default = { svg: "http://www.w3.org/2000/svg", xhtml, xlink: "http://www.w3.org/1999/xlink", xml: "http://www.w3.org/XML/1998/namespace", xmlns: "http://www.w3.org/2000/xmlns/" }; // ../../../node_modules/d3-selection/src/namespace.js function namespace_default(name) { var prefix = name += "", i = prefix.indexOf(":"); if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); return namespaces_default.hasOwnProperty(prefix) ? { space: namespaces_default[prefix], local: name } : name; } // ../../../node_modules/d3-selection/src/creator.js function creatorInherit(name) { return function() { var document2 = this.ownerDocument, uri = this.namespaceURI; return uri === xhtml && document2.documentElement.namespaceURI === xhtml ? document2.createElement(name) : document2.createElementNS(uri, name); }; } function creatorFixed(fullname) { return function() { return this.ownerDocument.createElementNS(fullname.space, fullname.local); }; } function creator_default(name) { var fullname = namespace_default(name); return (fullname.local ? creatorFixed : creatorInherit)(fullname); } // ../../../node_modules/d3-selection/src/selector.js function none() { } function selector_default(selector) { return selector == null ? none : function() { return this.querySelector(selector); }; } // ../../../node_modules/d3-selection/src/selection/select.js function select_default(select) { if (typeof select !== "function") select = selector_default(select); for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { if ("__data__" in node) subnode.__data__ = node.__data__; subgroup[i] = subnode; } } } return new Selection(subgroups, this._parents); } // ../../../node_modules/d3-selection/src/array.js function array(x) { return x == null ? [] : Array.isArray(x) ? x : Array.from(x); } // ../../../node_modules/d3-selection/src/selectorAll.js function empty() { return []; } function selectorAll_default(selector) { return selector == null ? empty : function() { return this.querySelectorAll(selector); }; } // ../../../node_modules/d3-selection/src/selection/selectAll.js function arrayAll(select) { return function() { return array(select.apply(this, arguments)); }; } function selectAll_default(select) { if (typeof select === "function") select = arrayAll(select); else select = selectorAll_default(select); for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { if (node = group[i]) { subgroups.push(select.call(node, node.__data__, i, group)); parents.push(node); } } } return new Selection(subgroups, parents); } // ../../../node_modules/d3-selection/src/matcher.js function matcher_default(selector) { return function() { return this.matches(selector); }; } function childMatcher(selector) { return function(node) { return node.matches(selector); }; } // ../../../node_modules/d3-selection/src/selection/selectChild.js var find = Array.prototype.find; function childFind(match) { return function() { return find.call(this.children, match); }; } function childFirst() { return this.firstElementChild; } function selectChild_default(match) { return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match))); } // ../../../node_modules/d3-selection/src/selection/selectChildren.js var filter = Array.prototype.filter; function children() { return Array.from(this.children); } function childrenFilter(match) { return function() { return filter.call(this.children, match); }; } function selectChildren_default(match) { return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match))); } // ../../../node_modules/d3-selection/src/selection/filter.js function filter_default(match) { if (typeof match !== "function") match = matcher_default(match); for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { if ((node = group[i]) && match.call(node, node.__data__, i, group)) { subgroup.push(node); } } } return new Selection(subgroups, this._parents); } // ../../../node_modules/d3-selection/src/selection/sparse.js function sparse_default(update) { return new Array(update.length); } // ../../../node_modules/d3-selection/src/selection/enter.js function enter_default() { return new Selection(this._enter || this._groups.map(sparse_default), this._parents); } function EnterNode(parent, datum2) { this.ownerDocument = parent.ownerDocument; this.namespaceURI = parent.namespaceURI; this._next = null; this._parent = parent; this.__data__ = datum2; } EnterNode.prototype = { constructor: EnterNode, appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, querySelector: function(selector) { return this._parent.querySelector(selector); }, querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } }; // ../../../node_modules/d3-selection/src/constant.js function constant_default(x) { return function() { return x; }; } // ../../../node_modules/d3-selection/src/selection/data.js function bindIndex(parent, group, enter, update, exit, data) { var i = 0, node, groupLength = group.length, dataLength = data.length; for (; i < dataLength; ++i) { if (node = group[i]) { node.__data__ = data[i]; update[i] = node; } else { enter[i] = new EnterNode(parent, data[i]); } } for (; i < groupLength; ++i) { if (node = group[i]) { exit[i] = node; } } } function bindKey(parent, group, enter, update, exit, data, key) { var i, node, nodeByKeyValue = /* @__PURE__ */ new Map(), groupLength = group.length, dataLength = data.length, keyValues = new Array(groupLength), keyValue; for (i = 0; i < groupLength; ++i) { if (node = group[i]) { keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + ""; if (nodeByKeyValue.has(keyValue)) { exit[i] = node; } else { nodeByKeyValue.set(keyValue, node); } } } for (i = 0; i < dataLength; ++i) { keyValue = key.call(parent, data[i], i, data) + ""; if (node = nodeByKeyValue.get(keyValue)) { update[i] = node; node.__data__ = data[i]; nodeByKeyValue.delete(keyValue); } else { enter[i] = new EnterNode(parent, data[i]); } } for (i = 0; i < groupLength; ++i) { if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) { exit[i] = node; } } } function datum(node) { return node.__data__; } function data_default(value, key) { if (!arguments.length) return Array.from(this, datum); var bind = key ? bindKey : bindIndex, parents = this._parents, groups = this._groups; if (typeof value !== "function") value = constant_default(value); for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { var parent = parents[j], group = groups[j], groupLength = group.length, data = arraylike(value.call(parent, parent && parent.__data__, j, parents)), dataLength = data.length, enterGroup = enter[j] = new Array(dataLength), updateGroup = update[j] = new Array(dataLength), exitGroup = exit[j] = new Array(groupLength); bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { if (previous = enterGroup[i0]) { if (i0 >= i1) i1 = i0 + 1; while (!(next = updateGroup[i1]) && ++i1 < dataLength) ; previous._next = next || null; } } } update = new Selection(update, parents); update._enter = enter; update._exit = exit; return update; } function arraylike(data) { return typeof data === "object" && "length" in data ? data : Array.from(data); } // ../../../node_modules/d3-selection/src/selection/exit.js function exit_default() { return new Selection(this._exit || this._groups.map(sparse_default), this._parents); } // ../../../node_modules/d3-selection/src/selection/join.js function join_default(onenter, onupdate, onexit) { var enter = this.enter(), update = this, exit = this.exit(); if (typeof onenter === "function") { enter = onenter(enter); if (enter) enter = enter.selection(); } else { enter = enter.append(onenter + ""); } if (onupdate != null) { update = onupdate(update); if (update) update = update.selection(); } if (onexit == null) exit.remove(); else onexit(exit); return enter && update ? enter.merge(update).order() : update; } // ../../../node_modules/d3-selection/src/selection/merge.js function merge_default(context) { var selection2 = context.selection ? context.selection() : context; for (var groups0 = this._groups, groups1 = selection2._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { if (node = group0[i] || group1[i]) { merge[i] = node; } } } for (; j < m0; ++j) { merges[j] = groups0[j]; } return new Selection(merges, this._parents); } // ../../../node_modules/d3-selection/src/selection/order.js function order_default() { for (var groups = this._groups, j = -1, m = groups.length; ++j < m; ) { for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { if (node = group[i]) { if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next); next = node; } } } return this; } // ../../../node_modules/d3-selection/src/selection/sort.js function sort_default(compare) { if (!compare) compare = ascending; function compareNode(a, b) { return a && b ? compare(a.__data__, b.__data__) : !a - !b; } for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { if (node = group[i]) { sortgroup[i] = node; } } sortgroup.sort(compareNode); } return new Selection(sortgroups, this._parents).order(); } function ascending(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; } // ../../../node_modules/d3-selection/src/selection/call.js function call_default() { var callback = arguments[0]; arguments[0] = this; callback.apply(null, arguments); return this; } // ../../../node_modules/d3-selection/src/selection/nodes.js function nodes_default() { return Array.from(this); } // ../../../node_modules/d3-selection/src/selection/node.js function node_default() { for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { var node = group[i]; if (node) return node; } } return null; } // ../../../node_modules/d3-selection/src/selection/size.js function size_default() { let size = 0; for (const node of this) ++size; return size; } // ../../../node_modules/d3-selection/src/selection/empty.js function empty_default() { return !this.node(); } // ../../../node_modules/d3-selection/src/selection/each.js function each_default(callback) { for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { if (node = group[i]) callback.call(node, node.__data__, i, group); } } return this; } // ../../../node_modules/d3-selection/src/selection/attr.js function attrRemove(name) { return function() { this.removeAttribute(name); }; } function attrRemoveNS(fullname) { return function() { this.removeAttributeNS(fullname.space, fullname.local); }; } function attrConstant(name, value) { return function() { this.setAttribute(name, value); }; } function attrConstantNS(fullname, value) { return function() { this.setAttributeNS(fullname.space, fullname.local, value); }; } function attrFunction(name, value) { return function() { var v = value.apply(this, arguments); if (v == null) this.removeAttribute(name); else this.setAttribute(name, v); }; } function attrFunctionNS(fullname, value) { return function() { var v = value.apply(this, arguments); if (v == null) this.removeAttributeNS(fullname.space, fullname.local); else this.setAttributeNS(fullname.space, fullname.local, v); }; } function attr_default(name, value) { var fullname = namespace_default(name); if (arguments.length < 2) { var node = this.node(); return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname); } return this.each((value == null ? fullname.local ? attrRemoveNS : attrRemove : typeof value === "function" ? fullname.local ? attrFunctionNS : attrFunction : fullname.local ? attrConstantNS : attrConstant)(fullname, value)); } // ../../../node_modules/d3-selection/src/window.js function window_default(node) { return node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView; } // ../../../node_modules/d3-selection/src/selection/style.js function styleRemove(name) { return function() { this.style.removeProperty(name); }; } function styleConstant(name, value, priority) { return function() { this.style.setProperty(name, value, priority); }; } function styleFunction(name, value, priority) { return function() { var v = value.apply(this, arguments); if (v == null) this.style.removeProperty(name); else this.style.setProperty(name, v, priority); }; } function style_default(name, value, priority) { return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name); } function styleValue(node, name) { return node.style.getPropertyValue(name) || window_default(node).getComputedStyle(node, null).getPropertyValue(name); } // ../../../node_modules/d3-selection/src/selection/property.js function propertyRemove(name) { return function() { delete this[name]; }; } function propertyConstant(name, value) { return function() { this[name] = value; }; } function propertyFunction(name, value) { return function() { var v = value.apply(this, arguments); if (v == null) delete this[name]; else this[name] = v; }; } function property_default(name, value) { return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name]; } // ../../../node_modules/d3-selection/src/selection/classed.js function classArray(string) { return string.trim().split(/^|\s+/); } function classList(node) { return node.classList || new ClassList(node); } function ClassList(node) { this._node = node; this._names = classArray(node.getAttribute("class") || ""); } ClassList.prototype = { add: function(name) { var i = this._names.indexOf(name); if (i < 0) { this._names.push(name); this._node.setAttribute("class", this._names.join(" ")); } }, remove: function(name) { var i = this._names.indexOf(name); if (i >= 0) { this._names.splice(i, 1); this._node.setAttribute("class", this._names.join(" ")); } }, contains: function(name) { return this._names.indexOf(name) >= 0; } }; function classedAdd(node, names) { var list = classList(node), i = -1, n = names.length; while (++i < n) list.add(names[i]); } function classedRemove(node, names) { var list = classList(node), i = -1, n = names.length; while (++i < n) list.remove(names[i]); } function classedTrue(names) { return function() { classedAdd(this, names); }; } function classedFalse(names) { return function() { classedRemove(this, names); }; } function classedFunction(names, value) { return function() { (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); }; } function classed_default(name, value) { var names = classArray(name + ""); if (arguments.length < 2) { var list = classList(this.node()), i = -1, n = names.length; while (++i < n) if (!list.contains(names[i])) return false; return true; } return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value)); } // ../../../node_modules/d3-selection/src/selection/text.js function textRemove() { this.textContent = ""; } function textConstant(value) { return function() { this.textContent = value; }; } function textFunction(value) { return function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; }; } function text_default(value) { return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent; } // ../../../node_modules/d3-selection/src/selection/html.js function htmlRemove() { this.innerHTML = ""; } function htmlConstant(value) { return function() { this.innerHTML = value; }; } function htmlFunction(value) { return function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; }; } function html_default(value) { return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML; } // ../../../node_modules/d3-selection/src/selection/raise.js function raise() { if (this.nextSibling) this.parentNode.appendChild(this); } function raise_default() { return this.each(raise); } // ../../../node_modules/d3-selection/src/selection/lower.js function lower() { if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); } function lower_default() { return this.each(lower); } // ../../../node_modules/d3-selection/src/selection/append.js function append_default(name) { var create = typeof name === "function" ? name : creator_default(name); return this.select(function() { return this.appendChild(create.apply(this, arguments)); }); } // ../../../node_modules/d3-selection/src/selection/insert.js function constantNull() { return null; } function insert_default(name, before) { var create = typeof name === "function" ? name : creator_default(name), select = before == null ? constantNull : typeof before === "function" ? before : selector_default(before); return this.select(function() { return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); }); } // ../../../node_modules/d3-selection/src/selection/remove.js function remove() { var parent = this.parentNode; if (parent) parent.removeChild(this); } function remove_default() { return this.each(remove); } // ../../../node_modules/d3-selection/src/selection/clone.js function selection_cloneShallow() { var clone = this.cloneNode(false), parent = this.parentNode; return parent ? parent.insertBefore(clone, this.nextSibling) : clone; } function selection_cloneDeep() { var clone = this.cloneNode(true), parent = this.parentNode; return parent ? parent.insertBefore(clone, this.nextSibling) : clone; } function clone_default(deep) { return this.select(deep ? selection_cloneDeep : selection_cloneShallow); } // ../../../node_modules/d3-selection/src/selection/datum.js function datum_default(value) { return arguments.length ? this.property("__data__", value) : this.node().__data__; } // ../../../node_modules/d3-selection/src/selection/on.js function contextListener(listener) { return function(event) { listener.call(this, event, this.__data__); }; } function parseTypenames(typenames) { return typenames.trim().split(/^|\s+/).map(function(t) { var name = "", i = t.indexOf("."); if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); return { type: t, name }; }); } function onRemove(typename) { return function() { var on = this.__on; if (!on) return; for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { this.removeEventListener(o.type, o.listener, o.options); } else { on[++i] = o; } } if (++i) on.length = i; else delete this.__on; }; } function onAdd(typename, value, options) { return function() { var on = this.__on, o, listener = contextListener(value); if (on) for (var j = 0, m = on.length; j < m; ++j) { if ((o = on[j]).type === typename.type && o.name === typename.name) { this.removeEventListener(o.type, o.listener, o.options); this.addEventListener(o.type, o.listener = listener, o.options = options); o.value = value; return; } } this.addEventListener(typename.type, listener, options); o = { type: typename.type, name: typename.name, value, listener, options }; if (!on) this.__on = [o]; else on.push(o); }; } function on_default(typename, value, options) { var typenames = parseTypenames(typename + ""), i, n = typenames.length, t; if (arguments.length < 2) { var on = this.node().__on; if (on) for (var j = 0, m = on.length, o; j < m; ++j) { for (i = 0, o = on[j]; i < n; ++i) { if ((t = typenames[i]).type === o.type && t.name === o.name) { return o.value; } } } return; } on = value ? onAdd : onRemove; for (i = 0; i < n; ++i) this.each(on(typenames[i], value, options)); return this; } // ../../../node_modules/d3-selection/src/selection/dispatch.js function dispatchEvent(node, type, params) { var window2 = window_default(node), event = window2.CustomEvent; if (typeof event === "function") { event = new event(type, params); } else { event = window2.document.createEvent("Event"); if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; else event.initEvent(type, false, false); } node.dispatchEvent(event); } function dispatchConstant(type, params) { return function() { return dispatchEvent(this, type, params); }; } function dispatchFunction(type, params) { return function() { return dispatchEvent(this, type, params.apply(this, arguments)); }; } function dispatch_default(type, params) { return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params)); } // ../../../node_modules/d3-selection/src/selection/iterator.js function* iterator_default() { for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { if (node = group[i]) yield node; } } } // ../../../node_modules/d3-selection/src/selection/index.js var root = [null]; function Selection(groups, parents) { this._groups = groups; this._parents = parents; } function selection() { return new Selection([[document.documentElement]], root); } function selection_selection() { return this; } Selection.prototype = selection.prototype = { constructor: Selection, select: select_default, selectAll: selectAll_default, selectChild: selectChild_default, selectChildren: selectChildren_default, filter: filter_default, data: data_default, enter: enter_default, exit: exit_default, join: join_default, merge: merge_default, selection: selection_selection, order: order_default, sort: sort_default, call: call_default, nodes: nodes_default, node: node_default, size: size_default, empty: empty_default, each: each_default, attr: attr_default, style: style_default, property: property_default, classed: classed_default, text: text_default, html: html_default, raise: raise_default, lower: lower_default, append: append_default, insert: insert_default, remove: remove_default, clone: clone_default, datum: datum_default, on: on_default, dispatch: dispatch_default, [Symbol.iterator]: iterator_default }; // ../../../node_modules/d3-selection/src/select.js function select_default2(selector) { return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root); } // ../../../node_modules/d3-time/src/interval.js var t0 = /* @__PURE__ */ new Date(); var t1 = /* @__PURE__ */ new Date(); function timeInterval(floori, offseti, count, field) { function interval(date) { return floori(date = arguments.length === 0 ? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date(+date)), date; } interval.floor = (date) => { return floori(date = /* @__PURE__ */ new Date(+date)), date; }; interval.ceil = (date) => { return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; }; interval.round = (date) => { const d0 = interval(date), d1 = interval.ceil(date); return date - d0 < d1 - date ? d0 : d1; }; interval.offset = (date, step) => { return offseti(date = /* @__PURE__ */ new Date(+date), step == null ? 1 : Math.floor(step)), date; }; interval.range = (start, stop, step) => { const range = []; start = interval.ceil(start); step = step == null ? 1 : Math.floor(step); if (!(start < stop) || !(step > 0)) return range; let previous; do range.push(previous = /* @__PURE__ */ new Date(+start)), offseti(start, step), floori(start); while (previous < start && start < stop); return range; }; interval.filter = (test) => { return timeInterval((date) => { if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); }, (date, step) => { if (date >= date) { if (step < 0) while (++step <= 0) { while (offseti(date, -1), !test(date)) { } } else while (--step >= 0) { while (offseti(date, 1), !test(date)) { } } } }); }; if (count) { interval.count = (start, end) => { t0.setTime(+start), t1.setTime(+end); floori(t0), floori(t1); return Math.floor(count(t0, t1)); }; interval.every = (step) => { step = Math.floor(step); return !isFinite(step) || !(step > 0) ? null : !(step > 1) ? interval : interval.filter(field ? (d) => field(d) % step === 0 : (d) => interval.count(0, d) % step === 0); }; } return interval; } // ../../../node_modules/d3-time/src/duration.js var durationSecond = 1e3; var durationMinute = durationSecond * 60; var durationHour = durationMinute * 60; var durationDay = durationHour * 24; var durationWeek = durationDay * 7; var durationMonth = durationDay * 30; var durationYear = durationDay * 365; // ../../../node_modules/d3-time/src/day.js var timeDay = timeInterval( (date) => date.setHours(0, 0, 0, 0), (date, step) => date.setDate(date.getDate() + step), (start, end) => (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay, (date) => date.getDate() - 1 ); var timeDays = timeDay.range; var utcDay = timeInterval((date) => { date.setUTCHours(0, 0, 0, 0); }, (date, step) => { date.setUTCDate(date.getUTCDate() + step); }, (start, end) => { return (end - start) / durationDay; }, (date) => { return date.getUTCDate() - 1; }); var utcDays = utcDay.range; var unixDay = timeInterval((date) => { date.setUTCHours(0, 0, 0, 0); }, (date, step) => { date.setUTCDate(date.getUTCDate() + step); }, (start, end) => { return (end - start) / durationDay; }, (date) => { return Math.floor(date / durationDay); }); var unixDays = unixDay.range; // ../../../node_modules/d3-time/src/week.js function timeWeekday(i) { return timeInterval((date) => { date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); date.setHours(0, 0, 0, 0); }, (date, step) => { date.setDate(date.getDate() + step * 7); }, (start, end) => { return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; }); } var timeSunday = timeWeekday(0); var timeMonday = timeWeekday(1); var timeTuesday = timeWeekday(2); var timeWednesday = timeWeekday(3); var timeThursday = timeWeekday(4); var timeFriday = timeWeekday(5); var timeSaturday = timeWeekday(6); var timeSundays = timeSunday.range; var timeMondays = timeMonday.range; var timeTuesdays = timeTuesday.range; var timeWednesdays = timeWednesday.range; var timeThursdays = timeThursday.range; var timeFridays = timeFriday.range; var timeSaturdays = timeSaturday.range; function utcWeekday(i) { return timeInterval((date) => { date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); date.setUTCHours(0, 0, 0, 0); }, (date, step) => { date.setUTCDate(date.getUTCDate() + step * 7); }, (start, end) => { return (end - start) / durationWeek; }); } var utcSunday = utcWeekday(0); var utcMonday = utcWeekday(1); var utcTuesday = utcWeekday(2); var utcWednesday = utcWeekday(3); var utcThursday = utcWeekday(4); var utcFriday = utcWeekday(5); var utcSaturday = utcWeekday(6); var utcSundays = utcSunday.range; var utcMondays = utcMonday.range; var utcTuesdays = utcTuesday.range; var utcWednesdays = utcWednesday.range; var utcThursdays = utcThursday.range; var utcFridays = utcFriday.range; var utcSaturdays = utcSaturday.range; // ../../../node_modules/d3-time/src/month.js var timeMonth = timeInterval((date) => { date.setDate(1); date.setHours(0, 0, 0, 0); }, (date, step) => { date.setMonth(date.getMonth() + step); }, (start, end) => { return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; }, (date) => { return date.getMonth(); }); var timeMonths = timeMonth.range; var utcMonth = timeInterval((date) => { date.setUTCDate(1); date.setUTCHours(0, 0, 0, 0); }, (date, step) => { date.setUTCMonth(date.getUTCMonth() + step); }, (start, end) => { return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; }, (date) => { return date.getUTCMonth(); }); var utcMonths = utcMonth.range; // ../../../node_modules/d3-time/src/year.js var timeYear = timeInterval((date) => { date.setMonth(0, 1); date.setHours(0, 0, 0, 0); }, (date, step) => { date.setFullYear(date.getFullYear() + step); }, (start, end) => { return end.getFullYear() - start.getFullYear(); }, (date) => { return date.getFullYear(); }); timeYear.every = (k) => { return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval((date) => { date.setFullYear(Math.floor(date.getFullYear() / k) * k); date.setMonth(0, 1); date.setHours(0, 0, 0, 0); }, (date, step) => { date.setFullYear(date.getFullYear() + step * k); }); }; var timeYears = timeYear.range; var utcYear = timeInterval((date) => { date.setUTCMonth(0, 1); date.setUTCHours(0, 0, 0, 0); }, (date, step) => { date.setUTCFullYear(date.getUTCFullYear() + step); }, (start, end) => { return end.getUTCFullYear() - start.getUTCFullYear(); }, (date) => { return date.getUTCFullYear(); }); utcYear.every = (k) => { return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval((date) => { date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); date.setUTCMonth(0, 1); date.setUTCHours(0, 0, 0, 0); }, (date, step) => { date.setUTCFullYear(date.getUTCFullYear() + step * k); }); }; var utcYears = utcYear.range; // ../../../node_modules/d3-array/src/ascending.js function ascending2(a, b) { return a == null || b == null ? NaN : a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; } // ../../../node_modules/d3-array/src/descending.js function descending(a, b) { return a == null || b == null ? NaN : b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; } // ../../../node_modules/d3-array/src/bisector.js function bisector(f) { let compare1, compare2, delta; if (f.length !== 2) { compare1 = ascending2; compare2 = (d, x) => ascending2(f(d), x); delta = (d, x) => f(d) - x; } else { compare1 = f === ascending2 || f === descending ? f : zero; compare2 = f; delta = f; } function left(a, x, lo = 0, hi = a.length) { if (lo < hi) { if (compare1(x, x) !== 0) return hi; do { const mid = lo + hi >>> 1; if (compare2(a[mid], x) < 0) lo = mid + 1; else hi = mid; } while (lo < hi); } return lo; } function right(a, x, lo = 0, hi = a.length) { if (lo < hi) { if (compare1(x, x) !== 0) return hi; do { const mid = lo + hi >>> 1; if (compare2(a[mid], x) <= 0) lo = mid + 1; else hi = mid; } while (lo < hi); } return lo; } function center(a, x, lo = 0, hi = a.length) { const i = left(a, x, lo, hi - 1); return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i; } return { left, center, right }; } function zero() { return 0; } // ../../../node_modules/d3-array/src/number.js function number(x) { return x === null ? NaN : +x; } // ../../../node_modules/d3-array/src/bisect.js var ascendingBisect = bisector(ascending2); var bisectRight = ascendingBisect.right; var bisectLeft = ascendingBisect.left; var bisectCenter = bisector(number).center; var bisect_default = bisectRight; // ../../../node_modules/d3-array/src/ticks.js var e10 = Math.sqrt(50); var e5 = Math.sqrt(10); var e2 = Math.sqrt(2); function tickSpec(start, stop, count) { const step = (stop - start) / Math.max(0, count), power = Math.floor(Math.log10(step)), error = step / Math.pow(10, power), factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1; let i1, i2, inc; if (power < 0) { inc = Math.pow(10, -power) / factor; i1 = Math.round(start * inc); i2 = Math.round(stop * inc); if (i1 / inc < start) ++i1; if (i2 / inc > stop) --i2; inc = -inc; } else { inc = Math.pow(10, power) * factor; i1 = Math.round(start / inc); i2 = Math.round(stop / inc); if (i1 * inc < start) ++i1; if (i2 * inc > stop) --i2; } if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2); return [i1, i2, inc]; } function ticks(start, stop, count) { stop = +stop, start = +start, count = +count; if (!(count > 0)) return []; if (start === stop) return [start]; const reverse = stop < start, [i1, i2, inc] = reverse ? tickSpec(stop, start, count) : tickSpec(start, stop, count); if (!(i2 >= i1)) return []; const n = i2 - i1 + 1, ticks2 = new Array(n); if (reverse) { if (inc < 0) for (let i = 0; i < n; ++i) ticks2[i] = (i2 - i) / -inc; else for (let i = 0; i < n; ++i) ticks2[i] = (i2 - i) * inc; } else { if (inc < 0) for (let i = 0; i < n; ++i) ticks2[i] = (i1 + i) / -inc; else for (let i = 0; i < n; ++i) ticks2[i] = (i1 + i) * inc; } return ticks2; } function tickIncrement(start, stop, count) { stop = +stop, start = +start, count = +count; return tickSpec(start, stop, count)[2]; } function tickStep(start, stop, count) { stop = +stop, start = +start, count = +count; const reverse = stop < start, inc = reverse ? tickIncrement(stop, start, count) : tickIncrement(start, stop, count); return (reverse ? -1 : 1) * (inc < 0 ? 1 / -inc : inc); } // ../../../node_modules/d3-array/src/max.js function max(values, valueof) { let max2; if (valueof === void 0) { for (const value of values) { if (value != null && (max2 < value || max2 === void 0 && value >= value)) { max2 = value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (max2 < value || max2 === void 0 && value >= value)) { max2 = value; } } } return max2; } // assets/src/shared/tooltip.ts function createTooltip() { document.querySelectorAll(".heatmap-tooltip").forEach((el) => el.remove()); const tip = document.createElement("div"); tip.className = "heatmap-tooltip"; tip.style.display = "none"; tip.style.position = "fixed"; document.body.appendChild(tip); return tip; } function showTooltip(tip, html, anchorRect, cellSize) { tip.innerHTML = html; tip.style.display = "block"; tip.style.left = `${anchorRect.left + cellSize / 2}px`; tip.style.top = `${anchorRect.top - tip.offsetHeight - 8}px`; } function hideTooltip(tip) { tip.style.display = "none"; } // ../../../node_modules/d3-scale/src/init.js function initRange(domain, range) { switch (arguments.length) { case 0: break; case 1: this.range(domain); break; default: this.range(range).domain(domain); break; } return this; } // ../../../node_modules/d3-format/src/formatDecimal.js function formatDecimal_default(x) { return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10); } function formatDecimalParts(x, p) { if (!isFinite(x) || x === 0) return null; var i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e"), coefficient = x.slice(0, i); return [ coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1) ]; } // ../../../node_modules/d3-format/src/exponent.js function exponent_default(x) { return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN; } // ../../../node_modules/d3-format/src/formatGroup.js function formatGroup_default(grouping, thousands) { return function(value, width) { var i = value.length, t = [], j = 0, g = grouping[0], length = 0; while (i > 0 && g > 0) { if (length + g + 1 > width) g = Math.max(1, width - length); t.push(value.substring(i -= g, i + g)); if ((length += g + 1) > width) break; g = grouping[j = (j + 1) % grouping.length]; } return t.reverse().join(thousands); }; } // ../../../node_modules/d3-format/src/formatNumerals.js function formatNumerals_default(numerals) { return function(value) { return value.replace(/[0-9]/g, function(i) { return numerals[+i]; }); }; } // ../../../node_modules/d3-format/src/formatSpecifier.js var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i; function formatSpecifier(specifier) { if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); var match; return new FormatSpecifier({ fill: match[1], align: match[2], sign: match[3], symbol: match[4], zero: match[5], width: match[6], comma: match[7], precision: match[8] && match[8].slice(1), trim: match[9], type: match[10] }); } formatSpecifier.prototype = FormatSpecifier.prototype; function FormatSpecifier(specifier) { this.fill = specifier.fill === void 0 ? " " : specifier.fill + ""; this.align = specifier.align === void 0 ? ">" : specifier.align + ""; this.sign = specifier.sign === void 0 ? "-" : specifier.sign + ""; this.symbol = specifier.symbol === void 0 ? "" : specifier.symbol + ""; this.zero = !!specifier.zero; this.width = specifier.width === void 0 ? void 0 : +specifier.width; this.comma = !!specifier.comma; this.precision = specifier.precision === void 0 ? void 0 : +specifier.precision; this.trim = !!specifier.trim; this.type = specifier.type === void 0 ? "" : specifier.type + ""; } FormatSpecifier.prototype.toString = function() { return this.fill + this.align + this.sign + this.symbol + (this.zero ? "0" : "") + (this.width === void 0 ? "" : Math.max(1, this.width | 0)) + (this.comma ? "," : "") + (this.precision === void 0 ? "" : "." + Math.max(0, this.precision | 0)) + (this.trim ? "~" : "") + this.type; }; // ../../../node_modules/d3-format/src/formatTrim.js function formatTrim_default(s) { out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) { switch (s[i]) { case ".": i0 = i1 = i; break; case "0": if (i0 === 0) i0 = i; i1 = i; break; default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break; } } return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s; } // ../../../node_modules/d3-format/src/formatPrefixAuto.js var prefixExponent; function formatPrefixAuto_default(x, p) { var d = formatDecimalParts(x, p); if (!d) return prefixExponent = void 0, x.toPrecision(p); var coefficient = d[0], exponent = d[1], i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, n = coefficient.length; return i === n ? coefficient : i > n ? coefficient + new Array(i - n + 1).join("0") : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; } // ../../../node_modules/d3-format/src/formatRounded.js function formatRounded_default(x, p) { var d = formatDecimalParts(x, p); if (!d) return x + ""; var coefficient = d[0], exponent = d[1]; return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) : coefficient + new Array(exponent - coefficient.length + 2).join("0"); } // ../../../node_modules/d3-format/src/formatTypes.js var formatTypes_default = { "%": (x, p) => (x * 100).toFixed(p), "b": (x) => Math.round(x).toString(2), "c": (x) => x + "", "d": formatDecimal_default, "e": (x, p) => x.toExponential(p), "f": (x, p) => x.toFixed(p), "g": (x, p) => x.toPrecision(p), "o": (x) => Math.round(x).toString(8), "p": (x, p) => formatRounded_default(x * 100, p), "r": formatRounded_default, "s": formatPrefixAuto_default, "X": (x) => Math.round(x).toString(16).toUpperCase(), "x": (x) => Math.round(x).toString(16) }; // ../../../node_modules/d3-format/src/identity.js function identity_default(x) { return x; } // ../../../node_modules/d3-format/src/locale.js var map = Array.prototype.map; var prefixes = ["y", "z", "a", "f", "p", "n", "\xB5", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"]; function locale_default(locale3) { var group = locale3.grouping === void 0 || locale3.thousands === void 0 ? identity_default : formatGroup_default(map.call(locale3.grouping, Number), locale3.thousands + ""), currencyPrefix = locale3.currency === void 0 ? "" : locale3.currency[0] + "", currencySuffix = locale3.currency === void 0 ? "" : locale3.currency[1] + "", decimal = locale3.decimal === void 0 ? "." : locale3.decimal + "", numerals = locale3.numerals === void 0 ? identity_default : formatNumerals_default(map.call(locale3.numerals, String)), percent = locale3.percent === void 0 ? "%" : locale3.percent + "", minus = locale3.minus === void 0 ? "\u2212" : locale3.minus + "", nan = locale3.nan === void 0 ? "NaN" : locale3.nan + ""; function newFormat(specifier, options) { specifier = formatSpecifier(specifier); var fill = specifier.fill, align = specifier.align, sign = specifier.sign, symbol = specifier.symbol, zero2 = specifier.zero, width = specifier.width, comma = specifier.comma, precision = specifier.precision, trim = specifier.trim, type = specifier.type; if (type === "n") comma = true, type = "g"; else if (!formatTypes_default[type]) precision === void 0 && (precision = 12), trim = true, type = "g"; if (zero2 || fill === "0" && align === "=") zero2 = true, fill = "0", align = "="; var prefix = (options && options.prefix !== void 0 ? options.prefix : "") + (symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : ""), suffix = (symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "") + (options && options.suffix !== void 0 ? options.suffix : ""); var formatType = formatTypes_default[type], maybeSuffix = /[defgprs%]/.test(type); precision = precision === void 0 ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision)); function format2(value) { var valuePrefix = prefix, valueSuffix = suffix, i, n, c; if (type === "c") { valueSuffix = formatType(value) + valueSuffix; value = ""; } else { value = +value; var valueNegative = value < 0 || 1 / value < 0; value = isNaN(value) ? nan : formatType(Math.abs(value), precision); if (trim) value = formatTrim_default(value); if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; valueSuffix = (type === "s" && !isNaN(value) && prefixExponent !== void 0 ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); if (maybeSuffix) { i = -1, n = value.length; while (++i < n) { if (c = value.charCodeAt(i), 48 > c || c > 57) { valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; value = value.slice(0, i); break; } } } } if (comma && !zero2) value = group(value, Infinity); var length = valuePrefix.length + value.length + valueSuffix.length, padding = length < width ? new Array(width - length + 1).join(fill) : ""; if (comma && zero2) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; switch (align) { case "<": value = valuePrefix + value + valueSuffix + padding; break; case "=": value = valuePrefix + padding + value + valueSuffix; break; case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break; default: value = padding + valuePrefix + value + valueSuffix; break; } return numerals(value); } format2.toString = function() { return specifier + ""; }; return format2; } function formatPrefix2(specifier, value) { var e = Math.max(-8, Math.min(8, Math.floor(exponent_default(value) / 3))) * 3, k = Math.pow(10, -e), f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), { suffix: prefixes[8 + e / 3] }); return function(value2) { return f(k * value2); }; } return { format: newFormat, formatPrefix: formatPrefix2 }; } // ../../../node_modules/d3-format/src/defaultLocale.js var locale; var format; var formatPrefix; defaultLocale({ thousands: ",", grouping: [3], currency: ["$", ""] }); function defaultLocale(definition) { locale = locale_default(definition); format = locale.format; formatPrefix = locale.formatPrefix; return locale; } // ../../../node_modules/d3-format/src/precisionFixed.js function precisionFixed_default(step) { return Math.max(0, -exponent_default(Math.abs(step))); } // ../../../node_modules/d3-format/src/precisionPrefix.js function precisionPrefix_default(step, value) { return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent_default(value) / 3))) * 3 - exponent_default(Math.abs(step))); } // ../../../node_modules/d3-format/src/precisionRound.js function precisionRound_default(step, max2) { step = Math.abs(step), max2 = Math.abs(max2) - step; return Math.max(0, exponent_default(max2) - exponent_default(step)) + 1; } // ../../../node_modules/d3-scale/src/tickFormat.js function tickFormat(start, stop, count, specifier) { var step = tickStep(start, stop, count), precision; specifier = formatSpecifier(specifier == null ? ",f" : specifier); switch (specifier.type) { case "s": { var value = Math.max(Math.abs(start), Math.abs(stop)); if (specifier.precision == null && !isNaN(precision = precisionPrefix_default(step, value))) specifier.precision = precision; return formatPrefix(specifier, value); } case "": case "e": case "g": case "p": case "r": { if (specifier.precision == null && !isNaN(precision = precisionRound_default(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); break; } case "f": case "%": { if (specifier.precision == null && !isNaN(precision = precisionFixed_default(step))) specifier.precision = precision - (specifier.type === "%") * 2; break; } } return format(specifier); } // ../../../node_modules/d3-scale/src/linear.js function linearish(scale) { var domain = scale.domain; scale.ticks = function(count) { var d = domain(); return ticks(d[0], d[d.length - 1], count == null ? 10 : count); }; scale.tickFormat = function(count, specifier) { var d = domain(); return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier); }; scale.nice = function(count) { if (count == null) count = 10; var d = domain(); var i0 = 0; var i1 = d.length - 1; var start = d[i0]; var stop = d[i1]; var prestep; var step; var maxIter = 10; if (stop < start) { step = start, start = stop, stop = step; step = i0, i0 = i1, i1 = step; } while (maxIter-- > 0) { step = tickIncrement(start, stop, count); if (step === prestep) { d[i0] = start; d[i1] = stop; return domain(d); } else if (step > 0) { start = Math.floor(start / step) * step; stop = Math.ceil(stop / step) * step; } else if (step < 0) { start = Math.ceil(start * step) / step; stop = Math.floor(stop * step) / step; } else { break; } prestep = step; } return scale; }; return scale; } // ../../../node_modules/d3-scale/src/quantize.js function quantize() { var x0 = 0, x1 = 1, n = 1, domain = [0.5], range = [0, 1], unknown; function scale(x) { return x != null && x <= x ? range[bisect_default(domain, x, 0, n)] : unknown; } function rescale() { var i = -1; domain = new Array(n); while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); return scale; } scale.domain = function(_) { return arguments.length ? ([x0, x1] = _, x0 = +x0, x1 = +x1, rescale()) : [x0, x1]; }; scale.range = function(_) { return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice(); }; scale.invertExtent = function(y) { var i = range.indexOf(y); return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]]; }; scale.unknown = function(_) { return arguments.length ? (unknown = _, scale) : scale; }; scale.thresholds = function() { return domain.slice(); }; scale.copy = function() { return quantize().domain([x0, x1]).range(range).unknown(unknown); }; return initRange.apply(linearish(scale), arguments); } // ../../../node_modules/d3-time-format/src/locale.js function localDate(d) { if (0 <= d.y && d.y < 100) { var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); date.setFullYear(d.y); return date; } return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); } function utcDate(d) { if (0 <= d.y && d.y < 100) { var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); date.setUTCFullYear(d.y); return date; } return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); } function newDate(y, m, d) { return { y, m, d, H: 0, M: 0, S: 0, L: 0 }; } function formatLocale(locale3) { var locale_dateTime = locale3.dateTime, locale_date = locale3.date, locale_time = locale3.time, locale_periods = locale3.periods, locale_weekdays = locale3.days, locale_shortWeekdays = locale3.shortDays, locale_months = locale3.months, locale_shortMonths = locale3.shortMonths; var periodRe = formatRe(locale_periods), periodLookup = formatLookup(locale_periods), weekdayRe = formatRe(locale_weekdays), weekdayLookup = formatLookup(locale_weekdays), shortWeekdayRe = formatRe(locale_shortWeekdays), shortWeekdayLookup = formatLookup(locale_shortWeekdays), monthRe = formatRe(locale_months), monthLookup = formatLookup(locale_months), shortMonthRe = formatRe(locale_shortMonths), shortMonthLookup = formatLookup(locale_shortMonths); var formats = { "a": formatShortWeekday, "A": formatWeekday, "b": formatShortMonth, "B": formatMonth, "c": null, "d": formatDayOfMonth, "e": formatDayOfMonth, "f": formatMicroseconds, "g": formatYearISO, "G": formatFullYearISO, "H": formatHour24, "I": formatHour12, "j": formatDayOfYear, "L": formatMilliseconds, "m": formatMonthNumber, "M": formatMinutes, "p": formatPeriod, "q": formatQuarter, "Q": formatUnixTimestamp, "s": formatUnixTimestampSeconds, "S": formatSeconds, "u": formatWeekdayNumberMonday, "U": formatWeekNumberSunday, "V": formatWeekNumberISO, "w": formatWeekdayNumberSunday, "W": formatWeekNumberMonday, "x": null, "X": null, "y": formatYear, "Y": formatFullYear, "Z": formatZone, "%": formatLiteralPercent }; var utcFormats = { "a": formatUTCShortWeekday, "A": formatUTCWeekday, "b": formatUTCShortMonth, "B": formatUTCMonth, "c": null, "d": formatUTCDayOfMonth, "e": formatUTCDayOfMonth, "f": formatUTCMicroseconds, "g": formatUTCYearISO, "G": formatUTCFullYearISO, "H": formatUTCHour24, "I": formatUTCHour12, "j": formatUTCDayOfYear, "L": formatUTCMilliseconds, "m": formatUTCMonthNumber, "M": formatUTCMinutes, "p": formatUTCPeriod, "q": formatUTCQuarter, "Q": formatUnixTimestamp, "s": formatUnixTimestampSeconds, "S": formatUTCSeconds, "u": formatUTCWeekdayNumberMonday, "U": formatUTCWeekNumberSunday, "V": formatUTCWeekNumberISO, "w": formatUTCWeekdayNumberSunday, "W": formatUTCWeekNumberMonday, "x": null, "X": null, "y": formatUTCYear, "Y": formatUTCFullYear, "Z": formatUTCZone, "%": formatLiteralPercent }; var parses = { "a": parseShortWeekday, "A": parseWeekday, "b": parseShortMonth, "B": parseMonth, "c": parseLocaleDateTime, "d": parseDayOfMonth, "e": parseDayOfMonth, "f": parseMicroseconds, "g": parseYear, "G": parseFullYear, "H": parseHour24, "I": parseHour24, "j": parseDayOfYear, "L": parseMilliseconds, "m": parseMonthNumber, "M": parseMinutes, "p": parsePeriod, "q": parseQuarter, "Q": parseUnixTimestamp, "s": parseUnixTimestampSeconds, "S": parseSeconds, "u": parseWeekdayNumberMonday, "U": parseWeekNumberSunday, "V": parseWeekNumberISO, "w": parseWeekdayNumberSunday, "W": parseWeekNumberMonday, "x": parseLocaleDate, "X": parseLocaleTime, "y": parseYear, "Y": parseFullYear, "Z": parseZone, "%": parseLiteralPercent }; formats.x = newFormat(locale_date, formats); formats.X = newFormat(locale_time, formats); formats.c = newFormat(locale_dateTime, formats); utcFormats.x = newFormat(locale_date, utcFormats); utcFormats.X = newFormat(locale_time, utcFormats); utcFormats.c = newFormat(locale_dateTime, utcFormats); function newFormat(specifier, formats2) { return function(date) { var string = [], i = -1, j = 0, n = specifier.length, c, pad2, format2; if (!(date instanceof Date)) date = /* @__PURE__ */ new Date(+date); while (++i < n) { if (specifier.charCodeAt(i) === 37) { string.push(specifier.slice(j, i)); if ((pad2 = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); else pad2 = c === "e" ? " " : "0"; if (format2 = formats2[c]) c = format2(date, pad2); string.push(c); j = i + 1; } } string.push(specifier.slice(j, i)); return string.join(""); }; } function newParse(specifier, Z) { return function(string) { var d = newDate(1900, void 0, 1), i = parseSpecifier(d, specifier, string += "", 0), week, day; if (i != string.length) return null; if ("Q" in d) return new Date(d.Q); if ("s" in d) return new Date(d.s * 1e3 + ("L" in d ? d.L : 0)); if (Z && !("Z" in d)) d.Z = 0; if ("p" in d) d.H = d.H % 12 + d.p * 12; if (d.m === void 0) d.m = "q" in d ? d.q : 0; if ("V" in d) { if (d.V < 1 || d.V > 53) return null; if (!("w" in d)) d.w = 1; if ("Z" in d) { week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay(); week = day > 4 || day === 0 ? utcMonday.ceil(week) : utcMonday(week); week = utcDay.offset(week, (d.V - 1) * 7); d.y = week.getUTCFullYear(); d.m = week.getUTCMonth(); d.d = week.getUTCDate() + (d.w + 6) % 7; } else { week = localDate(newDate(d.y, 0, 1)), day = week.getDay(); week = day > 4 || day === 0 ? timeMonday.ceil(week) : timeMonday(week); week = timeDay.offset(week, (d.V - 1) * 7); d.y = week.getFullYear(); d.m = week.getMonth(); d.d = week.getDate() + (d.w + 6) % 7; } } else if ("W" in d || "U" in d) { if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; day = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay(); d.m = 0; d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7; } if ("Z" in d) { d.H += d.Z / 100 | 0; d.M += d.Z % 100; return utcDate(d); } return localDate(d); }; } function parseSpecifier(d, specifier, string, j) { var i = 0, n = specifier.length, m = string.length, c, parse; while (i < n) { if (j >= m) return -1; c = specifier.charCodeAt(i++); if (c === 37) { c = specifier.charAt(i++); parse = parses[c in pads ? specifier.charAt(i++) : c]; if (!parse || (j = parse(d, string, j)) < 0) return -1; } else if (c != string.charCodeAt(j++)) { return -1; } } return j; } function parsePeriod(d, string, i) { var n = periodRe.exec(string.slice(i)); return n ? (d.p = periodLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; } function parseShortWeekday(d, string, i) { var n = shortWeekdayRe.exec(string.slice(i)); return n ? (d.w = shortWeekdayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; } function parseWeekday(d, string, i) { var n = weekdayRe.exec(string.slice(i)); return n ? (d.w = weekdayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; } function parseShortMonth(d, string, i) { var n = shortMonthRe.exec(string.slice(i)); return n ? (d.m = shortMonthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; } function parseMonth(d, string, i) { var n = monthRe.exec(string.slice(i)); return n ? (d.m = monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; } function parseLocaleDateTime(d, string, i) { return parseSpecifier(d, locale_dateTime, string, i); } function parseLocaleDate(d, string, i) { return parseSpecifier(d, locale_date, string, i); } function parseLocaleTime(d, string, i) { return parseSpecifier(d, locale_time, string, i); } function formatShortWeekday(d) { return locale_shortWeekdays[d.getDay()]; } function formatWeekday(d) { return locale_weekdays[d.getDay()]; } function formatShortMonth(d) { return locale_shortMonths[d.getMonth()]; } function formatMonth(d) { return locale_months[d.getMonth()]; } function formatPeriod(d) { return locale_periods[+(d.getHours() >= 12)]; } function formatQuarter(d) { return 1 + ~~(d.getMonth() / 3); } function formatUTCShortWeekday(d) { return locale_shortWeekdays[d.getUTCDay()]; } function formatUTCWeekday(d) { return locale_weekdays[d.getUTCDay()]; } function formatUTCShortMonth(d) { return locale_shortMonths[d.getUTCMonth()]; } function formatUTCMonth(d) { return locale_months[d.getUTCMonth()]; } function formatUTCPeriod(d) { return locale_periods[+(d.getUTCHours() >= 12)]; } function formatUTCQuarter(d) { return 1 + ~~(d.getUTCMonth() / 3); } return { format: function(specifier) { var f = newFormat(specifier += "", formats); f.toString = function() { return specifier; }; return f; }, parse: function(specifier) { var p = newParse(specifier += "", false); p.toString = function() { return specifier; }; return p; }, utcFormat: function(specifier) { var f = newFormat(specifier += "", utcFormats); f.toString = function() { return specifier; }; return f; }, utcParse: function(specifier) { var p = newParse(specifier += "", true); p.toString = function() { return specifier; }; return p; } }; } var pads = { "-": "", "_": " ", "0": "0" }; var numberRe = /^\s*\d+/; var percentRe = /^%/; var requoteRe = /[\\^$*+?|[\]().{}]/g; function pad(value, fill, width) { var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); } function requote(s) { return s.replace(requoteRe, "\\$&"); } function formatRe(names) { return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); } function formatLookup(names) { return new Map(names.map((name, i) => [name.toLowerCase(), i])); } function parseWeekdayNumberSunday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 1)); return n ? (d.w = +n[0], i + n[0].length) : -1; } function parseWeekdayNumberMonday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 1)); return n ? (d.u = +n[0], i + n[0].length) : -1; } function parseWeekNumberSunday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.U = +n[0], i + n[0].length) : -1; } function parseWeekNumberISO(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.V = +n[0], i + n[0].length) : -1; } function parseWeekNumberMonday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.W = +n[0], i + n[0].length) : -1; } function parseFullYear(d, string, i) { var n = numberRe.exec(string.slice(i, i + 4)); return n ? (d.y = +n[0], i + n[0].length) : -1; } function parseYear(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2e3), i + n[0].length) : -1; } function parseZone(d, string, i) { var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; } function parseQuarter(d, string, i) { var n = numberRe.exec(string.slice(i, i + 1)); return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1; } function parseMonthNumber(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.m = n[0] - 1, i + n[0].length) : -1; } function parseDayOfMonth(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.d = +n[0], i + n[0].length) : -1; } function parseDayOfYear(d, string, i) { var n = numberRe.exec(string.slice(i, i + 3)); return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; } function parseHour24(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.H = +n[0], i + n[0].length) : -1; } function parseMinutes(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.M = +n[0], i + n[0].length) : -1; } function parseSeconds(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.S = +n[0], i + n[0].length) : -1; } function parseMilliseconds(d, string, i) { var n = numberRe.exec(string.slice(i, i + 3)); return n ? (d.L = +n[0], i + n[0].length) : -1; } function parseMicroseconds(d, string, i) { var n = numberRe.exec(string.slice(i, i + 6)); return n ? (d.L = Math.floor(n[0] / 1e3), i + n[0].length) : -1; } function parseLiteralPercent(d, string, i) { var n = percentRe.exec(string.slice(i, i + 1)); return n ? i + n[0].length : -1; } function parseUnixTimestamp(d, string, i) { var n = numberRe.exec(string.slice(i)); return n ? (d.Q = +n[0], i + n[0].length) : -1; } function parseUnixTimestampSeconds(d, string, i) { var n = numberRe.exec(string.slice(i)); return n ? (d.s = +n[0], i + n[0].length) : -1; } function formatDayOfMonth(d, p) { return pad(d.getDate(), p, 2); } function formatHour24(d, p) { return pad(d.getHours(), p, 2); } function formatHour12(d, p) { return pad(d.getHours() % 12 || 12, p, 2); } function formatDayOfYear(d, p) { return pad(1 + timeDay.count(timeYear(d), d), p, 3); } function formatMilliseconds(d, p) { return pad(d.getMilliseconds(), p, 3); } function formatMicroseconds(d, p) { return formatMilliseconds(d, p) + "000"; } function formatMonthNumber(d, p) { return pad(d.getMonth() + 1, p, 2); } function formatMinutes(d, p) { return pad(d.getMinutes(), p, 2); } function formatSeconds(d, p) { return pad(d.getSeconds(), p, 2); } function formatWeekdayNumberMonday(d) { var day = d.getDay(); return day === 0 ? 7 : day; } function formatWeekNumberSunday(d, p) { return pad(timeSunday.count(timeYear(d) - 1, d), p, 2); } function dISO(d) { var day = d.getDay(); return day >= 4 || day === 0 ? timeThursday(d) : timeThursday.ceil(d); } function formatWeekNumberISO(d, p) { d = dISO(d); return pad(timeThursday.count(timeYear(d), d) + (timeYear(d).getDay() === 4), p, 2); } function formatWeekdayNumberSunday(d) { return d.getDay(); } function formatWeekNumberMonday(d, p) { return pad(timeMonday.count(timeYear(d) - 1, d), p, 2); } function formatYear(d, p) { return pad(d.getFullYear() % 100, p, 2); } function formatYearISO(d, p) { d = dISO(d); return pad(d.getFullYear() % 100, p, 2); } function formatFullYear(d, p) { return pad(d.getFullYear() % 1e4, p, 4); } function formatFullYearISO(d, p) { var day = d.getDay(); d = day >= 4 || day === 0 ? timeThursday(d) : timeThursday.ceil(d); return pad(d.getFullYear() % 1e4, p, 4); } function formatZone(d) { var z = d.getTimezoneOffset(); return (z > 0 ? "-" : (z *= -1, "+")) + pad(z / 60 | 0, "0", 2) + pad(z % 60, "0", 2); } function formatUTCDayOfMonth(d, p) { return pad(d.getUTCDate(), p, 2); } function formatUTCHour24(d, p) { return pad(d.getUTCHours(), p, 2); } function formatUTCHour12(d, p) { return pad(d.getUTCHours() % 12 || 12, p, 2); } function formatUTCDayOfYear(d, p) { return pad(1 + utcDay.count(utcYear(d), d), p, 3); } function formatUTCMilliseconds(d, p) { return pad(d.getUTCMilliseconds(), p, 3); } function formatUTCMicroseconds(d, p) { return formatUTCMilliseconds(d, p) + "000"; } function formatUTCMonthNumber(d, p) { return pad(d.getUTCMonth() + 1, p, 2); } function formatUTCMinutes(d, p) { return pad(d.getUTCMinutes(), p, 2); } function formatUTCSeconds(d, p) { return pad(d.getUTCSeconds(), p, 2); } function formatUTCWeekdayNumberMonday(d) { var dow = d.getUTCDay(); return dow === 0 ? 7 : dow; } function formatUTCWeekNumberSunday(d, p) { return pad(utcSunday.count(utcYear(d) - 1, d), p, 2); } function UTCdISO(d) { var day = d.getUTCDay(); return day >= 4 || day === 0 ? utcThursday(d) : utcThursday.ceil(d); } function formatUTCWeekNumberISO(d, p) { d = UTCdISO(d); return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2); } function formatUTCWeekdayNumberSunday(d) { return d.getUTCDay(); } function formatUTCWeekNumberMonday(d, p) { return pad(utcMonday.count(utcYear(d) - 1, d), p, 2); } function formatUTCYear(d, p) { return pad(d.getUTCFullYear() % 100, p, 2); } function formatUTCYearISO(d, p) { d = UTCdISO(d); return pad(d.getUTCFullYear() % 100, p, 2); } function formatUTCFullYear(d, p) { return pad(d.getUTCFullYear() % 1e4, p, 4); } function formatUTCFullYearISO(d, p) { var day = d.getUTCDay(); d = day >= 4 || day === 0 ? utcThursday(d) : utcThursday.ceil(d); return pad(d.getUTCFullYear() % 1e4, p, 4); } function formatUTCZone() { return "+0000"; } function formatLiteralPercent() { return "%"; } function formatUnixTimestamp(d) { return +d; } function formatUnixTimestampSeconds(d) { return Math.floor(+d / 1e3); } // ../../../node_modules/d3-time-format/src/defaultLocale.js var locale2; var timeFormat; var timeParse; var utcFormat; var utcParse; defaultLocale2({ dateTime: "%x, %X", date: "%-m/%-d/%Y", time: "%-I:%M:%S %p", periods: ["AM", "PM"], days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] }); function defaultLocale2(definition) { locale2 = formatLocale(definition); timeFormat = locale2.format; timeParse = locale2.parse; utcFormat = locale2.utcFormat; utcParse = locale2.utcParse; return locale2; } // assets/src/shared/color-scale.ts var FALLBACK_COLORS = ["#9be9a8", "#40c463", "#30a14e", "#216e39"]; function buildColorScale(days, metric = "hours") { const accessor = metric === "hours" ? (d) => d.hours : (d) => d.count; const maxVal = max(days, accessor) || 1; return quantize().domain([0, maxVal]).range(FALLBACK_COLORS); } // assets/src/shared/date-utils.ts var DAY_LABELS_MONDAY = ["Mon", "", "Wed", "", "Fri", "", ""]; var DAY_LABELS_SUNDAY = ["Sun", "", "Tue", "", "Thu", "", "Sat"]; function getDayLabels(weekStart) { return weekStart === "sunday" ? DAY_LABELS_SUNDAY : DAY_LABELS_MONDAY; } var MONTH_FORMAT = timeFormat("%b"); var DATE_FORMAT = timeFormat("%Y-%m-%d"); var DISPLAY_FORMAT = timeFormat("%a, %b %-d, %Y"); function buildDateMap(days) { const map2 = /* @__PURE__ */ new Map(); for (const d of days) { map2.set(d.date, d); } return map2; } function getWeekInterval(weekStart) { return weekStart === "sunday" ? timeSunday : timeMonday; } function generateCells(begin, end, dateMap, weekStart = "monday") { const weekInterval = getWeekInterval(weekStart); const firstWeekDay = weekInterval.floor(begin); const cells = []; let current = new Date(begin); while (current <= end) { const dateStr = DATE_FORMAT(current); const weeksSinceStart = weekInterval.count(firstWeekDay, current); const jsDay = current.getDay(); const dayOfWeek = weekStart === "sunday" ? jsDay : (jsDay + 6) % 7; const isWeekend = jsDay === 0 || jsDay === 6; cells.push({ date: new Date(current), dateStr, entry: dateMap.get(dateStr) || null, week: weeksSinceStart, day: dayOfWeek, isWeekend }); current = timeDay.offset(current, 1); } return cells; } // assets/src/renderers/year.ts var YearModeRenderer = class { mode = "year"; tooltip = null; render(ctx) { ctx.container.innerHTML = ""; if (!ctx.data.days || ctx.data.days.length === 0) { const msg = document.createElement("div"); msg.textContent = ctx.emptyMessage || "No tracking data available"; msg.style.padding = "1rem"; msg.style.color = "var(--tblr-secondary, #6c757d)"; ctx.container.appendChild(msg); return; } this.destroy(); this.tooltip = createTooltip(); const weekStart = ctx.state.weekStart; const dateMap = buildDateMap(ctx.data.days); const begin = new Date(ctx.data.range.begin); const end = new Date(ctx.data.range.end); const cells = generateCells(begin, end, dateMap, weekStart); const colorScale = buildColorScale(ctx.data.days, ctx.state.metric); const { cellGap, marginTop, marginLeft, marginBottom } = ctx.config; const numWeeks = (max(cells, (c) => c.week) ?? 0) + 1; const containerWidth = ctx.container.clientWidth || 800; const maxCellSize = 22; const minCellSize = 10; const cellSize = Math.min(maxCellSize, Math.max(minCellSize, Math.floor((containerWidth - marginLeft) / numWeeks) - cellGap)); const step = cellSize + cellGap; const svgWidth = marginLeft + numWeeks * step; const svgHeight = marginTop + 7 * step + marginBottom; const wrapper = document.createElement("div"); wrapper.style.maxWidth = `${svgWidth}px`; wrapper.style.margin = "0 auto"; ctx.container.appendChild(wrapper); const svg = select_default2(wrapper).append("svg").attr("width", svgWidth).attr("height", svgHeight).attr("class", "heatmap-svg"); const weekInterval = getWeekInterval(weekStart); const months = []; const firstWeekDay = weekInterval.floor(begin); timeMonth.range(timeMonth.ceil(begin), end).forEach((m) => { months.push({ date: m, week: weekInterval.count(firstWeekDay, m) }); }); svg.selectAll(".month-label").data(months).join("text").attr("class", "heatmap-label month-label").attr("x", (d) => marginLeft + d.week * step).attr("y", marginTop - 6).text((d) => MONTH_FORMAT(d.date)); svg.selectAll(".day-label").data(getDayLabels(weekStart)).join("text").attr("class", "heatmap-label day-label").attr("x", marginLeft - 6).attr("y", (_d, i) => marginTop + i * step + cellSize - 2).attr("text-anchor", "end").text((d) => d); const tooltip = this.tooltip; svg.selectAll(".heatmap-cell").data(cells).join("rect").attr("class", (d) => { let cls = "heatmap-cell"; if (!d.entry) cls += " heatmap-empty"; if (d.isWeekend) cls += " heatmap-weekend"; return cls; }).attr("x", (d) => marginLeft + d.week * step).attr("y", (d) => marginTop + d.day * step).attr("width", cellSize).attr("height", cellSize).attr("fill", (d) => { if (!d.entry) return ""; const val = ctx.state.metric === "hours" ? d.entry.hours : d.entry.count; return colorScale(val); }).on("mouseenter", function(event, d) { const hours = d.entry ? d.entry.hours.toFixed(1) : "0.0"; const count = d.entry ? d.entry.count : 0; const html = `${DISPLAY_FORMAT(d.date)}
${hours}h (${count} entries)`; const rect = event.target.getBoundingClientRect(); showTooltip(tooltip, html, rect, cellSize); }).on("mouseleave", function() { hideTooltip(tooltip); }).on("click", function(_event, d) { if (!ctx.onCellClick) return; ctx.onCellClick(d.dateStr); }); } destroy() { this.tooltip?.remove(); this.tooltip = null; } }; // assets/src/shared/stats.ts var DATE_FORMAT2 = timeFormat("%Y-%m-%d"); var DISPLAY_FORMAT2 = timeFormat("%a, %b %-d, %Y"); function calculateStreak(days) { if (days.length === 0) return 0; const tracked = new Set( days.filter((d) => d.hours > 0).map((d) => d.date) ); if (tracked.size === 0) return 0; const today = /* @__PURE__ */ new Date(); today.setHours(0, 0, 0, 0); let current = new Date(today); if (!tracked.has(DATE_FORMAT2(current))) { current = timeDay.offset(current, -1); } let streak = 0; while (tracked.has(DATE_FORMAT2(current))) { streak++; current = timeDay.offset(current, -1); } return streak; } function calculateStats(days) { const withEntries = days.filter((d) => d.hours > 0); if (withEntries.length === 0) { return { totalHours: 0, avgHours: 0, busiestDay: null }; } const totalHours = Math.round(withEntries.reduce((sum, d) => sum + d.hours, 0) * 10) / 10; const avgHours = Math.round(totalHours / withEntries.length * 10) / 10; const busiest = withEntries.reduce((best, d) => d.hours > best.hours ? d : best); return { totalHours, avgHours, busiestDay: { date: busiest.date, hours: busiest.hours } }; } function renderStats(container, days) { const existing = container.querySelector(".heatmap-stats"); if (existing) existing.remove(); const streak = calculateStreak(days); const stats = calculateStats(days); const statsDiv = document.createElement("div"); statsDiv.className = "heatmap-stats"; const parts = []; parts.push(`\u{1F525} ${streak} days`); parts.push(`Total: ${stats.totalHours}h`); parts.push(`Avg: ${stats.avgHours}h/day`); if (stats.busiestDay) { const d = /* @__PURE__ */ new Date(stats.busiestDay.date + "T00:00:00"); const label = DISPLAY_FORMAT2(d); parts.push(`Busiest: ${label} \u2014 ${stats.busiestDay.hours.toFixed(1)}h`); } statsDiv.innerHTML = parts.join(""); container.appendChild(statsDiv); } // assets/src/heatmap.ts registerRenderer(new YearModeRenderer()); function init(container) { const baseUrl = container.getAttribute("data-url"); if (!baseUrl) { console.error("KimaiHeatmap: missing data-url attribute"); return; } const timesheetUrl = container.getAttribute("data-timesheet-url") || "/en/timesheet/"; const weekStart = container.getAttribute("data-week-start") || "monday"; const projectsJson = container.getAttribute("data-projects"); const projects = projectsJson ? JSON.parse(projectsJson) : []; const state = createInitialState(weekStart); const onCellClick = (dateStr) => { const daterange = `${dateStr} - ${dateStr}`; let url = `${timesheetUrl}?daterange=${encodeURIComponent(daterange)}`; if (state.filters.projectId) { url += `&projects[]=${state.filters.projectId}`; } window.location.href = url; }; container.innerHTML = ""; const wrapper = document.createElement("div"); wrapper.className = "heatmap-wrapper"; const svgArea = document.createElement("div"); svgArea.className = "heatmap-svg-area"; wrapper.appendChild(svgArea); const doRender = () => { if (!state.data) return; const renderer = getRenderer(state.mode); renderer.destroy?.(); renderer.render({ container: svgArea, data: state.data, state, config: { cellSize: 13, cellGap: 2, marginTop: 20, marginLeft: 30, marginBottom: 4 }, onCellClick, emptyMessage: state.filters.projectId ? "No tracking data for this project" : void 0 }); renderStats(container, state.data.days); svgArea.scrollLeft = svgArea.scrollWidth; }; if (projects.length > 0) { const filterDiv = document.createElement("div"); filterDiv.className = "heatmap-filter"; const selectEl = document.createElement("select"); selectEl.className = "form-select form-select-sm"; selectEl.setAttribute("aria-label", "Filter by project"); const defaultOpt = document.createElement("option"); defaultOpt.value = ""; defaultOpt.textContent = "All Projects"; selectEl.appendChild(defaultOpt); for (const p of projects) { const opt = document.createElement("option"); opt.value = String(p.id); opt.textContent = p.name; selectEl.appendChild(opt); } selectEl.addEventListener("change", () => { const val = selectEl.value; state.filters.projectId = val ? parseInt(val, 10) : null; const fetchUrl = val ? `${baseUrl}?project=${val}` : baseUrl; fetch(fetchUrl).then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }).then((data) => { state.data = data; doRender(); }).catch((err) => { console.error("KimaiHeatmap: failed to load filtered data", err); }); }); filterDiv.appendChild(selectEl); wrapper.appendChild(filterDiv); } container.appendChild(wrapper); let resizeTimer; window.addEventListener("resize", () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { if (state.data) doRender(); }, 200); }); fetch(baseUrl).then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }).then((data) => { state.data = data; doRender(); }).catch((err) => { console.error("KimaiHeatmap: failed to load data", err); svgArea.textContent = "Failed to load heatmap data"; }); } return __toCommonJS(heatmap_exports); })(); //# sourceMappingURL=heatmap.js.map