mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-21 15:26:17 -04:00
Centralize evaluation of URL parameters to clean up code and remove redundancies, and streamline Base64Dialog class
This commit is contained in:
parent
cb86ce0e39
commit
10b658e2e9
6 changed files with 195 additions and 179 deletions
|
@ -521,8 +521,13 @@
|
||||||
<x-dialog id="base64-paste-dialog">
|
<x-dialog id="base64-paste-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
|
<div class="row center p2">
|
||||||
|
<h2 class="dialog-title"></h2>
|
||||||
|
</div>
|
||||||
|
<div class="row p2">
|
||||||
<button class="btn btn-rounded btn-grey center" id="base64-paste-btn" title="Paste"></button>
|
<button class="btn btn-rounded btn-grey center" id="base64-paste-btn" title="Paste"></button>
|
||||||
<div class="textarea" placeholder="Paste here to send files" title="CMD/⌘ + V" contenteditable hidden></div>
|
<div class="textarea" title="CMD/⌘ + V" contenteditable hidden></div>
|
||||||
|
</div>
|
||||||
<div class="row-reverse center btn-row">
|
<div class="row-reverse center btn-row">
|
||||||
<button class="btn btn-rounded btn-grey" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -80,9 +80,11 @@
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"receive-text-title": "Message Received",
|
"receive-text-title": "Message Received",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
|
"base64-title-files": "Share Files",
|
||||||
|
"base64-title-text": "Share Text",
|
||||||
"base64-processing": "Processing…",
|
"base64-processing": "Processing…",
|
||||||
"base64-tap-to-paste": "Tap here to paste {{type}}",
|
"base64-tap-to-paste": "Tap here to share {{type}}",
|
||||||
"base64-paste-to-send": "Paste here to send {{type}}",
|
"base64-paste-to-send": "Paste clipboard here to share {{type}}",
|
||||||
"base64-text": "text",
|
"base64-text": "text",
|
||||||
"base64-files": "files",
|
"base64-files": "files",
|
||||||
"file-other-description-image": "and 1 other image",
|
"file-other-description-image": "and 1 other image",
|
||||||
|
|
|
@ -59,6 +59,9 @@ class PairDrop {
|
||||||
|
|
||||||
await this.hydrate();
|
await this.hydrate();
|
||||||
console.log("UI hydrated.");
|
console.log("UI hydrated.");
|
||||||
|
|
||||||
|
// Evaluate url params as soon as ws is connected
|
||||||
|
Events.on('ws-connected', _ => this.evaluateUrlParams(), {once: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerServiceWorker() {
|
registerServiceWorker() {
|
||||||
|
@ -171,6 +174,44 @@ class PairDrop {
|
||||||
this.server = new ServerConnection();
|
this.server = new ServerConnection();
|
||||||
this.peers = new PeersManager(this.server);
|
this.peers = new PeersManager(this.server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async evaluateUrlParams() {
|
||||||
|
// get url params
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const hash = window.location.hash.substring(1);
|
||||||
|
|
||||||
|
// evaluate url params
|
||||||
|
if (urlParams.has('pair_key')) {
|
||||||
|
const pairKey = urlParams.get('pair_key');
|
||||||
|
this.pairDeviceDialog._pairDeviceJoin(pairKey);
|
||||||
|
}
|
||||||
|
else if (urlParams.has('room_id')) {
|
||||||
|
const roomId = urlParams.get('room_id');
|
||||||
|
this.publicRoomDialog._joinPublicRoom(roomId);
|
||||||
|
}
|
||||||
|
else if (urlParams.has('base64text')) {
|
||||||
|
const base64Text = urlParams.get('base64text');
|
||||||
|
await this.base64Dialog.evaluateBase64Text(base64Text, hash);
|
||||||
|
}
|
||||||
|
else if (urlParams.has('base64zip')) {
|
||||||
|
const base64Zip = urlParams.get('base64zip');
|
||||||
|
await this.base64Dialog.evaluateBase64Zip(base64Zip, hash);
|
||||||
|
}
|
||||||
|
else if (urlParams.has("share_target")) {
|
||||||
|
const shareTargetType = urlParams.get("share_target");
|
||||||
|
const title = urlParams.get('title') || '';
|
||||||
|
const text = urlParams.get('text') || '';
|
||||||
|
const url = urlParams.get('url') || '';
|
||||||
|
await this.webShareTargetUI.evaluateShareTarget(shareTargetType, title, text, url);
|
||||||
|
}
|
||||||
|
else if (urlParams.has("file_handler")) {
|
||||||
|
await this.webFileHandlersUI.evaluateLaunchQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove url params from url
|
||||||
|
const urlWithoutParams = getUrlWithoutArguments();
|
||||||
|
window.history.replaceState({}, "Rewrite URL", urlWithoutParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pairDrop = new PairDrop();
|
const pairDrop = new PairDrop();
|
|
@ -1321,8 +1321,6 @@ class PairDeviceDialog extends Dialog {
|
||||||
this.$el.addEventListener('paste', e => this._onPaste(e));
|
this.$el.addEventListener('paste', e => this._onPaste(e));
|
||||||
this.$qrCode.addEventListener('click', _ => this._copyPairUrl());
|
this.$qrCode.addEventListener('click', _ => this._copyPairUrl());
|
||||||
|
|
||||||
this.evaluateUrlAttributes();
|
|
||||||
|
|
||||||
this.pairPeer = {};
|
this.pairPeer = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1344,15 +1342,6 @@ class PairDeviceDialog extends Dialog {
|
||||||
this.inputKeyContainer._onPaste(pastedKey);
|
this.inputKeyContainer._onPaste(pastedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateUrlAttributes() {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
if (urlParams.has('pair_key')) {
|
|
||||||
this._pairDeviceJoin(urlParams.get('pair_key'));
|
|
||||||
const url = getUrlWithoutArguments();
|
|
||||||
window.history.replaceState({}, "Rewrite URL", url); //remove pair_key from url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_pairDeviceInitiate() {
|
_pairDeviceInitiate() {
|
||||||
Events.fire('pair-device-initiate');
|
Events.fire('pair-device-initiate');
|
||||||
}
|
}
|
||||||
|
@ -1700,8 +1689,6 @@ class PublicRoomDialog extends Dialog {
|
||||||
this.$el.addEventListener('paste', e => this._onPaste(e));
|
this.$el.addEventListener('paste', e => this._onPaste(e));
|
||||||
this.$qrCode.addEventListener('click', _ => this._copyShareRoomUrl());
|
this.$qrCode.addEventListener('click', _ => this._copyShareRoomUrl());
|
||||||
|
|
||||||
this.evaluateUrlAttributes();
|
|
||||||
|
|
||||||
Events.on('ws-connected', _ => this._onWsConnected());
|
Events.on('ws-connected', _ => this._onWsConnected());
|
||||||
Events.on('translation-loaded', _ => this.setFooterBadge());
|
Events.on('translation-loaded', _ => this.setFooterBadge());
|
||||||
}
|
}
|
||||||
|
@ -1791,15 +1778,6 @@ class PublicRoomDialog extends Dialog {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateUrlAttributes() {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
if (urlParams.has('room_id')) {
|
|
||||||
this._joinPublicRoom(urlParams.get('room_id'));
|
|
||||||
const url = getUrlWithoutArguments();
|
|
||||||
window.history.replaceState({}, "Rewrite URL", url); //remove pair_key from url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onWsConnected() {
|
_onWsConnected() {
|
||||||
let roomId = sessionStorage.getItem('public_room_id');
|
let roomId = sessionStorage.getItem('public_room_id');
|
||||||
|
|
||||||
|
@ -2147,61 +2125,47 @@ class Base64Dialog extends Dialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('base64-paste-dialog');
|
super('base64-paste-dialog');
|
||||||
const urlParams = new URL(window.location).searchParams;
|
|
||||||
const base64Text = urlParams.get('base64text');
|
|
||||||
const base64Zip = urlParams.get('base64zip');
|
|
||||||
const base64Hash = window.location.hash.substring(1);
|
|
||||||
|
|
||||||
|
this.$title = this.$el.querySelector('.dialog-title');
|
||||||
this.$pasteBtn = this.$el.querySelector('#base64-paste-btn');
|
this.$pasteBtn = this.$el.querySelector('#base64-paste-btn');
|
||||||
this.$fallbackTextarea = this.$el.querySelector('.textarea');
|
this.$fallbackTextarea = this.$el.querySelector('.textarea');
|
||||||
|
}
|
||||||
|
|
||||||
|
async evaluateBase64Text(base64Text, hash) {
|
||||||
|
this.$title.innerText = Localization.getTranslation('dialogs.base64-title-text');
|
||||||
|
|
||||||
if (base64Text) {
|
|
||||||
this.show();
|
|
||||||
if (base64Text === 'paste') {
|
if (base64Text === 'paste') {
|
||||||
// ?base64text=paste
|
// ?base64text=paste
|
||||||
// base64 encoded string is ready to be pasted from clipboard
|
// base64 encoded string is ready to be pasted from clipboard
|
||||||
this.preparePasting('text');
|
this.preparePasting('text');
|
||||||
|
this.show();
|
||||||
}
|
}
|
||||||
else if (base64Text === 'hash') {
|
else if (base64Text === 'hash') {
|
||||||
// ?base64text=hash#BASE64ENCODED
|
// ?base64text=hash#BASE64ENCODED
|
||||||
// base64 encoded string is url hash which is never sent to server and faster (recommended)
|
// base64 encoded text is url hash which cannot be seen by the server and is faster (recommended)
|
||||||
this.processBase64Text(base64Hash)
|
this.show();
|
||||||
.catch(_ => {
|
await this.processBase64Text(hash)
|
||||||
Events.fire('notify-user', Localization.getTranslation("notifications.text-content-incorrect"));
|
|
||||||
console.log("Text content incorrect.");
|
|
||||||
}).finally(() => {
|
|
||||||
this.hide();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// ?base64text=BASE64ENCODED
|
// ?base64text=BASE64ENCODED
|
||||||
// base64 encoded string was part of url param (not recommended)
|
// base64 encoded text is part of the url param. Seen by server and slow (not recommended)
|
||||||
this.processBase64Text(base64Text)
|
|
||||||
.catch(_ => {
|
|
||||||
Events.fire('notify-user', Localization.getTranslation("notifications.text-content-incorrect"));
|
|
||||||
console.log("Text content incorrect.");
|
|
||||||
}).finally(() => {
|
|
||||||
this.hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (base64Zip) {
|
|
||||||
this.show();
|
this.show();
|
||||||
if (base64Zip === "hash") {
|
await this.processBase64Text(base64Text)
|
||||||
// ?base64zip=hash#BASE64ENCODED
|
|
||||||
// base64 encoded zip file is url hash which is never sent to the server
|
|
||||||
this.processBase64Zip(base64Hash)
|
|
||||||
.catch(_ => {
|
|
||||||
Events.fire('notify-user', Localization.getTranslation("notifications.file-content-incorrect"));
|
|
||||||
console.log("File content incorrect.");
|
|
||||||
}).finally(() => {
|
|
||||||
this.hide();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
|
|
||||||
|
async evaluateBase64Zip(base64Zip, hash) {
|
||||||
|
this.$title.innerText = Localization.getTranslation('dialogs.base64-title-files');
|
||||||
|
|
||||||
|
if (base64Zip === 'paste') {
|
||||||
// ?base64zip=paste || ?base64zip=true
|
// ?base64zip=paste || ?base64zip=true
|
||||||
this.preparePasting('files');
|
this.preparePasting('files');
|
||||||
|
this.show();
|
||||||
}
|
}
|
||||||
|
else if (base64Zip === 'hash') {
|
||||||
|
// ?base64zip=hash#BASE64ENCODED
|
||||||
|
// base64 encoded zip file is url hash which cannot be seen by the server
|
||||||
|
await this.processBase64Zip(hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2234,28 +2198,15 @@ class Base64Dialog extends Dialog {
|
||||||
async processInput(type) {
|
async processInput(type) {
|
||||||
const base64 = this.$fallbackTextarea.textContent;
|
const base64 = this.$fallbackTextarea.textContent;
|
||||||
this.$fallbackTextarea.textContent = '';
|
this.$fallbackTextarea.textContent = '';
|
||||||
await this.processBase64(type, base64);
|
await this.processPastedBase64(type, base64);
|
||||||
}
|
}
|
||||||
|
|
||||||
async processClipboard(type) {
|
async processClipboard(type) {
|
||||||
const base64 = await navigator.clipboard.readText();
|
const base64 = await navigator.clipboard.readText();
|
||||||
await this.processBase64(type, base64);
|
await this.processPastedBase64(type, base64);
|
||||||
}
|
}
|
||||||
|
|
||||||
isValidBase64(base64) {
|
async processPastedBase64(type, base64) {
|
||||||
try {
|
|
||||||
// check if input is base64 encoded
|
|
||||||
window.atob(base64);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
// input is not base64 string.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async processBase64(type, base64) {
|
|
||||||
if (!base64 || !this.isValidBase64(base64)) return;
|
|
||||||
this._setPasteBtnToProcessing();
|
|
||||||
try {
|
try {
|
||||||
if (type === 'text') {
|
if (type === 'text') {
|
||||||
await this.processBase64Text(base64);
|
await this.processBase64Text(base64);
|
||||||
|
@ -2263,51 +2214,50 @@ class Base64Dialog extends Dialog {
|
||||||
else {
|
else {
|
||||||
await this.processBase64Zip(base64);
|
await this.processBase64Zip(base64);
|
||||||
}
|
}
|
||||||
} catch(_) {
|
}
|
||||||
|
catch(e) {
|
||||||
Events.fire('notify-user', Localization.getTranslation("notifications.clipboard-content-incorrect"));
|
Events.fire('notify-user', Localization.getTranslation("notifications.clipboard-content-incorrect"));
|
||||||
console.log("Clipboard content is incorrect.")
|
console.log("Clipboard content is incorrect.")
|
||||||
}
|
}
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
processBase64Text(base64Text){
|
async processBase64Text(base64){
|
||||||
return new Promise((resolve) => {
|
|
||||||
this._setPasteBtnToProcessing();
|
this._setPasteBtnToProcessing();
|
||||||
let decodedText = decodeURIComponent(escape(window.atob(base64Text)));
|
|
||||||
|
try {
|
||||||
|
const decodedText = await decodeBase64Text(base64);
|
||||||
if (ShareTextDialog.isApproveShareTextSet()) {
|
if (ShareTextDialog.isApproveShareTextSet()) {
|
||||||
Events.fire('share-text-dialog', decodedText);
|
Events.fire('share-text-dialog', decodedText);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Events.fire('activate-share-mode', {text: decodedText});
|
Events.fire('activate-share-mode', {text: decodedText});
|
||||||
}
|
}
|
||||||
resolve();
|
}
|
||||||
});
|
catch (e) {
|
||||||
|
Events.fire('notify-user', Localization.getTranslation("notifications.text-content-incorrect"));
|
||||||
|
console.log("Text content incorrect.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async processBase64Zip(base64zip) {
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
async processBase64Zip(base64) {
|
||||||
this._setPasteBtnToProcessing();
|
this._setPasteBtnToProcessing();
|
||||||
let bstr = atob(base64zip), n = bstr.length, u8arr = new Uint8Array(n);
|
|
||||||
while (n--) {
|
try {
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
const decodedFiles = await decodeBase64Files(base64);
|
||||||
|
Events.fire('activate-share-mode', {files: decodedFiles});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Events.fire('notify-user', Localization.getTranslation("notifications.file-content-incorrect"));
|
||||||
|
console.log("File content incorrect.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const zipBlob = new File([u8arr], 'archive.zip');
|
this.hide();
|
||||||
|
|
||||||
let files = [];
|
|
||||||
const zipEntries = await zipper.getEntries(zipBlob);
|
|
||||||
for (let i = 0; i < zipEntries.length; i++) {
|
|
||||||
let fileBlob = await zipper.getData(zipEntries[i]);
|
|
||||||
files.push(new File([fileBlob], zipEntries[i].filename));
|
|
||||||
}
|
|
||||||
Events.fire('activate-share-mode', {files: files});
|
|
||||||
}
|
|
||||||
|
|
||||||
clearBrowserHistory() {
|
|
||||||
const url = getUrlWithoutArguments();
|
|
||||||
window.history.replaceState({}, "Rewrite URL", url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.clearBrowserHistory();
|
|
||||||
this.$pasteBtn.removeEventListener('click', _ => this._clickCallback());
|
this.$pasteBtn.removeEventListener('click', _ => this._clickCallback());
|
||||||
this.$fallbackTextarea.removeEventListener('input', _ => this._inputCallback());
|
this.$fallbackTextarea.removeEventListener('input', _ => this._inputCallback());
|
||||||
super.hide();
|
super.hide();
|
||||||
|
@ -2529,16 +2479,10 @@ class NetworkStatusUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebShareTargetUI {
|
class WebShareTargetUI {
|
||||||
constructor() {
|
|
||||||
const urlParams = new URL(window.location).searchParams;
|
|
||||||
const share_target_type = urlParams.get("share-target")
|
|
||||||
if (share_target_type) {
|
|
||||||
if (share_target_type === "text") {
|
|
||||||
const title = urlParams.get('title') || '';
|
|
||||||
const text = urlParams.get('text') || '';
|
|
||||||
const url = urlParams.get('url') || '';
|
|
||||||
let shareTargetText;
|
|
||||||
|
|
||||||
|
async evaluateShareTarget(shareTargetType, title, text, url) {
|
||||||
|
if (shareTargetType === "text") {
|
||||||
|
let shareTargetText;
|
||||||
if (url) {
|
if (url) {
|
||||||
shareTargetText = url; // we share only the link - no text.
|
shareTargetText = url; // we share only the link - no text.
|
||||||
}
|
}
|
||||||
|
@ -2551,11 +2495,12 @@ class WebShareTargetUI {
|
||||||
|
|
||||||
if (ShareTextDialog.isApproveShareTextSet()) {
|
if (ShareTextDialog.isApproveShareTextSet()) {
|
||||||
Events.fire('share-text-dialog', shareTargetText);
|
Events.fire('share-text-dialog', shareTargetText);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Events.fire('activate-share-mode', {text: shareTargetText});
|
Events.fire('activate-share-mode', {text: shareTargetText});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (share_target_type === "files") {
|
else if (shareTargetType === "files") {
|
||||||
let openRequest = window.indexedDB.open('pairdrop_store')
|
let openRequest = window.indexedDB.open('pairdrop_store')
|
||||||
openRequest.onsuccess = e => {
|
openRequest.onsuccess = e => {
|
||||||
const db = e.target.result;
|
const db = e.target.result;
|
||||||
|
@ -2564,10 +2509,12 @@ class WebShareTargetUI {
|
||||||
const request = store.getAll();
|
const request = store.getAll();
|
||||||
request.onsuccess = _ => {
|
request.onsuccess = _ => {
|
||||||
const fileObjects = request.result;
|
const fileObjects = request.result;
|
||||||
|
|
||||||
let filesReceived = [];
|
let filesReceived = [];
|
||||||
for (let i = 0; i < fileObjects.length; i++) {
|
for (let i = 0; i < fileObjects.length; i++) {
|
||||||
filesReceived.push(new File([fileObjects[i].buffer], fileObjects[i].name));
|
filesReceived.push(new File([fileObjects[i].buffer], fileObjects[i].name));
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearRequest = store.clear()
|
const clearRequest = store.clear()
|
||||||
clearRequest.onsuccess = _ => db.close();
|
clearRequest.onsuccess = _ => db.close();
|
||||||
|
|
||||||
|
@ -2575,34 +2522,29 @@ class WebShareTargetUI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const url = getUrlWithoutArguments();
|
|
||||||
window.history.replaceState({}, "Rewrite URL", url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebFileHandlersUI {
|
class WebFileHandlersUI {
|
||||||
constructor() {
|
async evaluateLaunchQueue() {
|
||||||
const urlParams = new URL(window.location).searchParams;
|
if (!"launchQueue" in window) return;
|
||||||
if (urlParams.has("file_handler") && "launchQueue" in window) {
|
|
||||||
launchQueue.setConsumer(async launchParams => {
|
launchQueue.setConsumer(async launchParams => {
|
||||||
console.log("Launched with: ", launchParams);
|
console.log("Launched with: ", launchParams);
|
||||||
if (!launchParams.files.length)
|
|
||||||
return;
|
if (!launchParams.files.length) return;
|
||||||
|
|
||||||
let files = [];
|
let files = [];
|
||||||
|
|
||||||
for (let i = 0; i < launchParams.files.length; i++) {
|
for (let i = 0; i < launchParams.files.length; i++) {
|
||||||
if (i !== 0 && await launchParams.files[i].isSameEntry(launchParams.files[i-1])) continue;
|
if (i !== 0 && await launchParams.files[i].isSameEntry(launchParams.files[i-1])) continue;
|
||||||
const fileHandle = launchParams.files[i];
|
|
||||||
const file = await fileHandle.getFile();
|
const file = await launchParams.files[i].getFile();
|
||||||
files.push(file);
|
files.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.fire('activate-share-mode', {files: files})
|
Events.fire('activate-share-mode', {files: files})
|
||||||
launchParams = null;
|
|
||||||
});
|
});
|
||||||
const url = getUrlWithoutArguments();
|
|
||||||
window.history.replaceState({}, "Rewrite URL", url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -505,3 +505,28 @@ function getResizedImageDataUrl(file, width = undefined, height = undefined, qua
|
||||||
image.onerror = _ => reject(`Could not create an image thumbnail from type ${file.type}`);
|
image.onerror = _ => reject(`Could not create an image thumbnail from type ${file.type}`);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function decodeBase64Files(base64) {
|
||||||
|
if (!base64) throw new Error('Base64 is empty');
|
||||||
|
|
||||||
|
let bstr = atob(base64), n = bstr.length, u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
const zipBlob = new File([u8arr], 'archive.zip');
|
||||||
|
|
||||||
|
let files = [];
|
||||||
|
const zipEntries = await zipper.getEntries(zipBlob);
|
||||||
|
for (let i = 0; i < zipEntries.length; i++) {
|
||||||
|
let fileBlob = await zipper.getData(zipEntries[i]);
|
||||||
|
files.push(new File([fileBlob], zipEntries[i].filename));
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decodeBase64Text(base64) {
|
||||||
|
if (!base64) throw new Error('Base64 is empty');
|
||||||
|
|
||||||
|
return decodeURIComponent(escape(window.atob(base64)))
|
||||||
|
}
|
|
@ -705,6 +705,7 @@ x-dialog .dialog-subheader {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
border: solid 12px #438cff;
|
border: solid 12px #438cff;
|
||||||
|
margin: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64-paste-dialog .textarea {
|
#base64-paste-dialog .textarea {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue