// ==UserScript==
// @name Third Eye - 'Sharty Port
// @description Display some soyjak.party images differently according to file name (MD5)
// @author Originally by Cunny Software Solutions
// @include https://*.soyjak*.party/*
// @match https://soyjak.party/*
// @namespace ThirdEyeSharty
// @match https://www.soyjak.party/*
// @license Public Domain
// @version 22
// @grant GM.xmlHttpRequest
// @grant GM_xmlHttpRequest
// @grant unsafeWindow
// ==/UserScript==
//
/* PLEASE REPORT BUGS & ISSUES @ https://git.coom.tech/cunnysoft/third-eye/issues */
/* You can also ask for help at /cumg/: https://boards.4channel.org/g/catalog#s=cumg */
//
/*eslint curly:0*/
(function() {
"use strict"
//
/* Added in v8, used to clear the cache on updates. Only bump when the cache needs to be cleared */
//
const THIRD_EYE_VERSION = 10
//
/* Enable or disable sources here */
//
const SOURCE_LIST = [
{name: "gelbooru", url: "https://gelbooru.com/index.php?page=dapi&s=post&q=index&json=1&tags=md5:", width: "sample_width", height: "sample_height"},
{name: "yande.re", url: "https://yande.re/post.json?tags=md5%3A", width: "sample_width", height: "sample_height"},
{name: "sankaku", url: "https://capi-v2.sankakucomplex.com/posts/keyset?tags=md5:", width: "sample_width", height: "sample_height"},
{name: "rule34", url: "https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&json=1&tags=md5:", width: "sample_width", height: "sample_height"},
]
//
/* Set to true to color Third Eye links */
//
const COLOR_THIRD_EYE_LINKS = false
//
/* Any color */
//
const THIRD_EYE_LINK_COLOR = "deeppink"
//
/* Set to false to disallow images from git.coom.tech/cunnysoft/oekaki */
//
const ENABLE_OEKAKI = true
//
/* Set to false to disallow explicit images */
//
const ALLOW_EXPLICIT = true
//
/* BLACKLIST: Uncomment one or add your own tags */
//const BLACKLIST = []
const BLACKLIST = ["scat"]
//const BLACKLIST = ["male_focus", "scat", "penis", "cum"]
//
/* THUMBNAIL SIZE */
//
const THUMBNAIL_SIZE = "125px"
const BIG_THUMBNAIL_SIZE = "250px"
//
/* Change at your own peril */
//
const PRESERVE_THUMBNAIL_SIZE = false
//
/* Options for when to use the source's preview image instead of the full image */
/* Some of these settings may interfere with PRESERVE_THUMBNAIL_SIZE */
//
const USE_PREVIEW_IMAGE_IN_OP = false // preview is often less than 250px, threatening loss of quality
const USE_PREVIEW_IMAGE_IN_REPLIES = true // preview is usually the right size
//
/* Avoid touching things below this line */
//
const XMLHttpRequest = (GM && GM.xmlHttpRequest) ?? GM_xmlHttpRequest
const THIRD_EYE_CACHE_PREFIX = "__thirdeye"
const THIRD_EYE_MD5_CACHE_PREFIX = "__thirdeye_md5__"
const THIRD_EYE_MISC_CACHE_PREFIX = "__thirdeye_misc__"
const must = (map, key) => {
if (map.has(key))
return map.get(key)
else
console.error("must() failed:", key)
}
const quote = (string) => {
return "'" + string + "'"
}
{
const clear_cache = () => {
for (const x in localStorage) {
if (x.startsWith(THIRD_EYE_CACHE_PREFIX))
localStorage.removeItem(x)
}
}
const _THIRD_EYE_VERSION = localStorage.getItem(THIRD_EYE_MISC_CACHE_PREFIX+"version")
if (!_THIRD_EYE_VERSION) { // _THIRD_EYE_VERSION === null
console.warn("third eye: No version in cache, so clearing cache.")
clear_cache()
} else if (_THIRD_EYE_VERSION < THIRD_EYE_VERSION) { // _THIRD_EYE_VERSION is a string like "8"
console.warn("third eye: Version in cache is outdated (" + _THIRD_EYE_VERSION + " < " + THIRD_EYE_VERSION + "), so clearing cache.")
clear_cache()
}
localStorage.setItem(THIRD_EYE_MISC_CACHE_PREFIX+"version", THIRD_EYE_VERSION)
}
for (const element of document.getElementsByClassName("fileThumb"))
element.__thirdeye_prepared = undefined
const whitelisted = (json) => {
// sankaku
if (typeof(json.tags) === "object") {
for (const tag of BLACKLIST) {
for (const tag2 in json.tags) {
if (tag2.name_en === tag)
return false
}
}
} else {
for (const tag of BLACKLIST) {
if ((json.tag_string_general || json.tags).split(" ").includes(tag))
return false
else if ((json.tag_string_character || "").split(" ").includes(tag))
return false
}
}
return ALLOW_EXPLICIT || json.rating !== "e"
}
const whitelisted_log = (json, md5sum) => {
const res = whitelisted(json)
if (!res)
console.info("third eye: skipped md5 " + md5sum + " due to one or more blacklisted tags")
return res
}
const _4cdn_image = (url) => {
return url.startsWith("https://i.4cdn.org/") || url.startsWith("https://s.4cdn.org/")
}
const _4cdn_full_image_cache = new Map()
const with_4cdn_full_image = (url, func) => {
if (_4cdn_full_image_cache.has(url)) {
func(_4cdn_full_image_cache.get(url))
} else {
const img = document.createElement("img")
img.src = url.replace("s.jpg", ".jpg")
img.onerror = () => {
img.src = url.replace("s.jpg", ".png")
img.onerror = () => {
img.src = url.replace("s.jpg", ".gif")
img.onerror = () => {
img.src = url.replace("s.jpg", ".webm")
}
}
}
img.onload = () => {
_4cdn_full_image_cache.set(url, img.src)
func(img.src)
}
}
}
const blob_cache = new Map()
const r_blob_cache = new Map()
const with_blob = (image, func) => {
if (blob_cache.has(image)) {
func(blob_cache.get(image))
} else
XMLHttpRequest({
method: "GET",
url: image,
responseType: "blob",
onload: (response) => {
const bimg = URL.createObjectURL(response.response)
blob_cache.set(image, bimg)
r_blob_cache.set(bimg, image)
func(bimg)
}
})
}
const add_events = (element, image) => {
const href0 = element.href
const href1 = () => {
if (_4cdn_image(href0))
return href0
else
return element.href
}
element.href = image
// TODO remove | try to fix race condition
setTimeout(() => {
element.href = image
}, 700)
// event handling
if (element.__thirdeye_observer) {
console.debug("third eye: disconnecting observer")
element.__thirdeye_observer.disconnect()
element.__thirdeye_observer = null // futureproofing
}
const config = { attributes: false, childList: true, subtree: false }
const callback = function(mutationsList, observer) {
if (element.children[1]?.className === "full-image")
element.children[1].style.visibility = "hidden"
}
const observer = new MutationObserver(callback)
observer.observe(element, config)
element.__thirdeye_observer = observer
element.addEventListener("click", (e) => {
setTimeout(() => {
const image2 = (element.parentElement.querySelector(".third-eye-swap-button")?.__thirdeye_show_original) ? href1() : image
with_blob(image2, (bimg) => {
element.href = image2
if (element.children[1])
element.children[1].style.visibility = "visible"
if (element.children[1]?.className === "full-image") {
element.children[1].src = bimg
}
})
}, 4)
})
// video thumbnails don't load and break the hoverUI
if (!element.href.endsWith(".mp4") && !element.href.endsWith(".webm")) {
element.children[0].addEventListener("mouseover", (e) => {
const handle_4chanX_hoverUI = (currentCB) => {
const hoverUI = document.getElementById("hoverUI")
if (!hoverUI || hoverUI.children.length === 0) {
if (currentCB < 125)
setTimeout(() => handle_4chanX_hoverUI(currentCB+1), 16) // DO NOT set this to below 16, it breaks
else {
if (hoverUI)
hoverUI.style.visibility = "visible"
console.warn("third eye: intercepting 4chanX HoverUI took too long. If 4chanX is not installed or hover is turned off, ignore this warning.")
}
} else {
const child = hoverUI.children[0]
const image2 = (element.parentElement.querySelector(".third-eye-swap-button")?.__thirdeye_show_original) ? href1() : image
with_blob(image2, (bimg) => {
child.src = bimg
const img = document.createElement("img")
img.src = bimg
img.onerror = () => {
console.error("Hover: Error loading img with src", quote(img.src))
hoverUI.style.visibility = "visible"
}
img.onload = () => {
const doc = document.documentElement
let width = img.width
let height = img.height
const maxWidth = doc.clientWidth
const maxHeight = doc.clientHeight - 25
const scale = Math.min(1, maxWidth / width, maxHeight / height)
width *= scale
height *= scale
width = Math.floor(width)
height = Math.floor(height)
child.src = bimg
child.style.width = width + "px"
child.style.height = height + "px"
child.style.maxWidth = width + "px"
child.style.maxHeight = height + "px"
child.style.top = Math.max(0, e.clientY * (doc.clientHeight - height) / doc.clientHeight) + "px"
hoverUI.style.visibility = "visible"
// event handling
if (child.__thirdeye_observer) {
//console.debug("third eye: disconnecting observer")
child.__thirdeye_observer.disconnect()
child.__thirdeye_observer = null // futureproofing
}
const config = { attributes: true, childList: false, subtree: false }
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for (const mutation of mutationsList) {
child.style.top = Math.max(0, e.clientY * (doc.clientHeight - height) / doc.clientHeight) + "px"
break
}
}
const observer = new MutationObserver(callback)
observer.observe(child, config)
child.__thirdeye_observer = observer
}
})
}
}
const hoverUI = document.getElementById("hoverUI")
if (hoverUI)
hoverUI.style.visibility = "hidden"
setTimeout(() => handle_4chanX_hoverUI(0), 4)
})
}
}
const thumbnail_cache = new Map()
const thumbnailize = (url, preview_url_or_undefined, md5sum, is_OP) => {
const work = (thumbnail) => {
thumbnail_cache.set(thumbnail, url)
return thumbnail
}
if (!USE_PREVIEW_IMAGE_IN_REPLIES || (is_OP && !USE_PREVIEW_IMAGE_IN_OP))
return url
else if (url.startsWith("https://img1.gelbooru.com/"))
return work("https://img1.gelbooru.com/thumbnails/" + md5sum[0] + md5sum[1] + "/" + md5sum[2] + md5sum[3] + "/thumbnail_" + md5sum + ".jpg")
else if (url.startsWith("https://img2.gelbooru.com/"))
return work("https://img2.gelbooru.com/thumbnails/" + md5sum[0] + md5sum[1] + "/" + md5sum[2] + md5sum[3] + "/thumbnail_" + md5sum + ".jpg")
else if (url.startsWith("https://img3.gelbooru.com/"))
return work("https://img3.gelbooru.com/thumbnails/" + md5sum[0] + md5sum[1] + "/" + md5sum[2] + md5sum[3] + "/thumbnail_" + md5sum + ".jpg")
else
return preview_url_or_undefined ? work(preview_url_or_undefined) : url
}
// loading function
const request_source = (element, md5sum, thumbnail_size, is_OP) => {
const callback = (i) => {
const source = SOURCE_LIST[i]
XMLHttpRequest({
method: "GET",
url: source.url+md5sum,
responseType: "json",
onload: (response) => {
// NOTE response.response should always exist, but this doesn't hurt
// https://git.coom.tech/cunnysoft/third-eye/pulls/1
let json = response?.response ?? JSON.parse(response?.responseText || "{}");
if (json && json.data !== undefined) // yande.re
json = json.data
// typeof(null) === "object"
// danbooru: json[0]
// gelbooru: json.post OR json.post[0]. Changed from json[0] on jan 2 2022
// yande.re: json[0]
// sankaku: json.data[0]
// rule34: json[0]
// lolibooru: json[0]
json = (json && typeof(json) == "object") ? (json.post || json[0]) : json
json = (json && typeof(json) == "object") ? (json[0] || json) : json // gelbooru exception
if (json && json.file_url && whitelisted_log(json, md5sum)) {
const child = element.children[0]
const thumbnail = {width: json[source.width], height: json[source.height]}
localStorage.setItem(THIRD_EYE_MD5_CACHE_PREFIX+md5sum,
JSON.stringify({url: json.file_url,
preview_url: json.preview_url /* yande.re, sankaku, lolibooru, rule34 */ || json.preview_file_url, // danbooru (not always available?)
width: thumbnail.width,
height: thumbnail.height,
rating: json.rating,
tags: json.tags,
tag_string_general: json.tag_string_general,
tag_string_character: json.tag_string_character}))
child.setAttribute("referrerpolicy", "no-referrer")
cache_src(json.file_url, child.src)
set_element_src(child, thumbnailize(json.file_url, json.preview_url, md5sum, is_OP))
if (PRESERVE_THUMBNAIL_SIZE) {
child.style.width = thumbnail.width+"px"
child.style.height = thumbnail.height+"px"
} else if (thumbnail.width > thumbnail.height) {
child.style.width = thumbnail_size
child.style.height = "auto"
} else {
child.style.height = thumbnail_size
child.style.width = "auto"
}
add_events(element, json.file_url)
console.debug("third eye: registering a new file (" + source.name + ")")
} else if (i < SOURCE_LIST.length-1)
callback(i+1)
else
console.debug("third eye: couldn't find anything for md5 " + md5sum)
}
})
}
callback(0)
}
const src_cache = new Map()
const cache_src = (image1, image2) => {
if (!src_cache.has(image1))
src_cache.set(image1, image2)
if (!src_cache.has(image2))
src_cache.set(image2, image1)
}
const set_element_src = (element, image) => {
cache_src(element.src, image)
element.src = image
}
const swap_element_src = (element) => {
element.src = must(src_cache, element.src)
}
const swap_element_src_then = (element, image, func) => {
with_blob(image, (bimg) => {
element.src = bimg
func()
})
}
let time = performance.now()
// main function
const main = () => {
// re-assign download href whenever it changes (4chanX)
// also add the swap button
for (const downloader of document.getElementsByClassName("download-button")) {
const fileThumb = downloader.parentElement.parentElement.parentElement.querySelector(".fileThumb")
if (downloader.__thirdeye_original_href === undefined)
downloader.__thirdeye_original_href = downloader.href
if (!_4cdn_image(fileThumb.href)) {
downloader.href = fileThumb.href
downloader.__thirdeye_replaced_href = downloader.href
}
if (!downloader.parentElement.querySelector(".third-eye-swap-button") &&
!_4cdn_image(fileThumb.children[0].src)) {
let image1 = fileThumb.children[0].src
const button = document.createElement("a")
// swap image button
button.className = "fa fa-eye third-eye-swap-button"
button.innerHTML = ""
button.style.marginLeft = "4px"
button.style.textDecoration = "none"
button.href = "javascript:void(null);"
button.__thirdeye_show_original = false
button.onclick = () => {
if (button.className == "fa fa-eye third-eye-swap-button")
button.className = "fa fa-eye third-eye-swap-button disabled"
else
button.className = "fa fa-eye third-eye-swap-button"
image1 = must(src_cache, image1)
const thumbnail = downloader.parentElement.parentElement.parentElement.querySelector(".fileThumb").children[0]
swap_element_src_then(thumbnail, image1, () => {
button.__thirdeye_show_original = !button.__thirdeye_show_original
const swap = (a, b, c) => {
if (a == b)
return c
else
return b
}
downloader.href = swap(downloader.href, downloader.__thirdeye_original_href, downloader.__thirdeye_replaced_href)
fileThumb.href = downloader.href
// only option to get the original 4chan image in replies
const img = document.createElement("img")
img.src = thumbnail.src
img.onerror = () => {
console.error("Swap1: Error loading img with src", quote(img.src))
}
img.onload = () => {
// fix dimensions of alt images
const child = fileThumb.children[0]
if (img.width > img.height) {
child.style.width = fileThumb.closest(".opContainer") ? BIG_THUMBNAIL_SIZE : THUMBNAIL_SIZE
child.style.height = "auto"
} else {
child.style.height = fileThumb.closest(".opContainer") ? BIG_THUMBNAIL_SIZE : THUMBNAIL_SIZE
child.style.width = "auto"
}
}
})
}
downloader.parentElement.insertBefore(button, downloader.nextSibling)
}
}
// 4chanX (fnswitch does not exist otherwise)
if (COLOR_THIRD_EYE_LINKS) {
for (const element of document.getElementsByClassName("fnswitch")) {
if (!_4cdn_image(element.parentElement.parentElement.parentElement.parentElement.querySelector(".fileThumb").href))
element.style.color = THIRD_EYE_LINK_COLOR
}
}
// for replies
for (const button of document.getElementsByClassName("third-eye-swap-button")) {
if (button.__thirdeye_show_original === undefined) {
button.__thirdeye_show_original = false
// 4chanX copies the replaced image so make sure the icon is right
button.className = "fa fa-eye third-eye-swap-button"
}
if (button.onclick === null) {
let image1 = undefined // wait for thumbnail to be replaced
button.onclick = () => {
if (button.className == "fa fa-eye third-eye-swap-button")
button.className = "fa fa-eye third-eye-swap-button disabled"
else
button.className = "fa fa-eye third-eye-swap-button"
button.__thirdeye_show_original = !button.__thirdeye_show_original
const downloader = button.parentElement.querySelector(".download-button")
const fileThumb = downloader.parentElement.parentElement.parentElement.querySelector(".fileThumb")
const thumbnail = fileThumb.children[0]
if (image1 === undefined) {
image1 = fileThumb.children[0].src
image1 = r_blob_cache.get(image1) || image1
}
image1 = must(src_cache, image1)
swap_element_src_then(thumbnail, image1, () => {
// only option to get the original 4chan image in replies
const img = document.createElement("img")
img.src = thumbnail.src
img.onerror = () => {
console.error("Swap2: Error loading img with src", quote(img.src))
}
img.onload = () => {
if (_4cdn_image(image1)) {
with_4cdn_full_image(image1, (image2) => {
downloader.href = image2
fileThumb.href = image2
})
} else {
downloader.href = thumbnail_cache.get(image1) || image1
fileThumb.href = downloader.href
}
// fix dimensions of alt images
const child = fileThumb.children[0]
if (img.width > img.height) {
child.style.width = fileThumb.closest(".opContainer") ? BIG_THUMBNAIL_SIZE : THUMBNAIL_SIZE
child.style.height = "auto"
} else {
child.style.height = fileThumb.closest(".opContainer") ? BIG_THUMBNAIL_SIZE : THUMBNAIL_SIZE
child.style.width = "auto"
}
}
})
}
}
}
let it = 0
for (const element of document.getElementsByClassName("fileThumb")) {
// the last is necessary for those that don't have an extended filename
const text_field = element.parentElement.children[0].children[0].title || // vanilla 4chan
element.parentElement.children[0].children[0].text || // vanilla 4chan (truncated filename...)
element.parentElement.children[0].children[0].children[0]?.children[0]?.children[1].innerText || // 4chanX
element.parentElement.children[0].children[0].children[0]?.text /* 4chanX */ || ""
const field_prefix = text_field.split(".")[0]
const is_valid_MD5 = (x) => {
const regexpr = /^[a-f0-9]{32}$/gi
return regexpr.test(x)
}
const skip_MD5 = (element, maybe_md5sum) => {
try {
const md5 = Array.from(atob(element.children[0].dataset.md5), aChar => ('0' + aChar.charCodeAt(0).toString(16)).slice(-2)).join('')
if (md5 === maybe_md5sum) {
console.debug("third eye: Skipping md5sum " + md5 + " because it is the same as the image MD5")
return true
} else
return false
} catch (error) {
console.error("third eye: skip_MD5 error: " + error + " (md5=" + maybe_md5sum + ")")
return true
}
}
// field_prefix !== "" for deleted files
it++
if (element.__thirdeye_prepared === undefined && field_prefix !== "" && !skip_MD5(element, field_prefix) && (is_valid_MD5(field_prefix) || (ENABLE_OEKAKI && (field_prefix.startsWith("oekaki--") || field_prefix.startsWith("oekakijpg--"))))) {
element.__thirdeye_prepared = true
// TODO
const is_OP = (it === 1 || element.closest(".opContainer"))
const thumbnail_size = is_OP ? BIG_THUMBNAIL_SIZE : THUMBNAIL_SIZE
let item = localStorage.getItem(THIRD_EYE_MD5_CACHE_PREFIX+field_prefix)
if (item === null) {
if (field_prefix.startsWith("oekaki--")) {
console.info("third eye: using oekaki (git.coom.tech/cunnysoft/oekaki) for file prefix " + field_prefix)
const child = element.children[0]
child.setAttribute("referrerpolicy", "no-referrer")
set_element_src(child, "https://git.coom.tech/cunnysoft/oekaki/raw/branch/%e3%82%bb%e3%82%af%e3%82%b7%e3%83%bc%e3%81%aa%e5%a5%b3%e3%81%ae%e5%ad%90/" + field_prefix.split("oekaki--")[1] + ".png")
child.onerror = () => {
console.error("Main1: Error loading img with src", quote(child.src))
}
child.onload = () => {
if (child.width > child.height) {
child.style.width = thumbnail_size
child.style.height = "auto"
} else {
child.style.height = thumbnail_size
child.style.width = "auto"
}
}
// we assume width:height ratio is the same for now
add_events(element, child.src)
// although .jpg files are not planned at the moment we'd like to make sure older clients support them in case they ever are
} else if (field_prefix.startsWith("oekakijpg--")) {
console.info("third eye: using oekaki jpg (git.coom.tech/cunnysoft/oekaki) for file prefix " + field_prefix)
const child = element.children[0]
child.setAttribute("referrerpolicy", "no-referrer")
set_element_src(child, "https://git.coom.tech/cunnysoft/oekaki/raw/branch/%e3%82%bb%e3%82%af%e3%82%b7%e3%83%bc%e3%81%aa%e5%a5%b3%e3%81%ae%e5%ad%90/" + field_prefix.split("oekakijpg--")[1] + ".jpg")
child.onerror = () => {
console.error("Main2: Error loading img with src", quote(child.src))
}
child.onload = () => {
if (child.width > child.height) {
child.style.width = thumbnail_size
child.style.height = "auto"
} else {
child.style.height = thumbnail_size
child.style.width = "auto"
}
}
// we assume width:height ratio is the same for now
add_events(element, child.src)
} else {
const md5sum = field_prefix
console.info("third eye: found valid md5 " + md5sum + ", running query")
request_source(element, md5sum, thumbnail_size, is_OP)
}
} else {
const md5sum = field_prefix
item = JSON.parse(item)
if (whitelisted_log(item, md5sum)) {
console.debug("third eye: reusing cached file for md5 " + md5sum)
const child = element.children[0]
child.setAttribute("referrerpolicy", "no-referrer")
cache_src(item.url, child.src)
set_element_src(child, thumbnailize(item.url, item.preview_url, md5sum, is_OP))
if (PRESERVE_THUMBNAIL_SIZE) {
child.style.width = item.width+"px"
child.style.height = item.height+"px"
} else if (item.width > item.height) {
child.style.width = thumbnail_size
child.style.height = "auto"
} else {
child.style.height = thumbnail_size
child.style.width = "auto"
}
add_events(element, item.url)
}
}
} else {
if (((performance.now() - time)/1000) >= 30) {
time = performance.now()
console.info("third eye: Skipping element with assigned 'prepared' field/invalid MD5 (delayed next log)")
}
}
}
}
main() // required if no 4chanX
// event handling
const config = { attributes: false, childList: true, subtree: true }
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for (const mutation of mutationsList) {
main()
break
}
}
const observer = new MutationObserver(callback)
observer.observe(document.getElementsByClassName("thread")[0], config)
})()
if (typeof exportFunction === "function") {
const THIRD_EYE_CACHE_PREFIX = "__thirdeye"
const THIRD_EYE_MD5_CACHE_PREFIX = "__thirdeye_md5__"
const THIRD_EYE_MISC_CACHE_PREFIX = "__thirdeye_misc__"
// exports for the client
const clear_third_eye_cache = () => {
for (const x in localStorage) {
if (x.startsWith(THIRD_EYE_CACHE_PREFIX))
localStorage.removeItem(x)
}
}
const display_third_eye_cache = () => {
for (const x in localStorage) {
if (x.startsWith(THIRD_EYE_CACHE_PREFIX))
console.info(x + " = " + localStorage[x])
}
}
unsafeWindow.clear_third_eye_cache = exportFunction(clear_third_eye_cache, unsafeWindow)
unsafeWindow.display_third_eye_cache = exportFunction(display_third_eye_cache, unsafeWindow)
// more searchable
unsafeWindow.third_eye_clear_cache = exportFunction(clear_third_eye_cache, unsafeWindow)
unsafeWindow.third_eye_display_cache = exportFunction(display_third_eye_cache, unsafeWindow)
unsafeWindow.third_eye_version = exportFunction(() => localStorage.getItem(THIRD_EYE_MISC_CACHE_PREFIX+"version"))
} else {
const THIRD_EYE_CACHE_PREFIX = "__thirdeye"
const THIRD_EYE_MD5_CACHE_PREFIX = "__thirdeye_md5__"
const THIRD_EYE_MISC_CACHE_PREFIX = "__thirdeye_misc__"
// exports for the client
clear_third_eye_cache = (() => {
for (const x in localStorage) {
if (x.startsWith(THIRD_EYE_CACHE_PREFIX))
localStorage.removeItem(x)
}
})
// more searchable
third_eye_clear_cache = clear_third_eye_cache
display_third_eye_cache = (() => {
for (const x in localStorage) {
if (x.startsWith(THIRD_EYE_CACHE_PREFIX))
console.info(x + " = " + localStorage[x])
}
})
// more searchable
third_eye_display_cache = display_third_eye_cache
third_eye_version = (() => {
return localStorage.getItem(THIRD_EYE_MISC_CACHE_PREFIX+"version")
})
}