mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-21 07:16:18 -04:00
add event listener to textbox
This commit is contained in:
parent
0aa27250a4
commit
8809ea6667
1 changed files with 520 additions and 508 deletions
|
@ -1,38 +1,38 @@
|
||||||
const $ = query => document.getElementById(query);
|
const $ = (query) => document.getElementById(query);
|
||||||
const $$ = query => document.body.querySelector(query);
|
const $$ = (query) => document.body.querySelector(query);
|
||||||
const isURL = text => /^((https?:\/\/|www)[^\s]+)/g.test(text.toLowerCase());
|
const isURL = (text) => /^((https?:\/\/|www)[^\s]+)/g.test(text.toLowerCase());
|
||||||
window.isDownloadSupported = (typeof document.createElement('a').download !== 'undefined');
|
window.isDownloadSupported =
|
||||||
window.isProductionEnvironment = !window.location.host.startsWith('localhost');
|
typeof document.createElement("a").download !== "undefined";
|
||||||
|
window.isProductionEnvironment = !window.location.host.startsWith("localhost");
|
||||||
window.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
window.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||||
|
|
||||||
// set display name
|
// set display name
|
||||||
Events.on('display-name', e => {
|
Events.on("display-name", (e) => {
|
||||||
const me = e.detail.message;
|
const me = e.detail.message;
|
||||||
const $displayName = $('displayName')
|
const $displayName = $("displayName");
|
||||||
$displayName.textContent = 'You are known as ' + me.displayName;
|
$displayName.textContent = "You are known as " + me.displayName;
|
||||||
$displayName.title = me.deviceName;
|
$displayName.title = me.deviceName;
|
||||||
});
|
});
|
||||||
|
|
||||||
class PeersUI {
|
class PeersUI {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
Events.on('peer-joined', e => this._onPeerJoined(e.detail));
|
Events.on("peer-joined", (e) => this._onPeerJoined(e.detail));
|
||||||
Events.on('peer-left', e => this._onPeerLeft(e.detail));
|
Events.on("peer-left", (e) => this._onPeerLeft(e.detail));
|
||||||
Events.on('peers', e => this._onPeers(e.detail));
|
Events.on("peers", (e) => this._onPeers(e.detail));
|
||||||
Events.on('file-progress', e => this._onFileProgress(e.detail));
|
Events.on("file-progress", (e) => this._onFileProgress(e.detail));
|
||||||
Events.on('paste', e => this._onPaste(e));
|
Events.on("paste", (e) => this._onPaste(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPeerJoined(peer) {
|
_onPeerJoined(peer) {
|
||||||
if ($(peer.id)) return; // peer already exists
|
if ($(peer.id)) return; // peer already exists
|
||||||
const peerUI = new PeerUI(peer);
|
const peerUI = new PeerUI(peer);
|
||||||
$$('x-peers').appendChild(peerUI.$el);
|
$$("x-peers").appendChild(peerUI.$el);
|
||||||
setTimeout(e => window.animateBackground(false), 1750); // Stop animation
|
setTimeout((e) => window.animateBackground(false), 1750); // Stop animation
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPeers(peers) {
|
_onPeers(peers) {
|
||||||
this._clearPeers();
|
this._clearPeers();
|
||||||
peers.forEach(peer => this._onPeerJoined(peer));
|
peers.forEach((peer) => this._onPeerJoined(peer));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPeerLeft(peerId) {
|
_onPeerLeft(peerId) {
|
||||||
|
@ -49,29 +49,30 @@ class PeersUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearPeers() {
|
_clearPeers() {
|
||||||
const $peers = $$('x-peers').innerHTML = '';
|
const $peers = ($$("x-peers").innerHTML = "");
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPaste(e) {
|
_onPaste(e) {
|
||||||
const files = e.clipboardData.files || e.clipboardData.items
|
const files =
|
||||||
.filter(i => i.type.indexOf('image') > -1)
|
e.clipboardData.files ||
|
||||||
.map(i => i.getAsFile());
|
e.clipboardData.items
|
||||||
const peers = document.querySelectorAll('x-peer');
|
.filter((i) => i.type.indexOf("image") > -1)
|
||||||
|
.map((i) => i.getAsFile());
|
||||||
|
const peers = document.querySelectorAll("x-peer");
|
||||||
// send the pasted image content to the only peer if there is one
|
// send the pasted image content to the only peer if there is one
|
||||||
// otherwise, select the peer somehow by notifying the client that
|
// otherwise, select the peer somehow by notifying the client that
|
||||||
// "image data has been pasted, click the client to which to send it"
|
// "image data has been pasted, click the client to which to send it"
|
||||||
// not implemented
|
// not implemented
|
||||||
if (files.length > 0 && peers.length === 1) {
|
if (files.length > 0 && peers.length === 1) {
|
||||||
Events.fire('files-selected', {
|
Events.fire("files-selected", {
|
||||||
files: files,
|
files: files,
|
||||||
to: $$('x-peer').id
|
to: $$("x-peer").id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeerUI {
|
class PeerUI {
|
||||||
|
|
||||||
html() {
|
html() {
|
||||||
return `
|
return `
|
||||||
<label class="column center" title="Click to send files or right click to send a text">
|
<label class="column center" title="Click to send files or right click to send a text">
|
||||||
|
@ -86,7 +87,7 @@ class PeerUI {
|
||||||
<div class="name font-subheading"></div>
|
<div class="name font-subheading"></div>
|
||||||
<div class="device-name font-body2"></div>
|
<div class="device-name font-body2"></div>
|
||||||
<div class="status font-body2"></div>
|
<div class="status font-body2"></div>
|
||||||
</label>`
|
</label>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(peer) {
|
constructor(peer) {
|
||||||
|
@ -96,29 +97,31 @@ class PeerUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
_initDom() {
|
_initDom() {
|
||||||
const el = document.createElement('x-peer');
|
const el = document.createElement("x-peer");
|
||||||
el.id = this._peer.id;
|
el.id = this._peer.id;
|
||||||
el.innerHTML = this.html();
|
el.innerHTML = this.html();
|
||||||
el.ui = this;
|
el.ui = this;
|
||||||
el.querySelector('svg use').setAttribute('xlink:href', this._icon());
|
el.querySelector("svg use").setAttribute("xlink:href", this._icon());
|
||||||
el.querySelector('.name').textContent = this._displayName();
|
el.querySelector(".name").textContent = this._displayName();
|
||||||
el.querySelector('.device-name').textContent = this._deviceName();
|
el.querySelector(".device-name").textContent = this._deviceName();
|
||||||
this.$el = el;
|
this.$el = el;
|
||||||
this.$progress = el.querySelector('.progress');
|
this.$progress = el.querySelector(".progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
_bindListeners(el) {
|
_bindListeners(el) {
|
||||||
el.querySelector('input').addEventListener('change', e => this._onFilesSelected(e));
|
el.querySelector("input").addEventListener("change", (e) =>
|
||||||
el.addEventListener('drop', e => this._onDrop(e));
|
this._onFilesSelected(e)
|
||||||
el.addEventListener('dragend', e => this._onDragEnd(e));
|
);
|
||||||
el.addEventListener('dragleave', e => this._onDragEnd(e));
|
el.addEventListener("drop", (e) => this._onDrop(e));
|
||||||
el.addEventListener('dragover', e => this._onDragOver(e));
|
el.addEventListener("dragend", (e) => this._onDragEnd(e));
|
||||||
el.addEventListener('contextmenu', e => this._onRightClick(e));
|
el.addEventListener("dragleave", (e) => this._onDragEnd(e));
|
||||||
el.addEventListener('touchstart', e => this._onTouchStart(e));
|
el.addEventListener("dragover", (e) => this._onDragOver(e));
|
||||||
el.addEventListener('touchend', e => this._onTouchEnd(e));
|
el.addEventListener("contextmenu", (e) => this._onRightClick(e));
|
||||||
|
el.addEventListener("touchstart", (e) => this._onTouchStart(e));
|
||||||
|
el.addEventListener("touchend", (e) => this._onTouchEnd(e));
|
||||||
// prevent browser's default file drop behavior
|
// prevent browser's default file drop behavior
|
||||||
Events.on('dragover', e => e.preventDefault());
|
Events.on("dragover", (e) => e.preventDefault());
|
||||||
Events.on('drop', e => e.preventDefault());
|
Events.on("drop", (e) => e.preventDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
_displayName() {
|
_displayName() {
|
||||||
|
@ -131,105 +134,109 @@ class PeerUI {
|
||||||
|
|
||||||
_icon() {
|
_icon() {
|
||||||
const device = this._peer.name.device || this._peer.name;
|
const device = this._peer.name.device || this._peer.name;
|
||||||
if (device.type === 'mobile') {
|
if (device.type === "mobile") {
|
||||||
return '#phone-iphone';
|
return "#phone-iphone";
|
||||||
}
|
}
|
||||||
if (device.type === 'tablet') {
|
if (device.type === "tablet") {
|
||||||
return '#tablet-mac';
|
return "#tablet-mac";
|
||||||
}
|
}
|
||||||
return '#desktop-mac';
|
return "#desktop-mac";
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFilesSelected(e) {
|
_onFilesSelected(e) {
|
||||||
const $input = e.target;
|
const $input = e.target;
|
||||||
const files = $input.files;
|
const files = $input.files;
|
||||||
Events.fire('files-selected', {
|
Events.fire("files-selected", {
|
||||||
files: files,
|
files: files,
|
||||||
to: this._peer.id
|
to: this._peer.id,
|
||||||
});
|
});
|
||||||
$input.value = null; // reset input
|
$input.value = null; // reset input
|
||||||
}
|
}
|
||||||
|
|
||||||
setProgress(progress) {
|
setProgress(progress) {
|
||||||
if (progress > 0) {
|
if (progress > 0) {
|
||||||
this.$el.setAttribute('transfer', '1');
|
this.$el.setAttribute("transfer", "1");
|
||||||
}
|
}
|
||||||
if (progress > 0.5) {
|
if (progress > 0.5) {
|
||||||
this.$progress.classList.add('over50');
|
this.$progress.classList.add("over50");
|
||||||
} else {
|
} else {
|
||||||
this.$progress.classList.remove('over50');
|
this.$progress.classList.remove("over50");
|
||||||
}
|
}
|
||||||
const degrees = `rotate(${360 * progress}deg)`;
|
const degrees = `rotate(${360 * progress}deg)`;
|
||||||
this.$progress.style.setProperty('--progress', degrees);
|
this.$progress.style.setProperty("--progress", degrees);
|
||||||
if (progress >= 1) {
|
if (progress >= 1) {
|
||||||
this.setProgress(0);
|
this.setProgress(0);
|
||||||
this.$el.removeAttribute('transfer');
|
this.$el.removeAttribute("transfer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDrop(e) {
|
_onDrop(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const files = e.dataTransfer.files;
|
const files = e.dataTransfer.files;
|
||||||
Events.fire('files-selected', {
|
Events.fire("files-selected", {
|
||||||
files: files,
|
files: files,
|
||||||
to: this._peer.id
|
to: this._peer.id,
|
||||||
});
|
});
|
||||||
this._onDragEnd();
|
this._onDragEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDragOver() {
|
_onDragOver() {
|
||||||
this.$el.setAttribute('drop', 1);
|
this.$el.setAttribute("drop", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDragEnd() {
|
_onDragEnd() {
|
||||||
this.$el.removeAttribute('drop');
|
this.$el.removeAttribute("drop");
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRightClick(e) {
|
_onRightClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Events.fire('text-recipient', this._peer.id);
|
Events.fire("text-recipient", this._peer.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTouchStart(e) {
|
_onTouchStart(e) {
|
||||||
this._touchStart = Date.now();
|
this._touchStart = Date.now();
|
||||||
this._touchTimer = setTimeout(_ => this._onTouchEnd(), 610);
|
this._touchTimer = setTimeout((_) => this._onTouchEnd(), 610);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTouchEnd(e) {
|
_onTouchEnd(e) {
|
||||||
if (Date.now() - this._touchStart < 500) {
|
if (Date.now() - this._touchStart < 500) {
|
||||||
clearTimeout(this._touchTimer);
|
clearTimeout(this._touchTimer);
|
||||||
} else { // this was a long tap
|
} else {
|
||||||
|
// this was a long tap
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
Events.fire('text-recipient', this._peer.id);
|
Events.fire("text-recipient", this._peer.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Dialog {
|
class Dialog {
|
||||||
constructor(id) {
|
constructor(id) {
|
||||||
this.$el = $(id);
|
this.$el = $(id);
|
||||||
this.$el.querySelectorAll('[close]').forEach(el => el.addEventListener('click', e => this.hide()))
|
this.$el.querySelectorAll("[close]").forEach((el) => {
|
||||||
this.$autoFocus = this.$el.querySelector('[autofocus]');
|
el.addEventListener("click", (e) => this.hide());
|
||||||
|
});
|
||||||
|
this.$el.querySelectorAll("[role=\"textbox\"]").forEach((el) => {
|
||||||
|
el.addEventListener("click", (e) => this.hide());
|
||||||
|
});
|
||||||
|
this.$autoFocus = this.$el.querySelector("[autofocus]");
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.$el.setAttribute('show', 1);
|
this.$el.setAttribute("show", 1);
|
||||||
if (this.$autoFocus) this.$autoFocus.focus();
|
if (this.$autoFocus) this.$autoFocus.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.$el.removeAttribute('show');
|
this.$el.removeAttribute("show");
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
window.blur();
|
window.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReceiveDialog extends Dialog {
|
class ReceiveDialog extends Dialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveDialog');
|
super("receiveDialog");
|
||||||
Events.on('file-received', e => {
|
Events.on("file-received", (e) => {
|
||||||
this._nextFile(e.detail);
|
this._nextFile(e.detail);
|
||||||
window.blop.play();
|
window.blop.play();
|
||||||
});
|
});
|
||||||
|
@ -245,78 +252,79 @@ class ReceiveDialog extends Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
_dequeueFile() {
|
_dequeueFile() {
|
||||||
if (!this._filesQueue.length) { // nothing to do
|
if (!this._filesQueue.length) {
|
||||||
|
// nothing to do
|
||||||
this._busy = false;
|
this._busy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// dequeue next file
|
// dequeue next file
|
||||||
setTimeout(_ => {
|
setTimeout((_) => {
|
||||||
this._busy = false;
|
this._busy = false;
|
||||||
this._nextFile();
|
this._nextFile();
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
_displayFile(file) {
|
_displayFile(file) {
|
||||||
const $a = this.$el.querySelector('#download');
|
const $a = this.$el.querySelector("#download");
|
||||||
const url = URL.createObjectURL(file.blob);
|
const url = URL.createObjectURL(file.blob);
|
||||||
$a.href = url;
|
$a.href = url;
|
||||||
$a.download = file.name;
|
$a.download = file.name;
|
||||||
|
|
||||||
if(this._autoDownload()){
|
if (this._autoDownload()) {
|
||||||
$a.click()
|
$a.click();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if(file.mime.split('/')[0] === 'image'){
|
if (file.mime.split("/")[0] === "image") {
|
||||||
console.log('the file is image');
|
console.log("the file is image");
|
||||||
this.$el.querySelector('.preview').style.visibility = 'inherit';
|
this.$el.querySelector(".preview").style.visibility = "inherit";
|
||||||
this.$el.querySelector("#img-preview").src = url;
|
this.$el.querySelector("#img-preview").src = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$el.querySelector('#fileName').textContent = file.name;
|
this.$el.querySelector("#fileName").textContent = file.name;
|
||||||
this.$el.querySelector('#fileSize').textContent = this._formatFileSize(file.size);
|
this.$el.querySelector("#fileSize").textContent = this._formatFileSize(
|
||||||
|
file.size
|
||||||
|
);
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
if (window.isDownloadSupported) return;
|
if (window.isDownloadSupported) return;
|
||||||
// fallback for iOS
|
// fallback for iOS
|
||||||
$a.target = '_blank';
|
$a.target = "_blank";
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = e => $a.href = reader.result;
|
reader.onload = (e) => ($a.href = reader.result);
|
||||||
reader.readAsDataURL(file.blob);
|
reader.readAsDataURL(file.blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
_formatFileSize(bytes) {
|
_formatFileSize(bytes) {
|
||||||
if (bytes >= 1e9) {
|
if (bytes >= 1e9) {
|
||||||
return (Math.round(bytes / 1e8) / 10) + ' GB';
|
return Math.round(bytes / 1e8) / 10 + " GB";
|
||||||
} else if (bytes >= 1e6) {
|
} else if (bytes >= 1e6) {
|
||||||
return (Math.round(bytes / 1e5) / 10) + ' MB';
|
return Math.round(bytes / 1e5) / 10 + " MB";
|
||||||
} else if (bytes > 1000) {
|
} else if (bytes > 1000) {
|
||||||
return Math.round(bytes / 1000) + ' KB';
|
return Math.round(bytes / 1000) + " KB";
|
||||||
} else {
|
} else {
|
||||||
return bytes + ' Bytes';
|
return bytes + " Bytes";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.$el.querySelector('.preview').style.visibility = 'hidden';
|
this.$el.querySelector(".preview").style.visibility = "hidden";
|
||||||
this.$el.querySelector("#img-preview").src = "";
|
this.$el.querySelector("#img-preview").src = "";
|
||||||
super.hide();
|
super.hide();
|
||||||
this._dequeueFile();
|
this._dequeueFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_autoDownload() {
|
||||||
_autoDownload(){
|
return !this.$el.querySelector("#autoDownload").checked;
|
||||||
return !this.$el.querySelector('#autoDownload').checked
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class SendTextDialog extends Dialog {
|
class SendTextDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('sendTextDialog');
|
super("sendTextDialog");
|
||||||
Events.on('text-recipient', e => this._onRecipient(e.detail))
|
Events.on("text-recipient", (e) => this._onRecipient(e.detail));
|
||||||
this.$text = this.$el.querySelector('#textInput');
|
this.$text = this.$el.querySelector("#textInput");
|
||||||
const button = this.$el.querySelector('form');
|
const button = this.$el.querySelector("form");
|
||||||
button.addEventListener('submit', e => this._send(e));
|
button.addEventListener("submit", (e) => this._send(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRecipient(recipient) {
|
_onRecipient(recipient) {
|
||||||
|
@ -330,40 +338,39 @@ class SendTextDialog extends Dialog {
|
||||||
range.selectNodeContents(this.$text);
|
range.selectNodeContents(this.$text);
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
sel.addRange(range);
|
sel.addRange(range);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleShareTargetText() {
|
_handleShareTargetText() {
|
||||||
if (!window.shareTargetText) return;
|
if (!window.shareTargetText) return;
|
||||||
this.$text.textContent = window.shareTargetText;
|
this.$text.textContent = window.shareTargetText;
|
||||||
window.shareTargetText = '';
|
window.shareTargetText = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_send(e) {
|
_send(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Events.fire('send-text', {
|
Events.fire("send-text", {
|
||||||
to: this._recipient,
|
to: this._recipient,
|
||||||
text: this.$text.innerText
|
text: this.$text.innerText,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReceiveTextDialog extends Dialog {
|
class ReceiveTextDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveTextDialog');
|
super("receiveTextDialog");
|
||||||
Events.on('text-received', e => this._onText(e.detail))
|
Events.on("text-received", (e) => this._onText(e.detail));
|
||||||
this.$text = this.$el.querySelector('#text');
|
this.$text = this.$el.querySelector("#text");
|
||||||
const $copy = this.$el.querySelector('#copy');
|
const $copy = this.$el.querySelector("#copy");
|
||||||
copy.addEventListener('click', _ => this._onCopy());
|
copy.addEventListener("click", (_) => this._onCopy());
|
||||||
}
|
}
|
||||||
|
|
||||||
_onText(e) {
|
_onText(e) {
|
||||||
this.$text.innerHTML = '';
|
this.$text.innerHTML = "";
|
||||||
const text = e.text;
|
const text = e.text;
|
||||||
if (isURL(text)) {
|
if (isURL(text)) {
|
||||||
const $a = document.createElement('a');
|
const $a = document.createElement("a");
|
||||||
$a.href = text;
|
$a.href = text;
|
||||||
$a.target = '_blank';
|
$a.target = "_blank";
|
||||||
$a.textContent = text;
|
$a.textContent = text;
|
||||||
this.$text.appendChild($a);
|
this.$text.appendChild($a);
|
||||||
} else {
|
} else {
|
||||||
|
@ -375,56 +382,56 @@ class ReceiveTextDialog extends Dialog {
|
||||||
|
|
||||||
async _onCopy() {
|
async _onCopy() {
|
||||||
await navigator.clipboard.writeText(this.$text.textContent);
|
await navigator.clipboard.writeText(this.$text.textContent);
|
||||||
Events.fire('notify-user', 'Copied to clipboard');
|
Events.fire("notify-user", "Copied to clipboard");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Toast extends Dialog {
|
class Toast extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('toast');
|
super("toast");
|
||||||
Events.on('notify-user', e => this._onNotfiy(e.detail));
|
Events.on("notify-user", (e) => this._onNotfiy(e.detail));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onNotfiy(message) {
|
_onNotfiy(message) {
|
||||||
this.$el.textContent = message;
|
this.$el.textContent = message;
|
||||||
this.show();
|
this.show();
|
||||||
setTimeout(_ => this.hide(), 3000);
|
setTimeout((_) => this.hide(), 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Notifications {
|
class Notifications {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Check if the browser supports notifications
|
// Check if the browser supports notifications
|
||||||
if (!('Notification' in window)) return;
|
if (!("Notification" in window)) return;
|
||||||
|
|
||||||
// Check whether notification permissions have already been granted
|
// Check whether notification permissions have already been granted
|
||||||
if (Notification.permission !== 'granted') {
|
if (Notification.permission !== "granted") {
|
||||||
this.$button = $('notification');
|
this.$button = $("notification");
|
||||||
this.$button.removeAttribute('hidden');
|
this.$button.removeAttribute("hidden");
|
||||||
this.$button.addEventListener('click', e => this._requestPermission());
|
this.$button.addEventListener("click", (e) => this._requestPermission());
|
||||||
}
|
}
|
||||||
Events.on('text-received', e => this._messageNotification(e.detail.text));
|
Events.on("text-received", (e) => this._messageNotification(e.detail.text));
|
||||||
Events.on('file-received', e => this._downloadNotification(e.detail.name));
|
Events.on("file-received", (e) =>
|
||||||
|
this._downloadNotification(e.detail.name)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestPermission() {
|
_requestPermission() {
|
||||||
Notification.requestPermission(permission => {
|
Notification.requestPermission((permission) => {
|
||||||
if (permission !== 'granted') {
|
if (permission !== "granted") {
|
||||||
Events.fire('notify-user', Notifications.PERMISSION_ERROR || 'Error');
|
Events.fire("notify-user", Notifications.PERMISSION_ERROR || "Error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._notify('Even more snappy sharing!');
|
this._notify("Even more snappy sharing!");
|
||||||
this.$button.setAttribute('hidden', 1);
|
this.$button.setAttribute("hidden", 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_notify(message, body, closeTimeout = 20000) {
|
_notify(message, body, closeTimeout = 20000) {
|
||||||
const config = {
|
const config = {
|
||||||
body: body,
|
body: body,
|
||||||
icon: '/images/logo_transparent_128x128.png',
|
icon: "/images/logo_transparent_128x128.png",
|
||||||
}
|
};
|
||||||
let notification;
|
let notification;
|
||||||
try {
|
try {
|
||||||
notification = new Notification(message, config);
|
notification = new Notification(message, config);
|
||||||
|
@ -436,7 +443,7 @@ class Notifications {
|
||||||
|
|
||||||
// Notification is persistent on Android. We have to close it manually
|
// Notification is persistent on Android. We have to close it manually
|
||||||
if (closeTimeout) {
|
if (closeTimeout) {
|
||||||
setTimeout(_ => notification.close(), closeTimeout);
|
setTimeout((_) => notification.close(), closeTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
|
@ -444,86 +451,91 @@ class Notifications {
|
||||||
|
|
||||||
_messageNotification(message) {
|
_messageNotification(message) {
|
||||||
if (isURL(message)) {
|
if (isURL(message)) {
|
||||||
const notification = this._notify(message, 'Click to open link');
|
const notification = this._notify(message, "Click to open link");
|
||||||
this._bind(notification, e => window.open(message, '_blank', null, true));
|
this._bind(notification, (e) =>
|
||||||
|
window.open(message, "_blank", null, true)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const notification = this._notify(message, 'Click to copy text');
|
const notification = this._notify(message, "Click to copy text");
|
||||||
this._bind(notification, e => this._copyText(message, notification));
|
this._bind(notification, (e) => this._copyText(message, notification));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_downloadNotification(message) {
|
_downloadNotification(message) {
|
||||||
const notification = this._notify(message, 'Click to download');
|
const notification = this._notify(message, "Click to download");
|
||||||
if (!window.isDownloadSupported) return;
|
if (!window.isDownloadSupported) return;
|
||||||
this._bind(notification, e => this._download(notification));
|
this._bind(notification, (e) => this._download(notification));
|
||||||
}
|
}
|
||||||
|
|
||||||
_download(notification) {
|
_download(notification) {
|
||||||
document.querySelector('x-dialog [download]').click();
|
document.querySelector("x-dialog [download]").click();
|
||||||
notification.close();
|
notification.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_copyText(message, notification) {
|
_copyText(message, notification) {
|
||||||
notification.close();
|
notification.close();
|
||||||
if (!navigator.clipboard.writeText(message)) return;
|
if (!navigator.clipboard.writeText(message)) return;
|
||||||
this._notify('Copied text to clipboard');
|
this._notify("Copied text to clipboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
_bind(notification, handler) {
|
_bind(notification, handler) {
|
||||||
if (notification.then) {
|
if (notification.then) {
|
||||||
notification.then(e => serviceWorker.getNotifications().then(notifications => {
|
notification.then((e) =>
|
||||||
serviceWorker.addEventListener('notificationclick', handler);
|
serviceWorker.getNotifications().then((notifications) => {
|
||||||
}));
|
serviceWorker.addEventListener("notificationclick", handler);
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
notification.onclick = handler;
|
notification.onclick = handler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NetworkStatusUI {
|
class NetworkStatusUI {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
window.addEventListener('offline', e => this._showOfflineMessage(), false);
|
window.addEventListener(
|
||||||
window.addEventListener('online', e => this._showOnlineMessage(), false);
|
"offline",
|
||||||
|
(e) => this._showOfflineMessage(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
window.addEventListener("online", (e) => this._showOnlineMessage(), false);
|
||||||
if (!navigator.onLine) this._showOfflineMessage();
|
if (!navigator.onLine) this._showOfflineMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
_showOfflineMessage() {
|
_showOfflineMessage() {
|
||||||
Events.fire('notify-user', 'You are offline');
|
Events.fire("notify-user", "You are offline");
|
||||||
}
|
}
|
||||||
|
|
||||||
_showOnlineMessage() {
|
_showOnlineMessage() {
|
||||||
Events.fire('notify-user', 'You are back online');
|
Events.fire("notify-user", "You are back online");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebShareTargetUI {
|
class WebShareTargetUI {
|
||||||
constructor() {
|
constructor() {
|
||||||
const parsedUrl = new URL(window.location);
|
const parsedUrl = new URL(window.location);
|
||||||
const title = parsedUrl.searchParams.get('title');
|
const title = parsedUrl.searchParams.get("title");
|
||||||
const text = parsedUrl.searchParams.get('text');
|
const text = parsedUrl.searchParams.get("text");
|
||||||
const url = parsedUrl.searchParams.get('url');
|
const url = parsedUrl.searchParams.get("url");
|
||||||
|
|
||||||
let shareTargetText = title ? title : '';
|
let shareTargetText = title ? title : "";
|
||||||
shareTargetText += text ? shareTargetText ? ' ' + text : text : '';
|
shareTargetText += text ? (shareTargetText ? " " + text : text) : "";
|
||||||
|
|
||||||
if(url) shareTargetText = url; // We share only the Link - no text. Because link-only text becomes clickable.
|
if (url) shareTargetText = url; // We share only the Link - no text. Because link-only text becomes clickable.
|
||||||
|
|
||||||
if (!shareTargetText) return;
|
if (!shareTargetText) return;
|
||||||
window.shareTargetText = shareTargetText;
|
window.shareTargetText = shareTargetText;
|
||||||
history.pushState({}, 'URL Rewrite', '/');
|
history.pushState({}, "URL Rewrite", "/");
|
||||||
console.log('Shared Target Text:', '"' + shareTargetText + '"');
|
console.log("Shared Target Text:", '"' + shareTargetText + '"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Snapdrop {
|
class Snapdrop {
|
||||||
constructor() {
|
constructor() {
|
||||||
const server = new ServerConnection();
|
const server = new ServerConnection();
|
||||||
const peers = new PeersManager(server);
|
const peers = new PeersManager(server);
|
||||||
const peersUI = new PeersUI();
|
const peersUI = new PeersUI();
|
||||||
Events.on('load', e => {
|
Events.on("load", (e) => {
|
||||||
const receiveDialog = new ReceiveDialog();
|
const receiveDialog = new ReceiveDialog();
|
||||||
const sendTextDialog = new SendTextDialog();
|
const sendTextDialog = new SendTextDialog();
|
||||||
const receiveTextDialog = new ReceiveTextDialog();
|
const receiveTextDialog = new ReceiveTextDialog();
|
||||||
|
@ -537,39 +549,38 @@ class Snapdrop {
|
||||||
|
|
||||||
const snapdrop = new Snapdrop();
|
const snapdrop = new Snapdrop();
|
||||||
|
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
navigator.serviceWorker
|
||||||
if ('serviceWorker' in navigator) {
|
.register("/service-worker.js")
|
||||||
navigator.serviceWorker.register('/service-worker.js')
|
.then((serviceWorker) => {
|
||||||
.then(serviceWorker => {
|
console.log("Service Worker registered");
|
||||||
console.log('Service Worker registered');
|
window.serviceWorker = serviceWorker;
|
||||||
window.serviceWorker = serviceWorker
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('beforeinstallprompt', e => {
|
window.addEventListener("beforeinstallprompt", (e) => {
|
||||||
if (window.matchMedia('(display-mode: standalone)').matches) {
|
if (window.matchMedia("(display-mode: standalone)").matches) {
|
||||||
// don't display install banner when installed
|
// don't display install banner when installed
|
||||||
return e.preventDefault();
|
return e.preventDefault();
|
||||||
} else {
|
} else {
|
||||||
const btn = document.querySelector('#install')
|
const btn = document.querySelector("#install");
|
||||||
btn.hidden = false;
|
btn.hidden = false;
|
||||||
btn.onclick = _ => e.prompt();
|
btn.onclick = (_) => e.prompt();
|
||||||
return e.preventDefault();
|
return e.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Background Animation
|
// Background Animation
|
||||||
Events.on('load', () => {
|
Events.on("load", () => {
|
||||||
let c = document.createElement('canvas');
|
let c = document.createElement("canvas");
|
||||||
document.body.appendChild(c);
|
document.body.appendChild(c);
|
||||||
let style = c.style;
|
let style = c.style;
|
||||||
style.width = '100%';
|
style.width = "100%";
|
||||||
style.position = 'absolute';
|
style.position = "absolute";
|
||||||
style.zIndex = -1;
|
style.zIndex = -1;
|
||||||
style.top = 0;
|
style.top = 0;
|
||||||
style.left = 0;
|
style.left = 0;
|
||||||
let ctx = c.getContext('2d');
|
let ctx = c.getContext("2d");
|
||||||
let x0, y0, w, h, dw;
|
let x0, y0, w, h, dw;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
@ -589,7 +600,7 @@ Events.on('load', () => {
|
||||||
function drawCircle(radius) {
|
function drawCircle(radius) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
let color = Math.round(255 * (1 - radius / Math.max(w, h)));
|
let color = Math.round(255 * (1 - radius / Math.max(w, h)));
|
||||||
ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
|
ctx.strokeStyle = "rgba(" + color + "," + color + "," + color + ",0.1)";
|
||||||
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
|
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
|
@ -600,7 +611,7 @@ Events.on('load', () => {
|
||||||
function drawCircles() {
|
function drawCircles() {
|
||||||
ctx.clearRect(0, 0, w, h);
|
ctx.clearRect(0, 0, w, h);
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
drawCircle(dw * i + step % dw);
|
drawCircle(dw * i + (step % dw));
|
||||||
}
|
}
|
||||||
step += 1;
|
step += 1;
|
||||||
}
|
}
|
||||||
|
@ -609,13 +620,13 @@ Events.on('load', () => {
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
if (loading || step % dw < dw - 5) {
|
if (loading || step % dw < dw - 5) {
|
||||||
requestAnimationFrame(function() {
|
requestAnimationFrame(function () {
|
||||||
drawCircles();
|
drawCircles();
|
||||||
animate();
|
animate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.animateBackground = function(l) {
|
window.animateBackground = function (l) {
|
||||||
loading = l;
|
loading = l;
|
||||||
animate();
|
animate();
|
||||||
};
|
};
|
||||||
|
@ -629,8 +640,9 @@ as the user has dismissed the permission prompt several times.
|
||||||
This can be reset in Page Info
|
This can be reset in Page Info
|
||||||
which can be accessed by clicking the lock icon next to the URL.`;
|
which can be accessed by clicking the lock icon next to the URL.`;
|
||||||
|
|
||||||
document.body.onclick = e => { // safari hack to fix audio
|
document.body.onclick = (e) => {
|
||||||
|
// safari hack to fix audio
|
||||||
document.body.onclick = null;
|
document.body.onclick = null;
|
||||||
if (!(/.*Version.*Safari.*/.test(navigator.userAgent))) return;
|
if (!/.*Version.*Safari.*/.test(navigator.userAgent)) return;
|
||||||
blop.play();
|
blop.play();
|
||||||
}
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue