/**
* 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)`)
})()