diff --git a/public/index.html b/public/index.html
index cbb0337..ad8658b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -238,6 +238,7 @@
+
diff --git a/public/scripts/network.js b/public/scripts/network.js
index 571749b..d624812 100644
--- a/public/scripts/network.js
+++ b/public/scripts/network.js
@@ -893,11 +893,11 @@ class Events {
window.dispatchEvent(new CustomEvent(type, { detail: detail }));
}
- static on(type, callback) {
- return window.addEventListener(type, callback, false);
+ static on(type, callback, options = false) {
+ return window.addEventListener(type, callback, options);
}
- static off(type, callback) {
- return window.removeEventListener(type, callback, false);
+ static off(type, callback, options = false) {
+ return window.removeEventListener(type, callback, options);
}
}
diff --git a/public/scripts/ui.js b/public/scripts/ui.js
index b7e75f3..580b73c 100644
--- a/public/scripts/ui.js
+++ b/public/scripts/ui.js
@@ -241,7 +241,7 @@ class PeersUI {
const _callback = (e) => this._sendClipboardData(e, files, text);
Events.on('paste-pointerdown', _callback);
- Events.on('deactivate-paste-mode', _ => this._deactivatePasteMode(_callback));
+ Events.on('deactivate-paste-mode', _ => this._deactivatePasteMode(_callback), { once: true });
this.$cancelPasteModeBtn.removeAttribute('hidden');
@@ -1245,21 +1245,21 @@ class Base64ZipDialog extends Dialog {
const base64Hash = window.location.hash.substring(1);
this.$pasteBtn = this.$el.querySelector('#base64-paste-btn');
+ this.$fallbackTextarea = this.$el.querySelector('.textarea');
if (base64Text) {
this.show();
if (base64Text === "paste") {
// ?base64text=paste
// base64 encoded string is ready to be pasted from clipboard
- this.$pasteBtn.innerText = 'Tap here to paste text';
- this.$pasteBtn.addEventListener('click', _ => this.processClipboard('text'));
+ this.preparePasting("text");
} else if (base64Text === "hash") {
// ?base64text=hash#BASE64ENCODED
// base64 encoded string is url hash which is never sent to server and faster (recommended)
this.processBase64Text(base64Hash)
.catch(_ => {
Events.fire('notify-user', 'Text content is incorrect.');
- console.log("Text content incorrect.")
+ console.log("Text content incorrect.");
}).finally(_ => {
this.hide();
});
@@ -1269,7 +1269,7 @@ class Base64ZipDialog extends Dialog {
this.processBase64Text(base64Text)
.catch(_ => {
Events.fire('notify-user', 'Text content is incorrect.');
- console.log("Text content incorrect.")
+ console.log("Text content incorrect.");
}).finally(_ => {
this.hide();
});
@@ -1282,14 +1282,13 @@ class Base64ZipDialog extends Dialog {
this.processBase64Zip(base64Hash)
.catch(_ => {
Events.fire('notify-user', 'File content is incorrect.');
- console.log("File content incorrect.")
+ console.log("File content incorrect.");
}).finally(_ => {
this.hide();
});
} else {
// ?base64zip=paste || ?base64zip=true
- this.$pasteBtn.innerText = 'Tap here to paste files';
- this.$pasteBtn.addEventListener('click', _ => this.processClipboard('file'));
+ this.preparePasting('files');
}
}
}
@@ -1299,39 +1298,60 @@ class Base64ZipDialog extends Dialog {
this.$pasteBtn.innerText = "Processing...";
}
- async processClipboard(type) {
- if (!navigator.clipboard.readText) {
- Events.fire('notify-user', 'This feature is not available on your browser.');
- console.log("navigator.clipboard.readText() is not available on your browser.")
- this.hide();
- return;
- }
-
- this._setPasteBtnToProcessing();
-
- const base64 = await navigator.clipboard.readText();
-
- if (!base64) return;
-
- if (type === "text") {
- this.processBase64Text(base64)
- .catch(_ => {
- Events.fire('notify-user', 'Clipboard content is incorrect.');
- console.log("Clipboard content is incorrect.")
- }).finally(_ => {
- this.hide();
- });
+ preparePasting(type) {
+ if (navigator.clipboard.readText) {
+ this.$pasteBtn.innerText = `Tap here to paste ${type}`;
+ this._clickCallback = _ => this.processClipboard(type);
+ this.$pasteBtn.addEventListener('click', _ => this._clickCallback());
} else {
- this.processBase64Zip(base64)
- .catch(_ => {
- Events.fire('notify-user', 'Clipboard content is incorrect.');
- console.log("Clipboard content is incorrect.")
- }).finally(_ => {
- this.hide();
- });
+ console.log("`navigator.clipboard.readText()` is not available on your browser.\nOn Firefox you can set `dom.events.asyncClipboard.readText` to true under `about:config` for convenience.")
+ this.$pasteBtn.setAttribute('hidden', '');
+ this.$fallbackTextarea.setAttribute('placeholder', `Paste here to send ${type}`);
+ this.$fallbackTextarea.removeAttribute('hidden');
+ this._inputCallback = _ => this.processInput(type);
+ this.$fallbackTextarea.addEventListener('input', _ => this._inputCallback());
+ this.$fallbackTextarea.focus();
}
}
+ async processInput(type) {
+ const base64 = this.$fallbackTextarea.textContent;
+ this.$fallbackTextarea.textContent = '';
+ await this.processBase64(type, base64);
+ }
+
+ async processClipboard(type) {
+ const base64 = await navigator.clipboard.readText();
+ await this.processBase64(type, base64);
+ }
+
+ isValidBase64(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 {
+ if (type === "text") {
+ await this.processBase64Text(base64);
+ } else {
+ await this.processBase64Zip(base64);
+ }
+ } catch(_) {
+ Events.fire('notify-user', 'Clipboard content is incorrect.');
+ console.log("Clipboard content is incorrect.")
+ }
+ this.hide();
+ }
+
processBase64Text(base64Text){
return new Promise((resolve) => {
this._setPasteBtnToProcessing();
@@ -1365,6 +1385,8 @@ class Base64ZipDialog extends Dialog {
hide() {
this.clearBrowserHistory();
+ this.$pasteBtn.removeEventListener('click', _ => this._clickCallback());
+ this.$fallbackTextarea.removeEventListener('input', _ => this._inputCallback());
super.hide();
}
}
diff --git a/public/styles.css b/public/styles.css
index 237764e..db87050 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -791,10 +791,29 @@ x-dialog .dialog-subheader {
margin: auto -24px;
}
-#base64-paste-btn {
+#base64-paste-btn,
+#base64-paste-dialog .textarea {
width: 100%;
height: 40vh;
border: solid 12px #438cff;
+ text-align: center;
+}
+
+#base64-paste-dialog .textarea {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+}
+
+#base64-paste-dialog .textarea::before {
+ font-size: 15px;
+ letter-spacing: 0.12em;
+ color: var(--primary-color);
+ font-weight: 700;
+ text-transform: uppercase;
+ content: attr(placeholder);
}
#base64-paste-dialog button {
diff --git a/public_included_ws_fallback/index.html b/public_included_ws_fallback/index.html
index a2a4b4d..549e16e 100644
--- a/public_included_ws_fallback/index.html
+++ b/public_included_ws_fallback/index.html
@@ -241,6 +241,7 @@
+
diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js
index 4fb57bb..b745b93 100644
--- a/public_included_ws_fallback/scripts/network.js
+++ b/public_included_ws_fallback/scripts/network.js
@@ -974,11 +974,11 @@ class Events {
window.dispatchEvent(new CustomEvent(type, { detail: detail }));
}
- static on(type, callback) {
- return window.addEventListener(type, callback, false);
+ static on(type, callback, options = false) {
+ return window.addEventListener(type, callback, options);
}
- static off(type, callback) {
- return window.removeEventListener(type, callback, false);
+ static off(type, callback, options = false) {
+ return window.removeEventListener(type, callback, options);
}
}
diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js
index 896608e..e45143e 100644
--- a/public_included_ws_fallback/scripts/ui.js
+++ b/public_included_ws_fallback/scripts/ui.js
@@ -241,7 +241,7 @@ class PeersUI {
const _callback = (e) => this._sendClipboardData(e, files, text);
Events.on('paste-pointerdown', _callback);
- Events.on('deactivate-paste-mode', _ => this._deactivatePasteMode(_callback));
+ Events.on('deactivate-paste-mode', _ => this._deactivatePasteMode(_callback), { once: true });
this.$cancelPasteModeBtn.removeAttribute('hidden');
@@ -1246,21 +1246,21 @@ class Base64ZipDialog extends Dialog {
const base64Hash = window.location.hash.substring(1);
this.$pasteBtn = this.$el.querySelector('#base64-paste-btn');
+ this.$fallbackTextarea = this.$el.querySelector('.textarea');
if (base64Text) {
this.show();
if (base64Text === "paste") {
// ?base64text=paste
// base64 encoded string is ready to be pasted from clipboard
- this.$pasteBtn.innerText = 'Tap here to paste text';
- this.$pasteBtn.addEventListener('click', _ => this.processClipboard('text'));
+ this.preparePasting("text");
} else if (base64Text === "hash") {
// ?base64text=hash#BASE64ENCODED
// base64 encoded string is url hash which is never sent to server and faster (recommended)
this.processBase64Text(base64Hash)
.catch(_ => {
Events.fire('notify-user', 'Text content is incorrect.');
- console.log("Text content incorrect.")
+ console.log("Text content incorrect.");
}).finally(_ => {
this.hide();
});
@@ -1270,7 +1270,7 @@ class Base64ZipDialog extends Dialog {
this.processBase64Text(base64Text)
.catch(_ => {
Events.fire('notify-user', 'Text content is incorrect.');
- console.log("Text content incorrect.")
+ console.log("Text content incorrect.");
}).finally(_ => {
this.hide();
});
@@ -1283,14 +1283,13 @@ class Base64ZipDialog extends Dialog {
this.processBase64Zip(base64Hash)
.catch(_ => {
Events.fire('notify-user', 'File content is incorrect.');
- console.log("File content incorrect.")
+ console.log("File content incorrect.");
}).finally(_ => {
this.hide();
});
} else {
// ?base64zip=paste || ?base64zip=true
- this.$pasteBtn.innerText = 'Tap here to paste files';
- this.$pasteBtn.addEventListener('click', _ => this.processClipboard('file'));
+ this.preparePasting('files');
}
}
}
@@ -1300,39 +1299,60 @@ class Base64ZipDialog extends Dialog {
this.$pasteBtn.innerText = "Processing...";
}
- async processClipboard(type) {
- if (!navigator.clipboard.readText) {
- Events.fire('notify-user', 'This feature is not available on your browser.');
- console.log("navigator.clipboard.readText() is not available on your browser.")
- this.hide();
- return;
- }
-
- this._setPasteBtnToProcessing();
-
- const base64 = await navigator.clipboard.readText();
-
- if (!base64) return;
-
- if (type === "text") {
- this.processBase64Text(base64)
- .catch(_ => {
- Events.fire('notify-user', 'Clipboard content is incorrect.');
- console.log("Clipboard content is incorrect.")
- }).finally(_ => {
- this.hide();
- });
+ preparePasting(type) {
+ if (navigator.clipboard.readText) {
+ this.$pasteBtn.innerText = `Tap here to paste ${type}`;
+ this._clickCallback = _ => this.processClipboard(type);
+ this.$pasteBtn.addEventListener('click', _ => this._clickCallback());
} else {
- this.processBase64Zip(base64)
- .catch(_ => {
- Events.fire('notify-user', 'Clipboard content is incorrect.');
- console.log("Clipboard content is incorrect.")
- }).finally(_ => {
- this.hide();
- });
+ console.log("`navigator.clipboard.readText()` is not available on your browser.\nOn Firefox you can set `dom.events.asyncClipboard.readText` to true under `about:config` for convenience.")
+ this.$pasteBtn.setAttribute('hidden', '');
+ this.$fallbackTextarea.setAttribute('placeholder', `Paste here to send ${type}`);
+ this.$fallbackTextarea.removeAttribute('hidden');
+ this._inputCallback = _ => this.processInput(type);
+ this.$fallbackTextarea.addEventListener('input', _ => this._inputCallback());
+ this.$fallbackTextarea.focus();
}
}
+ async processInput(type) {
+ const base64 = this.$fallbackTextarea.textContent;
+ this.$fallbackTextarea.textContent = '';
+ await this.processBase64(type, base64);
+ }
+
+ async processClipboard(type) {
+ const base64 = await navigator.clipboard.readText();
+ await this.processBase64(type, base64);
+ }
+
+ isValidBase64(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 {
+ if (type === "text") {
+ await this.processBase64Text(base64);
+ } else {
+ await this.processBase64Zip(base64);
+ }
+ } catch(_) {
+ Events.fire('notify-user', 'Clipboard content is incorrect.');
+ console.log("Clipboard content is incorrect.")
+ }
+ this.hide();
+ }
+
processBase64Text(base64Text){
return new Promise((resolve) => {
this._setPasteBtnToProcessing();
diff --git a/public_included_ws_fallback/styles.css b/public_included_ws_fallback/styles.css
index 0f8742d..2d29898 100644
--- a/public_included_ws_fallback/styles.css
+++ b/public_included_ws_fallback/styles.css
@@ -817,10 +817,29 @@ x-dialog .dialog-subheader {
margin: auto -24px;
}
-#base64-paste-btn {
+#base64-paste-btn,
+#base64-paste-dialog .textarea {
width: 100%;
height: 40vh;
border: solid 12px #438cff;
+ text-align: center;
+}
+
+#base64-paste-dialog .textarea {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+}
+
+#base64-paste-dialog .textarea::before {
+ font-size: 15px;
+ letter-spacing: 0.12em;
+ color: var(--primary-color);
+ font-weight: 700;
+ text-transform: uppercase;
+ content: attr(placeholder);
}
#base64-paste-dialog button {