motimeline 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,6 +19,7 @@ Responsive two-column timeline layout library — plain JavaScript, zero depende
19
19
  - **Badges & arrows** — numbered badges on the center line, directional arrows
20
20
  - **Optional theme** — built-in card theme with image banners and overlapping avatars
21
21
  - **CSS custom properties** — override colors and sizes with one line of CSS
22
+ - **Category filtering** — tag items with `categories` and call `filterByCategory()` to show/hide by category; active filter persists automatically through lazy-loaded batches
22
23
  - **Dynamic items** — append, insert, or inject `<li>` elements at any time via `initNewItems()`, `addItems()`, or `insertItem()`
23
24
  - **Custom card renderer** — pass `renderCard(item, cardEl)` to inject any HTML, vanilla JS, or full React components into each card slot; the library handles everything else
24
25
  - **Publisher-ready ad slots** — the most publisher-friendly timeline on npm: `adSlots` injects viewport-triggered `<li>` placeholders at configurable cadences (`every_n` or `random`), fires `onEnterViewport` exactly once per slot at ≥ 50% visibility, works seamlessly with infinite scroll, and cleans up on `destroy()`. Drop in AdSense, house ads, or any network with three lines of code.
@@ -157,6 +158,7 @@ The library injects classes and elements into your markup. Here is what a fully
157
158
  | Attribute | Element | Description |
158
159
  |---|---|---|
159
160
  | `data-mo-icon` | `<li>` | URL of the image shown inside the badge when `showCounterStyle: 'image'`. Accepts any web-safe format including inline SVG data URIs. Falls back to a built-in SVG icon if absent. Also set automatically by `addItems()` when an `icon` field is provided. |
161
+ | `data-categories` | `<li>` | Space-separated list of category tokens this item belongs to (e.g. `"development architecture"`). Used by `filterByCategory()`. Set automatically by `addItems()` / `insertItem()` when a `categories` field is provided. Can also be set manually in HTML. |
160
162
 
161
163
  ---
162
164
 
@@ -180,6 +182,7 @@ The library injects classes and elements into your markup. Here is what a fully
180
182
  | `mo-card-body` | `<div>` | Text content area. Padding and typography when `mo-theme` is active. |
181
183
  | `mo-meta` | `<p>` | Date / subtitle line inside a card body. Muted colour, smaller font. |
182
184
  | `js-mo-item` · `js-mo-inverted` | `<li>` | JS-only selector mirrors of `mo-item` / `mo-inverted`. Use in your own JS queries to avoid coupling to styling class names. |
185
+ | `mo-filtered-out` | `<li>` | Added by `filterByCategory()` to items that do not match the active filter. Sets `display: none` and excludes the item from column-placement calculations. Removed when the filter is cleared or the item's category is selected. |
183
186
 
184
187
  ---
185
188
 
@@ -188,12 +191,13 @@ The library injects classes and elements into your markup. Here is what a fully
188
191
  ```js
189
192
  const tl = new MoTimeline(elementOrSelector, options);
190
193
 
191
- tl.refresh(); // re-layout all items (called automatically on resize)
192
- tl.initNewItems(); // pick up manually appended <li> elements
193
- tl.addItems(items); // create and append <li> from an array of item objects (or JSON string)
194
- tl.insertItem(item, index); // insert a single item at a specific index (or random if omitted)
195
- tl.clear(); // remove all items and ad slots, reset counters instance stays alive
196
- tl.destroy(); // remove listeners and reset DOM classes
194
+ tl.refresh(); // re-layout all items (called automatically on resize)
195
+ tl.initNewItems(); // pick up manually appended <li> elements
196
+ tl.addItems(items); // create and append <li> from an array of item objects (or JSON string)
197
+ tl.insertItem(item, index); // insert a single item at a specific index (or random if omitted)
198
+ tl.filterByCategory(category); // show only items matching category; null / 'all' shows everything
199
+ tl.clear(); // remove all items and ad slots, reset counters — instance stays alive
200
+ tl.destroy(); // remove listeners and reset DOM classes
197
201
  ```
198
202
 
199
203
  ### insertItem
@@ -221,7 +225,8 @@ tl.addItems([
221
225
  text: "Kicked off the roadmap.", // body paragraph
222
226
  banner: "images/banner.jpg", // img.mo-banner (optional)
223
227
  avatar: "images/avatar.jpg", // img.mo-avatar (optional)
224
- icon: "images/icon.svg" // data-mo-icon on <li>, used by showCounterStyle:'image'
228
+ icon: "images/icon.svg", // data-mo-icon on <li>, used by showCounterStyle:'image'
229
+ categories: ["development", "architecture"] // used by filterByCategory() — array or space-separated string
225
230
  },
226
231
  ]);
227
232
 
@@ -376,6 +381,41 @@ const tl = new MoTimeline('#my-timeline', {
376
381
 
377
382
  Slots are injected after each `addItems()` call, so they work seamlessly with infinite scroll.
378
383
 
384
+ ## Category filter recipe
385
+
386
+ Tag items with `categories` (array or space-separated string) and wire `filterByCategory()` to your own filter buttons. The active filter is stored on the instance — items added later via `addItems()` are filtered automatically, making it fully compatible with infinite scroll.
387
+
388
+ ```js
389
+ const tl = new MoTimeline('#my-timeline', { theme: true, showBadge: true });
390
+
391
+ // Load items with categories
392
+ tl.addItems([
393
+ { title: 'API cleanup', meta: 'Jan 2025', text: '...', categories: ['development', 'architecture'] },
394
+ { title: 'Sprint review', meta: 'Feb 2025', text: '...', categories: 'management' },
395
+ ]);
396
+
397
+ // Wire filter buttons
398
+ document.querySelectorAll('.filters button').forEach(btn => {
399
+ btn.addEventListener('click', () => {
400
+ document.querySelectorAll('.filters button').forEach(b => b.classList.remove('active'));
401
+ btn.classList.add('active');
402
+ tl.filterByCategory(btn.dataset.cat); // pass null or 'all' to show everything
403
+ });
404
+ });
405
+
406
+ // Active filter persists through lazy-loaded batches
407
+ tl.addItems(moreItemsFromServer); // auto-filtered to match current selection
408
+ ```
409
+
410
+ Or set `data-categories` directly in HTML and use `initNewItems()`:
411
+
412
+ ```html
413
+ <li data-categories="development architecture">...</li>
414
+ <li data-categories="management">...</li>
415
+ ```
416
+
417
+ ---
418
+
379
419
  ## Infinite scroll recipe
380
420
 
381
421
  moTimeline handles the layout — you own the data fetching. Wire an `IntersectionObserver` to a sentinel element below the list and call `addItems()` when it comes into view.
@@ -468,6 +508,12 @@ No framework option needed. Wrap the `<ul>` inside a Bootstrap `.container`:
468
508
 
469
509
  ## Changelog
470
510
 
511
+ ### v2.13.0
512
+ - New: **category filtering** — tag items with `categories` (array or space-separated string) and call `filterByCategory(category)` to show only matching items. Pass `null` or `'all'` to clear the filter. The active filter is stored on the instance and applied automatically to items added via `addItems()` or `initNewItems()`, making it fully compatible with infinite scroll / server-side pagination. Column placement recalculates on every filter change so the two-column layout stays correct. New class `mo-filtered-out` is used internally (`display: none`) and new attribute `data-categories` is set on each `<li>`.
513
+
514
+ ### v2.12.1
515
+ - Fix: badge width — use `width` instead of `min-width` to prevent oversized badges ([#8](https://github.com/MattOpen/moTimeline/issues/8))
516
+
471
517
  ### v2.12.0
472
518
  - New method `clear()` — removes all `.mo-item` and `.mo-ad-slot` elements from the container and resets internal counters (`lastItemIdx`, `_adRealCount`) without destroying the instance. Active `IntersectionObserver`s are disconnected but kept alive so they re-observe items added by the next `addItems()` call. Use this in React wrappers to reinitialize timeline content when props change without recreating the instance.
473
519
 
@@ -1,6 +1,6 @@
1
- "use strict";var C=Object.defineProperty;var S=(l,e,t)=>e in l?C(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var _=(l,e,t)=>S(l,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});/*!
2
- * moTimeline v2.12.0
1
+ "use strict";var A=Object.defineProperty;var S=(l,e,t)=>e in l?A(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var _=(l,e,t)=>S(l,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});/*!
2
+ * moTimeline v2.13.0
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const h=new WeakMap,b={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0,animate:!1,renderCard:null,adSlots:null},A="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function w(){const l=window.innerWidth;return l<600?"xs":l<992?"sm":l<1200?"md":"lg"}function I(l,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>l(...s),e)}}function v(l){return l?{o:l.offsetTop,h:l.offsetHeight,gppu:l.offsetTop+l.offsetHeight}:{o:0,h:0,gppu:0}}function y(l,e){const t=[];let s=l.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const f=class f{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},b,t),this.settings.columnCount=Object.assign({},b.columnCount,t.columnCount),this._resizeHandler=I(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(h.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});if(h.set(e,t),f.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),t.animate){const s=t.animate===!0?"fade":t.animate;e.classList.add("mo-animate",`mo-animate-${s}`),this._observer=new IntersectionObserver(o=>{o.forEach(r=>{r.isIntersecting&&(r.target.classList.add("mo-visible"),this._observer.unobserve(r.target))})},{threshold:.1})}t.adSlots&&(t._adRealCount=0,typeof t.adSlots.onEnterViewport=="function"&&(this._adObserver=new IntersectionObserver(s=>{s.forEach(o=>{o.isIntersecting&&(this._adObserver.unobserve(o.target),t.adSlots.onEnterViewport(o.target,Number(o.target.dataset.moAdPosition)))})},{threshold:.5}))),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){f.instances.forEach(e=>{const t=e.element,s=h.get(t);s&&(s.col=s.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(o=>{e._setPostPosition(o)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}insertItem(e,t){const s=this.element,o=this._getData();if(!o)return;const r=this._createItemElement(e),i=Array.from(s.children).filter(n=>n.classList.contains("js-mo-item")),a=t==null?Math.floor(Math.random()*(i.length+1)):Math.max(0,Math.min(t,i.length));if(a>=i.length?s.appendChild(r):s.insertBefore(r,i[a]),r.id||(r.id="moT"+crypto.randomUUID()+"_"+a),r.classList.add("mo-item","js-mo-item"),e.fullWidth)r.classList.add("mo-fullwidth","js-mo-fullwidth");else if(o.randomFullWidth){const n=o.randomFullWidth===!0?.33:o.randomFullWidth;Math.random()<n&&r.classList.add("mo-fullwidth","js-mo-fullwidth")}return o.showBadge&&this._createBadge(r,a+1),o.showArrow&&this._createArrow(r),o.showBadge&&o.showCounterStyle==="counter"&&Array.from(s.querySelectorAll(".js-mo-item")).forEach((n,c)=>{const d=n.querySelector(".js-mo-badge");d&&(d.textContent=c+1)}),o.lastItemIdx=Array.from(s.children).length,h.set(s,o),this.refresh(),this._observeItems([r]),r.querySelectorAll("img").forEach(n=>{n.complete||n.addEventListener("load",this._resizeHandler,{once:!0})}),r}clear(){const e=this._getData();e&&(this._observer&&this._observer.disconnect(),this._adObserver&&this._adObserver.disconnect(),Array.from(this.element.children).forEach(t=>{(t.classList.contains("js-mo-item")||t.classList.contains("mo-ad-slot"))&&t.remove()}),e.lastItemIdx=0,e.adSlots&&(e._adRealCount=0))}destroy(){window.removeEventListener("resize",this._resizeHandler),this._observer&&(this._observer.disconnect(),this._observer=null),this._adObserver&&(this._adObserver.disconnect(),this._adObserver=null),Array.from(this.element.querySelectorAll(".mo-ad-slot")).forEach(e=>e.remove()),h.delete(this.element),f.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol","mo-animate","mo-animate-fade","mo-animate-slide"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset","mo-fullwidth","js-mo-fullwidth","mo-visible"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return h.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[w()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,o=Array.from(e.children),r=o.slice(s);if(r.length!==0){if(r.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+s)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const i=t.randomFullWidth===!0?.33:t.randomFullWidth;r.forEach(a=>{!a.classList.contains("mo-fullwidth")&&Math.random()<i&&a.classList.add("mo-fullwidth","js-mo-fullwidth")})}r.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+s+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=o.length,h.set(e,t),this.refresh(),this._observeItems(r),this._injectAdSlots(r),r.forEach(i=>{i.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const s=t.col,o=y(e,".js-mo-inverted")[0]||null,r=y(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=v(r),a=v(o),n=v(e);let c=0,d=0;if(s>1){i.gppu>n.o+1&&(c=1),a.gppu>i.gppu&&(c=0);const m=e.previousElementSibling;m&&Math.abs(n.o-v(m).o)<40&&(d=1)}return{lr:c,badge_offset:d}}_createBadge(e,t){const s=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",s.showCounterStyle==="none")o.style.opacity="0";else if(s.showCounterStyle==="image"){const r=document.createElement("img");r.className="mo-badge-icon",r.alt="",r.src=e.dataset.moIcon||A,o.appendChild(r)}else o.textContent=t;e.prepend(o)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const s=document.createElement("div");s.className="mo-card";const o=this._getData();if(o&&typeof o.renderCard=="function")o.renderCard(e,s);else{if(e.banner){const i=document.createElement("div");i.className="mo-card-image";const a=document.createElement("img");if(a.className="mo-banner",a.src=e.banner,a.alt="",i.appendChild(a),e.avatar){const n=document.createElement("img");n.className="mo-avatar",n.src=e.avatar,n.alt="",i.appendChild(n)}s.appendChild(i)}const r=document.createElement("div");if(r.className="mo-card-body",e.title){const i=document.createElement("h3");i.textContent=e.title,r.appendChild(i)}if(e.meta){const i=document.createElement("p");i.className="mo-meta",i.textContent=e.meta,r.appendChild(i)}if(e.text){const i=document.createElement("p");i.textContent=e.text,r.appendChild(i)}s.appendChild(r)}return t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}_injectAdSlots(e){const t=this._getData();if(!t||!t.adSlots||!e.length)return;const{mode:s,interval:o,style:r}=t.adSlots,i=r==="fullwidth",a=this.element;let n=!1;if(s==="every_n")e.forEach((c,d)=>{if((t._adRealCount+d+1)%o===0){const m=this._createAdSlot(i);c.after(m),m.dataset.moAdPosition=String(Array.from(a.children).indexOf(m)),this._adObserver&&this._adObserver.observe(m),i&&(n=!0)}});else if(s==="random"){let c=t._adRealCount%o,d=0;for(;d<e.length;){const m=o-c,u=e.slice(d,d+m);if(u.length===m){const E=u[Math.floor(Math.random()*u.length)],g=this._createAdSlot(i);E.after(g),g.dataset.moAdPosition=String(Array.from(a.children).indexOf(g)),this._adObserver&&this._adObserver.observe(g),i&&(n=!0),c=0}else c+=u.length;d+=m}}t._adRealCount+=e.length,n&&this.refresh()}_createAdSlot(e){const t=document.createElement("li");return t.className="mo-ad-slot",e&&t.classList.add("mo-fullwidth"),t}_observeItems(e){this._observer&&e.forEach(t=>{t.classList.contains("mo-visible")||this._observer.observe(t)})}};_(f,"instances",new Set);let p=f;exports.MoTimeline=p;exports.default=p;
6
+ */const h=new WeakMap,b={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0,animate:!1,renderCard:null,adSlots:null},C="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function y(){const l=window.innerWidth;return l<600?"xs":l<992?"sm":l<1200?"md":"lg"}function L(l,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>l(...s),e)}}function v(l){return l?{o:l.offsetTop,h:l.offsetHeight,gppu:l.offsetTop+l.offsetHeight}:{o:0,h:0,gppu:0}}function w(l,e){const t=[];let s=l.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const f=class f{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},b,t),this.settings.columnCount=Object.assign({},b.columnCount,t.columnCount),this._resizeHandler=L(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(h.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});if(h.set(e,t),f.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),t.animate){const s=t.animate===!0?"fade":t.animate;e.classList.add("mo-animate",`mo-animate-${s}`),this._observer=new IntersectionObserver(r=>{r.forEach(o=>{o.isIntersecting&&(o.target.classList.add("mo-visible"),this._observer.unobserve(o.target))})},{threshold:.1})}t.adSlots&&(t._adRealCount=0,typeof t.adSlots.onEnterViewport=="function"&&(this._adObserver=new IntersectionObserver(s=>{s.forEach(r=>{r.isIntersecting&&(this._adObserver.unobserve(r.target),t.adSlots.onEnterViewport(r.target,Number(r.target.dataset.moAdPosition)))})},{threshold:.5}))),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){f.instances.forEach(e=>{const t=e.element,s=h.get(t);s&&(s.col=s.columnCount[y()],e._setDivider(),Array.from(t.children).forEach(r=>{e._setPostPosition(r)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}insertItem(e,t){const s=this.element,r=this._getData();if(!r)return;const o=this._createItemElement(e),i=Array.from(s.children).filter(n=>n.classList.contains("js-mo-item")),a=t==null?Math.floor(Math.random()*(i.length+1)):Math.max(0,Math.min(t,i.length));if(a>=i.length?s.appendChild(o):s.insertBefore(o,i[a]),o.id||(o.id="moT"+crypto.randomUUID()+"_"+a),o.classList.add("mo-item","js-mo-item"),e.fullWidth)o.classList.add("mo-fullwidth","js-mo-fullwidth");else if(r.randomFullWidth){const n=r.randomFullWidth===!0?.33:r.randomFullWidth;Math.random()<n&&o.classList.add("mo-fullwidth","js-mo-fullwidth")}return r.showBadge&&this._createBadge(o,a+1),r.showArrow&&this._createArrow(o),r.showBadge&&r.showCounterStyle==="counter"&&Array.from(s.querySelectorAll(".js-mo-item")).forEach((n,c)=>{const d=n.querySelector(".js-mo-badge");d&&(d.textContent=c+1)}),r.lastItemIdx=Array.from(s.children).length,h.set(s,r),this.refresh(),this._observeItems([o]),o.querySelectorAll("img").forEach(n=>{n.complete||n.addEventListener("load",this._resizeHandler,{once:!0})}),o}clear(){const e=this._getData();e&&(this._observer&&this._observer.disconnect(),this._adObserver&&this._adObserver.disconnect(),Array.from(this.element.children).forEach(t=>{(t.classList.contains("js-mo-item")||t.classList.contains("mo-ad-slot"))&&t.remove()}),e.lastItemIdx=0,e.adSlots&&(e._adRealCount=0))}destroy(){window.removeEventListener("resize",this._resizeHandler),this._observer&&(this._observer.disconnect(),this._observer=null),this._adObserver&&(this._adObserver.disconnect(),this._adObserver=null),Array.from(this.element.querySelectorAll(".mo-ad-slot")).forEach(e=>e.remove()),h.delete(this.element),f.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol","mo-animate","mo-animate-fade","mo-animate-slide"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset","mo-fullwidth","js-mo-fullwidth","mo-visible","mo-filtered-out"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}filterByCategory(e){const t=this._getData();t&&(t._activeFilter=e==null||e===""||e==="all"?null:String(e),h.set(this.element,t),this._applyFilter(Array.from(this.element.querySelectorAll(".js-mo-item"))),this.refresh())}_applyFilter(e){const t=this._getData();if(!t)return;const s=t._activeFilter||null;e.forEach(r=>{if(!s)r.classList.remove("mo-filtered-out");else{const o=(r.dataset.categories||"").split(/\s+/).filter(Boolean);r.classList.toggle("mo-filtered-out",!o.includes(s))}})}_getData(){return h.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[y()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,r=Array.from(e.children),o=r.slice(s);if(o.length!==0){if(o.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+s)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const i=t.randomFullWidth===!0?.33:t.randomFullWidth;o.forEach(a=>{!a.classList.contains("mo-fullwidth")&&Math.random()<i&&a.classList.add("mo-fullwidth","js-mo-fullwidth")})}o.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+s+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=r.length,h.set(e,t),this._applyFilter(o),this.refresh(),this._observeItems(o),this._injectAdSlots(o),o.forEach(i=>{i.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-filtered-out"))return;if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const s=t.col,r=w(e,".js-mo-inverted:not(.mo-filtered-out)")[0]||null,o=w(e,".js-mo-item:not(.js-mo-inverted):not(.mo-filtered-out)")[0]||null,i=v(o),a=v(r),n=v(e);let c=0,d=0;if(s>1){i.gppu>n.o+1&&(c=1),a.gppu>i.gppu&&(c=0);const m=e.previousElementSibling;m&&Math.abs(n.o-v(m).o)<40&&(d=1)}return{lr:c,badge_offset:d}}_createBadge(e,t){const s=this._getData(),r=document.createElement("span");if(r.className="mo-badge js-mo-badge",s.showCounterStyle==="none")r.style.opacity="0";else if(s.showCounterStyle==="image"){const o=document.createElement("img");o.className="mo-badge-icon",o.alt="",o.src=e.dataset.moIcon||C,r.appendChild(o)}else r.textContent=t;e.prepend(r)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon),e.categories&&(t.dataset.categories=Array.isArray(e.categories)?e.categories.join(" "):String(e.categories));const s=document.createElement("div");s.className="mo-card";const r=this._getData();if(r&&typeof r.renderCard=="function")r.renderCard(e,s);else{if(e.banner){const i=document.createElement("div");i.className="mo-card-image";const a=document.createElement("img");if(a.className="mo-banner",a.src=e.banner,a.alt="",i.appendChild(a),e.avatar){const n=document.createElement("img");n.className="mo-avatar",n.src=e.avatar,n.alt="",i.appendChild(n)}s.appendChild(i)}const o=document.createElement("div");if(o.className="mo-card-body",e.title){const i=document.createElement("h3");i.textContent=e.title,o.appendChild(i)}if(e.meta){const i=document.createElement("p");i.className="mo-meta",i.textContent=e.meta,o.appendChild(i)}if(e.text){const i=document.createElement("p");i.textContent=e.text,o.appendChild(i)}s.appendChild(o)}return t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}_injectAdSlots(e){const t=this._getData();if(!t||!t.adSlots||!e.length)return;const{mode:s,interval:r,style:o}=t.adSlots,i=o==="fullwidth",a=this.element;let n=!1;if(s==="every_n")e.forEach((c,d)=>{if((t._adRealCount+d+1)%r===0){const m=this._createAdSlot(i);c.after(m),m.dataset.moAdPosition=String(Array.from(a.children).indexOf(m)),this._adObserver&&this._adObserver.observe(m),i&&(n=!0)}});else if(s==="random"){let c=t._adRealCount%r,d=0;for(;d<e.length;){const m=r-c,u=e.slice(d,d+m);if(u.length===m){const E=u[Math.floor(Math.random()*u.length)],g=this._createAdSlot(i);E.after(g),g.dataset.moAdPosition=String(Array.from(a.children).indexOf(g)),this._adObserver&&this._adObserver.observe(g),i&&(n=!0),c=0}else c+=u.length;d+=m}}t._adRealCount+=e.length,n&&this.refresh()}_createAdSlot(e){const t=document.createElement("li");return t.className="mo-ad-slot",e&&t.classList.add("mo-fullwidth"),t}_observeItems(e){this._observer&&e.forEach(t=>{t.classList.contains("mo-visible")||this._observer.observe(t)})}};_(f,"instances",new Set);let p=f;exports.MoTimeline=p;exports.default=p;
@@ -1,3 +1,3 @@
1
1
  /*!
2
- * moTimeline v2.12.0 — CSS
3
- */:root{--mo-line-color: #dde1e7;--mo-badge-bg: #4f46e5;--mo-badge-color: #fff;--mo-badge-size: 26px;--mo-badge-font-size: 12px;--mo-arrow-color: #dde1e7}.mo-timeline{display:block;list-style:none;margin:0;padding:0;position:relative;width:100%}.mo-timeline:after{content:"";display:table;clear:both}.mo-timeline.mo-twocol:before{background-color:var(--mo-line-color);bottom:0;content:"";left:50%;margin-left:-1.5px;position:absolute;top:0;width:3px;z-index:0}.mo-timeline>.mo-item{box-sizing:border-box;display:block;float:left;position:relative;width:50%}.mo-timeline:not(.mo-twocol)>.mo-item{float:none;width:100%}.mo-timeline>.mo-item.mo-inverted{float:right}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth{clear:both;float:none;width:100%}.mo-theme>.mo-item.mo-fullwidth .mo-card{margin:var(--mo-card-margin-fullwidth, .5rem)}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-badge,.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-arrow{display:none}.mo-badge{align-items:center;background:var(--mo-badge-bg);border-radius:50%;color:var(--mo-badge-color);display:flex;font-size:var(--mo-badge-font-size);font-weight:700;height:var(--mo-badge-size);justify-content:center;min-width:var(--mo-badge-size);overflow:hidden;position:absolute;top:18px;z-index:2}.mo-badge .mo-badge-icon{border-radius:50%;height:100%;object-fit:cover;width:100%}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-badge{left:auto;right:calc(var(--mo-badge-size) / -2)}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-badge{left:calc(var(--mo-badge-size) / -2);right:auto}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-badge{top:calc(18px + var(--mo-badge-size) + 10px)}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-arrow{top:calc(26px + var(--mo-badge-size) + 10px)}.mo-timeline:not(.mo-twocol)>.mo-item .mo-badge{display:none}.mo-arrow{border:8px solid transparent;display:block;height:0;position:absolute;top:26px;width:0;z-index:1}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left:8px solid var(--mo-arrow-color);border-right:none;left:auto;right:0}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right:8px solid var(--mo-arrow-color);border-left:none;left:0;right:auto}.mo-timeline:not(.mo-twocol)>.mo-item .mo-arrow{display:none}.mo-theme{--mo-arrow-color: #fff}.mo-theme>.mo-item .mo-card{background:#fff;border-radius:var(--mo-card-border-radius, 8px);box-shadow:0 2px 14px #0000001a;margin:var(--mo-card-margin, .5rem 1.25rem .5rem .5rem);position:relative}.mo-theme>.mo-item.mo-inverted .mo-card{margin:var(--mo-card-margin-inverted, .5rem .5rem .5rem 1.25rem)}.mo-theme>.mo-item .mo-banner{border-radius:var(--mo-card-border-radius, 8px) var(--mo-card-border-radius, 8px) 0 0;display:block;max-height:240px;object-fit:cover;width:100%}.mo-theme>.mo-item .mo-card-image{overflow:visible;position:relative}.mo-theme>.mo-item .mo-avatar{border:3px solid #fff;border-radius:50%;bottom:calc(var(--mo-avatar-size, 50px) * -.44);box-shadow:0 2px 8px #0000002e;height:var(--mo-avatar-size, 50px);object-fit:cover;position:absolute;right:14px;width:var(--mo-avatar-size, 50px);z-index:1}.mo-theme>.mo-item .mo-card-body{padding:1.75rem 1rem 1rem}.mo-theme>.mo-item .mo-card-body h3{font-size:1rem;font-weight:700;margin:0 0 .3rem}.mo-theme>.mo-item .mo-card-body .mo-meta{color:#9ca3af;font-size:.75rem;margin-bottom:.5rem}.mo-theme>.mo-item .mo-card-body p{color:#6b7280;font-size:.875rem;line-height:1.55;margin:0}.mo-theme.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left-color:var(--mo-arrow-color);right:12px}.mo-theme.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right-color:var(--mo-arrow-color);left:12px}.mo-theme .mo-badge{background:#fff;border:2px solid var(--mo-line-color);box-shadow:0 2px 6px #0000001a;color:#374151}.mo-timeline.mo-animate>.mo-item{transition:opacity var(--mo-animate-duration, .5s) ease,transform var(--mo-animate-duration, .5s) ease}.mo-timeline.mo-animate-fade>.mo-item{opacity:0}.mo-timeline.mo-animate-slide>.mo-item:not(.mo-inverted){opacity:0;transform:translate(-40px)}.mo-timeline.mo-animate-slide>.mo-item.mo-inverted{opacity:0;transform:translate(40px)}.mo-timeline:not(.mo-twocol).mo-animate-slide>.mo-item{transform:translate(-40px)}.mo-timeline.mo-animate>.mo-item.mo-visible{opacity:1;transform:translate(0)}.mo-timeline>.mo-item.mo-ad-slot{min-height:100px}
2
+ * moTimeline v2.13.0 — CSS
3
+ */:root{--mo-line-color: #dde1e7;--mo-badge-bg: #4f46e5;--mo-badge-color: #fff;--mo-badge-size: 26px;--mo-badge-font-size: 12px;--mo-arrow-color: #dde1e7}.mo-timeline{display:block;list-style:none;margin:0;padding:0;position:relative;width:100%}.mo-timeline:after{content:"";display:table;clear:both}.mo-timeline.mo-twocol:before{background-color:var(--mo-line-color);bottom:0;content:"";left:50%;margin-left:-1.5px;position:absolute;top:0;width:3px;z-index:0}.mo-timeline>.mo-item{box-sizing:border-box;display:block;float:left;position:relative;width:50%}.mo-timeline:not(.mo-twocol)>.mo-item{float:none;width:100%}.mo-timeline>.mo-item.mo-inverted{float:right}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth{clear:both;float:none;width:100%}.mo-theme>.mo-item.mo-fullwidth .mo-card{margin:var(--mo-card-margin-fullwidth, .5rem)}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-badge,.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-arrow{display:none}.mo-badge{align-items:center;background:var(--mo-badge-bg);border-radius:50%;color:var(--mo-badge-color);display:flex;font-size:var(--mo-badge-font-size);font-weight:700;height:var(--mo-badge-size);justify-content:center;width:var(--mo-badge-size);overflow:hidden;position:absolute;top:18px;z-index:2}.mo-badge .mo-badge-icon{border-radius:50%;height:100%;object-fit:cover;width:100%}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-badge{left:auto;right:calc(var(--mo-badge-size) / -2)}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-badge{left:calc(var(--mo-badge-size) / -2);right:auto}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-badge{top:calc(18px + var(--mo-badge-size) + 10px)}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-arrow{top:calc(26px + var(--mo-badge-size) + 10px)}.mo-timeline:not(.mo-twocol)>.mo-item .mo-badge{display:none}.mo-arrow{border:8px solid transparent;display:block;height:0;position:absolute;top:26px;width:0;z-index:1}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left:8px solid var(--mo-arrow-color);border-right:none;left:auto;right:0}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right:8px solid var(--mo-arrow-color);border-left:none;left:0;right:auto}.mo-timeline:not(.mo-twocol)>.mo-item .mo-arrow{display:none}.mo-theme{--mo-arrow-color: #fff}.mo-theme>.mo-item .mo-card{background:#fff;border-radius:var(--mo-card-border-radius, 8px);box-shadow:0 2px 14px #0000001a;margin:var(--mo-card-margin, .5rem 1.25rem .5rem .5rem);position:relative}.mo-theme>.mo-item.mo-inverted .mo-card{margin:var(--mo-card-margin-inverted, .5rem .5rem .5rem 1.25rem)}.mo-theme>.mo-item .mo-banner{border-radius:var(--mo-card-border-radius, 8px) var(--mo-card-border-radius, 8px) 0 0;display:block;max-height:240px;object-fit:cover;width:100%}.mo-theme>.mo-item .mo-card-image{overflow:visible;position:relative}.mo-theme>.mo-item .mo-avatar{border:3px solid #fff;border-radius:50%;bottom:calc(var(--mo-avatar-size, 50px) * -.44);box-shadow:0 2px 8px #0000002e;height:var(--mo-avatar-size, 50px);object-fit:cover;position:absolute;right:14px;width:var(--mo-avatar-size, 50px);z-index:1}.mo-theme>.mo-item .mo-card-body{padding:1.75rem 1rem 1rem}.mo-theme>.mo-item .mo-card-body h3{font-size:1rem;font-weight:700;margin:0 0 .3rem}.mo-theme>.mo-item .mo-card-body .mo-meta{color:#9ca3af;font-size:.75rem;margin-bottom:.5rem}.mo-theme>.mo-item .mo-card-body p{color:#6b7280;font-size:.875rem;line-height:1.55;margin:0}.mo-theme.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left-color:var(--mo-arrow-color);right:12px}.mo-theme.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right-color:var(--mo-arrow-color);left:12px}.mo-theme .mo-badge{background:#fff;border:2px solid var(--mo-line-color);box-shadow:0 2px 6px #0000001a;color:#374151}.mo-timeline.mo-animate>.mo-item{transition:opacity var(--mo-animate-duration, .5s) ease,transform var(--mo-animate-duration, .5s) ease}.mo-timeline.mo-animate-fade>.mo-item{opacity:0}.mo-timeline.mo-animate-slide>.mo-item:not(.mo-inverted){opacity:0;transform:translate(-40px)}.mo-timeline.mo-animate-slide>.mo-item.mo-inverted{opacity:0;transform:translate(40px)}.mo-timeline:not(.mo-twocol).mo-animate-slide>.mo-item{transform:translate(-40px)}.mo-timeline.mo-animate>.mo-item.mo-visible{opacity:1;transform:translate(0)}.mo-timeline>.mo-item.mo-ad-slot{min-height:100px}.mo-timeline>.mo-item.mo-filtered-out{display:none}
@@ -1,8 +1,8 @@
1
- var C = Object.defineProperty;
2
- var A = (l, e, t) => e in l ? C(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t;
3
- var p = (l, e, t) => A(l, typeof e != "symbol" ? e + "" : e, t);
1
+ var A = Object.defineProperty;
2
+ var C = (l, e, t) => e in l ? A(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t;
3
+ var v = (l, e, t) => C(l, typeof e != "symbol" ? e + "" : e, t);
4
4
  /*!
5
- * moTimeline v2.12.0
5
+ * moTimeline v2.13.0
6
6
  * Responsive two-column timeline layout library
7
7
  * https://github.com/MattOpen/moTimeline
8
8
  * MIT License
@@ -27,18 +27,18 @@ const h = /* @__PURE__ */ new WeakMap(), _ = {
27
27
  // (item, cardEl) => void — custom card renderer; skips built-in HTML
28
28
  adSlots: null
29
29
  // { mode, interval, style, onEnterViewport } — see docs
30
- }, I = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";
30
+ }, S = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";
31
31
  function b() {
32
32
  const l = window.innerWidth;
33
33
  return l < 600 ? "xs" : l < 992 ? "sm" : l < 1200 ? "md" : "lg";
34
34
  }
35
- function S(l, e = 100) {
35
+ function L(l, e = 100) {
36
36
  let t;
37
37
  return (...s) => {
38
38
  clearTimeout(t), t = setTimeout(() => l(...s), e);
39
39
  };
40
40
  }
41
- function v(l) {
41
+ function p(l) {
42
42
  return l ? {
43
43
  o: l.offsetTop,
44
44
  h: l.offsetHeight,
@@ -55,7 +55,7 @@ function w(l, e) {
55
55
  const f = class f {
56
56
  constructor(e, t = {}) {
57
57
  if (typeof e == "string" && (e = document.querySelector(e)), !e) throw new Error("moTimeline: element not found");
58
- this.element = e, this.settings = Object.assign({}, _, t), this.settings.columnCount = Object.assign({}, _.columnCount, t.columnCount), this._resizeHandler = S(() => this.refresh(), 100), this._initialized = !1, this.init();
58
+ this.element = e, this.settings = Object.assign({}, _, t), this.settings.columnCount = Object.assign({}, _.columnCount, t.columnCount), this._resizeHandler = L(() => this.refresh(), 100), this._initialized = !1, this.init();
59
59
  }
60
60
  init() {
61
61
  const e = this.element;
@@ -66,23 +66,23 @@ const f = class f {
66
66
  const t = Object.assign({}, this.settings, { lastItemIdx: 0 });
67
67
  if (h.set(e, t), f.instances.add(this), e.classList.add("mo-timeline"), t.theme && e.classList.add("mo-theme"), e.style.setProperty("--mo-card-border-radius", t.cardBorderRadius), e.style.setProperty("--mo-avatar-size", t.avatarSize), e.style.setProperty("--mo-card-margin", t.cardMargin), e.style.setProperty("--mo-card-margin-inverted", t.cardMarginInverted), e.style.setProperty("--mo-card-margin-fullwidth", t.cardMarginFullWidth), t.animate) {
68
68
  const s = t.animate === !0 ? "fade" : t.animate;
69
- e.classList.add("mo-animate", `mo-animate-${s}`), this._observer = new IntersectionObserver((o) => {
70
- o.forEach((r) => {
71
- r.isIntersecting && (r.target.classList.add("mo-visible"), this._observer.unobserve(r.target));
69
+ e.classList.add("mo-animate", `mo-animate-${s}`), this._observer = new IntersectionObserver((r) => {
70
+ r.forEach((o) => {
71
+ o.isIntersecting && (o.target.classList.add("mo-visible"), this._observer.unobserve(o.target));
72
72
  });
73
73
  }, { threshold: 0.1 });
74
74
  }
75
75
  t.adSlots && (t._adRealCount = 0, typeof t.adSlots.onEnterViewport == "function" && (this._adObserver = new IntersectionObserver((s) => {
76
- s.forEach((o) => {
77
- o.isIntersecting && (this._adObserver.unobserve(o.target), t.adSlots.onEnterViewport(o.target, Number(o.target.dataset.moAdPosition)));
76
+ s.forEach((r) => {
77
+ r.isIntersecting && (this._adObserver.unobserve(r.target), t.adSlots.onEnterViewport(r.target, Number(r.target.dataset.moAdPosition)));
78
78
  });
79
79
  }, { threshold: 0.5 }))), this._initialized = !0, window.addEventListener("resize", this._resizeHandler), Array.from(e.children).length > 0 && this._initItems();
80
80
  }
81
81
  refresh() {
82
82
  f.instances.forEach((e) => {
83
83
  const t = e.element, s = h.get(t);
84
- s && (s.col = s.columnCount[b()], e._setDivider(), Array.from(t.children).forEach((o) => {
85
- e._setPostPosition(o);
84
+ s && (s.col = s.columnCount[b()], e._setDivider(), Array.from(t.children).forEach((r) => {
85
+ e._setPostPosition(r);
86
86
  }));
87
87
  });
88
88
  }
@@ -109,21 +109,21 @@ const f = class f {
109
109
  * @returns {HTMLElement} the inserted <li> element
110
110
  */
111
111
  insertItem(e, t) {
112
- const s = this.element, o = this._getData();
113
- if (!o) return;
114
- const r = this._createItemElement(e), i = Array.from(s.children).filter((n) => n.classList.contains("js-mo-item")), a = t == null ? Math.floor(Math.random() * (i.length + 1)) : Math.max(0, Math.min(t, i.length));
115
- if (a >= i.length ? s.appendChild(r) : s.insertBefore(r, i[a]), r.id || (r.id = "moT" + crypto.randomUUID() + "_" + a), r.classList.add("mo-item", "js-mo-item"), e.fullWidth)
116
- r.classList.add("mo-fullwidth", "js-mo-fullwidth");
117
- else if (o.randomFullWidth) {
118
- const n = o.randomFullWidth === !0 ? 0.33 : o.randomFullWidth;
119
- Math.random() < n && r.classList.add("mo-fullwidth", "js-mo-fullwidth");
112
+ const s = this.element, r = this._getData();
113
+ if (!r) return;
114
+ const o = this._createItemElement(e), i = Array.from(s.children).filter((n) => n.classList.contains("js-mo-item")), a = t == null ? Math.floor(Math.random() * (i.length + 1)) : Math.max(0, Math.min(t, i.length));
115
+ if (a >= i.length ? s.appendChild(o) : s.insertBefore(o, i[a]), o.id || (o.id = "moT" + crypto.randomUUID() + "_" + a), o.classList.add("mo-item", "js-mo-item"), e.fullWidth)
116
+ o.classList.add("mo-fullwidth", "js-mo-fullwidth");
117
+ else if (r.randomFullWidth) {
118
+ const n = r.randomFullWidth === !0 ? 0.33 : r.randomFullWidth;
119
+ Math.random() < n && o.classList.add("mo-fullwidth", "js-mo-fullwidth");
120
120
  }
121
- return o.showBadge && this._createBadge(r, a + 1), o.showArrow && this._createArrow(r), o.showBadge && o.showCounterStyle === "counter" && Array.from(s.querySelectorAll(".js-mo-item")).forEach((n, c) => {
121
+ return r.showBadge && this._createBadge(o, a + 1), r.showArrow && this._createArrow(o), r.showBadge && r.showCounterStyle === "counter" && Array.from(s.querySelectorAll(".js-mo-item")).forEach((n, c) => {
122
122
  const d = n.querySelector(".js-mo-badge");
123
123
  d && (d.textContent = c + 1);
124
- }), o.lastItemIdx = Array.from(s.children).length, h.set(s, o), this.refresh(), this._observeItems([r]), r.querySelectorAll("img").forEach((n) => {
124
+ }), r.lastItemIdx = Array.from(s.children).length, h.set(s, r), this.refresh(), this._observeItems([o]), o.querySelectorAll("img").forEach((n) => {
125
125
  n.complete || n.addEventListener("load", this._resizeHandler, { once: !0 });
126
- }), r;
126
+ }), o;
127
127
  }
128
128
  clear() {
129
129
  const e = this._getData();
@@ -133,10 +133,34 @@ const f = class f {
133
133
  }
134
134
  destroy() {
135
135
  window.removeEventListener("resize", this._resizeHandler), this._observer && (this._observer.disconnect(), this._observer = null), this._adObserver && (this._adObserver.disconnect(), this._adObserver = null), Array.from(this.element.querySelectorAll(".mo-ad-slot")).forEach((e) => e.remove()), h.delete(this.element), f.instances.delete(this), this.element.style.removeProperty("--mo-card-border-radius"), this.element.style.removeProperty("--mo-avatar-size"), this.element.style.removeProperty("--mo-card-margin"), this.element.style.removeProperty("--mo-card-margin-inverted"), this.element.style.removeProperty("--mo-card-margin-fullwidth"), this.element.classList.remove("mo-timeline", "mo-theme", "mo-twocol", "mo-animate", "mo-animate-fade", "mo-animate-slide"), Array.from(this.element.children).forEach((e) => {
136
- e.classList.remove("mo-item", "js-mo-item", "mo-inverted", "js-mo-inverted", "mo-offset", "mo-fullwidth", "js-mo-fullwidth", "mo-visible"), e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach((t) => t.remove());
136
+ e.classList.remove("mo-item", "js-mo-item", "mo-inverted", "js-mo-inverted", "mo-offset", "mo-fullwidth", "js-mo-fullwidth", "mo-visible", "mo-filtered-out"), e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach((t) => t.remove());
137
137
  });
138
138
  }
139
+ /**
140
+ * Filter visible items by category.
141
+ * Pass null / 'all' / '' to show everything.
142
+ * The active filter is remembered and applied automatically to items added later.
143
+ *
144
+ * @param {string|null} category — value to match against data-categories on each item
145
+ */
146
+ filterByCategory(e) {
147
+ const t = this._getData();
148
+ t && (t._activeFilter = e == null || e === "" || e === "all" ? null : String(e), h.set(this.element, t), this._applyFilter(Array.from(this.element.querySelectorAll(".js-mo-item"))), this.refresh());
149
+ }
139
150
  // ─── Private ────────────────────────────────────────────────────────────────
151
+ _applyFilter(e) {
152
+ const t = this._getData();
153
+ if (!t) return;
154
+ const s = t._activeFilter || null;
155
+ e.forEach((r) => {
156
+ if (!s)
157
+ r.classList.remove("mo-filtered-out");
158
+ else {
159
+ const o = (r.dataset.categories || "").split(/\s+/).filter(Boolean);
160
+ r.classList.toggle("mo-filtered-out", !o.includes(s));
161
+ }
162
+ });
163
+ }
140
164
  _getData() {
141
165
  return h.get(this.element);
142
166
  }
@@ -147,19 +171,19 @@ const f = class f {
147
171
  _initItems() {
148
172
  const e = this.element, t = this._getData();
149
173
  if (!t) return;
150
- const s = t.lastItemIdx, o = Array.from(e.children), r = o.slice(s);
151
- if (r.length !== 0) {
152
- if (r.forEach((i, a) => {
174
+ const s = t.lastItemIdx, r = Array.from(e.children), o = r.slice(s);
175
+ if (o.length !== 0) {
176
+ if (o.forEach((i, a) => {
153
177
  i.id || (i.id = "moT" + crypto.randomUUID() + "_" + (a + s)), i.classList.add("mo-item", "js-mo-item");
154
178
  }), this._setDivider(), t.randomFullWidth) {
155
179
  const i = t.randomFullWidth === !0 ? 0.33 : t.randomFullWidth;
156
- r.forEach((a) => {
180
+ o.forEach((a) => {
157
181
  !a.classList.contains("mo-fullwidth") && Math.random() < i && a.classList.add("mo-fullwidth", "js-mo-fullwidth");
158
182
  });
159
183
  }
160
- r.forEach((i, a) => {
184
+ o.forEach((i, a) => {
161
185
  t.showBadge && this._createBadge(i, a + s + 1), t.showArrow && this._createArrow(i);
162
- }), t.lastItemIdx = o.length, h.set(e, t), this.refresh(), this._observeItems(r), this._injectAdSlots(r), r.forEach((i) => {
186
+ }), t.lastItemIdx = r.length, h.set(e, t), this._applyFilter(o), this.refresh(), this._observeItems(o), this._injectAdSlots(o), o.forEach((i) => {
163
187
  i.querySelectorAll("img").forEach((a) => {
164
188
  a.complete || a.addEventListener("load", this._resizeHandler, { once: !0 });
165
189
  });
@@ -167,6 +191,7 @@ const f = class f {
167
191
  }
168
192
  }
169
193
  _setPostPosition(e) {
194
+ if (e.classList.contains("mo-filtered-out")) return;
170
195
  if (e.classList.contains("mo-fullwidth")) {
171
196
  e.classList.remove("mo-inverted", "js-mo-inverted", "mo-offset");
172
197
  return;
@@ -178,34 +203,34 @@ const f = class f {
178
203
  if (!e) return null;
179
204
  const t = this._getData();
180
205
  if (!t) return null;
181
- const s = t.col, o = w(e, ".js-mo-inverted")[0] || null, r = w(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, i = v(r), a = v(o), n = v(e);
206
+ const s = t.col, r = w(e, ".js-mo-inverted:not(.mo-filtered-out)")[0] || null, o = w(e, ".js-mo-item:not(.js-mo-inverted):not(.mo-filtered-out)")[0] || null, i = p(o), a = p(r), n = p(e);
182
207
  let c = 0, d = 0;
183
208
  if (s > 1) {
184
209
  i.gppu > n.o + 1 && (c = 1), a.gppu > i.gppu && (c = 0);
185
210
  const m = e.previousElementSibling;
186
- m && Math.abs(n.o - v(m).o) < 40 && (d = 1);
211
+ m && Math.abs(n.o - p(m).o) < 40 && (d = 1);
187
212
  }
188
213
  return { lr: c, badge_offset: d };
189
214
  }
190
215
  _createBadge(e, t) {
191
- const s = this._getData(), o = document.createElement("span");
192
- if (o.className = "mo-badge js-mo-badge", s.showCounterStyle === "none")
193
- o.style.opacity = "0";
216
+ const s = this._getData(), r = document.createElement("span");
217
+ if (r.className = "mo-badge js-mo-badge", s.showCounterStyle === "none")
218
+ r.style.opacity = "0";
194
219
  else if (s.showCounterStyle === "image") {
195
- const r = document.createElement("img");
196
- r.className = "mo-badge-icon", r.alt = "", r.src = e.dataset.moIcon || I, o.appendChild(r);
220
+ const o = document.createElement("img");
221
+ o.className = "mo-badge-icon", o.alt = "", o.src = e.dataset.moIcon || S, r.appendChild(o);
197
222
  } else
198
- o.textContent = t;
199
- e.prepend(o);
223
+ r.textContent = t;
224
+ e.prepend(r);
200
225
  }
201
226
  _createItemElement(e) {
202
227
  const t = document.createElement("li");
203
- e.icon && (t.dataset.moIcon = e.icon);
228
+ e.icon && (t.dataset.moIcon = e.icon), e.categories && (t.dataset.categories = Array.isArray(e.categories) ? e.categories.join(" ") : String(e.categories));
204
229
  const s = document.createElement("div");
205
230
  s.className = "mo-card";
206
- const o = this._getData();
207
- if (o && typeof o.renderCard == "function")
208
- o.renderCard(e, s);
231
+ const r = this._getData();
232
+ if (r && typeof r.renderCard == "function")
233
+ r.renderCard(e, s);
209
234
  else {
210
235
  if (e.banner) {
211
236
  const i = document.createElement("div");
@@ -217,20 +242,20 @@ const f = class f {
217
242
  }
218
243
  s.appendChild(i);
219
244
  }
220
- const r = document.createElement("div");
221
- if (r.className = "mo-card-body", e.title) {
245
+ const o = document.createElement("div");
246
+ if (o.className = "mo-card-body", e.title) {
222
247
  const i = document.createElement("h3");
223
- i.textContent = e.title, r.appendChild(i);
248
+ i.textContent = e.title, o.appendChild(i);
224
249
  }
225
250
  if (e.meta) {
226
251
  const i = document.createElement("p");
227
- i.className = "mo-meta", i.textContent = e.meta, r.appendChild(i);
252
+ i.className = "mo-meta", i.textContent = e.meta, o.appendChild(i);
228
253
  }
229
254
  if (e.text) {
230
255
  const i = document.createElement("p");
231
- i.textContent = e.text, r.appendChild(i);
256
+ i.textContent = e.text, o.appendChild(i);
232
257
  }
233
- s.appendChild(r);
258
+ s.appendChild(o);
234
259
  }
235
260
  return t.appendChild(s), t;
236
261
  }
@@ -241,19 +266,19 @@ const f = class f {
241
266
  _injectAdSlots(e) {
242
267
  const t = this._getData();
243
268
  if (!t || !t.adSlots || !e.length) return;
244
- const { mode: s, interval: o, style: r } = t.adSlots, i = r === "fullwidth", a = this.element;
269
+ const { mode: s, interval: r, style: o } = t.adSlots, i = o === "fullwidth", a = this.element;
245
270
  let n = !1;
246
271
  if (s === "every_n")
247
272
  e.forEach((c, d) => {
248
- if ((t._adRealCount + d + 1) % o === 0) {
273
+ if ((t._adRealCount + d + 1) % r === 0) {
249
274
  const m = this._createAdSlot(i);
250
275
  c.after(m), m.dataset.moAdPosition = String(Array.from(a.children).indexOf(m)), this._adObserver && this._adObserver.observe(m), i && (n = !0);
251
276
  }
252
277
  });
253
278
  else if (s === "random") {
254
- let c = t._adRealCount % o, d = 0;
279
+ let c = t._adRealCount % r, d = 0;
255
280
  for (; d < e.length; ) {
256
- const m = o - c, u = e.slice(d, d + m);
281
+ const m = r - c, u = e.slice(d, d + m);
257
282
  if (u.length === m) {
258
283
  const E = u[Math.floor(Math.random() * u.length)], g = this._createAdSlot(i);
259
284
  E.after(g), g.dataset.moAdPosition = String(Array.from(a.children).indexOf(g)), this._adObserver && this._adObserver.observe(g), i && (n = !0), c = 0;
@@ -274,7 +299,7 @@ const f = class f {
274
299
  });
275
300
  }
276
301
  };
277
- p(f, "instances", /* @__PURE__ */ new Set());
302
+ v(f, "instances", /* @__PURE__ */ new Set());
278
303
  let y = f;
279
304
  export {
280
305
  y as MoTimeline,
@@ -1,6 +1,6 @@
1
- (function(d,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(d=typeof globalThis<"u"?globalThis:d||self,l(d.MoTimeline={}))})(this,function(d){"use strict";var I=Object.defineProperty;var L=(d,l,u)=>l in d?I(d,l,{enumerable:!0,configurable:!0,writable:!0,value:u}):d[l]=u;var E=(d,l,u)=>L(d,typeof l!="symbol"?l+"":l,u);/*!
2
- * moTimeline v2.12.0
1
+ (function(d,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(d=typeof globalThis<"u"?globalThis:d||self,n(d.MoTimeline={}))})(this,function(d){"use strict";var L=Object.defineProperty;var I=(d,n,u)=>n in d?L(d,n,{enumerable:!0,configurable:!0,writable:!0,value:u}):d[n]=u;var E=(d,n,u)=>I(d,typeof n!="symbol"?n+"":n,u);/*!
2
+ * moTimeline v2.13.0
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const l=new WeakMap,u={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0,animate:!1,renderCard:null,adSlots:null},C="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function w(){const c=window.innerWidth;return c<600?"xs":c<992?"sm":c<1200?"md":"lg"}function S(c,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>c(...s),e)}}function p(c){return c?{o:c.offsetTop,h:c.offsetHeight,gppu:c.offsetTop+c.offsetHeight}:{o:0,h:0,gppu:0}}function y(c,e){const t=[];let s=c.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const g=class g{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},u,t),this.settings.columnCount=Object.assign({},u.columnCount,t.columnCount),this._resizeHandler=S(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(l.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});if(l.set(e,t),g.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),t.animate){const s=t.animate===!0?"fade":t.animate;e.classList.add("mo-animate",`mo-animate-${s}`),this._observer=new IntersectionObserver(o=>{o.forEach(r=>{r.isIntersecting&&(r.target.classList.add("mo-visible"),this._observer.unobserve(r.target))})},{threshold:.1})}t.adSlots&&(t._adRealCount=0,typeof t.adSlots.onEnterViewport=="function"&&(this._adObserver=new IntersectionObserver(s=>{s.forEach(o=>{o.isIntersecting&&(this._adObserver.unobserve(o.target),t.adSlots.onEnterViewport(o.target,Number(o.target.dataset.moAdPosition)))})},{threshold:.5}))),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){g.instances.forEach(e=>{const t=e.element,s=l.get(t);s&&(s.col=s.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(o=>{e._setPostPosition(o)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}insertItem(e,t){const s=this.element,o=this._getData();if(!o)return;const r=this._createItemElement(e),i=Array.from(s.children).filter(a=>a.classList.contains("js-mo-item")),n=t==null?Math.floor(Math.random()*(i.length+1)):Math.max(0,Math.min(t,i.length));if(n>=i.length?s.appendChild(r):s.insertBefore(r,i[n]),r.id||(r.id="moT"+crypto.randomUUID()+"_"+n),r.classList.add("mo-item","js-mo-item"),e.fullWidth)r.classList.add("mo-fullwidth","js-mo-fullwidth");else if(o.randomFullWidth){const a=o.randomFullWidth===!0?.33:o.randomFullWidth;Math.random()<a&&r.classList.add("mo-fullwidth","js-mo-fullwidth")}return o.showBadge&&this._createBadge(r,n+1),o.showArrow&&this._createArrow(r),o.showBadge&&o.showCounterStyle==="counter"&&Array.from(s.querySelectorAll(".js-mo-item")).forEach((a,h)=>{const m=a.querySelector(".js-mo-badge");m&&(m.textContent=h+1)}),o.lastItemIdx=Array.from(s.children).length,l.set(s,o),this.refresh(),this._observeItems([r]),r.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})}),r}clear(){const e=this._getData();e&&(this._observer&&this._observer.disconnect(),this._adObserver&&this._adObserver.disconnect(),Array.from(this.element.children).forEach(t=>{(t.classList.contains("js-mo-item")||t.classList.contains("mo-ad-slot"))&&t.remove()}),e.lastItemIdx=0,e.adSlots&&(e._adRealCount=0))}destroy(){window.removeEventListener("resize",this._resizeHandler),this._observer&&(this._observer.disconnect(),this._observer=null),this._adObserver&&(this._adObserver.disconnect(),this._adObserver=null),Array.from(this.element.querySelectorAll(".mo-ad-slot")).forEach(e=>e.remove()),l.delete(this.element),g.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol","mo-animate","mo-animate-fade","mo-animate-slide"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset","mo-fullwidth","js-mo-fullwidth","mo-visible"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return l.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[w()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,o=Array.from(e.children),r=o.slice(s);if(r.length!==0){if(r.forEach((i,n)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(n+s)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const i=t.randomFullWidth===!0?.33:t.randomFullWidth;r.forEach(n=>{!n.classList.contains("mo-fullwidth")&&Math.random()<i&&n.classList.add("mo-fullwidth","js-mo-fullwidth")})}r.forEach((i,n)=>{t.showBadge&&this._createBadge(i,n+s+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=o.length,l.set(e,t),this.refresh(),this._observeItems(r),this._injectAdSlots(r),r.forEach(i=>{i.querySelectorAll("img").forEach(n=>{n.complete||n.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const s=t.col,o=y(e,".js-mo-inverted")[0]||null,r=y(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=p(r),n=p(o),a=p(e);let h=0,m=0;if(s>1){i.gppu>a.o+1&&(h=1),n.gppu>i.gppu&&(h=0);const f=e.previousElementSibling;f&&Math.abs(a.o-p(f).o)<40&&(m=1)}return{lr:h,badge_offset:m}}_createBadge(e,t){const s=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",s.showCounterStyle==="none")o.style.opacity="0";else if(s.showCounterStyle==="image"){const r=document.createElement("img");r.className="mo-badge-icon",r.alt="",r.src=e.dataset.moIcon||C,o.appendChild(r)}else o.textContent=t;e.prepend(o)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const s=document.createElement("div");s.className="mo-card";const o=this._getData();if(o&&typeof o.renderCard=="function")o.renderCard(e,s);else{if(e.banner){const i=document.createElement("div");i.className="mo-card-image";const n=document.createElement("img");if(n.className="mo-banner",n.src=e.banner,n.alt="",i.appendChild(n),e.avatar){const a=document.createElement("img");a.className="mo-avatar",a.src=e.avatar,a.alt="",i.appendChild(a)}s.appendChild(i)}const r=document.createElement("div");if(r.className="mo-card-body",e.title){const i=document.createElement("h3");i.textContent=e.title,r.appendChild(i)}if(e.meta){const i=document.createElement("p");i.className="mo-meta",i.textContent=e.meta,r.appendChild(i)}if(e.text){const i=document.createElement("p");i.textContent=e.text,r.appendChild(i)}s.appendChild(r)}return t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}_injectAdSlots(e){const t=this._getData();if(!t||!t.adSlots||!e.length)return;const{mode:s,interval:o,style:r}=t.adSlots,i=r==="fullwidth",n=this.element;let a=!1;if(s==="every_n")e.forEach((h,m)=>{if((t._adRealCount+m+1)%o===0){const f=this._createAdSlot(i);h.after(f),f.dataset.moAdPosition=String(Array.from(n.children).indexOf(f)),this._adObserver&&this._adObserver.observe(f),i&&(a=!0)}});else if(s==="random"){let h=t._adRealCount%o,m=0;for(;m<e.length;){const f=o-h,_=e.slice(m,m+f);if(_.length===f){const A=_[Math.floor(Math.random()*_.length)],b=this._createAdSlot(i);A.after(b),b.dataset.moAdPosition=String(Array.from(n.children).indexOf(b)),this._adObserver&&this._adObserver.observe(b),i&&(a=!0),h=0}else h+=_.length;m+=f}}t._adRealCount+=e.length,a&&this.refresh()}_createAdSlot(e){const t=document.createElement("li");return t.className="mo-ad-slot",e&&t.classList.add("mo-fullwidth"),t}_observeItems(e){this._observer&&e.forEach(t=>{t.classList.contains("mo-visible")||this._observer.observe(t)})}};E(g,"instances",new Set);let v=g;d.MoTimeline=v,d.default=v,Object.defineProperties(d,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
6
+ */const n=new WeakMap,u={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0,animate:!1,renderCard:null,adSlots:null},A="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function y(){const c=window.innerWidth;return c<600?"xs":c<992?"sm":c<1200?"md":"lg"}function S(c,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>c(...s),e)}}function p(c){return c?{o:c.offsetTop,h:c.offsetHeight,gppu:c.offsetTop+c.offsetHeight}:{o:0,h:0,gppu:0}}function w(c,e){const t=[];let s=c.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const g=class g{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},u,t),this.settings.columnCount=Object.assign({},u.columnCount,t.columnCount),this._resizeHandler=S(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(n.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});if(n.set(e,t),g.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),t.animate){const s=t.animate===!0?"fade":t.animate;e.classList.add("mo-animate",`mo-animate-${s}`),this._observer=new IntersectionObserver(r=>{r.forEach(o=>{o.isIntersecting&&(o.target.classList.add("mo-visible"),this._observer.unobserve(o.target))})},{threshold:.1})}t.adSlots&&(t._adRealCount=0,typeof t.adSlots.onEnterViewport=="function"&&(this._adObserver=new IntersectionObserver(s=>{s.forEach(r=>{r.isIntersecting&&(this._adObserver.unobserve(r.target),t.adSlots.onEnterViewport(r.target,Number(r.target.dataset.moAdPosition)))})},{threshold:.5}))),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){g.instances.forEach(e=>{const t=e.element,s=n.get(t);s&&(s.col=s.columnCount[y()],e._setDivider(),Array.from(t.children).forEach(r=>{e._setPostPosition(r)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}insertItem(e,t){const s=this.element,r=this._getData();if(!r)return;const o=this._createItemElement(e),i=Array.from(s.children).filter(l=>l.classList.contains("js-mo-item")),a=t==null?Math.floor(Math.random()*(i.length+1)):Math.max(0,Math.min(t,i.length));if(a>=i.length?s.appendChild(o):s.insertBefore(o,i[a]),o.id||(o.id="moT"+crypto.randomUUID()+"_"+a),o.classList.add("mo-item","js-mo-item"),e.fullWidth)o.classList.add("mo-fullwidth","js-mo-fullwidth");else if(r.randomFullWidth){const l=r.randomFullWidth===!0?.33:r.randomFullWidth;Math.random()<l&&o.classList.add("mo-fullwidth","js-mo-fullwidth")}return r.showBadge&&this._createBadge(o,a+1),r.showArrow&&this._createArrow(o),r.showBadge&&r.showCounterStyle==="counter"&&Array.from(s.querySelectorAll(".js-mo-item")).forEach((l,h)=>{const m=l.querySelector(".js-mo-badge");m&&(m.textContent=h+1)}),r.lastItemIdx=Array.from(s.children).length,n.set(s,r),this.refresh(),this._observeItems([o]),o.querySelectorAll("img").forEach(l=>{l.complete||l.addEventListener("load",this._resizeHandler,{once:!0})}),o}clear(){const e=this._getData();e&&(this._observer&&this._observer.disconnect(),this._adObserver&&this._adObserver.disconnect(),Array.from(this.element.children).forEach(t=>{(t.classList.contains("js-mo-item")||t.classList.contains("mo-ad-slot"))&&t.remove()}),e.lastItemIdx=0,e.adSlots&&(e._adRealCount=0))}destroy(){window.removeEventListener("resize",this._resizeHandler),this._observer&&(this._observer.disconnect(),this._observer=null),this._adObserver&&(this._adObserver.disconnect(),this._adObserver=null),Array.from(this.element.querySelectorAll(".mo-ad-slot")).forEach(e=>e.remove()),n.delete(this.element),g.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol","mo-animate","mo-animate-fade","mo-animate-slide"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset","mo-fullwidth","js-mo-fullwidth","mo-visible","mo-filtered-out"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}filterByCategory(e){const t=this._getData();t&&(t._activeFilter=e==null||e===""||e==="all"?null:String(e),n.set(this.element,t),this._applyFilter(Array.from(this.element.querySelectorAll(".js-mo-item"))),this.refresh())}_applyFilter(e){const t=this._getData();if(!t)return;const s=t._activeFilter||null;e.forEach(r=>{if(!s)r.classList.remove("mo-filtered-out");else{const o=(r.dataset.categories||"").split(/\s+/).filter(Boolean);r.classList.toggle("mo-filtered-out",!o.includes(s))}})}_getData(){return n.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[y()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,r=Array.from(e.children),o=r.slice(s);if(o.length!==0){if(o.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+s)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const i=t.randomFullWidth===!0?.33:t.randomFullWidth;o.forEach(a=>{!a.classList.contains("mo-fullwidth")&&Math.random()<i&&a.classList.add("mo-fullwidth","js-mo-fullwidth")})}o.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+s+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=r.length,n.set(e,t),this._applyFilter(o),this.refresh(),this._observeItems(o),this._injectAdSlots(o),o.forEach(i=>{i.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-filtered-out"))return;if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const s=t.col,r=w(e,".js-mo-inverted:not(.mo-filtered-out)")[0]||null,o=w(e,".js-mo-item:not(.js-mo-inverted):not(.mo-filtered-out)")[0]||null,i=p(o),a=p(r),l=p(e);let h=0,m=0;if(s>1){i.gppu>l.o+1&&(h=1),a.gppu>i.gppu&&(h=0);const f=e.previousElementSibling;f&&Math.abs(l.o-p(f).o)<40&&(m=1)}return{lr:h,badge_offset:m}}_createBadge(e,t){const s=this._getData(),r=document.createElement("span");if(r.className="mo-badge js-mo-badge",s.showCounterStyle==="none")r.style.opacity="0";else if(s.showCounterStyle==="image"){const o=document.createElement("img");o.className="mo-badge-icon",o.alt="",o.src=e.dataset.moIcon||A,r.appendChild(o)}else r.textContent=t;e.prepend(r)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon),e.categories&&(t.dataset.categories=Array.isArray(e.categories)?e.categories.join(" "):String(e.categories));const s=document.createElement("div");s.className="mo-card";const r=this._getData();if(r&&typeof r.renderCard=="function")r.renderCard(e,s);else{if(e.banner){const i=document.createElement("div");i.className="mo-card-image";const a=document.createElement("img");if(a.className="mo-banner",a.src=e.banner,a.alt="",i.appendChild(a),e.avatar){const l=document.createElement("img");l.className="mo-avatar",l.src=e.avatar,l.alt="",i.appendChild(l)}s.appendChild(i)}const o=document.createElement("div");if(o.className="mo-card-body",e.title){const i=document.createElement("h3");i.textContent=e.title,o.appendChild(i)}if(e.meta){const i=document.createElement("p");i.className="mo-meta",i.textContent=e.meta,o.appendChild(i)}if(e.text){const i=document.createElement("p");i.textContent=e.text,o.appendChild(i)}s.appendChild(o)}return t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}_injectAdSlots(e){const t=this._getData();if(!t||!t.adSlots||!e.length)return;const{mode:s,interval:r,style:o}=t.adSlots,i=o==="fullwidth",a=this.element;let l=!1;if(s==="every_n")e.forEach((h,m)=>{if((t._adRealCount+m+1)%r===0){const f=this._createAdSlot(i);h.after(f),f.dataset.moAdPosition=String(Array.from(a.children).indexOf(f)),this._adObserver&&this._adObserver.observe(f),i&&(l=!0)}});else if(s==="random"){let h=t._adRealCount%r,m=0;for(;m<e.length;){const f=r-h,_=e.slice(m,m+f);if(_.length===f){const C=_[Math.floor(Math.random()*_.length)],b=this._createAdSlot(i);C.after(b),b.dataset.moAdPosition=String(Array.from(a.children).indexOf(b)),this._adObserver&&this._adObserver.observe(b),i&&(l=!0),h=0}else h+=_.length;m+=f}}t._adRealCount+=e.length,l&&this.refresh()}_createAdSlot(e){const t=document.createElement("li");return t.className="mo-ad-slot",e&&t.classList.add("mo-fullwidth"),t}_observeItems(e){this._observer&&e.forEach(t=>{t.classList.contains("mo-visible")||this._observer.observe(t)})}};E(g,"instances",new Set);let v=g;d.MoTimeline=v,d.default=v,Object.defineProperties(d,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});