From 3ec9264deb436af8fd6f5efc5efc3c7872127b05 Mon Sep 17 00:00:00 2001 From: Feuerfuchs Date: Mon, 24 Jun 2019 21:54:39 +0200 Subject: Improved design, add setting for image previews, add prompt for new location --- js/KeyValueStore.ts | 89 ++++++++++++++++++++++++ js/main.ts | 191 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 254 insertions(+), 26 deletions(-) create mode 100644 js/KeyValueStore.ts (limited to 'js') diff --git a/js/KeyValueStore.ts b/js/KeyValueStore.ts new file mode 100644 index 0000000..fd8f39e --- /dev/null +++ b/js/KeyValueStore.ts @@ -0,0 +1,89 @@ +type KeyValueStoreCallback = (value: T) => void; + +interface IKeyValueStoreParam { + value: T; + valueRange?: T[]; + callbacks?: KeyValueStoreCallback[]; +} + +type KeyValueStoreData = { [K in keyof T]: IKeyValueStoreParam }; + +class KeyValueStore { + constructor(private data: KeyValueStoreData) { + for (const id of Object.keys(data)) { + const props = data[id as keyof typeof data]; + + if (props.valueRange && props.valueRange.indexOf(props.value) === -1) { + throw new Error(`Invalid value "${props.value}" for ID "${id}"`); + } + } + } + + public getValue(id: keyof T) { + return this.data[id].value; + } + + public setValue(id: K, value: T[K]) { + const props = this.data[id]; + + if (props.valueRange) { + const valueIndex = props.valueRange.indexOf(value); + if (valueIndex === -1) { + throw new Error(`Invalid value "${value}" for ID "${id}"`); + } + } + + props.value = value; + + if (props.callbacks) { + props.callbacks.forEach(callback => { + callback(value); + }); + } + } + + public cycleValue(id: K, dir: -1 | 1 = 1) { + const props = this.data[id]; + + if (!props) { + throw new Error(`Invalid ID "${id}"`); + } + + let value = props.value; + + if (props.valueRange) { + let valueIndex = props.valueRange.indexOf(value) + dir; + + if (valueIndex >= props.valueRange.length) { + valueIndex = 0; + } else if (valueIndex < 0) { + valueIndex = props.valueRange.length - 1; + } + + value = props.value = props.valueRange[valueIndex]; + } else if (typeof value === "number") { + (value as number) += dir; + props.value = value; + } else { + throw new Error(`Can't cycle "${id}"`); + } + + if (props.callbacks) { + props.callbacks.forEach(callback => { + callback(value); + }); + } + + return value; + } + + public addCallback(id: K, callback: KeyValueStoreCallback) { + const props = this.data[id]; + + if (!props.callbacks) { + props.callbacks = []; + } + + props.callbacks.push(callback); + } +} diff --git a/js/main.ts b/js/main.ts index 21e589d..7ef7020 100644 --- a/js/main.ts +++ b/js/main.ts @@ -1,42 +1,181 @@ -let linkQryEls = document.getElementsByClassName('link--QRY'); -let i = linkQryEls.length; -while (i--) { - linkQryEls[i].addEventListener('click', e => { +/// + +// + +function ensureSetting(key: string, defaultValue: string): string { + let strValue = localStorage.getItem(key); + if (strValue === null) { + strValue = defaultValue; + localStorage.setItem(key, strValue); + } + return strValue; +} + +const settings = new KeyValueStore({ + imagePreviews: { + value: ensureSetting('image-previews', '1') === '1', + valueRange: [false, true] + } +}); + +// + +(() => { + const linkQryEls = document.getElementsByClassName('link--QRY'); + let i = linkQryEls.length; + while (i--) { + linkQryEls[i].addEventListener('click', e => { + e.preventDefault(); + + const resp = prompt('Please enter required input: ', ''); + if ((resp !== null) && (resp !== "")) { + window.location.href = (e.target as HTMLAnchorElement).href + '?' + resp; + } + + return false; + }); + } +})(); + +(() => { + const locationPrefixEl = document.getElementsByClassName('location__prefix')[0]; + locationPrefixEl.addEventListener('click', e => { e.preventDefault(); - const resp = prompt('Please enter required input: ', ''); + const resp = prompt('Please enter new location: ', ''); if ((resp !== null) && (resp !== "")) { - window.location.href = (e.target as HTMLAnchorElement).href + '?' + resp; + window.location.href = window.location.origin + '/' + resp; } return false; }); -} +})(); -let imgPreviewEls = document.getElementsByClassName('img-preview'); -i = imgPreviewEls.length; -while (i--) { - const imgPreviewEl = imgPreviewEls[i] as HTMLAnchorElement; - const child = imgPreviewEl.children[0] as HTMLImageElement; - const thumbnailUrl = child.src; +(() => { + const settingImagePreviewEl = document.getElementsByClassName('setting--image-previews')[0]; + const settingImagePreviewValueEl = settingImagePreviewEl.getElementsByClassName('setting__value')[0]; + const settingImagePreviewCallback = (value: boolean, init = false) => { + if (value) { + generateImageThumbnails(); + } else if (!init) { + removeImageThumbnails(); + } - child.addEventListener('load', e => { - child.classList.remove('faded'); - }); + localStorage.setItem('image-previews', value ? '1' : '0'); + settingImagePreviewValueEl.textContent = value ? '[yes]' : '[no]'; + } - imgPreviewEls[i].addEventListener('click', e => { + settingImagePreviewValueEl.addEventListener('click', e => { e.preventDefault(); + settings.cycleValue('imagePreviews'); + return false; + }); - child.classList.add('faded'); + settingImagePreviewCallback(settings.getValue('imagePreviews'), true); + settings.addCallback('imagePreviews', settingImagePreviewCallback); +})(); - if (child.classList.contains('expanded')) { - child.classList.remove('expanded'); - child.src = thumbnailUrl; - } else { - child.classList.add('expanded'); - child.src = imgPreviewEl.href; - } - +(() => { + const modalEls = document.getElementsByClassName('modal'); + let i = modalEls.length; + while (i--) { + const modalEl = modalEls[i]; + const modalContentEl = modalEl.getElementsByClassName('modal__content')[0]; + const modalCloseBtnEl = modalEl.getElementsByClassName('modal__close-btn')[0]; + + document.addEventListener('click', e => { + if (!modalEl.classList.contains('modal--visible')) { + return; + } + if (e.target !== modalContentEl && !modalContentEl.contains(e.target as Element)) { + modalEl.classList.remove('modal--visible'); + e.preventDefault(); + e.stopPropagation(); + } + }, true); + + document.addEventListener('keydown', e => { + if (!modalEl.classList.contains('modal--visible')) { + return; + } + if (e.keyCode === 27) { + modalEl.classList.remove('modal--visible'); + } + }); + + modalCloseBtnEl.addEventListener('click', e => { + e.preventDefault(); + modalEl.classList.remove('modal--visible'); + return false; + }); + } + + // + + const settingsBtnEl = document.getElementsByClassName('settings-btn')[0]; + const settingsModalEl = document.getElementsByClassName('modal--settings')[0]; + + settingsBtnEl.addEventListener('click', e => { + e.preventDefault(); + settingsModalEl.classList.add('modal--visible'); return false; }); +})(); + +function generateImageThumbnails() { + const linkImgEls = document.querySelectorAll('.link--IMG, .link--GIF'); + let i = linkImgEls.length; + while (i--) { + const linkImgEl = linkImgEls[i] as HTMLAnchorElement; + const thumbnailUrl = linkImgEl.href.replace(/^(.*?)\/I/, '$1/T'); + + const lineBreakEl = document.createTextNode('\n'); + + const typeAnnotEl = document.createElement('span'); + typeAnnotEl.classList.add('type-annotation'); + typeAnnotEl.textContent = ' -> '; + + const thumbnailEl = document.createElement('img'); + thumbnailEl.src = thumbnailUrl; + thumbnailEl.addEventListener('load', e => { + thumbnailEl.classList.remove('faded'); + }); + + const thumbnailAnchorEl = document.createElement('a'); + thumbnailAnchorEl.classList.add('img-preview'); + thumbnailAnchorEl.href = linkImgEl.href; + thumbnailAnchorEl.addEventListener('click', e => { + e.preventDefault(); + + thumbnailEl.classList.add('faded'); + + if (thumbnailEl.classList.contains('expanded')) { + thumbnailEl.classList.remove('expanded'); + thumbnailEl.src = thumbnailUrl; + } else { + thumbnailEl.classList.add('expanded'); + thumbnailEl.src = thumbnailAnchorEl.href; + } + + return false; + }); + + thumbnailAnchorEl.append(thumbnailEl); + linkImgEl.parentNode!.insertBefore(thumbnailAnchorEl, linkImgEl.nextSibling); + linkImgEl.parentNode!.insertBefore(typeAnnotEl, thumbnailAnchorEl); + linkImgEl.parentNode!.insertBefore(lineBreakEl, typeAnnotEl); + } +} + +function removeImageThumbnails() { + const linkImgEls = document.querySelectorAll('.link--IMG, .link--GIF'); + let i = linkImgEls.length; + while (i--) { + const linkImgEl = linkImgEls[i]; + let j = 3; + + while (j-- && linkImgEl.nextSibling) { + linkImgEl.nextSibling.remove(); + } + } } -- cgit v1.2.3-70-g09d2