+
+
+
+
Set the file's Password.
+
+
diff --git a/src/js/options.js b/src/js/options.js
index 3be260d..55865f2 100644
--- a/src/js/options.js
+++ b/src/js/options.js
@@ -17,12 +17,12 @@ document
* @function initOptions
*/
async function initOptions() {
- // console.log('initOptions')
+ // console.debug('initOptions')
document.getElementById('version').textContent =
chrome.runtime.getManifest().version
const { options } = await chrome.storage.sync.get(['options'])
- console.log('options:', options)
+ console.debug('options:', options)
updateOptions(options)
if (!options?.siteUrl) {
const siteUrl = document.getElementById('siteUrl')
@@ -42,10 +42,10 @@ async function initOptions() {
* @param {String} namespace
*/
function onChanged(changes, namespace) {
- // console.log('onChanged:', changes, namespace)
+ // console.debug('onChanged:', changes, namespace)
for (const [key, { newValue }] of Object.entries(changes)) {
if (namespace === 'sync' && key === 'options') {
- console.log('newValue:', newValue)
+ console.debug('newValue:', newValue)
updateOptions(newValue)
}
}
@@ -57,7 +57,7 @@ function onChanged(changes, namespace) {
* @param {FormDataEvent} event
*/
async function saveOptions(event) {
- // console.log('saveOptions:', event)
+ // console.debug('saveOptions:', event)
const { options } = await chrome.storage.sync.get(['options'])
if (event.target.type === 'checkbox') {
options[event.target.id] = event.target.checked
@@ -75,8 +75,8 @@ async function saveOptions(event) {
} else {
options[event.target.id] = event.target.value
}
- console.log(`Set: "${event.target.id}" to target:`, event.target)
- console.log('options:', options)
+ console.info(`Set: "${event.target.id}" to target:`, event.target)
+ console.debug('options:', options)
await chrome.storage.sync.set({ options })
}
@@ -87,7 +87,7 @@ async function saveOptions(event) {
*/
function updateOptions(options) {
for (const [key, value] of Object.entries(options)) {
- // console.log(`${key}: ${value}`)
+ // console.debug(`${key}: ${value}`)
const element = document.getElementById(key)
if (element) {
if (typeof value === 'boolean') {
diff --git a/src/js/popup.js b/src/js/popup.js
index 10c01dd..8afba5c 100644
--- a/src/js/popup.js
+++ b/src/js/popup.js
@@ -2,6 +2,11 @@
chrome.runtime.onMessage.addListener(onMessage)
document.addEventListener('DOMContentLoaded', initPopup)
+document.getElementById('expire-form').addEventListener('submit', expireForm)
+
+document
+ .getElementById('password-form')
+ .addEventListener('submit', passwordForm)
document
.getElementById('confirm-delete')
.addEventListener('click', deleteConfirm)
@@ -18,6 +23,14 @@ document
.querySelectorAll('[data-bs-toggle="tooltip"]')
.forEach((el) => new bootstrap.Tooltip(el))
+document.querySelectorAll('.modal').forEach((el) =>
+ el.addEventListener('shown.bs.modal', (event) => {
+ const input = event.target.querySelector('input')
+ input?.focus()
+ input?.select()
+ })
+)
+
const filesTable = document.getElementById('files-table')
const errorAlert = document.getElementById('error-alert')
const authButton = document.getElementById('auth-button')
@@ -25,13 +38,22 @@ const alwaysAuth = document.getElementById('always-auth')
const mediaOuter = document.getElementById('media-outer')
const mediaImage = document.getElementById('media-image')
const mediaError = document.getElementById('media-error')
-const deleteName = document.getElementById('delete-name')
+const ctxMenuRow = document.getElementById('ctx-menu-row')
+const expireInput = document.getElementById('expire-input')
+const passwordInput = document.getElementById('password-input')
const deleteModal = bootstrap.Modal.getOrCreateInstance('#delete-modal')
+const expireModal = bootstrap.Modal.getOrCreateInstance('#expire-modal')
+const passwordModal = bootstrap.Modal.getOrCreateInstance('#password-modal')
+
+const clipboard = new ClipboardJS('.clip')
+clipboard.on('success', () => showToast('Copied to Clipboard'))
+clipboard.on('error', () => showToast('Clipboard Copy Failed', 'warning'))
const loadingImage = '../media/loading.gif'
let authError = false
let timeoutID
let timeout
+let fileData
/**
* Initialize Popup
@@ -39,11 +61,11 @@ let timeout
* @function initPopup
*/
async function initPopup() {
- console.log('initPopup')
+ console.debug('initPopup')
// Get options
const { options } = await chrome.storage.sync.get(['options'])
- console.log('options:', options)
+ console.debug('options:', options)
// Set Options (this is currently the only one in the popup)
document.getElementById('popupPreview').checked = options.popupPreview
@@ -87,12 +109,11 @@ async function initPopup() {
cache: 'no-cache',
}
let response
- let data
try {
const url = new URL(`${options.siteUrl}/api/recent/`)
url.searchParams.append('amount', options.recentFiles || '10')
response = await fetch(url, opts)
- data = await response.json()
+ fileData = await response.json()
} catch (e) {
console.warn(e)
return displayAlert({
@@ -101,31 +122,41 @@ async function initPopup() {
auth: true,
})
}
- console.log(`response.status: ${response.status}`, response, data)
+ console.debug(`response.status: ${response.status}`, response, fileData)
// Check response data is valid and has files
if (!response?.ok) {
- console.warn(`error: ${data.error}`)
- return displayAlert({ message: data.error, type: 'danger', auth: true })
- } else if (data === undefined) {
+ console.warn(`error: ${fileData.error}`)
+ return displayAlert({
+ message: fileData.error,
+ type: 'danger',
+ auth: true,
+ })
+ } else if (fileData === undefined) {
return displayAlert({ message: 'Response Data Undefined.', auth: true })
- } else if (!data.length) {
+ } else if (!fileData.length) {
return displayAlert({ message: 'No Files Returned.' })
}
// Update table should only be called here, changes should use initPopup()
- updateTable(data)
+ updateTable(fileData, options)
- // Re-init clipboardJS and popupLinks after updateTable
- new ClipboardJS('.clip')
+ // CTX menus are re-generated, eventListener re-addd
+ document
+ .querySelectorAll('[data-action]')
+ .forEach((el) => el.addEventListener('click', ctxMenu))
+
+ // File Links are re-generated, eventListener re-addd
document
.querySelectorAll('a[href]')
.forEach((el) => el.addEventListener('click', popupLinks))
+ // Re-init clipboardJS after updateTable
+ new ClipboardJS('.clip')
+
// Enable Popup Mouseover Preview if popupPreview
timeout = options.popupTimeout * 1000
if (options.popupPreview) {
- console.log('Enabling Mouseover Preview')
initPopupMouseover()
}
}
@@ -137,7 +168,7 @@ async function initPopup() {
* @param {MouseEvent} event
*/
async function popupLinks(event) {
- console.log('popupLinks:', event)
+ console.debug('popupLinks:', event)
event.preventDefault()
const anchor = event.target.closest('a')
console.log(`anchor.href: ${anchor.href}`)
@@ -157,8 +188,8 @@ async function popupLinks(event) {
async function onMessage(message) {
// console.log('onMessage: message, sender:', message, sender)
if (message?.siteUrl && message?.authToken) {
- console.log(`url: ${message.siteUrl}`)
- console.log(`token: ${message.authToken}`)
+ console.debug(`url: ${message.siteUrl}`)
+ console.debug(`token: ${message.authToken}`)
const { options } = await chrome.storage.sync.get(['options'])
if (
options?.siteUrl !== message.siteUrl ||
@@ -169,7 +200,7 @@ async function onMessage(message) {
authToken: message.authToken,
}
await chrome.storage.local.set({ auth })
- console.log('New Authentication Found.')
+ console.info('New Authentication Found.')
if (options.checkAuth) {
alwaysAuth.classList.remove('d-none')
}
@@ -189,15 +220,15 @@ async function saveOptions(event) {
// console.log('saveOptions:', event)
const { options } = await chrome.storage.sync.get(['options'])
options[event.target.id] = event.target.checked
- console.log(`Set: "${event.target.id}" to target:`, event.target)
- console.log('options:', options)
+ console.info(`Set: "${event.target.id}" to target:`, event.target)
+ console.debug('options:', options)
await chrome.storage.sync.set({ options })
if (event.target.id === 'popupPreview') {
if (event.target.checked) {
- console.log('popupPreview Enabled. Running initPopupMouseover...')
+ console.debug('popupPreview Enabled. Running initPopupMouseover...')
initPopupMouseover()
} else {
- console.log('popupPreview Disabled. Removing Event Listeners...')
+ console.debug('popupPreview Disabled. Removing Event Listeners...')
document.querySelectorAll('.link-underline').forEach((el) => {
el.removeEventListener('mouseover', onMouseOver)
el.removeEventListener('mouseout', onMouseOut)
@@ -213,15 +244,15 @@ async function saveOptions(event) {
* @param {MouseEvent} event
*/
async function authCredentials(event) {
- console.log('authCredentials:', event)
+ console.debug('authCredentials:', event)
const { auth } = await chrome.storage.local.get(['auth'])
- console.log('auth:', auth)
+ console.debug('auth:', auth)
if (auth?.authToken && auth?.siteUrl) {
const { options } = await chrome.storage.sync.get(['options'])
options.authToken = auth.authToken
options.siteUrl = auth.siteUrl
await chrome.storage.sync.set({ options })
- console.log('Auth Credentials Updated...')
+ console.info('Auth Credentials Updated...')
authButton.classList.add('d-none')
errorAlert.classList.add('d-none')
alwaysAuth.classList.add('d-none')
@@ -238,7 +269,7 @@ async function authCredentials(event) {
* @param {Number} rows
*/
function genLoadingData(rows) {
- console.log('genLoadingData:', rows)
+ console.debug('genLoadingData:', rows)
const number = parseInt(rows, 10)
if (number > 0) {
filesTable.classList.remove('d-none')
@@ -262,105 +293,298 @@ function genLoadingData(rows) {
* Update Popup Table with Data
* @function updateTable
* @param {Array} data
+ * @param {Object} options
*/
-function updateTable(data) {
- console.log('updateTable:', data)
+function updateTable(data, options) {
+ console.debug('updateTable:', data)
const tbody = filesTable.querySelector('tbody')
const length = tbody.rows.length
- // console.log(`data.length: ${data.length}`)
- // console.log(`tbody.rows.length: ${tbody.rows.length}`)
+ // console.debug(`data.length: ${data.length}`)
+ // console.debug(`tbody.rows.length: ${tbody.rows.length}`)
for (let i = 0; i < length; i++) {
- // console.log(`i: ${i}`)
+ // console.debug(`i: ${i}`)
let row = tbody.rows[i]
if (!row) {
row = tbody.insertRow()
}
if (data.length === i) {
- console.log('End of data. Removing remaining rows...')
+ console.info('End of data. Removing remaining rows...')
const rowsToRemove = length - i
for (let j = 0; j < rowsToRemove; j++) {
tbody.deleteRow(tbody.rows.length - 1)
}
break
}
- const value = data[i]
- // TODO: This should not happen because of above condition
- if (!value) {
- console.warn(`No Data Value at Index: ${i}`, row)
- continue
+ row.id = `row-${i}`
+ // TODO: Backwards Compatible with Older DJ Versions
+ let url
+ let name
+ let href
+ if (typeof data[i] === 'object') {
+ url = new URL(data[i].url)
+ name = data[i].name
+ href = data[i].url
+ } else {
+ url = new URL(data[i])
+ name = url.pathname.replace(/^\/u\//, '')
+ href = data[i]
}
- // TODO: This throws an error if value is not valid URL
- const url = new URL(value)
- const name = url.pathname.replace(/^\/u\//, '')
-
- const copy = document.createElement('a')
- copy.title = 'Copy'
- copy.setAttribute('role', 'button')
- copy.classList.add('link-body-emphasis', 'clip')
- copy.innerHTML = '
'
- copy.dataset.clipboardText = value
- copy.addEventListener('click', clipClick)
+ const raw = url.origin + url.pathname.replace(/^\/u\//, '/raw/')
+ let rawURL = new URL(raw)
+ const password = url.searchParams.get('password')
+ if (password) {
+ rawURL.searchParams.append('password', password)
+ }
+
+ // CTX Button -> 0
+ const button = document.createElement('a')
+ button.classList.add('link-body-emphasis', 'ctx-button')
+ button.setAttribute('role', 'button')
+ button.setAttribute('aria-expanded', 'false')
+ button.dataset.bsToggle = 'dropdown'
+ button.innerHTML = '
'
+
+ // CTX Drop Down -> Menu
+ const drop = document
+ .querySelector('.d-none .dropdown-menu')
+ .cloneNode(true)
+ drop.id = `ctx-${i}`
+ if (typeof data[i] === 'object') {
+ updateContextMenu(drop, data[i]).then()
+ }
+ const fileName = drop.querySelector('li.mouse-link')
+ fileName.innerText = name
+ fileName.dataset.clipboardText = name
+ fileName.dataset.raw = `${raw}?token=${options.authToken}&view=gallery`
+ drop.querySelector('.copy-link').dataset.clipboardText = href
+ drop.querySelector('.copy-raw').dataset.clipboardText = rawURL.href
+ drop.querySelectorAll('.raw').forEach((el) => (el.href = rawURL.href))
+ button.appendChild(drop)
+
+ // Cell: 0
const cell0 = row.cells[0]
cell0.classList.add('align-middle')
cell0.style.width = '20px'
cell0.innerHTML = ''
- cell0.appendChild(copy)
+ cell0.appendChild(button)
+ // File Link -> 1
const link = document.createElement('a')
link.text = name
link.title = name
- link.href = value
+ link.href = href
link.setAttribute('role', 'button')
link.classList.add(
'link-underline',
'link-underline-opacity-0',
'link-underline-opacity-75-hover',
- 'file-link'
+ 'file-link',
+ 'mouse-link'
)
link.target = '_blank'
link.dataset.name = name
- link.dataset.raw =
- url.origin +
- url.pathname.replace(/^\/u\//, '/raw/') +
- '?view=gallery'
+ link.dataset.row = i.toString()
+ link.dataset.raw = `${raw}?token=${options.authToken}&view=gallery`
+
+ // Cell: 1
const cell1 = row.cells[1]
cell1.classList.add('text-break')
cell1.innerHTML = ''
cell1.appendChild(link)
+ }
+}
- const del = document.createElement('a')
- del.title = 'Delete'
- del.setAttribute('role', 'button')
- del.classList.add('link-danger')
- del.innerHTML = '
'
- del.addEventListener('click', deleteClick)
- const cell2 = row.cells[2]
- cell2.classList.add('align-middle')
- cell2.style.width = '20px'
- cell2.innerHTML = ''
- cell2.appendChild(del)
+/**
+ * @function updateContextMenu
+ * @param {HTMLElement} ctx
+ * @param {Object} data
+ * @return {Promise
}
+ */
+async function updateContextMenu(ctx, data) {
+ // console.debug('updateContextMenu:', ctx, data)
+ if (data.view) {
+ const views = ctx.querySelector('.view-text')
+ views.innerText = data.view
+ enableEl(ctx, '.view-text')
+ enableEl(ctx, '.fa-eye')
+ }
+ if (data.private) {
+ enableEl(ctx, '.fa-lock', 'text-danger-emphasis')
+ }
+ if (data.password) {
+ enableEl(ctx, '.fa-key', 'text-warning-emphasis')
+ // const link = ctx.querySelector('.pass-link')
+ // link.classList.add('clip')
+ // link.dataset.clipboardText = data.password
+ } else {
+ disableEl(ctx, '.fa-key', 'text-warning-emphasis')
+ }
+ if (data.expr) {
+ enableEl(ctx, '.fa-hourglass-start')
+ ctx.querySelector('.expr-text').innerText = data.expr
+ } else {
+ disableEl(ctx, '.fa-hourglass-start')
+ ctx.querySelector('.expr-text').innerText = ''
}
}
+function enableEl(ctx, selector, add = 'text-body-emphasis') {
+ const el = ctx.querySelector(selector)
+ el.classList.remove('text-body-tertiary')
+ el.classList.add(add)
+}
+
+function disableEl(ctx, selector, remove = 'text-body-emphasis') {
+ const el = ctx.querySelector(selector)
+ el.classList.remove(remove)
+ el.classList.add('text-body-tertiary')
+}
+
/**
- * Delete Click Callback
- * @function deleteClick
+ * Context Menu Click Callback
+ * @function ctxMenu
* @param {MouseEvent} event
*/
-async function deleteClick(event) {
- console.log('deleteClick:', event)
- const closest = event.target?.closest('tr')?.querySelector('.file-link')
- const name = closest.dataset?.name
- console.log('name:', name)
- if (!name) {
- return console.error('No name for: event, closest', event, closest)
- }
- deleteName.textContent = name
- const { options } = await chrome.storage.sync.get(['options'])
- if (options.deleteConfirm) {
- deleteModal.show()
+async function ctxMenu(event) {
+ console.debug('ctxMenu:', event)
+ event.preventDefault()
+ const anchor = event.target.closest('a')
+ // console.debug('anchor:', anchor)
+ const action = anchor.dataset?.action
+ console.debug('action:', action)
+ const fileLink = event.target?.closest('tr')?.querySelector('.file-link')
+ console.debug('row:', fileLink.dataset?.row)
+ if (!fileLink.dataset?.row) {
+ console.error('404: fileLink.dataset?.row - Fatal Error!')
+ }
+ ctxMenuRow.value = fileLink.dataset?.row
+ const file = fileData[fileLink.dataset?.row]
+ console.debug('file:', file)
+ let name
+ if (typeof file === 'object') {
+ name = file.name
} else {
- await deleteConfirm(event)
+ name = fileLink.dataset.name
+ }
+ console.debug('name:', name)
+ if (action === 'delete') {
+ document.querySelector('#delete-modal .file-name').textContent = name
+ const { options } = await chrome.storage.sync.get(['options'])
+ if (options.deleteConfirm) {
+ deleteModal.show()
+ } else {
+ await deleteConfirm(event)
+ }
+ } else if (action === 'expire') {
+ expireInput.value = file.expr
+ document.querySelector('#expire-modal .file-name').textContent = name
+ expireModal.show()
+ } else if (action === 'password') {
+ passwordInput.value = file.password
+ document.querySelector('#password-modal .file-name').textContent = name
+ passwordModal.show()
+ } else if (action === 'private') {
+ await togglePrivate()
+ }
+}
+
+async function togglePrivate() {
+ console.debug('togglePrivate')
+ // event.preventDefault()
+ const file = fileData[ctxMenuRow.value]
+ console.debug('file:', file)
+ const data = { private: !file.private }
+ const response = await handleFile(file.name, 'POST', data)
+ console.debug('response:', response)
+ if (response.ok) {
+ const json = await response.json()
+ console.debug('json:', json)
+ fileData[ctxMenuRow.value] = json
+ const ctx = document.getElementById(`ctx-${ctxMenuRow.value}`)
+ console.debug('ctx:', ctx)
+ if (json.private) {
+ enableEl(ctx, '.fa-lock', 'text-danger-emphasis')
+ } else {
+ disableEl(ctx, '.fa-lock', 'text-danger-emphasis')
+ }
+ showToast(`Privacy Updated: ${file.name}`)
+ } else {
+ console.info(`Private Error: "${file.name}", response:`, response)
+ showToast(`Error Setting Privacy: ${file.name}`, 'danger')
+ }
+}
+
+/**
+ * Password Form Submit Callback
+ * @function passwordForm
+ * @param {SubmitEvent} event
+ */
+async function passwordForm(event) {
+ console.debug('passwordForm:', event)
+ event.preventDefault()
+ const file = fileData[ctxMenuRow.value]
+ console.debug('file:', file)
+ const password = passwordInput.value
+ if (password === file.password) {
+ console.info(`Passwords Identical: ${password} === ${file.password}`)
+ showToast(`Passwords Identical: ${file.name}`, 'warning')
+ return passwordModal.hide()
+ }
+ console.log(`Setting Password: "${password}" on file: ${file.name}`)
+ const data = { password: password }
+ // TODO: Catch Error? Throw should happen during init...
+ const response = await handleFile(file.name, 'POST', data)
+ console.debug('response:', response)
+ if (response.ok) {
+ showToast(`Password Updated: ${file.name}`)
+ const json = await response.json()
+ console.debug('json:', json)
+ const ctx = document.getElementById(`ctx-${ctxMenuRow.value}`)
+ console.debug('ctx:', ctx)
+ fileData[ctxMenuRow.value] = json
+ await updateContextMenu(ctx, json)
+ passwordModal.hide()
+ } else {
+ console.info(`Password Error: "${password}", response:`, response)
+ showToast(`Error Setting Password: ${file.name}`, 'danger')
+ passwordModal.hide()
+ }
+}
+
+/**
+ * Expire Form Submit Callback
+ * @function expireForm
+ * @param {SubmitEvent} event
+ */
+async function expireForm(event) {
+ console.debug('expireForm:', event)
+ event.preventDefault()
+ const file = fileData[ctxMenuRow.value]
+ console.debug('file:', file)
+ const expr = expireInput.value
+ if (expr === file.expr) {
+ console.info(`New Expire Value Same as Old: ${expr}`)
+ showToast(`New Expire same as Previous: ${file.name}`, 'warning')
+ return expireModal.hide()
+ }
+ console.log(`Setting Expire: "${expr}" on file: ${file.name}`)
+ const data = { expr: expr }
+ // TODO: Catch Error? Throw should happen during init...
+ const response = await handleFile(file.name, 'POST', data)
+ console.debug('response:', response)
+ if (response.ok) {
+ showToast(`Expire Updated: ${file.name}`)
+ const json = await response.json()
+ console.debug('json:', json)
+ const ctx = document.getElementById(`ctx-${ctxMenuRow.value}`)
+ console.debug('ctx:', ctx)
+ fileData[ctxMenuRow.value] = json
+ await updateContextMenu(ctx, json)
+ expireModal.hide()
+ } else {
+ console.info(`Error Setting Expire: "${expr}", response:`, response)
+ showToast(`Error Setting Expire: ${file.name}`, 'danger')
+ expireModal.hide()
}
}
@@ -370,39 +594,49 @@ async function deleteClick(event) {
* @param {MouseEvent} event
*/
async function deleteConfirm(event) {
- console.log('deleteConfirm:', event)
- const name = deleteName.textContent
- console.log(`Deleting File: ${name}`)
+ console.debug('deleteConfirm:', event)
+ event.preventDefault()
+ const file = fileData[ctxMenuRow.value]
+ console.debug('file:', file)
+ const name = document.querySelector('#delete-modal .file-name').textContent
+ console.log(`deleteConfirm await deleteFile: ${name}`)
// TODO: Catch Error? Throw should happen during init...
- const response = await deleteFile(name)
- console.log('response:', response)
+ const response = await handleFile(name, 'DELETE')
+ console.debug('response:', response)
if (response.ok) {
mediaOuter.classList.add('d-none')
deleteModal.hide()
await initPopup()
} else {
- console.error(`Error Deleting File: "${name}", response:`, response)
- showToast(`Error Deleting: ${name}`, 'danger')
+ console.info(`Error Deleting: "${name}", response:`, response)
+ showToast(`Error Deleting: ${name}`, 'danger')
deleteModal.hide()
}
}
/**
- * Post URL to endpoint
- * @function deleteFile
+ * Delete File Request
+ * @function handleFile
* @param {String} name
+ * @param {String} method
+ * @param {Object} data
* @return {Response}
*/
-async function deleteFile(name) {
- console.log(`deleteFile: ${name}`)
+async function handleFile(name, method, data = null) {
+ console.debug(`handleFile: ${name}`)
const { options } = await chrome.storage.sync.get(['options'])
- // console.log('options:', options)
+ // console.debug('options:', options)
const headers = { Authorization: options.authToken }
const opts = {
- method: 'DELETE',
+ method: method,
headers: headers,
}
- const apiUrl = `${options.siteUrl}/api/delete/${name}`
+ if (data) {
+ opts.body = JSON.stringify(data)
+ }
+ // TODO: Update to /file/ Endpoint...
+ const apiUrl = `${options.siteUrl}/api/file/${name}`
+ // const apiUrl = `${options.siteUrl}/api/delete/${name}`
return await fetch(apiUrl, opts)
}
@@ -413,34 +647,20 @@ async function deleteFile(name) {
* @param {String} type
*/
function showToast(message, type = 'success') {
- console.log(`showToast: ${type}:`, message)
- const element = document.querySelector('.d-none .toast').cloneNode(true)
- element.classList.add(`text-bg-${type}`)
- element.querySelector('.toast-body').innerHTML = message
- document.getElementById('toast-container').appendChild(element)
- const toast = new bootstrap.Toast(element)
- toast.show()
- const callback = () => {
+ console.debug(`showToast: ${type}: ${message}`)
+ const clone = document.querySelector('.d-none .toast')
+ const container = document.getElementById('toast-container')
+ if (clone && container) {
+ const element = clone.cloneNode(true)
+ element.querySelector('.toast-body').innerHTML = message
+ element.classList.add(`text-bg-${type}`)
+ container.appendChild(element)
+ const toast = new bootstrap.Toast(element)
element.addEventListener('mouseover', () => toast.hide())
+ toast.show()
+ } else {
+ console.info('Missing clone or container:', clone, container)
}
- setTimeout(callback, 1000)
-}
-
-/**
- * Clipboard Click Callback
- * @function clipClick
- * @param {MouseEvent} event
- */
-function clipClick(event) {
- console.log('clipClick:', event)
- const element = event.target.closest('a')
- // console.log('element:', element)
- element.classList.add('link-success')
- element.classList.remove('link-body-emphasis')
- setTimeout(() => {
- element.classList.add('link-body-emphasis')
- element.classList.remove('link-success')
- }, 500)
}
/**
@@ -451,7 +671,7 @@ function clipClick(event) {
* @param {Boolean} auth
*/
function displayAlert({ message, type = 'warning', auth = false } = {}) {
- console.log(`displayAlert: ${type}:`, message)
+ console.info(`displayAlert: ${type}:`, message)
filesTable.classList.add('d-none')
errorAlert.innerHTML = message
errorAlert.classList.add(`alert-${type}`)
@@ -463,12 +683,13 @@ function displayAlert({ message, type = 'warning', auth = false } = {}) {
}
async function checkSiteAuth() {
+ console.debug('checkSiteAuth')
try {
const [tab] = await chrome.tabs.query({
currentWindow: true,
active: true,
})
- console.log('tab:', tab)
+ console.debug('tab:', tab)
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['/js/auth.js'],
@@ -477,7 +698,7 @@ async function checkSiteAuth() {
}
function initPopupMouseover() {
- console.log('initPopupMouseover')
+ console.debug('initPopupMouseover')
mediaOuter.addEventListener('mouseover', () => {
mediaOuter.classList.add('d-none')
mediaImage.src = loadingImage
@@ -485,20 +706,20 @@ function initPopupMouseover() {
clearTimeout(timeoutID)
}
})
- mediaImage.addEventListener('error', () => {
- // console.log('mediaError:', event)
+ mediaImage.addEventListener('error', (event) => {
+ console.debug('mediaError:', event)
mediaImage.classList.add('d-none')
mediaError.classList.remove('d-none')
mediaImage.src = '../media/loading.gif'
})
- document.querySelectorAll('.file-link').forEach((el) => {
+ document.querySelectorAll('.mouse-link').forEach((el) => {
el.addEventListener('mouseover', onMouseOver)
el.addEventListener('mouseout', onMouseOut)
})
}
function onMouseOver(event) {
- // console.log('onMouseOver:', event)
+ // console.debug('onMouseOver:', event)
mediaError.classList.add('d-none')
mediaImage.classList.remove('d-none')
if (event.pageY < window.innerHeight / 2) {
@@ -508,8 +729,6 @@ function onMouseOver(event) {
mediaOuter.classList.remove('bottom-0')
mediaOuter.classList.add('top-0')
}
- // console.log('name:', event.target.innerText)
- // console.log('raw:', event.target.dataset.raw)
const str = event.target.innerText
const imageExtensions = /\.(gif|ico|jpeg|jpg|png|svg|webp)$/i
if (str.match(imageExtensions)) {
@@ -520,13 +739,13 @@ function onMouseOver(event) {
mediaOuter.classList.add('d-none')
mediaImage.src = loadingImage
}
- // console.log('timeoutID:', timeoutID)
if (timeoutID) {
clearTimeout(timeoutID)
}
}
function onMouseOut() {
+ // console.debug('onMouseOut')
timeoutID = setTimeout(function () {
mediaOuter.classList.add('d-none')
mediaImage.src = loadingImage
diff --git a/src/js/service-worker.js b/src/js/service-worker.js
index 85904d6..d7260cc 100644
--- a/src/js/service-worker.js
+++ b/src/js/service-worker.js
@@ -83,7 +83,7 @@ async function contextMenusClicked(ctx) {
* @param {String} notificationId
*/
async function notificationsClicked(notificationId) {
- console.log(`notifications.onClicked: ${notificationId}`)
+ console.debug(`notifications.onClicked: ${notificationId}`)
chrome.notifications.clear(notificationId)
if (notificationId.startsWith('http')) {
await chrome.tabs.create({ active: true, url: notificationId })
@@ -97,7 +97,7 @@ async function notificationsClicked(notificationId) {
* @param {String} namespace
*/
function onChanged(changes, namespace) {
- // console.log('onChanged:', changes, namespace)
+ // console.debug('onChanged:', changes, namespace)
for (const [key, { oldValue, newValue }] of Object.entries(changes)) {
if (key === 'options' && namespace === 'sync' && oldValue && newValue) {
if (oldValue.contextMenu !== newValue.contextMenu) {
@@ -118,7 +118,7 @@ function onChanged(changes, namespace) {
* @function createContextMenus
*/
function createContextMenus() {
- console.log('createContextMenus')
+ console.debug('createContextMenus')
chrome.contextMenus.removeAll()
const ctx = ['link', 'image', 'video', 'audio']
const contexts = [
@@ -147,9 +147,9 @@ function createContextMenus() {
* @return {Response}
*/
async function postURL(endpoint, url) {
- console.log(`postURL: "${endpoint}", "${url}"`)
+ console.debug(`postURL: "${endpoint}", "${url}"`)
const { options } = await chrome.storage.sync.get(['options'])
- console.log('options:', options)
+ console.debug('options:', options)
if (!options?.siteUrl || !options?.authToken) {
throw new Error('Missing URL or Token.')
}
@@ -163,7 +163,7 @@ async function postURL(endpoint, url) {
}
const apiUrl = `${options.siteUrl}/api/${endpoint}/`
const response = await fetch(apiUrl, opts)
- console.log('response:', response)
+ console.debug('response:', response)
return response
}
@@ -175,18 +175,18 @@ async function postURL(endpoint, url) {
* @param {String} message
*/
async function processRemote(endpoint, url, message) {
- console.log(`processRemote: "${endpoint}", "${url}", "${message}"`)
+ console.debug(`processRemote: "${endpoint}", "${url}", "${message}"`)
let response
try {
response = await postURL(endpoint, url)
} catch (e) {
- console.log('error:', e)
+ console.info('error:', e)
return await sendNotification('Fetch Error', `Error: ${error.message}`)
}
// console.log('response:', response)
if (response.ok) {
const data = await response.json()
- console.log('data:', data)
+ console.debug('data:', data)
await clipboardWrite(data.url)
await sendNotification(message, data.url, data.url)
} else {
@@ -195,7 +195,7 @@ async function processRemote(endpoint, url, message) {
console.log('data:', data)
await sendNotification('Processing Error', `Error: ${data.error}`)
} catch (e) {
- console.log('error:', e)
+ console.info('error:', e)
await sendNotification(
'Processing Error',
`Error: Response Status: ${response.status}`
@@ -213,7 +213,7 @@ async function processRemote(endpoint, url, message) {
* @param {Number} timeout - Optional
*/
async function sendNotification(title, text, id = '', timeout = 10) {
- console.log(`sendNotification: ${id || 'randomID'}: ${title} - ${text}`)
+ console.debug(`sendNotification: ${id || 'randomID'}: ${title} - ${text}`)
const options = {
type: 'basic',
iconUrl: chrome.runtime.getURL('media/logo96.png'),