Jake Vanderwerf
2026-01-01 f0fb57aea8652e4c66290fc34a9b4f5b10cd5530
=FeedBlock updates for timeline items
4 files modified
188 ■■■■ changed files
build/feed/view.asset.php 2 ●●● patch | view | raw | blame | history
build/feed/view.js 2 ●●● patch | view | raw | blame | history
inc/rest/routes/FeedRoutes.php 3 ●●●●● patch | view | raw | blame | history
src/feed/view.js 181 ●●●● patch | view | raw | blame | history
build/feed/view.asset.php
@@ -1 +1 @@
<?php return array('dependencies' => array(), 'version' => 'ee536fd4d956e3380ce5');
<?php return array('dependencies' => array(), 'version' => '1f14f5ad570227cddb2c');
build/feed/view.js
@@ -1 +1 @@
(()=>{class e{constructor(){this.container=document.querySelector("section.feed-block"),this.container&&(this.a11y=window.jvbA11y,this.cache=new window.jvbCache("feed"),this.error=window.jvbError,this.config={source:"",context:"",highlight:null,gallery:!1,view:this.cache.get("feedView")||"grid",...this.container.dataset},this.initElements(),this.initFilters(),this.loadWhenAble())}loadWhenAble(){"requestIdleCallback"in window?requestIdleCallback((()=>{this.initTaxonomies(),this.initStore(),this.initListeners(),this.initGallery()}),{timeout:2e3}):setTimeout((()=>{this.initTaxonomies(),this.initStore(),this.initListeners(),this.initGallery()}),100)}initElements(){var e;this.currentTaxonomies=new Set,this.taxonomyFilters={},this.elements={filterTrigger:"[data-filter]",filters:{content:'[data-filter="content"]',orderby:'[data-filter="orderby"]',order:'[data-filter="order"]',match:'[data-filter="match"]',favourites:'[data-filter="favourites"]',taxonomy:'[data-filter^="taxonomy"]'},selectedTax:".selected-items",clearFilter:"button.clear-filters",loadMore:"button.load-more",filterContainer:".filters",grid:".item-grid"},this.ui=window.uiFromSelectors(this.elements),this.ui.content=null!==(e=this.ui.filterContainer.querySelectorAll('[name="content"]'))&&void 0!==e&&e,this.ui.taxonomies=this.ui.filterContainer.querySelectorAll("[data-taxonomy]"),this.ui.content&&this.ui.content.length>0?this.contentTypes=Array.from(this.ui.content).map((e=>e.value)):this.contentTypes=[this.container.dataset.content],this.ui.taxonomies.length>0?this.taxonomies=Array.from(this.ui.taxonomies).map((e=>e.dataset.taxonomy)):this.taxonomies=[]}async initTaxonomies(){this.selector=window.jvbSelector;const e=document.querySelectorAll('[data-filter="taxonomy"]');this.selector.isInitializing=!0,e.forEach((e=>{const t=e.dataset.taxonomy;this.currentTaxonomies.add(t),this.selector.registerFilterButton(e,{button:e,buttonSelector:'[data-filter="taxonomy"]',selected:this.ui.selectedTax}),this.addTaxonomyPreloadListeners(e,t)})),this.selector.isInitializing=!1,this.selector.subscribe(((e,t)=>{"selected-terms"===e&&this.handleTaxonomyChange(t)}))}addTaxonomyPreloadListeners(e,t){const i=()=>{this.selector.preloadTaxonomy(t)};e.addEventListener("mouseenter",i,{once:!0}),e.addEventListener("pointerdown",i,{once:!0}),e.addEventListener("focus",i,{once:!0})}handleTaxonomyChange(e){const{terms:t,taxonomy:i}=e;t.size>0?this.taxonomyFilters[i]=Array.from(t.keys()):delete this.taxonomyFilters[i];let s={page:1};Object.keys(this.taxonomyFilters).length>0&&(s.taxonomy=this.taxonomyFilters),this.updateFilter(s)}clearAllTaxonomies(){this.taxonomyFilters={},window.removeChildren(this.ui.selectedTax),this.updateFilter({taxonomy:null,page:1})}initFilters(){this.filters={content:this.contentTypes[0],orderby:"date",order:"desc",page:1},this.config.context&&(this.filters.context=this.config.context),this.config.source&&(this.filters.source=this.config.source),this.processCachedFilters(),this.processURLFilters(),this.syncUIToFilters()}syncUIToFilters(){this.ui.filterContainer&&Object.entries(this.filters).forEach((([e,t])=>{const i=this.ui.filterContainer.querySelector(`[data-filter="${e}"][value="${t}"]`);i&&(i.checked=!0)})),this.updateContentFor(this.filters.content)}nextPage(){this.store.setFilter("page",this.store.filters.page++)}initStore(){const e=window.jvbStore.register("feed",{storeName:"feed",endpoint:"feed",keyPath:"id",indexes:[{name:"content",keyPath:"content"},{name:"taxonomy",keyPath:"taxonomy"},{name:"user",keyPath:"user"},{name:"date",keyPath:"modified"},{name:"title",keyPath:"title"}],filters:this.filters,TTL:216e5,showLoading:!0,required:"content",delayFetch:!0});this.store=e.feed,this.store.subscribe(((e,t)=>{"data-loaded"===e&&(this.renderItems(),this.ui.loadMore.hidden=!0,this.store.lastResponse&&this.store.lastResponse.has_more&&(this.ui.loadMore.hidden=!this.store.lastResponse.has_more))}))}initGallery(){this.gallery=!!this.config.gallery&&window.jvbGallery,this.gallery&&this.gallery.subscribe(((e,t)=>{"load-more"===e&&this.store.lastResponse&&this.store.lastResponse.has_more&&this.nextPage()}))}processCachedFilters(){Object.keys(this.filters).forEach((e=>{let t=this.cache.get(`${this.config.source}_${this.config.context}_${e}`);t&&t!==this.filters[e]&&(this.filters[e]=t)}))}processURLFilters(){if(this.filters.page>1)return!1;const e=new URLSearchParams(window.location.search);if(!e.toString())return!1;["content","order","orderby","favourites","match"].forEach((t=>{let i=e.get(`f_${t}`);if(i){this.filters[t]=i;let e=this.ui.filters[t];e&&(e.checked=!0)}}));let t=!1;if(e.forEach(((e,i)=>{if(i.startsWith("f_tax_")){t=!0;const s=i.replace("f_tax_","");this.taxonomyFilters[s]||(this.taxonomyFilters[s]=[]),this.taxonomyFilters[s]=e.split(",").map(Number)}})),this.ui.filterContainer&&t)for(let[e,t]in Object.entries(this.taxonomyFilters)){let i=this.ui.filterContainer.querySelector(`[data-taxonomy="${e}"]`);i&&(i.dataset.fieldId?(this.selector.get(i.dataset.fieldId).selectedTerms=new Set(t),this.selector.initFieldDisplay(i.dataset.fieldId)):this.selector.registerField(i,{button:i,buttonSelector:'[data-filter="taxonomy"]',selected:this.ui.selectedTax,selectedItems:t}))}return!0}updateURL(){const e=new URLSearchParams;["content","order","orderby","match"].forEach((t=>{this.filters[t]&&e.set(`f_${t}`,this.filters[t])})),Object.entries(this.taxonomyFilters).forEach((([t,i])=>{i.length>0&&e.set(`f_tax_${t}`,i.join(","))}));const t=`${window.location.pathname}${e.toString()?"?"+e.toString():""}`;window.history.pushState({filters:this.filters},"",t)}renderItems(){let e=this.store.getFiltered();if(1===this.store.filters.page&&window.removeChildren(this.ui.grid),0===e.length)return void this.a11y.announceItems(0,this.store.filters.page>0);const t=document.createDocumentFragment(),i=s=>{const r=Math.min(s+10,e.length);for(let i=s;i<r;i++){const s=e[i],r=this.createItemElement(s);t.appendChild(r)}r<e.length?requestAnimationFrame((()=>i(r))):(this.removePlaceholders(),this.ui.grid.append(t),this.config.gallery&&this.gallery.updateGalleryItems(this.gallery.getGalleryItems()),this.a11y.makeNavigable(this.ui.grid.querySelectorAll(".item:not([data-keyboard-nav])")),this.a11y.announceItems(e.length,this.store.filters.page>1,this.store.hasMore))};e.length>0?i(0):this.a11y.announceItems(0,this.store.filters.page>1,!1),this.ui.filters.match&&(this.ui.filters.match.hidden=0===Object.keys(this.taxonomyFilters).length),this.ui.clearFilter&&(this.ui.clearFilter.hidden=0===Object.keys(this.taxonomyFilters).length)}createItemElement(e){let t=window.getTemplate(`feedItem${window.uppercaseFirst(e.content)}`);if(Object.hasOwn(t.dataset,"timeline"))return this.createTimelineElement(e,t);for(let[i,s]of Object.entries(e.fields)){let r=t.querySelector(`[data-field="${i}"]`);r&&(Object.keys(e.images).map((e=>parseInt(e))).includes(s)?[r.src,r.srcset,r.alt]=[e.images[s].tiny,`${e.images[s].tiny} 50w, ${e.images[s].small} 300w, ${e.images[s].medium} 1024w`,e.images[s]["image-alt-text"]]:"TIME"===r.tagName?(r.setAttribute("datetime",s),r.textContent=window.formatTimeAgo(s)):r.textContent=s,""===s&&r.remove())}let i=t.querySelector("a");var s;return i&&""!==e.url&&([i.href,i.title]=[e.url,`View ${null!==(s=e.fields.post_title)&&void 0!==s?s:"Item"}`]),t}createTimelineElement(e,t){var i,s;console.log(e),console.log(t);let[r,n,o,a,l,h,d,c,m,u]=[t,t.querySelector("a"),t.querySelector("img.before"),t.querySelector("img.after"),t.querySelector("summary span:last-of-type"),t.querySelector("p.started time"),t.querySelector("p.updated time"),t.querySelector("p.total b"),t.querySelector(".term-list"),Object.values(e.fields.order)],f=u.length-1,y=e.images[u[0].post_thumbnail],g=e.images[u[f].post_thumbnail];return[r.dataset.id,n.href,o.src,o.dataset.small,o.dataset.medium,a.src,a.dataset.small,a.dataset.medium,l.textContent,h.textContent,d.textContent,c.textContent]=[e.id,e.url,y.tiny,y.small,y.medium,g.tiny,g.small,g.medium,`${l.textContent} ${f} Tx`,null!==(i=u[0].date)&&void 0!==i?i:e.date,null!==(s=u[f].date)&&void 0!==s?s:"",`${f} Treatments`],t}removePlaceholders(){const e=this.ui.grid.querySelectorAll(".placeholder");e.length>0&&e.forEach((e=>e.remove()))}addPlaceholders(){let e=this.contentTypes.length;const t=document.createDocumentFragment();for(let i=0;i<12;i++){let i,s=window.getTemplate("placeholderTemplate"),r=Math.floor(Math.random()*e);i=this.ui.content&&this.ui.content.length>0?this.ui.content.filter((e=>e.value===this.contentTypes[r])).querySelector(".icon").cloneNode(!0):window.getIcon(this.container.dataset.icon),s.append(i),t.append(s)}this.ui.grid.append(t)}updateFilter(e){let t=["taxonomy","favourites","match",...Object.keys(this.filters)];e=Object.keys(e).filter((e=>t.includes(e))).reduce(((t,i)=>(t[i]=e[i],t)),{}),window.getDifferences.map(this.filters,e)&&(this.filters={...this.filters,...e},this.updateURL(),this.store.setFilters(e))}updateContentFor(e){this.ui.filterContainer.querySelectorAll('[data-filter="taxonomy"]').forEach((t=>{const i=t.dataset.for?.split(",")||[];t.hidden=i.length>0&&!i.includes(e)})),this.ui.filterContainer.querySelectorAll("[data-for]").forEach((t=>{const i=t.dataset.for?.split(",")||[];i.length>0&&(t.hidden=!i.includes(e),t.hidden&&t.checked&&(t.checked=!1))}));const t=this.ui.filterContainer.querySelector('[name="orderby"]:checked');this.updateOrderDirectionVisibility(t?.value)}updateOrderDirectionVisibility(e){const t=this.ui.filterContainer.querySelector(".order-direction");if(t){const i=t.dataset.forOrder?.split(",")||[];t.hidden=i.length>0&&!i.includes(e)}}initListeners(){this.popStateHandler=this.handlePopState.bind(this),this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.imageObserver=null,this.resizeObserver=null,"IntersectionObserver"in window&&(this.imageObserver=new IntersectionObserver((e=>{e.forEach((e=>{this.loadImage(e.target),this.imageObserver.unobserve(e.target)}))}),{rootMargin:"100px",threshold:.1})),"ResizeObserver"in window?this.resizeObserver=new ResizeObserver((()=>{window.debouncer.schedule("feed-update-images",(()=>this.updateImageSizes()),250)})):window.addEventListener("resize",(()=>{window.debouncer.schedule("feed-update-images",(()=>this.updateImageSizes()),250)})),window.addEventListener("popstate",this.popStateHandler),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler)}handlePopState(e){e.state?.filters&&this.processURLFilters()&&(this.store.setFilters(this.filters),this.a11y.announce("Feed filters updated from browser history"))}handleClick(e){window.targetCheck(e,this.elements.loadMore)?this.nextPage():window.targetCheck(e,this.elements.clearFilter)?this.clearAllTaxonomies():window.targetCheck(e,".remove-item")&&this.handleRemoveSelectedTerm(e)}handleRemoveSelectedTerm(e){const t=e.target.closest(".selected-item");if(!t)return;const i=parseInt(t.dataset.id),s=t.dataset.taxonomy;this.taxonomyFilters[s]&&(this.taxonomyFilters[s]=this.taxonomyFilters[s].filter((e=>e!==i)),0===this.taxonomyFilters[s].length&&delete this.taxonomyFilters[s]),t.remove(),this.updateFilter({taxonomy:Object.keys(this.taxonomyFilters).length>0?this.taxonomyFilters:null,page:1})}handleChange(e){let t=e.target;Object.hasOwn(t.dataset,"filter")&&("content"===t.dataset.filter?(this.updateContentFor(t.value),this.updateFilter({content:t.value,page:1})):"orderby"===t.dataset.filter?(this.updateOrderDirectionVisibility(t.value),this.updateFilter({orderby:t.value,page:1})):"order"===t.dataset.filter?this.updateFilter({order:t.value,page:1}):"match"===t.dataset.filter?this.updateFilter({match:t.checked?"all":"any",page:1}):"favourites"===t.dataset.filter&&this.updateFilter({favourites:t.checked,page:1}))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.feedBlock=new e)}))}))})();
(()=>{class e{constructor(){this.container=document.querySelector("section.feed-block"),this.container&&(this.a11y=window.jvbA11y,this.cache=new window.jvbCache("feed"),this.error=window.jvbError,this.config={source:"",context:"",highlight:null,gallery:!1,view:this.cache.get("feedView")||"grid",...this.container.dataset},this.initElements(),this.initFilters(),this.loadWhenAble())}loadWhenAble(){"requestIdleCallback"in window?requestIdleCallback((()=>{this.initTaxonomies(),this.initStore(),this.initListeners(),this.initGallery()}),{timeout:2e3}):setTimeout((()=>{this.initTaxonomies(),this.initStore(),this.initListeners(),this.initGallery()}),100)}initElements(){var e;this.currentTaxonomies=new Set,this.taxonomyFilters={},this.elements={filterTrigger:"[data-filter]",filters:{content:'[data-filter="content"]',orderby:'[data-filter="orderby"]',order:'[data-filter="order"]',match:'[data-filter="match"]',favourites:'[data-filter="favourites"]',taxonomy:'[data-filter^="taxonomy"]'},selectedTax:".selected-items",clearFilter:"button.clear-filters",loadMore:"button.load-more",filterContainer:".filters",grid:".item-grid"},this.ui=window.uiFromSelectors(this.elements),this.ui.content=null!==(e=this.ui.filterContainer.querySelectorAll('[name="content"]'))&&void 0!==e&&e,this.ui.taxonomies=this.ui.filterContainer.querySelectorAll("[data-taxonomy]"),this.ui.content&&this.ui.content.length>0?this.contentTypes=Array.from(this.ui.content).map((e=>e.value)):this.contentTypes=[this.container.dataset.content],this.ui.taxonomies.length>0?this.taxonomies=Array.from(this.ui.taxonomies).map((e=>e.dataset.taxonomy)):this.taxonomies=[]}async initTaxonomies(){this.selector=window.jvbSelector;const e=document.querySelectorAll('[data-filter="taxonomy"]');this.selector.isInitializing=!0,e.forEach((e=>{const t=e.dataset.taxonomy;this.currentTaxonomies.add(t),this.selector.registerFilterButton(e,{button:e,buttonSelector:'[data-filter="taxonomy"]',selected:this.ui.selectedTax}),this.addTaxonomyPreloadListeners(e,t)})),this.selector.isInitializing=!1,this.selector.subscribe(((e,t)=>{"selected-terms"===e&&this.handleTaxonomyChange(t)}))}addTaxonomyPreloadListeners(e,t){const i=()=>{this.selector.preloadTaxonomy(t)};e.addEventListener("mouseenter",i,{once:!0}),e.addEventListener("pointerdown",i,{once:!0}),e.addEventListener("focus",i,{once:!0})}handleTaxonomyChange(e){const{terms:t,taxonomy:i}=e;t.size>0?this.taxonomyFilters[i]=Array.from(t.keys()):delete this.taxonomyFilters[i];let s={page:1};Object.keys(this.taxonomyFilters).length>0&&(s.taxonomy=this.taxonomyFilters),this.updateFilter(s)}clearAllTaxonomies(){this.taxonomyFilters={},window.removeChildren(this.ui.selectedTax),this.updateFilter({taxonomy:null,page:1})}initFilters(){this.filters={content:this.contentTypes[0],orderby:"date",order:"desc",page:1},this.config.context&&(this.filters.context=this.config.context),this.config.source&&(this.filters.source=this.config.source),this.processCachedFilters(),this.processURLFilters(),this.syncUIToFilters()}syncUIToFilters(){this.ui.filterContainer&&Object.entries(this.filters).forEach((([e,t])=>{const i=this.ui.filterContainer.querySelector(`[data-filter="${e}"][value="${t}"]`);i&&(i.checked=!0)})),this.updateContentFor(this.filters.content)}nextPage(){this.store.setFilter("page",this.store.filters.page++)}initStore(){const e=window.jvbStore.register("feed",{storeName:"feed",endpoint:"feed",keyPath:"id",indexes:[{name:"content",keyPath:"content"},{name:"taxonomy",keyPath:"taxonomy"},{name:"user",keyPath:"user"},{name:"date",keyPath:"modified"},{name:"title",keyPath:"title"}],filters:this.filters,TTL:216e5,showLoading:!0,required:"content",delayFetch:!0});this.store=e.feed,this.store.subscribe(((e,t)=>{"data-loaded"===e&&(this.renderItems(),this.ui.loadMore.hidden=!0,this.store.lastResponse&&this.store.lastResponse.has_more&&(this.ui.loadMore.hidden=!this.store.lastResponse.has_more))}))}initGallery(){this.gallery=!!this.config.gallery&&window.jvbGallery,this.gallery&&this.gallery.subscribe(((e,t)=>{"load-more"===e&&this.store.lastResponse&&this.store.lastResponse.has_more&&this.nextPage()}))}processCachedFilters(){Object.keys(this.filters).forEach((e=>{let t=this.cache.get(`${this.config.source}_${this.config.context}_${e}`);t&&t!==this.filters[e]&&(this.filters[e]=t)}))}processURLFilters(){if(this.filters.page>1)return!1;const e=new URLSearchParams(window.location.search);if(!e.toString())return!1;["content","order","orderby","favourites","match"].forEach((t=>{let i=e.get(`f_${t}`);if(i){this.filters[t]=i;let e=this.ui.filters[t];e&&(e.checked=!0)}}));let t=!1;if(e.forEach(((e,i)=>{if(i.startsWith("f_tax_")){t=!0;const s=i.replace("f_tax_","");this.taxonomyFilters[s]||(this.taxonomyFilters[s]=[]),this.taxonomyFilters[s]=e.split(",").map(Number)}})),this.ui.filterContainer&&t)for(let[e,t]in Object.entries(this.taxonomyFilters)){let i=this.ui.filterContainer.querySelector(`[data-taxonomy="${e}"]`);i&&(i.dataset.fieldId?(this.selector.get(i.dataset.fieldId).selectedTerms=new Set(t),this.selector.initFieldDisplay(i.dataset.fieldId)):this.selector.registerField(i,{button:i,buttonSelector:'[data-filter="taxonomy"]',selected:this.ui.selectedTax,selectedItems:t}))}return!0}updateURL(){const e=new URLSearchParams;["content","order","orderby","match"].forEach((t=>{this.filters[t]&&e.set(`f_${t}`,this.filters[t])})),Object.entries(this.taxonomyFilters).forEach((([t,i])=>{i.length>0&&e.set(`f_tax_${t}`,i.join(","))}));const t=`${window.location.pathname}${e.toString()?"?"+e.toString():""}`;window.history.pushState({filters:this.filters},"",t)}renderItems(){let e=this.store.getFiltered();if(1===this.store.filters.page&&window.removeChildren(this.ui.grid),0===e.length)return void this.a11y.announceItems(0,this.store.filters.page>0);const t=document.createDocumentFragment(),i=s=>{const r=Math.min(s+10,e.length);for(let i=s;i<r;i++){const s=e[i],r=this.createItemElement(s);t.appendChild(r)}r<e.length?requestAnimationFrame((()=>i(r))):(this.removePlaceholders(),this.ui.grid.append(t),this.config.gallery&&this.gallery.updateGalleryItems(this.gallery.getGalleryItems()),this.a11y.makeNavigable(this.ui.grid.querySelectorAll(".item:not([data-keyboard-nav])")),this.a11y.announceItems(e.length,this.store.filters.page>1,this.store.hasMore))};e.length>0?i(0):this.a11y.announceItems(0,this.store.filters.page>1,!1),this.ui.filters.match&&(this.ui.filters.match.hidden=0===Object.keys(this.taxonomyFilters).length),this.ui.clearFilter&&(this.ui.clearFilter.hidden=0===Object.keys(this.taxonomyFilters).length)}createItemElement(e){let t=window.getTemplate(`feedItem${window.uppercaseFirst(e.content)}`);if(Object.hasOwn(t.dataset,"timeline"))return this.createTimelineElement(e,t);for(let[i,s]of Object.entries(e.fields)){let r=t.querySelector(`[data-field="${i}"]`);r&&(Object.keys(e.images).map((e=>parseInt(e))).includes(s)?[r.src,r.srcset,r.alt]=[e.images[s].tiny,`${e.images[s].tiny} 50w, ${e.images[s].small} 300w, ${e.images[s].medium} 1024w`,e.images[s]["image-alt-text"]]:"TIME"===r.tagName?(r.setAttribute("datetime",s),r.textContent=window.formatTimeAgo(s)):r.textContent=s,""===s&&r.remove())}let i=t.querySelector("a");var s;return i&&""!==e.url&&([i.href,i.title]=[e.url,`View ${null!==(s=e.fields.post_title)&&void 0!==s?s:"Item"}`]),t}splitIDs(e){return e.split(",").map((e=>parseInt(trim(e)))).filter((e=>e))}isImageField(e,t){return!(!Object.hasOwn(e,"images")||0===Object.keys(e.images).length)&&(this.splitIDs(t).forEach((t=>{if(Object.keys(e.images).includes(t))return!0})),!1)}formatImageFields(e,t,i){if(0!==t.length)if(t.length>1){let s=e.querySelector("img");if(!s)return;t.forEach((t=>{let r=s.cloneNode(!0);this.formatImageField(r,t,i),e.append(r)})),s.remove()}else{if("IMG"!==e.tagName&&!(e=e.querySelector("img")))return;this.formatImageField(e,t[0],i)}}formatImageField(e,t,i){[e.src,e.srset,e.alt]=[i.images[t].tiny,`${i.images[t].tiny} 50w, ${i.images[t].small} 300w, ${i.images[t].medium} 1024w`,i.images[t]["image-alt-text"]]}isTaxonomyField(e,t){return!(!Object.hasOwn(e,"taxonomies")||0===Object.keys(e.taxonomies).length||(Object.keys(e.taxonomies).forEach((e=>{if(e===t)return!0})),1))}formatTaxonomyField(e,t,i,s){if("UL"!==e.tagName||!e.querySelector("li"))return;let r=this.splitIDs(s),o=e.querySelector("li");for(let s of r){var n;let r=null!==(n=t.taxonomies[i][s])&&void 0!==n&&n;if(!r)continue;let a=o.cloneNode(!0),l=a.querySelector("a");l&&([l.href,l.title,l.textContent]=[r.url,`See more ${r.title}`,r.title],e.append(a))}o.remove()}isTimeField(e){return"TIME"===e.tagName||null!==e.querySelector("time")}formatTimeField(e,t){("TIME"===e.tagName||(e=e.querySelector("time")))&&(e.setAttribute("datetime",t),e.textContent=window.formatTimeAgo(t))}formatField(e,t){e.textContent=t}createTimelineElement(e,t){console.log(e),console.log(t);for(let[i,s]of Object.entries(e.fields))if(!["timeline","number"].includes(i)){let r=t.querySelector(`[data-field="${i}"]`);if(r||console.log(`Element Not found for ${i}`),!r||""===s)continue;this.isImageField(e,s)?this.formatImageFields(r,s,e):this.isTaxonomyField(e,i)?this.formatTaxonomyField(r,e,i,s):this.isTimeField(r)?this.formatTimeField(r,s):this.formatField(r,s)}let[i,s,r,o]=[t.querySelector("span.after-text"),t.querySelector('[data-field="number"] b'),t.querySelector('[data-field="started"] time'),t.querySelector('[data-field="updated"] time')];return i&&(i.textContent=`After ${e.fields.number} Tx`),s&&(s.textContent=e.fields.number),r&&this.formatTimeField(r,e.fields.timeline[0].post_date),o&&this.formatTimeField(o,e.fields.timeline[e.fields.timeline.length-1].post_date),t}removePlaceholders(){const e=this.ui.grid.querySelectorAll(".placeholder");e.length>0&&e.forEach((e=>e.remove()))}addPlaceholders(){let e=this.contentTypes.length;const t=document.createDocumentFragment();for(let i=0;i<12;i++){let i,s=window.getTemplate("placeholderTemplate"),r=Math.floor(Math.random()*e);i=this.ui.content&&this.ui.content.length>0?this.ui.content.filter((e=>e.value===this.contentTypes[r])).querySelector(".icon").cloneNode(!0):window.getIcon(this.container.dataset.icon),s.append(i),t.append(s)}this.ui.grid.append(t)}updateFilter(e){let t=["taxonomy","favourites","match",...Object.keys(this.filters)];e=Object.keys(e).filter((e=>t.includes(e))).reduce(((t,i)=>(t[i]=e[i],t)),{}),window.getDifferences.map(this.filters,e)&&(this.filters={...this.filters,...e},this.updateURL(),this.store.setFilters(e))}updateContentFor(e){this.ui.filterContainer.querySelectorAll('[data-filter="taxonomy"]').forEach((t=>{const i=t.dataset.for?.split(",")||[];t.hidden=i.length>0&&!i.includes(e)})),this.ui.filterContainer.querySelectorAll("[data-for]").forEach((t=>{const i=t.dataset.for?.split(",")||[];i.length>0&&(t.hidden=!i.includes(e),t.hidden&&t.checked&&(t.checked=!1))}));const t=this.ui.filterContainer.querySelector('[name="orderby"]:checked');this.updateOrderDirectionVisibility(t?.value)}updateOrderDirectionVisibility(e){const t=this.ui.filterContainer.querySelector(".order-direction");if(t){const i=t.dataset.forOrder?.split(",")||[];t.hidden=i.length>0&&!i.includes(e)}}initListeners(){this.popStateHandler=this.handlePopState.bind(this),this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.imageObserver=null,this.resizeObserver=null,"IntersectionObserver"in window&&(this.imageObserver=new IntersectionObserver((e=>{e.forEach((e=>{this.loadImage(e.target),this.imageObserver.unobserve(e.target)}))}),{rootMargin:"100px",threshold:.1})),"ResizeObserver"in window?this.resizeObserver=new ResizeObserver((()=>{window.debouncer.schedule("feed-update-images",(()=>this.updateImageSizes()),250)})):window.addEventListener("resize",(()=>{window.debouncer.schedule("feed-update-images",(()=>this.updateImageSizes()),250)})),window.addEventListener("popstate",this.popStateHandler),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler)}handlePopState(e){e.state?.filters&&this.processURLFilters()&&(this.store.setFilters(this.filters),this.a11y.announce("Feed filters updated from browser history"))}handleClick(e){window.targetCheck(e,this.elements.loadMore)?this.nextPage():window.targetCheck(e,this.elements.clearFilter)?this.clearAllTaxonomies():window.targetCheck(e,".remove-item")&&this.handleRemoveSelectedTerm(e)}handleRemoveSelectedTerm(e){const t=e.target.closest(".selected-item");if(!t)return;const i=parseInt(t.dataset.id),s=t.dataset.taxonomy;this.taxonomyFilters[s]&&(this.taxonomyFilters[s]=this.taxonomyFilters[s].filter((e=>e!==i)),0===this.taxonomyFilters[s].length&&delete this.taxonomyFilters[s]),t.remove(),this.updateFilter({taxonomy:Object.keys(this.taxonomyFilters).length>0?this.taxonomyFilters:null,page:1})}handleChange(e){let t=e.target;Object.hasOwn(t.dataset,"filter")&&("content"===t.dataset.filter?(this.updateContentFor(t.value),this.updateFilter({content:t.value,page:1})):"orderby"===t.dataset.filter?(this.updateOrderDirectionVisibility(t.value),this.updateFilter({orderby:t.value,page:1})):"order"===t.dataset.filter?this.updateFilter({order:t.value,page:1}):"match"===t.dataset.filter?this.updateFilter({match:t.checked?"all":"any",page:1}):"favourites"===t.dataset.filter&&this.updateFilter({favourites:t.checked,page:1}))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.feedBlock=new e)}))}))})();
inc/rest/routes/FeedRoutes.php
@@ -249,6 +249,9 @@
            $item['taxonomies'] = array_merge($item['taxonomies'], $this->extractTaxonomies($f, $postID, jvbNoBase($post->post_type)));
            $images[$f['post_thumbnail']] = jvbImageData((int) $f['post_thumbnail']);
        }
        $item['fields']['number'] = count($children);
        $item['fields']['before'] = get_post_thumbnail_id($children[0]);
        $item['fields']['after'] = get_post_thumbnail_id($children[array_key_last($children)]);
        $item['fields']['timeline'] = $subFields;
        $item['images'] = $item['images'] + $images;
src/feed/view.js
@@ -435,65 +435,148 @@
        }
        return template;
    }
    splitIDs(value) {
        return value.split(',').map((value) => parseInt(trim(value))).filter(value=>value);
    }
    isImageField(item, value) {
        if (!Object.hasOwn(item, 'images') || Object.keys(item.images).length === 0) {
            return false;
        }
        let values = this.splitIDs(value);
        values.forEach(v => {
            if (Object.keys(item.images).includes(v)) {
                return true;
            }
        });
        return false;
    }
    formatImageFields(element, value, item) {
        if (value.length === 0) return;
        //If it's a gallery, we're cloning the original image, then removing it
        if (value.length > 1) {
            let image = element.querySelector('img');
            if (!image) return;
            value.forEach(imgID => {
                let img = image.cloneNode(true);
                this.formatImageField(img, imgID, item);
                element.append(img);
            });
            image.remove();
        } else {
            if (element.tagName !== 'IMG') {
                element = element.querySelector('img');
                if (!element) return;
            }
            this.formatImageField(element, value[0], item);
        }
    }
        formatImageField(element, value, item) {
            [
                element.src,
                element.srset,
                element.alt
            ] = [
                item.images[value].tiny,
                `${item.images[value].tiny} 50w, ${item.images[value].small} 300w, ${item.images[value].medium} 1024w`,
                item.images[value]['image-alt-text']
            ]
        }
    isTaxonomyField(item, field) {
        if (!Object.hasOwn(item, 'taxonomies') || Object.keys(item.taxonomies).length === 0) {
            return false;
        }
        Object.keys(item.taxonomies).forEach(taxonomy => {
            if (taxonomy === field) {
                return true;
            }
        });
        return false;
    }
    formatTaxonomyField(element, item, field, value) {
        if (element.tagName !== 'UL' || !element.querySelector('li')) return;
        let values = this.splitIDs(value);
        let listItem = element.querySelector('li');
        for (let termID of values) {
            let term = item.taxonomies[field][termID]??false;
            if (!term) continue;
            let termItem = listItem.cloneNode(true);
            let link = termItem.querySelector('a');
            if (!link) continue;
            [
                link.href,
                link.title,
                link.textContent
            ] = [
                term.url,
                `See more ${term.title}`,
                term.title
            ];
            element.append(termItem);
        }
        listItem.remove();
    }
    isTimeField(el) {
        return el.tagName === 'TIME' || el.querySelector('time') !== null;
    }
    formatTimeField(element, value) {
        if (element.tagName !== 'TIME') {
            element = element.querySelector('time');
            if (!element) return;
        }
        element.setAttribute('datetime', value);
        element.textContent = window.formatTimeAgo(value);
    }
    formatField(element, value) {
        element.textContent = value;
    }
    createTimelineElement(item, template) {
        console.log(item);
        console.log(template);
        for (let [field, value] of Object.entries(item.fields)) {
            if (!['timeline', 'number'].includes(field)) {
                let el = template.querySelector(`[data-field="${field}"]`);
                if (!el) {
                    console.log(`Element Not found for ${field}`);
                }
                if (!el || value === '') continue;
                if (this.isImageField(item, value)) {
                    this.formatImageFields(el, value, item);
                } else if (this.isTaxonomyField(item, field)) {
                    this.formatTaxonomyField(el, item, field, value);
                } else if (this.isTimeField(el)) {
                    this.formatTimeField(el, value);
                } else {
                    this.formatField(el, value);
                }
            }
        }
        let [
            main,
            link,
            beforeImg,
            afterImg,
            afterText,
            afterEl,
            number,
            started,
            lastTreated,
            total,
            termList,
            timeline
            last
        ] = [
            template,
            template.querySelector('a'),
            template.querySelector('img.before'),
            template.querySelector('img.after'),
            template.querySelector('summary span:last-of-type'),
            template.querySelector('p.started time'),
            template.querySelector('p.updated time'),
            template.querySelector('p.total b'),
            template.querySelector('.term-list'),
            Object.values(item.fields.order)
            template.querySelector('span.after-text'),
            template.querySelector('[data-field="number"] b'),
            template.querySelector('[data-field="started"] time'),
            template.querySelector('[data-field="updated"] time')
        ];
        let numberTreatments = timeline.length - 1;
        let beforeImgData = item.images[timeline[0]['post_thumbnail']];
        let afterImgData = item.images[timeline[numberTreatments]['post_thumbnail']];
        if (afterEl) {
            afterEl.textContent = `After ${item.fields.number} Tx`;
        }
        if (number) {
            number.textContent = item.fields.number;
        }
        if (started) {
            this.formatTimeField(started, item.fields.timeline[0]['post_date']);
        }
        if (last) {
            this.formatTimeField(last, item.fields.timeline[item.fields.timeline.length - 1]['post_date']);
        }
        [
            main.dataset.id,
            link.href,
            beforeImg.src,
            beforeImg.dataset.small,
            beforeImg.dataset.medium,
            afterImg.src,
            afterImg.dataset.small,
            afterImg.dataset.medium,
            afterText.textContent,
            started.textContent,
            lastTreated.textContent,
            total.textContent
        ] = [
            item.id,
            item.url,
            beforeImgData['tiny'],
            beforeImgData.small,
            beforeImgData.medium,
            afterImgData['tiny'],
            afterImgData.small,
            afterImgData.medium,
            `${afterText.textContent} ${numberTreatments} Tx`,
            timeline[0].date??item.date,
            timeline[numberTreatments].date??'',
            `${numberTreatments} Treatments`
        ];
        return template;
    }