/** * INIT DATA */ const Elements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'li', 'a'] const Toast = renderToast() let SelectButton = null let RemoveButton = null let Div = null let listSelector = [] let currentTarget let currentElementActive = null let currentFont let timer let domainOrigin = 'https://fonts.solucommerce.com' let mode = null let elementModeEdit = null /** * HELPER GENERATE SELECTOR - LINK STYLESHEET */ function generateQuerySelector(el) { let selector = el.tagName.toLowerCase() const attrs = el.attributes for (var i = 0; i < attrs.length; i++) { let attr = attrs.item(i) if (attr.name === 'id') { return (selector = '' + `#${attr.value}`) } // if (attr.name === "class") // selector += attr.value.length // ? attr.value // .split(" ") // .map((c) => `.${c}`) // .join("") // : ""; // if (attr.name === "name") selector += `[${attr.name}=${attr.value}]`; } return selector } function generateStyleSheet(google_font_name, variants_google_font) { let str = `https://fonts.googleapis.com/css?family=${google_font_name.replace( / /g, '+', )}:${String(variants_google_font)}` return str } /** * RENDER PANEL */ function renderPanel() { const body = document.body const divEl = document.createElement('div') const buttonEl = document.createElement('button') const bEl = document.createElement('b') bEl.textContent = 'Click on any element to apply font' buttonEl.textContent = 'Exit' divEl.classList.add('afontify-panel') divEl.append(bEl, buttonEl) /** * css for p */ bEl.style.fontSize = '13px' /** * css for div */ divEl.style.width = '100%' divEl.style.borderBottom = '1px solid rgba(0,0,0,.1)' divEl.style.backgroundColor = 'white' divEl.style.padding = '10px 20px' divEl.style.marginBottom = '20px' divEl.style.position = 'fixed' divEl.style.top = '0' divEl.style.zIndex = '999999999999' divEl.style.display = 'flex' divEl.style.alignItems = 'center' divEl.style.justifyContent = 'space-between' /** * css for button */ buttonEl.style.backgroundColor = 'rgb(0, 128, 96)' buttonEl.style.border = '1px solid rgb(186, 191, 195)' buttonEl.style.border = 'none' buttonEl.style.padding = '10px 20px' buttonEl.style.borderRadius = '3px' buttonEl.style.cursor = 'pointer' buttonEl.style.transition = '0.3s all' buttonEl.style.color = 'white' body.append(divEl) // attach on event when click destroy mode get element buttonEl.addEventListener('click', (e) => { // handle discard post message window.close() }) } /** * RENDER TOAST MESSAGE */ function renderToast() { let textHtml = `
` const el = new DOMParser().parseFromString(textHtml, 'text/html').body.firstChild document.body.appendChild(el) } /** * RENDER BUTTON REMOVE APPLY FONT */ function renderButtonRemove() { const id = 'insertButtonRemove' document.getElementById(id)?.remove() const button = document.createElement('button') button.setAttribute('id', id) button.textContent = 'Remove' document.body.insertAdjacentElement('beforeend', button) button.addEventListener('click', (e) => { e.preventDefault() const toast = document.querySelector('.afontify-toast') const toastMessage = toast.querySelector('.afontify-text') toastMessage.textContent = 'Remove Element Apply' let findPath = currentTarget let path = [generateQuerySelector(findPath)] while (findPath != document.body) { findPath = findPath.parentElement if (findPath.id) { path.push(generateQuerySelector(findPath)) break } else { path.push(generateQuerySelector(findPath)) } } let generatePath = path.reverse().join(' > ') if (listSelector.length) { for (let i = 0; i < listSelector.length; i++) { let element = listSelector[i] if (element === generatePath) { let el = document.querySelector(element) el.style.fontFamily = 'inherit' listSelector.splice(i, 1) } } } if (timer) { clearTimeout(timer) } toast.classList.add('active') timer = setTimeout(() => { toast.classList.remove('active') }, 1000) window.opener.postMessage({ action: mode, dataRemove: generatePath }, domainOrigin) }) return button } /** * RENDER BUTTON SELECT APPLY FONT */ function renderButtonSelect() { const id = 'insertButton' document.getElementById(id)?.remove() const button = document.createElement('button') button.setAttribute('id', id) button.textContent = 'Select' document.body.insertAdjacentElement('beforeend', button) button.addEventListener('click', (e) => { e.preventDefault() const toast = document.querySelector('.afontify-toast') const toastMessage = toast.querySelector('.afontify-text') toastMessage.textContent = 'The element added to apply' // let selector = generateQuerySelector(currentTarget) // .split(">") // .slice(0, generateQuerySelector(currentTarget).split(">").length - 1) // .join(" > "); let findPath = currentTarget let path = [generateQuerySelector(findPath)] while (findPath != document.body) { findPath = findPath.parentElement if (findPath.id) { path.push(generateQuerySelector(findPath)) break } else { path.push(generateQuerySelector(findPath)) } } let generatePath = path.reverse().join(' > ') currentElementActive = generatePath let el = document.querySelector(generatePath) el.style.setProperty('font-family', currentFont, 'important') if (timer) { clearTimeout(timer) } toast.classList.add('active') timer = setTimeout(() => { toast.classList.remove('active') }, 1000) if (!listSelector.includes(generatePath)) { listSelector.push(generatePath) } window.opener.postMessage({ action: mode, dataSelected: generatePath }, domainOrigin) }) return button } /** * RENDER SHAPE */ function renderDiv() { const div = document.createElement('div') div.id = 'insertShape' div.innerHTML = ` ` document.body.insertAdjacentElement('beforeend', div) return div } /** * HANDLE MOUSE EVENT */ function handleMouseOverElement(e) { currentTarget = e.target const insertButtonElement = document.getElementById('insertButton') const insertButtonRemoveElement = document.getElementById('insertButtonRemove') if (!SelectButton) SelectButton = document.getElementById('insertButton') if (!SelectButton) SelectButton = renderButtonSelect() if (!RemoveButton) RemoveButton = document.getElementById('insertButtonRemove') if (!RemoveButton) RemoveButton = renderButtonRemove() insertButtonElement.style.transform = 'translateY(0)' insertButtonRemoveElement.style.transform = 'translateY(0)' // console.log(currentTarget.getBoundingClientRect()); const { width, left, top, height, bottom, right } = currentTarget.getBoundingClientRect() const newTop = top + window.scrollY - SelectButton.clientHeight const newLeft = left + (width / 2 + SelectButton.clientWidth / 2) - SelectButton.clientWidth const newBottom = bottom + window.scrollY const newRight = right - width / 2 - RemoveButton.clientWidth / 2 SelectButton.style.setProperty('--insertTop', newTop + 'px') SelectButton.style.setProperty('--insertLeft', newLeft + 'px') RemoveButton.style.setProperty('--insertBottom', newBottom + 'px') RemoveButton.style.setProperty('--insertRight', newRight + 'px') if (!Div) Div = document.getElementById('insertShape') if (!Div) Div = renderDiv() Div.style.setProperty('--widthShape', width + 'px') Div.style.setProperty('--heightShape', height + 'px') Div.style.setProperty('--topShape', top + window.scrollY + 'px') Div.style.setProperty('--leftShape', left + 'px') } function onMouseOver(e) { if (!SelectButton) SelectButton = document.getElementById('insertButton') if (!SelectButton) SelectButton = renderButtonSelect() if (!RemoveButton) RemoveButton = document.getElementById('insertButtonRemove') if (!RemoveButton) RemoveButton = renderButtonRemove() RemoveButton.style.opacity = 1 SelectButton.style.opacity = 1 let waiting if ( e.target.closest('#insertButton') || e.target.closest('#insertButtonRemove') || e.target.closest('.afontify-panel') || !Elements.includes(e.target.tagName.toLowerCase()) ) return if (waiting) { clearTimeout(waiting) } else { waiting = setTimeout(() => { handleMouseOverElement(e) // waiting = null; }, 100) } } /** * CHECK ORIGIN AND DESTROY POST MESSAGE */ function onMessage(event) { if (event.data.action === 'destroy') { window.removeEventListener('mouseover', onMouseOver) return } if (event.data.action === 'desktop_connected') { mode = event.data.action /** * check field "kind" in object * if has kind => google font * upload font if not */ renderPanel() if (!('kind' in event.data.data)) { const style = document.createElement('style') let fontFace = event.data.data.variants .map((_item) => { let str = ` @font-face { \tfont-family: "${event.data.data.family}"; \tsrc: url(${event.data.data.files.normal}) format("truetype") } ` return str }) .join('') style.setAttribute('type', 'text/css') style.id = 'any-font' style.textContent = fontFace Array.from(document.querySelectorAll('style')) .filter((el) => el.id === 'any-font') .forEach((el) => document.head.removeChild(el)) document.head.appendChild(style) } else { let link = document.createElement('link') link.rel = 'stylesheet' link.href = generateStyleSheet(event.data.data?.family, event.data.data?.variants) document.head.append(link) } currentFont = event.data.data?.family } if (event.data.action === 'mobile_connected') { mode = event.data.action renderPanel() /** * check field "kind" in object * if has kind => google font */ if (!('kind' in event.data.data)) { const style = document.createElement('style') let fontFace = event.data.data.variants .map((_item) => { let str = ` @font-face { \tfont-family: "${event.data.data.family}"; \tfont-weight: ${_item}; \tsrc: url(${event.data.data.files.normal}) format("truetype") } ` return str }) .join('') style.setAttribute('type', 'text/css') style.id = 'any-font' style.textContent = fontFace Array.from(document.querySelectorAll('style')) .filter((el) => el.id === 'any-font') .forEach((el) => document.head.removeChild(el)) document.head.appendChild(style) } else { let link = document.createElement('link') link.rel = 'stylesheet' link.href = generateStyleSheet(event.data.data?.family, event.data.data?.variants) document.head.append(link) } currentFont = event.data.data?.family } if (event.origin !== window.location.origin || event.origin.action === 'destroy') { return } } /** * MAIN */ ;(() => { if (window.opener === null) return const params = new URLSearchParams(window.location.search) const parsed = Object.fromEntries(params.entries()) const _mode = parsed.mode || sessionStorage.getItem('ANYFONT_APP_MODE') if (_mode !== 'element-selector') return sessionStorage.setItem('ANYFONT_APP_MODE', 'element-selector') window.addEventListener('message', onMessage, false) window.addEventListener('mouseover', onMouseOver) /** * ******************************************************************************************* * * * when loading file js success, window send message to parent alert file js loaded. * * after parent can send message to child while action "ready" * * * * ******************************************************************************************* */ window.opener.postMessage({ action: 'ready' }, domainOrigin) console.log(`AZ: Google Font & Custom Fonts (2024112000)`) })()