Skip to content

Instantly share code, notes, and snippets.

@husseyexplores
Last active April 22, 2022 15:09
Show Gist options
  • Save husseyexplores/470e7a55034bb721fef486f716f90e49 to your computer and use it in GitHub Desktop.
Save husseyexplores/470e7a55034bb721fef486f716f90e49 to your computer and use it in GitHub Desktop.
[Stamped Rating Badge] Custom element Api fallback
<script>
!function(){let t=document.getElementById("stamped-main-widget");let e=!1,a=["scroll","mousedown","wheel","DOMMouseScroll","mousewheel","keyup","touchmove"];function r(){e=!1}function i(i){if(!t)return;let n=t.getAttribute("data-offset")||1,o=t.getAttribute("data-offset-mobile")||1,d=i||t.getAttribute("data-animation-duration"),c=window.innerWidth<768?Number(o):Number(n);!function(t,i){const n=document.scrollingElement||document.documentElement,o=n.scrollTop,d=t-o,c=+new Date;e=!0,a.forEach(t=>{document.documentElement.addEventListener(t,r)}),function l(){if(!e)return;const s=+new Date-c;var u,m,p;n.scrollTop=parseInt((u=s,m=o,p=d,(u/=i/2)<1?p/2*u*u+m:-p/2*(--u*(u-2)-1)+m)),s<i?requestAnimationFrame(l):(n.scrollTop=t,e=!1,a.forEach(t=>{document.documentElement.removeEventListener(t,r)}))}()}(function(t){let e=t.getBoundingClientRect();return{top:e.top+window.scrollY,left:e.left+window.scrollX}}(t).top-c,d||700)}function n(t){return t&&"stamped-rating-badge"===t.localName&&t.hasAttribute("data-revalidate")}function o(t){const e=t instanceof NodeList?Array.from(t).filter(n):Array.isArray(t)?t.filter(n):n(t)?t:Array.from(document.querySelectorAll("stamped-rating-badge[data-revalidate]"));if(!e.length)return Promise.resolve([]);const a=e.reduce((t,a,r)=>{let i=a.getAttribute("data-product-id");return i?t[i]=a:e.splice(r,1),t},{}),r=e[0],i=r.getAttribute("data-stamped-api-key"),o=r.getAttribute("data-stamped-sid");if(!i||!o)return console.warn("`apiKey` or `sId` is missing..."),Promise.resolve([]);const d=e.map(t=>t.getAttribute("data-product-id"));return(c={apiKey:i,sId:o,productIds:d},fetch("https://stamped.io/api/widget/badges",{method:"POST",headers:{accept:"application/json","content-type":"application/json"},body:JSON.stringify({apiKey:c.apiKey,sId:c.sId,productIds:c.productIds.map(t=>({productId:t}))})}).then(t=>t.json())).then(t=>(t.forEach(t=>{!function(t,e){if(!t||!e)return;t.querySelectorAll("[data-rating]").forEach(t=>{t.style.setProperty("--rating",e.rating)});let a=e.count,r=1===a?"review":0===a?"Write a review":"reviews";t.querySelectorAll("[data-rating-count]").forEach(t=>{t.setAttribute("data-rating-count",e.count),t.textContent=(0===a?"":a)+" "+r}),t.removeAttribute("data-revalidate"),"true"===t.getAttribute("aria-hidden")&&a>0&&(t.removeAttribute("aria-hidden"),t.style.display="")}(a[t.productId],t)}),t));var c}window.StampedScrollToMainWidget=i,window.StampedCustomRatingBadgesFetch=o,o(),t&&document.querySelectorAll("[data-scroll-to-main-widget]").forEach(e=>{!function(e){e.hasAttribute("data-scroll-to-main-widget")&&e.addEventListener("click",function(){let a=e.getAttribute("data-main-widget-collapse-el"),r=e.getAttribute("data-main-widget-collapse-trigger"),n=e.getAttribute("data-main-widget-collapse-open-class");if(a&&n){let e=t.closest(a),i=e&&e.querySelector(r);e&&(e.classList.contains(n)||i&&i.classList.contains(n)||(i||e).click())}i()})}(e)})}();
</script>
{%- comment -%}
<script>
;(function () {
let MAIN_WIDGET_EL = document.getElementById('stamped-main-widget')
function isMobile() {
return window.innerWidth < 768
}
function absoluteOffset(element) {
let rect = element.getBoundingClientRect()
return { top: rect.top + window.scrollY, left: rect.left + window.scrollX }
}
// t = current time
// b = start value
// c = change in value
// d = duration
function easeInOutQuad(t, b, c, d) {
t /= d / 2
if (t < 1) return (c / 2) * t * t + b
t--
return (-c / 2) * (t * (t - 2) - 1) + b
}
let isAnimating = false
let events = [
'scroll',
'mousedown',
'wheel',
'DOMMouseScroll',
'mousewheel',
'keyup',
'touchmove',
]
function stopAnimationFn() {
isAnimating = false
}
function animateScrollTo(to, duration) {
const doc = document.scrollingElement || document.documentElement,
start = doc.scrollTop,
change = to - start,
startDate = +new Date()
isAnimating = true
events.forEach(event => {
document.documentElement.addEventListener(event, stopAnimationFn)
})
function animateScroll() {
if (!isAnimating) return
const currentDate = +new Date()
const currentTime = currentDate - startDate
doc.scrollTop = parseInt(
easeInOutQuad(currentTime, start, change, duration),
)
if (currentTime < duration) {
requestAnimationFrame(animateScroll)
} else {
doc.scrollTop = to
isAnimating = false
events.forEach(event => {
document.documentElement.removeEventListener(event, stopAnimationFn)
})
}
}
animateScroll()
}
function StampedScrollToMainWidget(duration) {
if (!MAIN_WIDGET_EL) {
return
}
let desktopOffset = MAIN_WIDGET_EL.getAttribute('data-offset') || 1
let mobileOffset = MAIN_WIDGET_EL.getAttribute('data-offset-mobile') || 1
let _duration =
duration || MAIN_WIDGET_EL.getAttribute('data-animation-duration')
let offset = isMobile() ? Number(mobileOffset) : Number(desktopOffset)
animateScrollTo(
absoluteOffset(MAIN_WIDGET_EL).top - offset,
_duration || 700,
)
}
window.StampedScrollToMainWidget = StampedScrollToMainWidget
// StampedScrollToMainWidget(4000)
//- ---------------------------------------------------
//- ---------------------------------------------------
//- ---------------------------------------------------
/**
*
* @param el {HTMLElement}
*/
function addScrollToMainWidgetListener(el) {
let scrollToMainWidget = el.hasAttribute('data-scroll-to-main-widget')
if (!scrollToMainWidget) {
return
}
el.addEventListener('click', function () {
let collapseQs = el.getAttribute('data-main-widget-collapse-el')
let collapseTriggerQs = el.getAttribute(
'data-main-widget-collapse-trigger',
)
let openClass = el.getAttribute('data-main-widget-collapse-open-class')
if (collapseQs && openClass) {
let collapseEl = MAIN_WIDGET_EL.closest(collapseQs)
let collapseTrigger =
collapseEl && collapseEl.querySelector(collapseTriggerQs)
if (collapseEl) {
let isOpen =
collapseEl.classList.contains(openClass) ||
(collapseTrigger && collapseTrigger.classList.contains(openClass))
if (!isOpen) {
;(collapseTrigger || collapseEl).click()
}
}
}
StampedScrollToMainWidget()
})
}
function fetchStampedRating(options) {
return fetch('https://stamped.io/api/widget/badges', {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
apiKey: options.apiKey,
sId: options.sId,
productIds: options.productIds.map(id => ({ productId: id })),
}),
}).then(r => r.json())
}
/**
*
* @param element {HTMLElement}
* @param reviewJson
* @returns
*/
function updateRatingDom(element, reviewJson) {
if (!element || !reviewJson) return
element.querySelectorAll('[data-rating]').forEach(el => {
el.style.setProperty('--rating', reviewJson.rating)
})
let count = reviewJson.count
let ratingText =
count === 1 ? 'review' : count === 0 ? 'Write a review' : 'reviews'
element.querySelectorAll('[data-rating-count]').forEach(el => {
el.setAttribute('data-rating-count', reviewJson.count)
el.textContent = (count === 0 ? '' : count) + ' ' + ratingText
})
element.removeAttribute('data-revalidate')
let isHidden = element.getAttribute('aria-hidden') === 'true'
if (isHidden && count > 0) {
element.removeAttribute('aria-hidden')
element.style.display = ''
}
}
function isRevalidateableNode(node) {
return (
node &&
node.localName === 'stamped-rating-badge' &&
node.hasAttribute('data-revalidate')
)
}
/**
*
* @param {badges | badges[] | NodeList<badges> | undefined} Rating badge node(s).
* @returns
*/
function fixRatingBadges(badges) {
const ratingBadges =
badges instanceof NodeList
? Array.from(badges).filter(isRevalidateableNode)
: Array.isArray(badges)
? badges.filter(isRevalidateableNode)
: isRevalidateableNode(badges)
? badges
: Array.from(
document.querySelectorAll('stamped-rating-badge[data-revalidate]'),
)
if (!ratingBadges.length) {
return Promise.resolve([])
}
const ratingBadgesLookup = ratingBadges.reduce((acc, el, index) => {
let pid = el.getAttribute('data-product-id')
if (pid) {
acc[pid] = el
} else {
ratingBadges.splice(index, 1)
}
return acc
}, {})
const firstEl = ratingBadges[0]
const apiKey = firstEl.getAttribute('data-stamped-api-key')
const sId = firstEl.getAttribute('data-stamped-sid')
if (!apiKey || !sId) {
console.warn('`apiKey` or `sId` is missing...')
return Promise.resolve([])
}
const productIds = ratingBadges.map(el =>
el.getAttribute('data-product-id'),
)
return fetchStampedRating({ apiKey, sId, productIds }).then(list => {
list.forEach(ratingJson => {
updateRatingDom(ratingBadgesLookup[ratingJson.productId], ratingJson)
})
return list
})
}
window.StampedCustomRatingBadgesFetch = fixRatingBadges
fixRatingBadges()
if (MAIN_WIDGET_EL) {
document.querySelectorAll('[data-scroll-to-main-widget]').forEach(el => {
addScrollToMainWidgetListener(el)
})
}
})()
</script>
{%- endcomment -%}
<script>
;(function() {
function callback(mutations, obs) {
let addedNodes = []
mutations.forEach(mut => {
if (mut.type == 'childList' && mut.addedNodes.length > 0) {
mut.addedNodes.forEach(n => {
if (n && n.querySelector) {
addedNodes.push(n)
}
})
}
})
let uniqBadges = new Set()
addedNodes.forEach(node => {
if (!node || !node.querySelectorAll) return
let badges = node.querySelectorAll('stamped-rating-badge[data-revalidate]')
if (!badges.length) return
badges.forEach(badge => {
uniqBadges.add(badge)
})
})
if (StampedCustomRatingBadgesFetch) {
StampedCustomRatingBadgesFetch(Array.from(uniqBadges))
}
}
// if (observer) observer.disconnect()
let observer = new MutationObserver(callback)
observer.observe(document.body, {
childList: true,
subtree: true,
})
})()
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment