Last active
March 8, 2026 04:31
-
-
Save tam710562/67ce3c4387d72e94a83cf0ae9f890cec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Easy Files | |
| * Written by Tam710562 | |
| */ | |
| (function () { | |
| 'use strict'; | |
| const gnoh = { | |
| file: { | |
| verifyAccept: function ({ fileName, mimeType }, accept) { | |
| if (!accept) { | |
| return true; | |
| } | |
| const mimeTypes = accept.split(',') | |
| .map(x => x.trim()) | |
| .filter(x => !!x && (x.startsWith('.') || /\w+\/([-+.\w]+|\*)/.test(x))); | |
| for (const mt of mimeTypes) { | |
| if ( | |
| mt.startsWith('.') && new RegExp(mt.replace('.', '.+\\.') + '$').test(fileName) | |
| || new RegExp(mt.replace('*', '.+')).test(mimeType) | |
| ) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }, | |
| }, | |
| i18n: { | |
| getMessageName: function (message, type) { | |
| message = (type ? type + '\x04' : '') + message; | |
| return message.replace(/[^a-z0-9]/g, function (i) { | |
| return '_' + i.codePointAt(0) + '_'; | |
| }) + '0'; | |
| }, | |
| getMessage: function (message, type) { | |
| return chrome.i18n.getMessage(this.getMessageName(message, type)) || message; | |
| }, | |
| }, | |
| addStyle: function (css, id, isNotMin) { | |
| this.styles = this.styles || {}; | |
| if (Array.isArray(css)) { | |
| css = css.join(isNotMin === true ? '\n' : ''); | |
| } | |
| id = id || this.uuid.generate(Object.keys(this.styles)); | |
| this.styles[id] = this.createElement('style', { | |
| html: css || '', | |
| 'data-id': id, | |
| }, document.head); | |
| return this.styles[id]; | |
| }, | |
| createElement: function (tagName, attribute, parent, inner, options) { | |
| if (typeof tagName === 'undefined') { | |
| return; | |
| } | |
| if (typeof options === 'undefined') { | |
| options = {}; | |
| } | |
| if (typeof options.isPrepend === 'undefined') { | |
| options.isPrepend = false; | |
| } | |
| const el = document.createElement(tagName); | |
| if (!!attribute && typeof attribute === 'object') { | |
| for (const key in attribute) { | |
| if (key === 'text') { | |
| el.textContent = attribute[key]; | |
| } else if (key === 'html') { | |
| el.innerHTML = attribute[key]; | |
| } else if (key === 'style' && typeof attribute[key] === 'object') { | |
| for (const css in attribute.style) { | |
| el.style.setProperty(css, attribute.style[css]); | |
| } | |
| } else if (key === 'events' && typeof attribute[key] === 'object') { | |
| for (const event in attribute.events) { | |
| if (typeof attribute.events[event] === 'function') { | |
| el.addEventListener(event, attribute.events[event]); | |
| } | |
| } | |
| } else if (typeof el[key] !== 'undefined') { | |
| el[key] = attribute[key]; | |
| } else { | |
| if (typeof attribute[key] === 'object') { | |
| attribute[key] = JSON.stringify(attribute[key]); | |
| } | |
| el.setAttribute(key, attribute[key]); | |
| } | |
| } | |
| } | |
| if (!!inner) { | |
| if (!Array.isArray(inner)) { | |
| inner = [inner]; | |
| } | |
| for (let i = 0; i < inner.length; i++) { | |
| if (inner[i].nodeName) { | |
| el.append(inner[i]); | |
| } else { | |
| el.append(this.createElementFromHTML(inner[i])); | |
| } | |
| } | |
| } | |
| if (typeof parent === 'string') { | |
| parent = document.querySelector(parent); | |
| } | |
| if (!!parent) { | |
| if (options.isPrepend) { | |
| parent.prepend(el); | |
| } else { | |
| parent.append(el); | |
| } | |
| } | |
| return el; | |
| }, | |
| createElementFromHTML: function (html) { | |
| return this.createElement('template', { | |
| html: (html || '').trim(), | |
| }).content; | |
| }, | |
| get constant() { | |
| return { | |
| dialogButtons: { | |
| submit: { | |
| label: this.i18n.getMessage('OK'), | |
| type: 'submit' | |
| }, | |
| cancel: { | |
| label: this.i18n.getMessage('Cancel'), | |
| cancel: true | |
| }, | |
| primary: { | |
| class: 'primary' | |
| }, | |
| danger: { | |
| class: 'danger' | |
| }, | |
| default: {}, | |
| } | |
| }; | |
| }, | |
| dialog: function (title, content, buttons = [], config) { | |
| let modalBg; | |
| let dialog; | |
| let cancelEvent; | |
| const id = this.uuid.generate(); | |
| if (!config) { | |
| config = {}; | |
| } | |
| if (typeof config.autoClose === 'undefined') { | |
| config.autoClose = true; | |
| } | |
| function onKeyCloseDialog(key) { | |
| if (key === 'Esc') { | |
| closeDialog(true); | |
| } | |
| } | |
| function onClickCloseDialog(event) { | |
| if (config.autoClose && !event.target.closest('.dialog-custom[data-dialog-id="' + id + '"]')) { | |
| closeDialog(true); | |
| } | |
| } | |
| function closeDialog(isCancel) { | |
| if (isCancel === true && cancelEvent) { | |
| cancelEvent.bind(this)(); | |
| } | |
| if (modalBg) { | |
| modalBg.remove(); | |
| } | |
| vivaldi.tabsPrivate.onKeyboardShortcut.removeListener(onKeyCloseDialog); | |
| document.removeEventListener('mousedown', onClickCloseDialog); | |
| } | |
| vivaldi.tabsPrivate.onKeyboardShortcut.addListener(onKeyCloseDialog); | |
| document.addEventListener('mousedown', onClickCloseDialog); | |
| const buttonElements = []; | |
| for (let button of buttons) { | |
| button.type = button.type || 'button'; | |
| const clickEvent = button.click; | |
| if (button.cancel === true && typeof clickEvent === 'function') { | |
| cancelEvent = clickEvent; | |
| } | |
| button.events = { | |
| click: function (event) { | |
| event.preventDefault(); | |
| if (typeof clickEvent === 'function') { | |
| clickEvent.bind(this)(); | |
| } | |
| if (button.closeDialog !== false) { | |
| closeDialog(); | |
| } | |
| } | |
| }; | |
| delete button.click; | |
| if (button.label) { | |
| button.value = button.label; | |
| delete button.label; | |
| } | |
| buttonElements.push(this.createElement('input', button)); | |
| } | |
| const focusModal = this.createElement('span', { | |
| class: 'focus_modal', | |
| tabindex: '0', | |
| }); | |
| const div = this.createElement('div', { | |
| style: { | |
| width: config.width ? config.width + 'px' : '', | |
| margin: '0 auto', | |
| } | |
| }); | |
| dialog = this.createElement('form', { | |
| 'data-dialog-id': id, | |
| class: 'dialog-custom modal-wrapper', | |
| }, div); | |
| if (config.class) { | |
| dialog.classList.add(config.class); | |
| } | |
| const dialogHeader = this.createElement('header', { | |
| class: 'dialog-header', | |
| }, dialog, '<h1>' + (title || '') + '</h1>'); | |
| const dialogContent = this.createElement('div', { | |
| class: 'dialog-content', | |
| }, dialog, content); | |
| if (buttons && buttons.length > 0) { | |
| const dialogFooter = this.createElement('footer', { | |
| class: 'dialog-footer', | |
| }, dialog, buttonElements); | |
| } | |
| modalBg = this.createElement('div', { | |
| id: 'modal-bg', | |
| class: 'slide', | |
| }, undefined, [focusModal.cloneNode(true), div, focusModal.cloneNode(true)]); | |
| const inner = document.querySelector('#main .inner'); | |
| if (inner) { | |
| inner.prepend(modalBg); | |
| } | |
| return { | |
| dialog: dialog, | |
| dialogHeader: dialogHeader, | |
| dialogContent: dialogContent, | |
| buttons: buttonElements, | |
| close: closeDialog, | |
| }; | |
| }, | |
| timeOut: function (callback, conditon, timeOut = 300) { | |
| let timeOutId = setTimeout(function wait() { | |
| let result; | |
| if (!conditon) { | |
| result = document.getElementById('browser'); | |
| } else if (typeof conditon === 'string') { | |
| result = document.querySelector(conditon); | |
| } else if (typeof conditon === 'function') { | |
| result = conditon(); | |
| } else { | |
| return; | |
| } | |
| if (result) { | |
| callback(result); | |
| } else { | |
| timeOutId = setTimeout(wait, timeOut); | |
| } | |
| }, timeOut); | |
| function stop() { | |
| if (timeOutId) { | |
| clearTimeout(timeOutId); | |
| } | |
| } | |
| return { | |
| stop, | |
| }; | |
| }, | |
| uuid: { | |
| generate: function (ids) { | |
| let d = Date.now() + performance.now(); | |
| let r; | |
| const id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | |
| r = (d + Math.random() * 16) % 16 | 0; | |
| d = Math.floor(d / 16); | |
| return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); | |
| }); | |
| if (Array.isArray(ids) && ids.includes(id)) { | |
| return this.uuid.generate(ids); | |
| } | |
| return id; | |
| }, | |
| }, | |
| }; | |
| const nameKey = 'easy-files'; | |
| const langs = { | |
| showAllFiles: gnoh.i18n.getMessage('Show all files...'), | |
| downloaded: gnoh.i18n.getMessage('Downloaded'), | |
| chooseAFile: gnoh.i18n.getMessage('Choose a File...'), | |
| }; | |
| const chunkSize = 1024 * 1024 * 10; // 10MB | |
| const maxAllowedSize = 1024 * 1024 * 5; // 5MB | |
| gnoh.addStyle([ | |
| `.${nameKey}.dialog-custom .dialog-content { flex-direction: row; gap: 18px }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper { overflow: hidden; margin: -2px; padding: 2px }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container { overflow: auto; margin: -2px; padding: 2px }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container .selectbox-image:hover { box-shadow: 0 0 0 2px var(--colorHighlightBg) }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container .selectbox-image img { width: 120px; height: 120px }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container .selectbox-title { width: 120px }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container .selectbox-title .filename-container { display: flex; flex-direction: row; overflow: hidden; width: 120px }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container .selectbox-title .filename-container .filename-text { white-space: nowrap; text-overflow: ellipsis; overflow: hidden }`, | |
| `.${nameKey}.dialog-custom .dialog-content .selectbox-wrapper .selectbox-container .selectbox-title .filename-container .filename-extension { white-space: nowrap }`, | |
| ], nameKey); | |
| function inject(nameKey) { | |
| if (window.easyFiles) { | |
| return; | |
| } else { | |
| window.easyFiles = true; | |
| } | |
| let fileData = []; | |
| let fileInput = null; | |
| function handleClick(event) { | |
| if (event.target.matches('input[type=file]:not([webkitdirectory])')) { | |
| event.preventDefault(); | |
| event.stopPropagation(); | |
| fileInput = event.target; | |
| const attributes = {}; | |
| for (attr of fileInput.attributes) { | |
| attributes[attr.name] = attr.value; | |
| } | |
| fileData.length = 0; | |
| chrome.runtime.sendMessage({ | |
| type: nameKey, | |
| action: 'click', | |
| attributes, | |
| }); | |
| } | |
| } | |
| window.addEventListener('click', handleClick); | |
| function changeFile(dataTransfer) { | |
| fileInput.files = dataTransfer.files; | |
| fileInput.dispatchEvent(new Event('input', { bubbles: true })); | |
| fileInput.dispatchEvent(new Event('change', { bubbles: true })); | |
| } | |
| chrome.runtime.onMessage.addListener((info, sender, sendResponse) => { | |
| if (info.type === nameKey) { | |
| switch (info.action) { | |
| case 'file': | |
| fileData = [...fileData, ...info.file.fileData]; | |
| if (fileData.length === info.file.fileDataLength) { | |
| const dataTransfer = new DataTransfer(); | |
| dataTransfer.items.add(new File( | |
| [new Uint8Array(fileData)], | |
| info.file.fileName, | |
| { type: info.file.mimeType }, | |
| )); | |
| changeFile(dataTransfer); | |
| } | |
| break; | |
| case 'picker': | |
| fileInput.showPicker(); | |
| break; | |
| default: | |
| return false; | |
| } | |
| } | |
| }); | |
| } | |
| function chunks(arr, n) { | |
| const result = []; | |
| for (let i = 0; i < arr.length; i += n) { | |
| result.push(arr.slice(i, i + n)); | |
| } | |
| return result; | |
| } | |
| async function readClipboardOnFocus() { | |
| if (document.hasFocus()) { | |
| return await navigator.clipboard.read(); | |
| } else { | |
| return new Promise((resolve, reject) => { | |
| const activeElement = document.activeElement; | |
| const addressField = document.querySelector('input.url.vivaldi-addressfield'); | |
| addressField.addEventListener('focus', async () => { | |
| try { | |
| const value = await navigator.clipboard.read(); | |
| resolve(value); | |
| } catch (error) { | |
| reject(error); | |
| } | |
| activeElement.focus(); | |
| }, { once: true }); | |
| addressField.focus(); | |
| }); | |
| } | |
| } | |
| async function readClipboard(accept) { | |
| const images = []; | |
| const supportedTypes = [ | |
| { | |
| extension: 'png', | |
| mimeType: 'image/png', | |
| }, | |
| { | |
| extension: 'jpeg', | |
| mimeType: 'image/jpeg', | |
| }, | |
| { | |
| extension: 'jpg', | |
| mimeType: 'image/jpeg', | |
| }, | |
| ] | |
| const supportedType = supportedTypes.find(s => gnoh.file.verifyAccept({ fileName: 'image.' + s.extension, mimeType: s.mimeType }, accept)); | |
| if (!supportedType) { | |
| return images; | |
| } | |
| try { | |
| const clipboardItems = await readClipboardOnFocus(); | |
| for (const clipboardItem of clipboardItems) { | |
| if (clipboardItem.types.includes('image/png')) { | |
| let blob = await clipboardItem.getType('image/png'); | |
| if (blob.size <= maxAllowedSize) { | |
| if (supportedType.mimeType === 'image/jpeg') { | |
| blob = await convertPngToJpeg(blob); | |
| } | |
| const uint8Array = new Uint8Array(await blob.arrayBuffer()); | |
| images.push({ | |
| previewDataUrl: await vivaldi.utilities.storeImage({ | |
| data: uint8Array, | |
| mimeType: supportedType.mimeType, | |
| }), | |
| fileData: chunks(uint8Array, chunkSize).map(a => Array.from(a)), | |
| fileDataLength: uint8Array.length, | |
| extension: supportedType.extension, | |
| mimeType: supportedType.mimeType, | |
| category: 'clipboard', | |
| }); | |
| } | |
| } | |
| } | |
| return images; | |
| } catch (error) { | |
| console.error(error); | |
| return images; | |
| } | |
| } | |
| async function convertPngToJpeg(blob) { | |
| const image = gnoh.createElement('img', { | |
| src: URL.createObjectURL(blob), | |
| }); | |
| await image.decode(); | |
| const canvas = gnoh.createElement('canvas', { | |
| width: image.width, | |
| height: image.height, | |
| }); | |
| const ctx = canvas.getContext('2d'); | |
| ctx.drawImage(image, 0, 0); | |
| return new Promise((resolve) => { | |
| canvas.toBlob(blob => { | |
| URL.revokeObjectURL(image.src); | |
| if (blob) { | |
| resolve(blob); | |
| } | |
| }, 'image/jpeg'); | |
| }); | |
| } | |
| async function getDownloadedFiles(accept) { | |
| const downloadedFiles = await chrome.downloads.search({ exists: true, state: 'complete', orderBy: ['-startTime'] }); | |
| const result = {}; | |
| for (let downloadedFile of downloadedFiles) { | |
| if ( | |
| downloadedFile.mime | |
| && downloadedFile.mime !== 'application/x-msdownload' | |
| && gnoh.file.verifyAccept({ fileName: downloadedFile.filename, mimeType: downloadedFile.mime }, accept) | |
| ) { | |
| const fileIcon = await chrome.downloads.getFileIcon(downloadedFile.id); | |
| downloadedFile = (await chrome.downloads.search({ id: downloadedFile.id }))[0]; | |
| if ( | |
| downloadedFile | |
| && downloadedFile.exists === true | |
| && downloadedFile.state === 'complete' | |
| && downloadedFile.fileSize <= maxAllowedSize | |
| && !result[downloadedFile.filename] | |
| ) { | |
| const file = { | |
| previewDataUrl: fileIcon, | |
| mimeType: downloadedFile.mime, | |
| path: downloadedFile.filename, | |
| fileName: downloadedFile.filename.replace(/^.*[\\/]/, ''), | |
| category: 'downloaded-file', | |
| }; | |
| switch (file.mimeType) { | |
| case 'image/jpeg': | |
| case 'image/png': | |
| case 'image/svg+xml': | |
| case 'image/webp': | |
| case 'image/gif': | |
| case 'image/bmp': | |
| file.previewDataUrl = await vivaldi.utilities.storeImage({ | |
| url: file.path, | |
| }); | |
| break; | |
| } | |
| result[downloadedFile.filename] = file; | |
| } | |
| } | |
| } | |
| return Object.values(result); | |
| } | |
| async function createSelectbox(sender, file, dialog) { | |
| const selectbox = gnoh.createElement('button', { | |
| title: file.fileName || '', | |
| class: 'selectbox', | |
| events: { | |
| click: async (event) => { | |
| event.preventDefault(); | |
| dialog.close(); | |
| switch (file.category) { | |
| case 'downloaded-file': | |
| if (!file.fileData) { | |
| const fileData = await vivaldi.utilities.readImage(file.path); | |
| file.fileData = chunks(fileData.data, chunkSize); | |
| file.fileDataLength = fileData.data.length; | |
| } | |
| break; | |
| case 'clipboard': | |
| const d = new Date(); | |
| const year = d.getFullYear(); | |
| const month = (d.getMonth() + 1).toString().padStart(2, '0'); | |
| const date = d.getDate().toString().padStart(2, '0'); | |
| const hour = d.getHours().toString().padStart(2, '0'); | |
| const minute = d.getMinutes().toString().padStart(2, '0'); | |
| const second = d.getSeconds().toString().padStart(2, '0'); | |
| const millisecond = d.getMilliseconds().toString().padStart(3, '0'); | |
| file.fileName = `image_${year}-${month}-${date}_${hour}${minute}${second}${millisecond}.${file.extension}`; | |
| break; | |
| } | |
| chooseFile(sender, file); | |
| }, | |
| }, | |
| }); | |
| const selectboxImage = gnoh.createElement('div', { | |
| class: 'selectbox-image', | |
| }, selectbox); | |
| const image = gnoh.createElement('img', null, selectboxImage); | |
| switch (file.mimeType) { | |
| case 'image/jpeg': | |
| case 'image/png': | |
| case 'image/svg+xml': | |
| case 'image/webp': | |
| case 'image/gif': | |
| case 'image/bmp': | |
| image.style.setProperty('object-fit', 'cover'); | |
| break; | |
| default: | |
| image.style.setProperty('object-fit', 'none'); | |
| break; | |
| } | |
| image.src = file.previewDataUrl; | |
| const selectboxTitle = gnoh.createElement('div', { | |
| class: 'selectbox-title', | |
| }, selectbox); | |
| const filenameText = gnoh.createElement('div', { | |
| class: 'filename-container', | |
| }, selectboxTitle); | |
| if (file.fileName) { | |
| const extension = file.fileName.split('.').pop(); | |
| const name = file.fileName.substring(0, file.fileName.length - extension.length - 1); | |
| const filenameContainer = gnoh.createElement('div', { | |
| class: 'filename-text', | |
| text: name, | |
| }, filenameText); | |
| const filenameExtension = gnoh.createElement('div', { | |
| class: 'filename-extension', | |
| text: '.' + extension, | |
| }, filenameText); | |
| } | |
| return selectbox; | |
| } | |
| async function showDialogChooseFile(data) { | |
| const buttonShowAllFilesElement = Object.assign({}, gnoh.constant.dialogButtons.submit, { | |
| label: langs.showAllFiles, | |
| click: () => showAllFiles(data.sender), | |
| }); | |
| const buttonCancelElement = Object.assign({}, gnoh.constant.dialogButtons.cancel); | |
| const dialog = gnoh.dialog( | |
| langs.chooseAFile, | |
| null, | |
| [buttonShowAllFilesElement, buttonCancelElement], | |
| { | |
| class: nameKey, | |
| } | |
| ); | |
| dialog.dialog.style.maxWidth = 570 + 'px'; | |
| if (data.images.length) { | |
| const selectboxWrapperClipboard = gnoh.createElement('div', { | |
| class: 'selectbox-wrapper', | |
| style: { | |
| 'flex': '0 0 auto', | |
| }, | |
| }); | |
| const h3Clipboard = gnoh.createElement('h3', { | |
| text: 'Clipboard', | |
| }, selectboxWrapperClipboard); | |
| const selectboxContainerClipboard = gnoh.createElement('div', { | |
| class: 'selectbox-container', | |
| }, selectboxWrapperClipboard); | |
| for (const image of data.images) { | |
| selectboxContainerClipboard.append(await createSelectbox(data.sender, image, dialog)); | |
| } | |
| dialog.dialogContent.append(selectboxWrapperClipboard); | |
| } | |
| if (data.downloadedFiles.length) { | |
| const selectboxWrapperDownloaded = gnoh.createElement('div', { | |
| class: 'selectbox-wrapper', | |
| }); | |
| const h3Downloaded = gnoh.createElement('h3', { | |
| text: 'Downloaded', | |
| }, selectboxWrapperDownloaded); | |
| const selectboxContainerDownloaded = gnoh.createElement('div', { | |
| class: 'selectbox-container', | |
| }, selectboxWrapperDownloaded); | |
| for (const downloadedFile of data.downloadedFiles) { | |
| selectboxContainerDownloaded.append(await createSelectbox(data.sender, downloadedFile, dialog)); | |
| } | |
| dialog.dialogContent.append(selectboxWrapperDownloaded); | |
| } | |
| } | |
| function showAllFiles(sender) { | |
| chrome.tabs.sendMessage(sender.tab.id, { | |
| type: nameKey, | |
| action: 'picker', | |
| tabId: sender.tab.id, | |
| frameId: sender.frameId, | |
| }, { | |
| frameId: sender.frameId, | |
| }); | |
| } | |
| async function chooseFile(sender, file) { | |
| for (const chunk of file.fileData) { | |
| await chrome.tabs.sendMessage(sender.tab.id, { | |
| type: nameKey, | |
| action: 'file', | |
| tabId: sender.tab.id, | |
| frameId: sender.frameId, | |
| file: { | |
| fileData: chunk, | |
| fileDataLength: file.fileDataLength, | |
| fileName: file.fileName, | |
| mimeType: file.mimeType, | |
| }, | |
| }, { | |
| frameId: sender.frameId, | |
| }); | |
| } | |
| } | |
| async function requestPermissions(sender) { | |
| const result = await navigator.permissions.query({ name: 'clipboard-read' }); | |
| switch (result.state) { | |
| case 'prompt': | |
| try { | |
| await readClipboardOnFocus(); | |
| return true; | |
| } catch (error) { | |
| const tab = await chrome.tabs.create({ url: 'chrome-extension://mpognobbkildjkofajifpdfhcoklimli/window.html', active: true }); | |
| return new Promise((resolve) => { | |
| chrome.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) { | |
| if (tabId === tab.id && changeInfo.status === 'complete') { | |
| chrome.tabs.onUpdated.removeListener(onUpdated); | |
| chrome.scripting.executeScript({ | |
| target: { | |
| tabId: tab.id | |
| }, | |
| func: async () => { | |
| try { | |
| await navigator.clipboard.read(); | |
| return true; | |
| } catch (error) { | |
| console.error(error); | |
| return false; | |
| } | |
| }, | |
| }, (results) => { | |
| chrome.tabs.remove(tab.id); | |
| chrome.tabs.update(sender.tab.id, { active: true }); | |
| if (results.length) { | |
| resolve(results[0].result); | |
| } else { | |
| resolve(false); | |
| } | |
| }); | |
| } | |
| }); | |
| }); | |
| } | |
| case 'granted': | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| chrome.runtime.onMessage.addListener(async (info, sender, sendResponse) => { | |
| if (info.type === nameKey) { | |
| switch (info.action) { | |
| case 'click': | |
| let images = []; | |
| if (await requestPermissions(sender)) { | |
| images = await readClipboard(info.attributes.accept); | |
| } | |
| const downloadedFiles = await getDownloadedFiles(info.attributes.accept); | |
| if (images.length || downloadedFiles.length) { | |
| showDialogChooseFile({ | |
| info, | |
| sender, | |
| images, | |
| downloadedFiles, | |
| }) | |
| } else { | |
| showAllFiles(sender); | |
| } | |
| break; | |
| } | |
| } | |
| }); | |
| gnoh.timeOut(() => { | |
| chrome.tabs.query({ windowId: window.vivaldiWindowId, windowType: 'normal' }, (tabs) => { | |
| tabs.forEach((tab) => { | |
| chrome.webNavigation.getAllFrames({ tabId: tab.id }, (details) => { | |
| details.forEach((detail) => { | |
| chrome.scripting.executeScript({ | |
| target: { | |
| tabId: tab.id, | |
| frameIds: [detail.frameId] | |
| }, | |
| func: inject, | |
| args: [nameKey], | |
| }); | |
| }); | |
| }); | |
| }); | |
| }); | |
| chrome.webNavigation.onCommitted.addListener((details) => { | |
| chrome.scripting.executeScript({ | |
| target: { | |
| tabId: details.tabId, | |
| frameIds: [details.frameId] | |
| }, | |
| func: inject, | |
| args: [nameKey], | |
| }); | |
| }); | |
| }, () => { | |
| return window.vivaldiWindowId != null; | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment