Cleanup
This commit is contained in:
parent
fdf3d4c21b
commit
bf8567d652
431
index.html
431
index.html
@ -507,438 +507,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id='ui_utils'>
|
<script id='ui_utils'>
|
||||||
// Global letiables
|
|
||||||
const domRefs = {};
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
|
|
||||||
//Checks for internet connection status
|
|
||||||
if (!navigator.onLine)
|
|
||||||
notify(
|
|
||||||
"There seems to be a problem connecting to the internet, Please check you internet connection.",
|
|
||||||
"error"
|
|
||||||
);
|
|
||||||
window.addEventListener("offline", () => {
|
|
||||||
notify(
|
|
||||||
"There seems to be a problem connecting to the internet, Please check you internet connection.",
|
|
||||||
"error",
|
|
||||||
{ pinned: true }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
window.addEventListener("online", () => {
|
|
||||||
getRef("notification_drawer").clearAll();
|
|
||||||
notify("We are back online.", "success");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Use instead of document.getElementById
|
|
||||||
function getRef(elementId) {
|
|
||||||
if (!domRefs.hasOwnProperty(elementId)) {
|
|
||||||
domRefs[elementId] = {
|
|
||||||
count: 1,
|
|
||||||
ref: null,
|
|
||||||
};
|
|
||||||
return document.getElementById(elementId);
|
|
||||||
} else {
|
|
||||||
if (domRefs[elementId].count < 3) {
|
|
||||||
domRefs[elementId].count = domRefs[elementId].count + 1;
|
|
||||||
return document.getElementById(elementId);
|
|
||||||
} else {
|
|
||||||
if (!domRefs[elementId].ref)
|
|
||||||
domRefs[elementId].ref = document.getElementById(elementId);
|
|
||||||
return domRefs[elementId].ref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns dom with specified element
|
|
||||||
function createElement(tagName, options = {}) {
|
|
||||||
const { className, textContent, innerHTML, attributes = {} } = options
|
|
||||||
const elem = document.createElement(tagName)
|
|
||||||
for (let attribute in attributes) {
|
|
||||||
elem.setAttribute(attribute, attributes[attribute])
|
|
||||||
}
|
|
||||||
if (className)
|
|
||||||
elem.className = className
|
|
||||||
if (textContent)
|
|
||||||
elem.textContent = textContent
|
|
||||||
if (innerHTML)
|
|
||||||
elem.innerHTML = innerHTML
|
|
||||||
return elem
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use when a function needs to be executed after user finishes changes
|
|
||||||
const debounce = (callback, wait) => {
|
|
||||||
let timeoutId = null;
|
|
||||||
return (...args) => {
|
|
||||||
window.clearTimeout(timeoutId);
|
|
||||||
timeoutId = window.setTimeout(() => {
|
|
||||||
callback.apply(null, args);
|
|
||||||
}, wait);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let zIndex = 10
|
|
||||||
// function required for popups or modals to appear
|
|
||||||
function showPopup(popupId, pinned) {
|
|
||||||
zIndex++
|
|
||||||
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
|
|
||||||
getRef(popupId).show({ pinned })
|
|
||||||
return getRef(popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// hides the popup or modal
|
|
||||||
function hidePopup() {
|
|
||||||
if (popupStack.peek() === undefined)
|
|
||||||
return;
|
|
||||||
popupStack.peek().popup.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('popupopened', e => {
|
|
||||||
})
|
|
||||||
document.addEventListener('popupclosed', e => {
|
|
||||||
zIndex--
|
|
||||||
switch (e.target.id) {
|
|
||||||
case 'preview_popup':
|
|
||||||
getRef('preview_container').innerHTML = ''
|
|
||||||
break;
|
|
||||||
case 'sign_in_popup':
|
|
||||||
getRef('sign_in').style = ''
|
|
||||||
getRef('sign_in').classList.remove('hide')
|
|
||||||
getRef('sign_up').style = ''
|
|
||||||
getRef('sign_up').classList.add('hide')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// displays a popup for asking permission. Use this instead of JS confirm
|
|
||||||
const getConfirmation = (title, options = {}) => {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const { message, cancelText = 'Cancel', confirmText = 'OK' } = options
|
|
||||||
showPopup('confirmation_popup', true)
|
|
||||||
getRef('confirm_title').textContent = title;
|
|
||||||
getRef('confirm_message').textContent = message;
|
|
||||||
let cancelButton = getRef('confirmation_popup').children[2].children[0],
|
|
||||||
submitButton = getRef('confirmation_popup').children[2].children[1]
|
|
||||||
submitButton.textContent = confirmText
|
|
||||||
cancelButton.textContent = cancelText
|
|
||||||
submitButton.onclick = () => {
|
|
||||||
hidePopup()
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
cancelButton.onclick = () => {
|
|
||||||
hidePopup()
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
|
|
||||||
function notify(message, mode, options = {}) {
|
|
||||||
const { pinned = false, sound = false } = options
|
|
||||||
let icon
|
|
||||||
switch (mode) {
|
|
||||||
case 'success':
|
|
||||||
icon = `<svg class="icon icon--success" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>`
|
|
||||||
break;
|
|
||||||
case 'error':
|
|
||||||
icon = `<svg class="icon icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
getRef("notification_drawer").push(message, { pinned, icon });
|
|
||||||
if (mode === 'error') {
|
|
||||||
console.error(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFormattedTime(time, format) {
|
|
||||||
try {
|
|
||||||
if (String(time).indexOf('_'))
|
|
||||||
time = String(time).split('_')[0]
|
|
||||||
const intTime = parseInt(time)
|
|
||||||
if (String(intTime).length < 13)
|
|
||||||
time *= 1000
|
|
||||||
let [day, month, date, year] = new Date(intTime).toString().split(' '),
|
|
||||||
minutes = new Date(intTime).getMinutes(),
|
|
||||||
hours = new Date(intTime).getHours(),
|
|
||||||
currentTime = new Date().toString().split(' ')
|
|
||||||
|
|
||||||
minutes = minutes < 10 ? `0${minutes}` : minutes
|
|
||||||
let finalHours = ``;
|
|
||||||
if (hours > 12)
|
|
||||||
finalHours = `${hours - 12}:${minutes}`
|
|
||||||
else if (hours === 0)
|
|
||||||
finalHours = `12:${minutes}`
|
|
||||||
else
|
|
||||||
finalHours = `${hours}:${minutes}`
|
|
||||||
|
|
||||||
finalHours = hours >= 12 ? `${finalHours} PM` : `${finalHours} AM`
|
|
||||||
switch (format) {
|
|
||||||
case 'date-only':
|
|
||||||
return `${month} ${date}, ${year}`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return `${month} ${date} ${year}, ${finalHours}`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// implement event delegation
|
|
||||||
function delegate(el, event, selector, fn) {
|
|
||||||
el.addEventListener(event, function (e) {
|
|
||||||
const potentialTarget = e.target.closest(selector)
|
|
||||||
if (potentialTarget) {
|
|
||||||
e.delegateTarget = potentialTarget
|
|
||||||
fn.call(this, e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('hashchange', e => showPage(window.location.hash))
|
|
||||||
window.addEventListener("load", () => {
|
|
||||||
document.body.classList.remove('hide')
|
|
||||||
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = floCrypto.validateAddr)
|
|
||||||
document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex)
|
|
||||||
document.addEventListener('keyup', (e) => {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
hidePopup()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
document.addEventListener('copy', () => {
|
|
||||||
notify('copied', 'success')
|
|
||||||
})
|
|
||||||
document.addEventListener("pointerdown", (e) => {
|
|
||||||
if (e.target.closest("button, sm-button:not([disabled]), .interact")) {
|
|
||||||
createRipple(e, e.target.closest("button, sm-button, .interact"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
function createRipple(event, target) {
|
|
||||||
const circle = document.createElement("span");
|
|
||||||
const diameter = Math.max(target.clientWidth, target.clientHeight);
|
|
||||||
const radius = diameter / 2;
|
|
||||||
const targetDimensions = target.getBoundingClientRect();
|
|
||||||
circle.style.width = circle.style.height = `${diameter}px`;
|
|
||||||
circle.style.left = `${event.clientX - (targetDimensions.left + radius)}px`;
|
|
||||||
circle.style.top = `${event.clientY - (targetDimensions.top + radius)}px`;
|
|
||||||
circle.classList.add("ripple");
|
|
||||||
const rippleAnimation = circle.animate(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
transform: "scale(4)",
|
|
||||||
opacity: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
duration: 600,
|
|
||||||
fill: "forwards",
|
|
||||||
easing: "ease-out",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
target.append(circle);
|
|
||||||
rippleAnimation.onfinish = () => {
|
|
||||||
circle.remove();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const pagesData = {
|
|
||||||
openedPages: [],
|
|
||||||
params: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showPage(targetPage, options = {}) {
|
|
||||||
const { firstLoad, hashChange, isPreview } = options
|
|
||||||
let pageId
|
|
||||||
let params = {}
|
|
||||||
let searchParams
|
|
||||||
if (targetPage === '') {
|
|
||||||
pageId = 'main_page'
|
|
||||||
} else {
|
|
||||||
if (targetPage.includes('/')) {
|
|
||||||
if (targetPage.includes('?')) {
|
|
||||||
const splitAddress = targetPage.split('?')
|
|
||||||
searchParams = splitAddress.pop()
|
|
||||||
const pages = splitAddress.pop().split('/')
|
|
||||||
pageId = pages[1]
|
|
||||||
subPageId = pages[2]
|
|
||||||
} else {
|
|
||||||
const pages = targetPage.split('/')
|
|
||||||
pageId = pages[1]
|
|
||||||
subPageId = pages[2]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pageId = targetPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pageId === 'dashboard' && !floGlobals.isSubAdmin) {
|
|
||||||
pageId = 'home'
|
|
||||||
history.replaceState(null, null, '#/home')
|
|
||||||
}
|
|
||||||
if (searchParams) {
|
|
||||||
const urlSearchParams = new URLSearchParams('?' + searchParams);
|
|
||||||
params = Object.fromEntries(urlSearchParams.entries());
|
|
||||||
}
|
|
||||||
switch (pageId) {
|
|
||||||
case 'home':
|
|
||||||
case 'main_page':
|
|
||||||
targetPage = 'main_page'
|
|
||||||
render.homepage()
|
|
||||||
break;
|
|
||||||
case 'article':
|
|
||||||
targetPage = 'article'
|
|
||||||
await render.article(params.articleID)
|
|
||||||
break;
|
|
||||||
case 'explore':
|
|
||||||
targetPage = 'explore'
|
|
||||||
render.explorePage(params)
|
|
||||||
break;
|
|
||||||
case 'writer':
|
|
||||||
targetPage = 'writer'
|
|
||||||
render.writerPage(params)
|
|
||||||
break;
|
|
||||||
case 'dashboard':
|
|
||||||
targetPage = 'dashboard'
|
|
||||||
await Promise.all([
|
|
||||||
floCloudAPI.requestObjectData('adminData'),
|
|
||||||
floCloudAPI.requestGeneralData('publishing_requests', {
|
|
||||||
callback: (d, e) => renderDashboard(d)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
calculateVotes()
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (targetPage === 'article') {
|
|
||||||
setTimeout(() => {
|
|
||||||
window.scroll({ top: 0 })
|
|
||||||
}, 0)
|
|
||||||
mobileQuery.addListener(handleMobileChange)
|
|
||||||
handleMobileChange(mobileQuery)
|
|
||||||
articleTitleObserver.observe(getRef('article_title'))
|
|
||||||
} else {
|
|
||||||
mobileQuery.removeListener(handleMobileChange)
|
|
||||||
articleTitleObserver.disconnect()
|
|
||||||
}
|
|
||||||
if (pageId !== 'loading') {
|
|
||||||
getRef('main').classList.remove('hide')
|
|
||||||
}
|
|
||||||
document.querySelectorAll('.page').forEach(page => page.classList.add('hide'))
|
|
||||||
getRef(targetPage).classList.remove('hide')
|
|
||||||
getRef(targetPage).animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300, fill: 'forwards', easing: 'ease' })
|
|
||||||
if (pagesData.lastPage !== pageId) {
|
|
||||||
pagesData.lastPage = pageId
|
|
||||||
if (!pagesData.openedPages.includes(pageId)) {
|
|
||||||
pagesData.openedPages.push(pageId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (params)
|
|
||||||
pagesData.params = params
|
|
||||||
}
|
|
||||||
// class based lazy loading
|
|
||||||
class LazyLoader {
|
|
||||||
constructor(container, elementsToRender, renderFn, options = {}) {
|
|
||||||
const { batchSize = 10 } = options
|
|
||||||
|
|
||||||
this.elementsToRender = elementsToRender
|
|
||||||
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
|
||||||
this.renderFn = renderFn
|
|
||||||
this.intersectionObserver
|
|
||||||
|
|
||||||
this.batchSize = batchSize
|
|
||||||
|
|
||||||
this.lazyContainer = document.querySelector(container)
|
|
||||||
|
|
||||||
this.update = this.update.bind(this)
|
|
||||||
this.render = this.render.bind(this)
|
|
||||||
this.init = this.init.bind(this)
|
|
||||||
this.clear = this.clear.bind(this)
|
|
||||||
}
|
|
||||||
init() {
|
|
||||||
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
observer.disconnect()
|
|
||||||
this.render({ lazyLoad: true })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, {
|
|
||||||
threshold: 0.3
|
|
||||||
})
|
|
||||||
this.mutationObserver = new MutationObserver(mutationList => {
|
|
||||||
mutationList.forEach(mutation => {
|
|
||||||
if (mutation.type === 'childList') {
|
|
||||||
if (mutation.addedNodes.length) {
|
|
||||||
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.mutationObserver.observe(this.lazyContainer, {
|
|
||||||
childList: true,
|
|
||||||
})
|
|
||||||
this.render()
|
|
||||||
}
|
|
||||||
update(elementsToRender) {
|
|
||||||
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
|
||||||
this.render()
|
|
||||||
}
|
|
||||||
render(options = {}) {
|
|
||||||
let { lazyLoad = false } = options
|
|
||||||
const frag = document.createDocumentFragment();
|
|
||||||
if (lazyLoad) {
|
|
||||||
this.updateStartIndex = this.updateEndIndex
|
|
||||||
this.updateEndIndex = this.arrayOfElements.length > this.updateEndIndex + this.batchSize ? this.updateEndIndex + this.batchSize : this.arrayOfElements.length
|
|
||||||
} else {
|
|
||||||
this.intersectionObserver.disconnect()
|
|
||||||
this.lazyContainer.innerHTML = ``;
|
|
||||||
this.updateStartIndex = 0
|
|
||||||
this.updateEndIndex = this.arrayOfElements.length > this.batchSize ? this.batchSize : this.arrayOfElements.length
|
|
||||||
}
|
|
||||||
for (let index = this.updateStartIndex; index < this.updateEndIndex; index++) {
|
|
||||||
frag.append(this.renderFn(this.arrayOfElements[index]))
|
|
||||||
}
|
|
||||||
this.lazyContainer.append(frag)
|
|
||||||
}
|
|
||||||
clear() {
|
|
||||||
this.intersectionObserver.disconnect()
|
|
||||||
this.mutationObserver.disconnect()
|
|
||||||
this.lazyContainer.innerHTML = ``;
|
|
||||||
}
|
|
||||||
reset() {
|
|
||||||
this.arrayOfElements = (typeof this.elementsToRender === 'function') ? this.elementsToRender() : this.elementsToRender || []
|
|
||||||
this.render()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function animateTo(element, keyframes, options) {
|
|
||||||
const anime = element.animate(keyframes, { ...options, fill: 'both' })
|
|
||||||
anime.finished.then(() => {
|
|
||||||
anime.commitStyles()
|
|
||||||
anime.cancel()
|
|
||||||
})
|
|
||||||
return anime
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// displays a popup for asking permission. Use this instead of JS confirm
|
|
||||||
const getConfirmation = (title, options = {}) => {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const { message, cancelText = 'Cancel', confirmText = 'OK' } = options
|
|
||||||
showPopup('confirmation_popup', true)
|
|
||||||
getRef('confirm_title').textContent = title;
|
|
||||||
getRef('confirm_message').textContent = message;
|
|
||||||
let cancelButton = getRef('confirmation_popup').children[2].children[0],
|
|
||||||
submitButton = getRef('confirmation_popup').children[2].children[1]
|
|
||||||
submitButton.textContent = confirmText
|
|
||||||
cancelButton.textContent = cancelText
|
|
||||||
submitButton.onclick = () => {
|
|
||||||
hidePopup()
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
cancelButton.onclick = () => {
|
|
||||||
hidePopup()
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -87,38 +87,22 @@ function throttle(func, delay) {
|
|||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Stack {
|
|
||||||
constructor() {
|
|
||||||
this.items = [];
|
|
||||||
}
|
|
||||||
push(element) {
|
|
||||||
this.items.push(element);
|
|
||||||
}
|
|
||||||
pop() {
|
|
||||||
if (this.items.length == 0)
|
|
||||||
return "Underflow";
|
|
||||||
return this.items.pop();
|
|
||||||
}
|
|
||||||
peek() {
|
|
||||||
return this.items[this.items.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let zIndex = 10
|
let zIndex = 10
|
||||||
// function required for popups or modals to appear
|
// function required for popups or modals to appear
|
||||||
function showPopup(popupId, pinned) {
|
function showPopup(popupId, pinned) {
|
||||||
zIndex++
|
zIndex++
|
||||||
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
|
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
|
||||||
getRef(popupId).show({ pinned })
|
getRef(popupId).show({ pinned })
|
||||||
return getRef(popupId);
|
return getRef(popupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// hides the popup or modal
|
// hides the popup or modal
|
||||||
function hidePopup() {
|
function hidePopup() {
|
||||||
if (popupStack.peek() === undefined)
|
if (popupStack.peek() === undefined)
|
||||||
return;
|
return;
|
||||||
popupStack.peek().popup.hide()
|
popupStack.peek().popup.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// displays a popup for asking permission. Use this instead of JS confirm
|
// displays a popup for asking permission. Use this instead of JS confirm
|
||||||
const getConfirmation = (title, message, cancelText = 'Cancel', confirmText = 'OK') => {
|
const getConfirmation = (title, message, cancelText = 'Cancel', confirmText = 'OK') => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user