diff options
Diffstat (limited to 'background.js')
| -rw-r--r-- | background.js | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/background.js b/background.js new file mode 100644 index 0000000..af5299d --- /dev/null +++ b/background.js @@ -0,0 +1,602 @@ +/* global chrome axios */ + +const storage = chrome.storage.local; + +const isFirefox = typeof InstallTrigger !== 'undefined'; + +chrome.runtime.onInstalled.addListener((details) => { + if (details.reason == 'update') { + + storage.get((items) => { + + let newItems = {}; + + if (items.textDomain) { + newItems.domain = items.textDomain; + storage.remove('textDomain'); + } + + if (items.textToken) { + newItems.token = items.textToken; + storage.remove('textToken'); + } + + storage.set(newItems); + + }); + + } +}); + +const contexts = ['image', 'video', 'audio']; + +let config = {}; + +storage.get({ + domain: '', + panelURL: '/dashboard', + token: '', + lastAlbum: null, + autoCopyUrl: false +}, (items) => { + config = items; + createContextMenus(); +}); + +chrome.storage.onChanged.addListener((changes) => { + for (const key in changes) { + config[key] = changes[key].newValue; + if (key === 'token') createContextMenus(); + } +}); + +function createContextMenus() { + + if (!config.domain) return; + + /* == Not the best way to do this but when have I ever done something efficiently? == */ + chrome.contextMenus.removeAll(() => { + + console.log('Removed old Context Menus'); + + /* == Parent Context Menu == */ + contextMenus.parent = chrome.contextMenus.create({ + title: 'strelizia', + contexts: ['all'], + onclick: () => chrome.tabs.create({ url: config.domain + config.panelURL }) + }); + + /* == Upload normally == */ + chrome.contextMenus.create({ + title: 'Send to safe', + parentId: contextMenus.parent, + contexts: contexts, + onclick: (info) => upload(info.srcUrl, info.pageUrl) + }); + + /* == Screenshot page == */ + chrome.contextMenus.create({ + title: 'Screenshot page', + parentId: contextMenus.parent, + contexts: ['page'], + onclick: () => { + chrome.tabs.captureVisibleTab({ format: 'png' }, (data) => { + let blob = b64toBlob(data.replace('data:image/png;base64,', ''), 'image/png'); + uploadScreenshot(blob); + }); + } + }); + + /* == Screenshot selection == */ + chrome.contextMenus.create({ + title: 'Screenshot selection', + parentId: contextMenus.parent, + contexts: ['page'], + onclick: () => { + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id, 'check', (response) => { + if (response) { + chrome.tabs.sendMessage(tabs[0].id, 'select'); + } else { + chrome.tabs.insertCSS(null, { file: 'content.css' }); + chrome.tabs.executeScript(null, { file: 'content.js' }, () => { + chrome.tabs.sendMessage(tabs[0].id, 'select'); + }); + } + }); + }); + } + }); + + if (config.token) { + + /* == Separator == */ + chrome.contextMenus.create({ + parentId: contextMenus.parent, + contexts: contexts, + type: 'separator' + }); + + /* == Refresh Album List == */ + chrome.contextMenus.create({ + title: 'Refresh Albums List', + parentId: contextMenus.parent, + contexts: contexts, + onclick: createContextMenus + }); + + /* == Separator == */ + chrome.contextMenus.create({ + parentId: contextMenus.parent, + contexts: contexts, + type: 'separator' + }); + + axios.get(config.domain + '/api/albums', { + headers: { token: config.token } + }).then((list) => { + + if (list.data.albums.length === 0) { + + chrome.contextMenus.create({ + title: 'No Albums Available', + parentId: contextMenus.parent, + contexts: contexts, + type: 'normal', + enabled: false + }); + + } else { + + const lastAlbum = list.data.albums.find(a => a.id === config.lastAlbum); + + contextMenus.lastAlbum = chrome.contextMenus.create({ + title: `Upload to: ${lastAlbum ? lastAlbum.name : 'None'}`, + parentId: contextMenus.parent, + contexts: contexts, + enabled: !!lastAlbum, + onclick: (info) => upload(info.srcUrl, info.pageUrl, lastAlbum.id, lastAlbum.name) + }); + + /* == Separator == */ + chrome.contextMenus.create({ + parentId: contextMenus.parent, + contexts: contexts, + type: 'separator' + }); + + contextMenus.albumsParent = chrome.contextMenus.create({ + title: 'Upload to:', + parentId: contextMenus.parent, + contexts: contexts, + type: 'normal', + enabled: false + }); + + list.data.albums.forEach((album) => { + console.log(album.id, album.name); + contextMenus.createAlbumMenu(album.id, album.name); + }); + + } + + }).catch((err) => { + + console.log(err); + + chrome.contextMenus.create({ + title: 'Error Getting Albums', + parentId: contextMenus.parent, + contexts: contexts, + type: 'normal', + enabled: false + }); + + }); + + } + + }); + +} + +let contextMenus = { + parent: null, + albumsParent: null, + lastAlbum: null, + createAlbumMenu: (id, name, enabled = true) => { + chrome.contextMenus.create({ + title: name.replace('&', '&&'), + parentId: contextMenus.parent, + // parentId: contextMenus.albumsParent, + contexts, enabled, + onclick: (info) => upload(info.srcUrl, info.pageUrl, id, name) + }); + } +}; + +let refererHeader = null; +let contentURL = ''; +let recentlyUploaded = new Map(); + +let opt_extraInfoSpec = ['blocking', 'requestHeaders']; + +const chromeVersionCheck = navigator.userAgent.match(/Chrome\/(\d+)/); + +if (chromeVersionCheck && parseInt(chromeVersionCheck[1]) >= 72) + opt_extraInfoSpec.push('extraHeaders'); + +/* == We need to set this header for image sources that check it for auth or to prevent hotlinking == */ +chrome.webRequest.onBeforeSendHeaders.addListener((details) => { + + if (details.tabId === -1 && details.method === 'GET' && refererHeader !== null) { + + details.requestHeaders.push({ + name: 'Referer', + value: refererHeader + }); + + details.requestHeaders.push({ + name: 'Referrer', + value: refererHeader + }); + + } + + return {requestHeaders: details.requestHeaders}; + +}, { urls: ['<all_urls>'] }, opt_extraInfoSpec); + +function upload(url, pageURL, albumID, albumName) { + + if (albumID) + storage.set({ lastAlbum: albumID }, () => { + chrome.contextMenus.update(contextMenus.lastAlbum, { + enabled: true, + title: `Upload to: ${albumName}`, + onclick: (info) => upload(info.srcUrl, info.pageUrl, albumID, albumName) + }); + }); + + let notification = createNotification('basic', 'Retriving file...', null, true); + + refererHeader = pageURL; + + axios.get(url, { responseType: 'blob' }).then((file) => { + + refererHeader = null; + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'progress', + message: 'Uploading...', + progress: 0 + }); + } + + let data = new FormData(); + data.append('files[]', file.data, 'upload' + fileExt(file.data.type)); + + let options = { + method: 'POST', + url: `${config.domain}/api/upload`, + data, + headers: { + token: config.token + }, + onUploadProgress: (progress) => { + if (!isFirefox) { + chrome.notifications.update(notification, { + progress: Math.round((progress.loaded * 100) / progress.total) + }); + } + } + }; + + if (albumID && config.token) + options.url = options.url + '/' + albumID; + + axios.request(options).then((response) => { + + if (response.data.success === true || response.data.files) { + + recentlyUploaded.set(notification, response.data.files[0].name); + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'basic', + message: 'Upload Complete!', + contextMessage: response.data.files[0].url, + buttons: config.autoCopyUrl ? [] : [{ title: 'Copy to clipboard' }, { title: 'Delete Upload' }] + }); + } else { + createNotification('basic', 'Upload Complete!'); + } + + if (config.autoCopyUrl) + copyText(response.data.files[0].url); + + contentURL = response.data.files[0].url; + + setTimeout(() => chrome.notifications.clear(notification), 5000); + + } else { + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'basic', + message: response.data.description || response.data.message, + contextMessage: url, + }); + } else { + createNotification('basic', response.data.description || response.data.message); + } + + } + + }).catch((err) => { + + console.error(err); + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'basic', + message: 'Error!', + contextMessage: err.toString(), + }); + } else { + createNotification('basic', `Error!\n${err.toString()}`); + } + + }); + + }); + +} + +function deleteFile(filename) { + let headers = { + token: config.token + }; + axios.get(`${config.domain}/api/uploads/0`, { headers }).then(response => { + let file = response.data.files.find(a => a.name === filename); + return axios.post(`${config.domain}/api/upload/delete`, { id: file.id }, { headers }).then(res => { + if (res.data.success) { + createNotification('basic', `File ${filename} was deleted!`); + } else { + createNotification('basic', 'Error\nUnable to delete the file.'); + } + }); + }).catch(() => { + createNotification('basic', 'Error\nUnable to delete the file.'); + }); +} + +function uploadScreenshot(blob, albumID) { + + let notification = createNotification('progress', 'Uploading...', null, true, 0); + + let data = new FormData(); + + data.append('files[]', blob, 'upload.png'); + + let options = { + method: 'POST', + url: `${config.domain}/api/upload`, + data, + headers: { + token: config.token + }, + onUploadProgress: (progress) => { + if (!isFirefox) { + chrome.notifications.update(notification, { + progress: Math.round((progress.loaded * 100) / progress.total) + }); + } + } + }; + + if (albumID && config.token) + options.url = options.url + '/' + albumID; + + axios.request(options).then((response) => { + + if (response.data.success === true || response.data.files) { + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'basic', + message: 'Upload Complete!', + contextMessage: response.data.files[0].url, + buttons: config.autoCopyUrl ? [] : [{ title: 'Copy to clipboard' }] + }); + } else { + createNotification('basic', 'Upload Complete!'); + } + + if (config.autoCopyUrl) + copyText(response.data.files[0].url); + + contentURL = response.data.files[0].url; + + setTimeout(() => chrome.notifications.clear(notification), 5000); + + } else { + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'basic', + message: response.data.description || response.data.message + }); + } else { + createNotification('basic', response.data.description || response.data.message); + } + + } + + }).catch((err) => { + + console.error(err); + + if (!isFirefox) { + chrome.notifications.update(notification, { + type: 'basic', + message: 'Error!', + contextMessage: err.toString(), + }); + } else { + createNotification('basic', `Error!\n${err.toString()}`); + } + + }); + +} + +chrome.runtime.onMessage.addListener((request) => { + + if ('coordinates' in request) { + + let pos = request.coordinates; + + chrome.tabs.captureVisibleTab({ format: 'png' }, (data) => { + + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + const img = new Image(); + + img.onload = () => { + + let resHeight = Math.abs(pos[0].y - pos[1].y); + let resWidth = Math.abs(pos[0].x - pos[1].x); + + if (resHeight === 0 || resWidth === 0) return; + + let posX = pos[0].x < pos[1].x + ? pos[0].x + : pos[1].x; + let posY = pos[0].y < pos[1].y + ? pos[0].y + : pos[1].y; + + canvas.height = resHeight; + canvas.width = resWidth; + + ctx.drawImage(img, -posX, -posY); + + let imageData = canvas.toDataURL(); + + let blob = b64toBlob(imageData.replace('data:image/png;base64,', ''), 'image/png'); + + uploadScreenshot(blob); + + }; + + img.src = data; + + }); + + } + +}); + +function createNotification(type, message, altText, sticky, progress) { + + let notificationContent = { + type, + title: 'strelizia', + message, + iconUrl: 'logo-128x128.png' + }; + + if (typeof InstallTrigger === 'undefined') // Does not work with firefox. + notificationContent.requireInteraction = sticky || false; + + if (altText && typeof altText === 'string') + notificationContent.contextMessage = altText; + + if (progress && typeof progress === 'number') + notificationContent.progress = progress; + + let id = 'notification_' + Date.now(); + + chrome.notifications.create(id, notificationContent); + + return id; + +} + +chrome.notifications.onClicked.addListener((id) => { + chrome.notifications.clear(id); +}); + +chrome.notifications.onClosed.addListener((id) => { + recentlyUploaded.delete(id); + contentURL = ''; +}); + +chrome.notifications.onButtonClicked.addListener((id, index) => { + if (!config.autoCopyUrl && index === 0) copyText(contentURL); + if (index === 1) deleteFile(recentlyUploaded.get(id)); + chrome.notifications.clear(id); +}); + +const mimetypes = { + 'image/png': '.png', + 'image/jpeg': '.jpg', + 'image/gif': '.gif', + 'image/bmp': '.bmp', + 'image/x-icon': '.ico', + 'video/mp4': '.mp4', + 'video/webm': '.webm', + 'video/quicktime': '.mov', + 'audio/mp4': '.mp4a', + 'audio/mpeg': '.mp3', + 'audio/ogg': '.ogg', + 'audio/x-aac': '.aac', + 'audio/x-wav': '.wav' +}; + +function fileExt(mimetype) { + return mimetypes[mimetype] || '.' + mimetype.split('/')[1]; +} + +function copyText(text) { + let input = document.createElement('textarea'); + document.body.appendChild(input); + input.value = text; + input.focus(); + input.select(); + document.execCommand('Copy'); + input.remove(); +} + +// http://stackoverflow.com/a/16245768 +function b64toBlob(b64Data, contentType, sliceSize) { + + contentType = contentType || ''; + sliceSize = sliceSize || 512; + + let byteCharacters = atob(b64Data); + let byteArrays = []; + + for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { + + let slice = byteCharacters.slice(offset, offset + sliceSize); + + let byteNumbers = new Array(slice.length); + + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + let byteArray = new Uint8Array(byteNumbers); + + byteArrays.push(byteArray); + + } + + let blob = new Blob(byteArrays, { type: contentType }); + + return blob; +}
\ No newline at end of file |