motimeline 2.10.0 → 2.12.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
@@ -20,6 +20,8 @@ Responsive two-column timeline layout library — plain JavaScript, zero depende
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
22
  - **Dynamic items** — append, insert, or inject `<li>` elements at any time via `initNewItems()`, `addItems()`, or `insertItem()`
23
+ - **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
+ - **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.
23
25
  - **Bootstrap compatible** — wrap the `<ul>` in a Bootstrap `.container`, no config needed
24
26
  - **ESM · CJS · UMD** — works with any bundler or as a plain `<script>` tag
25
27
 
@@ -146,6 +148,7 @@ The library injects classes and elements into your markup. Here is what a fully
146
148
  | `randomFullWidth` | number \| boolean | `0` | `0`/`false` = off. A number `0–1` sets the probability that each item is randomly promoted to full-width during init. `true` = 33% chance. Items can also be set manually by adding the `mo-fullwidth` class to the `<li>`. |
147
149
  | `animate` | string \| boolean | `false` | Animate items as they scroll into view using `IntersectionObserver`. `'fade'` — items fade in. `'slide'` — left-column items slide in from the left, right-column items from the right. `true` = `'fade'`. Disable for individual items by adding `mo-visible` manually. Control speed via `--mo-animate-duration`. |
148
150
  | `renderCard` | function \| null | `null` | `(item, cardEl) => void`. When set, called for every item instead of the built-in HTML renderer. `cardEl` is the `.mo-card` div already placed inside the `<li>`. Populate it via `innerHTML` or DOM methods. The library still owns the `<li>`, column placement, spine, badge, arrow, `addItems()`, and scroll pagination. |
151
+ | `adSlots` | object \| null | `null` | Inject ad slot placeholders into the timeline and observe them. See **Ad slots** below. |
149
152
 
150
153
  ---
151
154
 
@@ -189,6 +192,7 @@ tl.refresh(); // re-layout all items (called automatically on resi
189
192
  tl.initNewItems(); // pick up manually appended <li> elements
190
193
  tl.addItems(items); // create and append <li> from an array of item objects (or JSON string)
191
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
192
196
  tl.destroy(); // remove listeners and reset DOM classes
193
197
  ```
194
198
 
@@ -326,6 +330,52 @@ export default function App() {
326
330
 
327
331
  ---
328
332
 
333
+ ## Ad slots
334
+
335
+ Inject ad placeholder `<li>` elements at configurable positions, observe them with `IntersectionObserver`, and fire a callback exactly once when each slot reaches 50% visibility. The library owns the slot element — you own what goes inside it.
336
+
337
+ ```js
338
+ const tl = new MoTimeline('#my-timeline', {
339
+ adSlots: {
340
+ mode: 'every_n', // 'every_n' | 'random'
341
+ interval: 10, // every_n: inject after every N real items
342
+ // random: inject once at a random position per N-item page
343
+ style: 'card', // 'card' | 'fullwidth'
344
+ onEnterViewport: (slotEl, position) => {
345
+ // slotEl = the <li class="mo-ad-slot"> element
346
+ // position = its 0-based index in the container at injection time
347
+ const ins = document.createElement('ins');
348
+ ins.className = 'adsbygoogle';
349
+ ins.style.display = 'block';
350
+ ins.dataset.adClient = 'ca-pub-XXXXXXXXXXXXXXXX';
351
+ ins.dataset.adSlot = '1234567890';
352
+ ins.dataset.adFormat = 'auto';
353
+ slotEl.appendChild(ins);
354
+ (window.adsbygoogle = window.adsbygoogle || []).push({});
355
+ },
356
+ },
357
+ });
358
+ ```
359
+
360
+ ### `adSlots` option shape
361
+
362
+ | Property | Type | Description |
363
+ |---|---|---|
364
+ | `mode` | `'every_n' \| 'random'` | `'every_n'` — inject after every `interval` real items. `'random'` — inject one slot at a random position within each `interval`-item page. |
365
+ | `interval` | number | Cadence for slot injection (see `mode`). |
366
+ | `style` | `'card' \| 'fullwidth'` | `'card'` — slot sits in the normal left/right column flow. `'fullwidth'` — slot spans both columns (adds `mo-fullwidth`). |
367
+ | `onEnterViewport` | `(slotEl: HTMLElement, position: number) => void` | Called once per slot when ≥ 50% of it enters the viewport. `position` is the 0-based child index of the slot in the container at injection time. |
368
+
369
+ **What the library provides:**
370
+ - A `<li class="mo-ad-slot">` element with `min-height: 100px` (so the observer can detect it before content loads)
371
+ - `fullwidth` layout via the existing `mo-fullwidth` mechanism when `style: 'fullwidth'`
372
+ - Exactly-once `IntersectionObserver` (threshold 0.5) per slot
373
+ - Automatic slot cleanup on `tl.destroy()`
374
+
375
+ **What you provide:** everything inside the slot — the ad creative, network scripts, markup.
376
+
377
+ Slots are injected after each `addItems()` call, so they work seamlessly with infinite scroll.
378
+
329
379
  ## Infinite scroll recipe
330
380
 
331
381
  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.
@@ -418,6 +468,12 @@ No framework option needed. Wrap the `<ul>` inside a Bootstrap `.container`:
418
468
 
419
469
  ## Changelog
420
470
 
471
+ ### v2.12.0
472
+ - 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
+
474
+ ### v2.11.0
475
+ - New option `adSlots` — inject ad slot `<li>` placeholders at configurable positions (`every_n` or `random` mode) and receive an `onEnterViewport(slotEl, position)` callback exactly once per slot when ≥ 50% of it is visible. Works with `addItems()` and infinite scroll. Slots are removed on `tl.destroy()`. See **Ad slots** section.
476
+
421
477
  ### v2.10.0
422
478
  - New option `renderCard(item, cardEl)` — custom card renderer. When provided, the library skips its built-in card HTML and calls this function instead, passing the item data object and the `.mo-card` div already inserted into the DOM. The library continues to own column placement, spine, badge, arrow, `addItems()`, and scroll pagination. Enables full React component injection via `createRoot(cardEl).render(...)`.
423
479
 
@@ -1,6 +1,6 @@
1
- "use strict";var _=Object.defineProperty;var b=(a,e,t)=>e in a?_(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var p=(a,e,t)=>b(a,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});/*!
2
- * moTimeline v2.10.0
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
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const m=new WeakMap,v={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},E="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 a=window.innerWidth;return a<600?"xs":a<992?"sm":a<1200?"md":"lg"}function I(a,e=100){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>a(...r),e)}}function f(a){return a?{o:a.offsetTop,h:a.offsetHeight,gppu:a.offsetTop+a.offsetHeight}:{o:0,h:0,gppu:0}}function y(a,e){const t=[];let r=a.previousElementSibling;for(;r;)(!e||r.matches(e))&&t.push(r),r=r.previousElementSibling;return t}const d=class d{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({},v,t),this.settings.columnCount=Object.assign({},v.columnCount,t.columnCount),this._resizeHandler=I(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(m.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});if(m.set(e,t),d.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 r=t.animate===!0?"fade":t.animate;e.classList.add("mo-animate",`mo-animate-${r}`),this._observer=new IntersectionObserver(i=>{i.forEach(s=>{s.isIntersecting&&(s.target.classList.add("mo-visible"),this._observer.unobserve(s.target))})},{threshold:.1})}this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){d.instances.forEach(e=>{const t=e.element,r=m.get(t);r&&(r.col=r.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(i=>{e._setPostPosition(i)}))})}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 r=this.element,i=this._getData();if(!i)return;const s=this._createItemElement(e),o=Array.from(r.children).filter(l=>l.classList.contains("js-mo-item")),n=t==null?Math.floor(Math.random()*(o.length+1)):Math.max(0,Math.min(t,o.length));if(n>=o.length?r.appendChild(s):r.insertBefore(s,o[n]),s.id||(s.id="moT"+crypto.randomUUID()+"_"+n),s.classList.add("mo-item","js-mo-item"),e.fullWidth)s.classList.add("mo-fullwidth","js-mo-fullwidth");else if(i.randomFullWidth){const l=i.randomFullWidth===!0?.33:i.randomFullWidth;Math.random()<l&&s.classList.add("mo-fullwidth","js-mo-fullwidth")}return i.showBadge&&this._createBadge(s,n+1),i.showArrow&&this._createArrow(s),i.showBadge&&i.showCounterStyle==="counter"&&Array.from(r.querySelectorAll(".js-mo-item")).forEach((l,c)=>{const h=l.querySelector(".js-mo-badge");h&&(h.textContent=c+1)}),i.lastItemIdx=Array.from(r.children).length,m.set(r,i),this.refresh(),this._observeItems([s]),s.querySelectorAll("img").forEach(l=>{l.complete||l.addEventListener("load",this._resizeHandler,{once:!0})}),s}destroy(){window.removeEventListener("resize",this._resizeHandler),this._observer&&(this._observer.disconnect(),this._observer=null),m.delete(this.element),d.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 m.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 r=t.lastItemIdx,i=Array.from(e.children),s=i.slice(r);if(s.length!==0){if(s.forEach((o,n)=>{o.id||(o.id="moT"+crypto.randomUUID()+"_"+(n+r)),o.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const o=t.randomFullWidth===!0?.33:t.randomFullWidth;s.forEach(n=>{!n.classList.contains("mo-fullwidth")&&Math.random()<o&&n.classList.add("mo-fullwidth","js-mo-fullwidth")})}s.forEach((o,n)=>{t.showBadge&&this._createBadge(o,n+r+1),t.showArrow&&this._createArrow(o)}),t.lastItemIdx=i.length,m.set(e,t),this.refresh(),this._observeItems(s),s.forEach(o=>{o.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 r=t.col,i=y(e,".js-mo-inverted")[0]||null,s=y(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,o=f(s),n=f(i),l=f(e);let c=0,h=0;if(r>1){o.gppu>l.o+1&&(c=1),n.gppu>o.gppu&&(c=0);const g=e.previousElementSibling;g&&Math.abs(l.o-f(g).o)<40&&(h=1)}return{lr:c,badge_offset:h}}_createBadge(e,t){const r=this._getData(),i=document.createElement("span");if(i.className="mo-badge js-mo-badge",r.showCounterStyle==="none")i.style.opacity="0";else if(r.showCounterStyle==="image"){const s=document.createElement("img");s.className="mo-badge-icon",s.alt="",s.src=e.dataset.moIcon||E,i.appendChild(s)}else i.textContent=t;e.prepend(i)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const r=document.createElement("div");r.className="mo-card";const i=this._getData();if(i&&typeof i.renderCard=="function")i.renderCard(e,r);else{if(e.banner){const o=document.createElement("div");o.className="mo-card-image";const n=document.createElement("img");if(n.className="mo-banner",n.src=e.banner,n.alt="",o.appendChild(n),e.avatar){const l=document.createElement("img");l.className="mo-avatar",l.src=e.avatar,l.alt="",o.appendChild(l)}r.appendChild(o)}const s=document.createElement("div");if(s.className="mo-card-body",e.title){const o=document.createElement("h3");o.textContent=e.title,s.appendChild(o)}if(e.meta){const o=document.createElement("p");o.className="mo-meta",o.textContent=e.meta,s.appendChild(o)}if(e.text){const o=document.createElement("p");o.textContent=e.text,s.appendChild(o)}r.appendChild(s)}return t.appendChild(r),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}_observeItems(e){this._observer&&e.forEach(t=>{t.classList.contains("mo-visible")||this._observer.observe(t)})}};p(d,"instances",new Set);let u=d;exports.MoTimeline=u;exports.default=u;
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;
@@ -1,3 +1,3 @@
1
1
  /*!
2
- * moTimeline v2.10.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)}
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}
@@ -1,13 +1,13 @@
1
- var _ = Object.defineProperty;
2
- var b = (a, e, t) => e in a ? _(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
- var g = (a, e, t) => b(a, typeof e != "symbol" ? e + "" : e, t);
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);
4
4
  /*!
5
- * moTimeline v2.10.0
5
+ * moTimeline v2.12.0
6
6
  * Responsive two-column timeline layout library
7
7
  * https://github.com/MattOpen/moTimeline
8
8
  * MIT License
9
9
  */
10
- const m = /* @__PURE__ */ new WeakMap(), p = {
10
+ const h = /* @__PURE__ */ new WeakMap(), _ = {
11
11
  columnCount: { xs: 1, sm: 2, md: 2, lg: 2 },
12
12
  showBadge: !1,
13
13
  showArrow: !1,
@@ -23,60 +23,66 @@ const m = /* @__PURE__ */ new WeakMap(), p = {
23
23
  // 0 = off; 0–1 = probability per item; true = 0.33
24
24
  animate: !1,
25
25
  // false | 'fade' | 'slide'
26
- renderCard: null
26
+ renderCard: null,
27
27
  // (item, cardEl) => void — custom card renderer; skips built-in HTML
28
- }, E = "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>";
29
- function v() {
30
- const a = window.innerWidth;
31
- return a < 600 ? "xs" : a < 992 ? "sm" : a < 1200 ? "md" : "lg";
28
+ adSlots: null
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>";
31
+ function b() {
32
+ const l = window.innerWidth;
33
+ return l < 600 ? "xs" : l < 992 ? "sm" : l < 1200 ? "md" : "lg";
32
34
  }
33
- function I(a, e = 100) {
35
+ function S(l, e = 100) {
34
36
  let t;
35
- return (...r) => {
36
- clearTimeout(t), t = setTimeout(() => a(...r), e);
37
+ return (...s) => {
38
+ clearTimeout(t), t = setTimeout(() => l(...s), e);
37
39
  };
38
40
  }
39
- function f(a) {
40
- return a ? {
41
- o: a.offsetTop,
42
- h: a.offsetHeight,
43
- gppu: a.offsetTop + a.offsetHeight
41
+ function v(l) {
42
+ return l ? {
43
+ o: l.offsetTop,
44
+ h: l.offsetHeight,
45
+ gppu: l.offsetTop + l.offsetHeight
44
46
  } : { o: 0, h: 0, gppu: 0 };
45
47
  }
46
- function w(a, e) {
48
+ function w(l, e) {
47
49
  const t = [];
48
- let r = a.previousElementSibling;
49
- for (; r; )
50
- (!e || r.matches(e)) && t.push(r), r = r.previousElementSibling;
50
+ let s = l.previousElementSibling;
51
+ for (; s; )
52
+ (!e || s.matches(e)) && t.push(s), s = s.previousElementSibling;
51
53
  return t;
52
54
  }
53
- const d = class d {
55
+ const f = class f {
54
56
  constructor(e, t = {}) {
55
57
  if (typeof e == "string" && (e = document.querySelector(e)), !e) throw new Error("moTimeline: element not found");
56
- this.element = e, this.settings = Object.assign({}, p, t), this.settings.columnCount = Object.assign({}, p.columnCount, t.columnCount), this._resizeHandler = I(() => 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 = S(() => this.refresh(), 100), this._initialized = !1, this.init();
57
59
  }
58
60
  init() {
59
61
  const e = this.element;
60
- if (m.has(e)) {
62
+ if (h.has(e)) {
61
63
  this.refresh();
62
64
  return;
63
65
  }
64
66
  const t = Object.assign({}, this.settings, { lastItemIdx: 0 });
65
- if (m.set(e, t), d.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) {
66
- const r = t.animate === !0 ? "fade" : t.animate;
67
- e.classList.add("mo-animate", `mo-animate-${r}`), this._observer = new IntersectionObserver((i) => {
68
- i.forEach((s) => {
69
- s.isIntersecting && (s.target.classList.add("mo-visible"), this._observer.unobserve(s.target));
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
+ 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));
70
72
  });
71
73
  }, { threshold: 0.1 });
72
74
  }
73
- this._initialized = !0, window.addEventListener("resize", this._resizeHandler), Array.from(e.children).length > 0 && this._initItems();
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)));
78
+ });
79
+ }, { threshold: 0.5 }))), this._initialized = !0, window.addEventListener("resize", this._resizeHandler), Array.from(e.children).length > 0 && this._initItems();
74
80
  }
75
81
  refresh() {
76
- d.instances.forEach((e) => {
77
- const t = e.element, r = m.get(t);
78
- r && (r.col = r.columnCount[v()], e._setDivider(), Array.from(t.children).forEach((i) => {
79
- e._setPostPosition(i);
82
+ f.instances.forEach((e) => {
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);
80
86
  }));
81
87
  });
82
88
  }
@@ -103,53 +109,59 @@ const d = class d {
103
109
  * @returns {HTMLElement} the inserted <li> element
104
110
  */
105
111
  insertItem(e, t) {
106
- const r = this.element, i = this._getData();
107
- if (!i) return;
108
- const s = this._createItemElement(e), o = Array.from(r.children).filter((l) => l.classList.contains("js-mo-item")), n = t == null ? Math.floor(Math.random() * (o.length + 1)) : Math.max(0, Math.min(t, o.length));
109
- if (n >= o.length ? r.appendChild(s) : r.insertBefore(s, o[n]), s.id || (s.id = "moT" + crypto.randomUUID() + "_" + n), s.classList.add("mo-item", "js-mo-item"), e.fullWidth)
110
- s.classList.add("mo-fullwidth", "js-mo-fullwidth");
111
- else if (i.randomFullWidth) {
112
- const l = i.randomFullWidth === !0 ? 0.33 : i.randomFullWidth;
113
- Math.random() < l && s.classList.add("mo-fullwidth", "js-mo-fullwidth");
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");
114
120
  }
115
- return i.showBadge && this._createBadge(s, n + 1), i.showArrow && this._createArrow(s), i.showBadge && i.showCounterStyle === "counter" && Array.from(r.querySelectorAll(".js-mo-item")).forEach((l, c) => {
116
- const h = l.querySelector(".js-mo-badge");
117
- h && (h.textContent = c + 1);
118
- }), i.lastItemIdx = Array.from(r.children).length, m.set(r, i), this.refresh(), this._observeItems([s]), s.querySelectorAll("img").forEach((l) => {
119
- l.complete || l.addEventListener("load", this._resizeHandler, { once: !0 });
120
- }), s;
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) => {
122
+ const d = n.querySelector(".js-mo-badge");
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) => {
125
+ n.complete || n.addEventListener("load", this._resizeHandler, { once: !0 });
126
+ }), r;
127
+ }
128
+ clear() {
129
+ const e = this._getData();
130
+ e && (this._observer && this._observer.disconnect(), this._adObserver && this._adObserver.disconnect(), Array.from(this.element.children).forEach((t) => {
131
+ (t.classList.contains("js-mo-item") || t.classList.contains("mo-ad-slot")) && t.remove();
132
+ }), e.lastItemIdx = 0, e.adSlots && (e._adRealCount = 0));
121
133
  }
122
134
  destroy() {
123
- window.removeEventListener("resize", this._resizeHandler), this._observer && (this._observer.disconnect(), this._observer = null), m.delete(this.element), d.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) => {
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) => {
124
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());
125
137
  });
126
138
  }
127
139
  // ─── Private ────────────────────────────────────────────────────────────────
128
140
  _getData() {
129
- return m.get(this.element);
141
+ return h.get(this.element);
130
142
  }
131
143
  _setDivider() {
132
144
  const e = this._getData();
133
- e && (e.col = e.columnCount[v()], this.element.classList.toggle("mo-twocol", e.col > 1));
145
+ e && (e.col = e.columnCount[b()], this.element.classList.toggle("mo-twocol", e.col > 1));
134
146
  }
135
147
  _initItems() {
136
148
  const e = this.element, t = this._getData();
137
149
  if (!t) return;
138
- const r = t.lastItemIdx, i = Array.from(e.children), s = i.slice(r);
139
- if (s.length !== 0) {
140
- if (s.forEach((o, n) => {
141
- o.id || (o.id = "moT" + crypto.randomUUID() + "_" + (n + r)), o.classList.add("mo-item", "js-mo-item");
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) => {
153
+ i.id || (i.id = "moT" + crypto.randomUUID() + "_" + (a + s)), i.classList.add("mo-item", "js-mo-item");
142
154
  }), this._setDivider(), t.randomFullWidth) {
143
- const o = t.randomFullWidth === !0 ? 0.33 : t.randomFullWidth;
144
- s.forEach((n) => {
145
- !n.classList.contains("mo-fullwidth") && Math.random() < o && n.classList.add("mo-fullwidth", "js-mo-fullwidth");
155
+ const i = t.randomFullWidth === !0 ? 0.33 : t.randomFullWidth;
156
+ r.forEach((a) => {
157
+ !a.classList.contains("mo-fullwidth") && Math.random() < i && a.classList.add("mo-fullwidth", "js-mo-fullwidth");
146
158
  });
147
159
  }
148
- s.forEach((o, n) => {
149
- t.showBadge && this._createBadge(o, n + r + 1), t.showArrow && this._createArrow(o);
150
- }), t.lastItemIdx = i.length, m.set(e, t), this.refresh(), this._observeItems(s), s.forEach((o) => {
151
- o.querySelectorAll("img").forEach((n) => {
152
- n.complete || n.addEventListener("load", this._resizeHandler, { once: !0 });
160
+ r.forEach((i, a) => {
161
+ 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) => {
163
+ i.querySelectorAll("img").forEach((a) => {
164
+ a.complete || a.addEventListener("load", this._resizeHandler, { once: !0 });
153
165
  });
154
166
  });
155
167
  }
@@ -166,74 +178,104 @@ const d = class d {
166
178
  if (!e) return null;
167
179
  const t = this._getData();
168
180
  if (!t) return null;
169
- const r = t.col, i = w(e, ".js-mo-inverted")[0] || null, s = w(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, o = f(s), n = f(i), l = f(e);
170
- let c = 0, h = 0;
171
- if (r > 1) {
172
- o.gppu > l.o + 1 && (c = 1), n.gppu > o.gppu && (c = 0);
173
- const u = e.previousElementSibling;
174
- u && Math.abs(l.o - f(u).o) < 40 && (h = 1);
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);
182
+ let c = 0, d = 0;
183
+ if (s > 1) {
184
+ i.gppu > n.o + 1 && (c = 1), a.gppu > i.gppu && (c = 0);
185
+ const m = e.previousElementSibling;
186
+ m && Math.abs(n.o - v(m).o) < 40 && (d = 1);
175
187
  }
176
- return { lr: c, badge_offset: h };
188
+ return { lr: c, badge_offset: d };
177
189
  }
178
190
  _createBadge(e, t) {
179
- const r = this._getData(), i = document.createElement("span");
180
- if (i.className = "mo-badge js-mo-badge", r.showCounterStyle === "none")
181
- i.style.opacity = "0";
182
- else if (r.showCounterStyle === "image") {
183
- const s = document.createElement("img");
184
- s.className = "mo-badge-icon", s.alt = "", s.src = e.dataset.moIcon || E, i.appendChild(s);
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";
194
+ 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);
185
197
  } else
186
- i.textContent = t;
187
- e.prepend(i);
198
+ o.textContent = t;
199
+ e.prepend(o);
188
200
  }
189
201
  _createItemElement(e) {
190
202
  const t = document.createElement("li");
191
203
  e.icon && (t.dataset.moIcon = e.icon);
192
- const r = document.createElement("div");
193
- r.className = "mo-card";
194
- const i = this._getData();
195
- if (i && typeof i.renderCard == "function")
196
- i.renderCard(e, r);
204
+ const s = document.createElement("div");
205
+ s.className = "mo-card";
206
+ const o = this._getData();
207
+ if (o && typeof o.renderCard == "function")
208
+ o.renderCard(e, s);
197
209
  else {
198
210
  if (e.banner) {
199
- const o = document.createElement("div");
200
- o.className = "mo-card-image";
201
- const n = document.createElement("img");
202
- if (n.className = "mo-banner", n.src = e.banner, n.alt = "", o.appendChild(n), e.avatar) {
203
- const l = document.createElement("img");
204
- l.className = "mo-avatar", l.src = e.avatar, l.alt = "", o.appendChild(l);
211
+ const i = document.createElement("div");
212
+ i.className = "mo-card-image";
213
+ const a = document.createElement("img");
214
+ if (a.className = "mo-banner", a.src = e.banner, a.alt = "", i.appendChild(a), e.avatar) {
215
+ const n = document.createElement("img");
216
+ n.className = "mo-avatar", n.src = e.avatar, n.alt = "", i.appendChild(n);
205
217
  }
206
- r.appendChild(o);
218
+ s.appendChild(i);
207
219
  }
208
- const s = document.createElement("div");
209
- if (s.className = "mo-card-body", e.title) {
210
- const o = document.createElement("h3");
211
- o.textContent = e.title, s.appendChild(o);
220
+ const r = document.createElement("div");
221
+ if (r.className = "mo-card-body", e.title) {
222
+ const i = document.createElement("h3");
223
+ i.textContent = e.title, r.appendChild(i);
212
224
  }
213
225
  if (e.meta) {
214
- const o = document.createElement("p");
215
- o.className = "mo-meta", o.textContent = e.meta, s.appendChild(o);
226
+ const i = document.createElement("p");
227
+ i.className = "mo-meta", i.textContent = e.meta, r.appendChild(i);
216
228
  }
217
229
  if (e.text) {
218
- const o = document.createElement("p");
219
- o.textContent = e.text, s.appendChild(o);
230
+ const i = document.createElement("p");
231
+ i.textContent = e.text, r.appendChild(i);
220
232
  }
221
- r.appendChild(s);
233
+ s.appendChild(r);
222
234
  }
223
- return t.appendChild(r), t;
235
+ return t.appendChild(s), t;
224
236
  }
225
237
  _createArrow(e) {
226
238
  const t = document.createElement("span");
227
239
  t.className = "mo-arrow js-mo-arrow", e.prepend(t);
228
240
  }
241
+ _injectAdSlots(e) {
242
+ const t = this._getData();
243
+ if (!t || !t.adSlots || !e.length) return;
244
+ const { mode: s, interval: o, style: r } = t.adSlots, i = r === "fullwidth", a = this.element;
245
+ let n = !1;
246
+ if (s === "every_n")
247
+ e.forEach((c, d) => {
248
+ if ((t._adRealCount + d + 1) % o === 0) {
249
+ const m = this._createAdSlot(i);
250
+ c.after(m), m.dataset.moAdPosition = String(Array.from(a.children).indexOf(m)), this._adObserver && this._adObserver.observe(m), i && (n = !0);
251
+ }
252
+ });
253
+ else if (s === "random") {
254
+ let c = t._adRealCount % o, d = 0;
255
+ for (; d < e.length; ) {
256
+ const m = o - c, u = e.slice(d, d + m);
257
+ if (u.length === m) {
258
+ const E = u[Math.floor(Math.random() * u.length)], g = this._createAdSlot(i);
259
+ E.after(g), g.dataset.moAdPosition = String(Array.from(a.children).indexOf(g)), this._adObserver && this._adObserver.observe(g), i && (n = !0), c = 0;
260
+ } else
261
+ c += u.length;
262
+ d += m;
263
+ }
264
+ }
265
+ t._adRealCount += e.length, n && this.refresh();
266
+ }
267
+ _createAdSlot(e) {
268
+ const t = document.createElement("li");
269
+ return t.className = "mo-ad-slot", e && t.classList.add("mo-fullwidth"), t;
270
+ }
229
271
  _observeItems(e) {
230
272
  this._observer && e.forEach((t) => {
231
273
  t.classList.contains("mo-visible") || this._observer.observe(t);
232
274
  });
233
275
  }
234
276
  };
235
- g(d, "instances", /* @__PURE__ */ new Set());
236
- let y = d;
277
+ p(f, "instances", /* @__PURE__ */ new Set());
278
+ let y = f;
237
279
  export {
238
280
  y as MoTimeline,
239
281
  y as default
@@ -1,6 +1,6 @@
1
- (function(d,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(d=typeof globalThis<"u"?globalThis:d||self,a(d.MoTimeline={}))})(this,function(d){"use strict";var I=Object.defineProperty;var C=(d,a,c)=>a in d?I(d,a,{enumerable:!0,configurable:!0,writable:!0,value:c}):d[a]=c;var _=(d,a,c)=>C(d,typeof a!="symbol"?a+"":a,c);/*!
2
- * moTimeline v2.10.0
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
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const a=new WeakMap,c={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},b="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 v(){const m=window.innerWidth;return m<600?"xs":m<992?"sm":m<1200?"md":"lg"}function E(m,e=100){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>m(...r),e)}}function g(m){return m?{o:m.offsetTop,h:m.offsetHeight,gppu:m.offsetTop+m.offsetHeight}:{o:0,h:0,gppu:0}}function w(m,e){const t=[];let r=m.previousElementSibling;for(;r;)(!e||r.matches(e))&&t.push(r),r=r.previousElementSibling;return t}const h=class h{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({},c,t),this.settings.columnCount=Object.assign({},c.columnCount,t.columnCount),this._resizeHandler=E(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(a.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});if(a.set(e,t),h.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 r=t.animate===!0?"fade":t.animate;e.classList.add("mo-animate",`mo-animate-${r}`),this._observer=new IntersectionObserver(i=>{i.forEach(s=>{s.isIntersecting&&(s.target.classList.add("mo-visible"),this._observer.unobserve(s.target))})},{threshold:.1})}this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){h.instances.forEach(e=>{const t=e.element,r=a.get(t);r&&(r.col=r.columnCount[v()],e._setDivider(),Array.from(t.children).forEach(i=>{e._setPostPosition(i)}))})}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 r=this.element,i=this._getData();if(!i)return;const s=this._createItemElement(e),o=Array.from(r.children).filter(l=>l.classList.contains("js-mo-item")),n=t==null?Math.floor(Math.random()*(o.length+1)):Math.max(0,Math.min(t,o.length));if(n>=o.length?r.appendChild(s):r.insertBefore(s,o[n]),s.id||(s.id="moT"+crypto.randomUUID()+"_"+n),s.classList.add("mo-item","js-mo-item"),e.fullWidth)s.classList.add("mo-fullwidth","js-mo-fullwidth");else if(i.randomFullWidth){const l=i.randomFullWidth===!0?.33:i.randomFullWidth;Math.random()<l&&s.classList.add("mo-fullwidth","js-mo-fullwidth")}return i.showBadge&&this._createBadge(s,n+1),i.showArrow&&this._createArrow(s),i.showBadge&&i.showCounterStyle==="counter"&&Array.from(r.querySelectorAll(".js-mo-item")).forEach((l,f)=>{const u=l.querySelector(".js-mo-badge");u&&(u.textContent=f+1)}),i.lastItemIdx=Array.from(r.children).length,a.set(r,i),this.refresh(),this._observeItems([s]),s.querySelectorAll("img").forEach(l=>{l.complete||l.addEventListener("load",this._resizeHandler,{once:!0})}),s}destroy(){window.removeEventListener("resize",this._resizeHandler),this._observer&&(this._observer.disconnect(),this._observer=null),a.delete(this.element),h.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 a.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[v()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const r=t.lastItemIdx,i=Array.from(e.children),s=i.slice(r);if(s.length!==0){if(s.forEach((o,n)=>{o.id||(o.id="moT"+crypto.randomUUID()+"_"+(n+r)),o.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const o=t.randomFullWidth===!0?.33:t.randomFullWidth;s.forEach(n=>{!n.classList.contains("mo-fullwidth")&&Math.random()<o&&n.classList.add("mo-fullwidth","js-mo-fullwidth")})}s.forEach((o,n)=>{t.showBadge&&this._createBadge(o,n+r+1),t.showArrow&&this._createArrow(o)}),t.lastItemIdx=i.length,a.set(e,t),this.refresh(),this._observeItems(s),s.forEach(o=>{o.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 r=t.col,i=w(e,".js-mo-inverted")[0]||null,s=w(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,o=g(s),n=g(i),l=g(e);let f=0,u=0;if(r>1){o.gppu>l.o+1&&(f=1),n.gppu>o.gppu&&(f=0);const y=e.previousElementSibling;y&&Math.abs(l.o-g(y).o)<40&&(u=1)}return{lr:f,badge_offset:u}}_createBadge(e,t){const r=this._getData(),i=document.createElement("span");if(i.className="mo-badge js-mo-badge",r.showCounterStyle==="none")i.style.opacity="0";else if(r.showCounterStyle==="image"){const s=document.createElement("img");s.className="mo-badge-icon",s.alt="",s.src=e.dataset.moIcon||b,i.appendChild(s)}else i.textContent=t;e.prepend(i)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const r=document.createElement("div");r.className="mo-card";const i=this._getData();if(i&&typeof i.renderCard=="function")i.renderCard(e,r);else{if(e.banner){const o=document.createElement("div");o.className="mo-card-image";const n=document.createElement("img");if(n.className="mo-banner",n.src=e.banner,n.alt="",o.appendChild(n),e.avatar){const l=document.createElement("img");l.className="mo-avatar",l.src=e.avatar,l.alt="",o.appendChild(l)}r.appendChild(o)}const s=document.createElement("div");if(s.className="mo-card-body",e.title){const o=document.createElement("h3");o.textContent=e.title,s.appendChild(o)}if(e.meta){const o=document.createElement("p");o.className="mo-meta",o.textContent=e.meta,s.appendChild(o)}if(e.text){const o=document.createElement("p");o.textContent=e.text,s.appendChild(o)}r.appendChild(s)}return t.appendChild(r),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}_observeItems(e){this._observer&&e.forEach(t=>{t.classList.contains("mo-visible")||this._observer.observe(t)})}};_(h,"instances",new Set);let p=h;d.MoTimeline=p,d.default=p,Object.defineProperties(d,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
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"}})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motimeline",
3
- "version": "2.10.0",
3
+ "version": "2.12.0",
4
4
  "description": "Responsive two-column timeline layout library. Plain JavaScript, no dependencies.",
5
5
  "main": "./dist/moTimeline.cjs",
6
6
  "module": "./dist/moTimeline.js",
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * moTimeline v2.10.0 — CSS
2
+ * moTimeline v2.12.0 — CSS
3
3
  */
4
4
 
5
5
  /* ── CSS custom properties (easy override) ─────────────────── */
@@ -299,3 +299,12 @@
299
299
  opacity: 1;
300
300
  transform: translateX(0);
301
301
  }
302
+
303
+ /* ═══════════════════════════════════════════════════════════════
304
+ AD SLOTS — injected by adSlots option
305
+ ═══════════════════════════════════════════════════════════════ */
306
+
307
+ /* Minimum height so IntersectionObserver can fire before content loads */
308
+ .mo-timeline > .mo-item.mo-ad-slot {
309
+ min-height: 100px;
310
+ }
package/src/moTimeline.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * moTimeline v2.10.0
2
+ * moTimeline v2.12.0
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
@@ -24,6 +24,7 @@ const DEFAULTS = {
24
24
  randomFullWidth: 0, // 0 = off; 0–1 = probability per item; true = 0.33
25
25
  animate: false, // false | 'fade' | 'slide'
26
26
  renderCard: null, // (item, cardEl) => void — custom card renderer; skips built-in HTML
27
+ adSlots: null, // { mode, interval, style, onEnterViewport } — see docs
27
28
  };
28
29
 
29
30
  const DEFAULT_BADGE_ICON = "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>";
@@ -115,6 +116,20 @@ export class MoTimeline {
115
116
  }, { threshold: 0.1 });
116
117
  }
117
118
 
119
+ if (data.adSlots) {
120
+ data._adRealCount = 0;
121
+ if (typeof data.adSlots.onEnterViewport === 'function') {
122
+ this._adObserver = new IntersectionObserver((entries) => {
123
+ entries.forEach((entry) => {
124
+ if (entry.isIntersecting) {
125
+ this._adObserver.unobserve(entry.target);
126
+ data.adSlots.onEnterViewport(entry.target, Number(entry.target.dataset.moAdPosition));
127
+ }
128
+ });
129
+ }, { threshold: 0.5 });
130
+ }
131
+ }
132
+
118
133
  this._initialized = true;
119
134
  window.addEventListener('resize', this._resizeHandler);
120
135
 
@@ -220,12 +235,37 @@ export class MoTimeline {
220
235
  return newEl;
221
236
  }
222
237
 
238
+ clear() {
239
+ const data = this._getData();
240
+ if (!data) return;
241
+
242
+ // Disconnect observers — they remain alive and will re-observe new items
243
+ if (this._observer) this._observer.disconnect();
244
+ if (this._adObserver) this._adObserver.disconnect();
245
+
246
+ // Remove all library-managed elements (real items + ad slots)
247
+ Array.from(this.element.children).forEach((child) => {
248
+ if (child.classList.contains('js-mo-item') || child.classList.contains('mo-ad-slot')) {
249
+ child.remove();
250
+ }
251
+ });
252
+
253
+ // Reset counters — ready for a fresh addItems() call
254
+ data.lastItemIdx = 0;
255
+ if (data.adSlots) data._adRealCount = 0;
256
+ }
257
+
223
258
  destroy() {
224
259
  window.removeEventListener('resize', this._resizeHandler);
225
260
  if (this._observer) {
226
261
  this._observer.disconnect();
227
262
  this._observer = null;
228
263
  }
264
+ if (this._adObserver) {
265
+ this._adObserver.disconnect();
266
+ this._adObserver = null;
267
+ }
268
+ Array.from(this.element.querySelectorAll('.mo-ad-slot')).forEach((s) => s.remove());
229
269
  instanceData.delete(this.element);
230
270
  MoTimeline.instances.delete(this);
231
271
  this.element.style.removeProperty('--mo-card-border-radius');
@@ -300,6 +340,7 @@ export class MoTimeline {
300
340
  this.refresh();
301
341
 
302
342
  this._observeItems(newItems);
343
+ this._injectAdSlots(newItems);
303
344
 
304
345
  // Re-layout after any unloaded images finish, because offsetHeight is
305
346
  // based on text-only height until images are ready.
@@ -436,6 +477,57 @@ export class MoTimeline {
436
477
  el.prepend(span);
437
478
  }
438
479
 
480
+ _injectAdSlots(newItems) {
481
+ const data = this._getData();
482
+ if (!data || !data.adSlots || !newItems.length) return;
483
+
484
+ const { mode, interval, style } = data.adSlots;
485
+ const fullWidth = style === 'fullwidth';
486
+ const el = this.element;
487
+ let needsRefresh = false;
488
+
489
+ if (mode === 'every_n') {
490
+ newItems.forEach((item, i) => {
491
+ if ((data._adRealCount + i + 1) % interval === 0) {
492
+ const slot = this._createAdSlot(fullWidth);
493
+ item.after(slot);
494
+ slot.dataset.moAdPosition = String(Array.from(el.children).indexOf(slot));
495
+ if (this._adObserver) this._adObserver.observe(slot);
496
+ if (fullWidth) needsRefresh = true;
497
+ }
498
+ });
499
+ } else if (mode === 'random') {
500
+ let pageOffset = data._adRealCount % interval;
501
+ let i = 0;
502
+ while (i < newItems.length) {
503
+ const remaining = interval - pageOffset;
504
+ const chunk = newItems.slice(i, i + remaining);
505
+ if (chunk.length === remaining) {
506
+ const anchor = chunk[Math.floor(Math.random() * chunk.length)];
507
+ const slot = this._createAdSlot(fullWidth);
508
+ anchor.after(slot);
509
+ slot.dataset.moAdPosition = String(Array.from(el.children).indexOf(slot));
510
+ if (this._adObserver) this._adObserver.observe(slot);
511
+ if (fullWidth) needsRefresh = true;
512
+ pageOffset = 0;
513
+ } else {
514
+ pageOffset += chunk.length;
515
+ }
516
+ i += remaining;
517
+ }
518
+ }
519
+
520
+ data._adRealCount += newItems.length;
521
+ if (needsRefresh) this.refresh();
522
+ }
523
+
524
+ _createAdSlot(fullWidth) {
525
+ const slot = document.createElement('li');
526
+ slot.className = 'mo-ad-slot';
527
+ if (fullWidth) slot.classList.add('mo-fullwidth');
528
+ return slot;
529
+ }
530
+
439
531
  _observeItems(items) {
440
532
  if (!this._observer) return;
441
533
  items.forEach((item) => {