Skip to content

Commit b862d6b

Browse files
authored
Merge pull request #650 from Sloth-on-meth/master
allow pasting images/videos in Web UI via clipboard
2 parents d0ed9c2 + 89adccf commit b862d6b

File tree

3 files changed

+45
-10
lines changed

3 files changed

+45
-10
lines changed

resources/lang/en.lang.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
'prerelease_channel' => 'Prerelease Channel',
105105
'no_upload_token' => 'You don\'t have a personal upload token. (Generate one and try again.)',
106106
'drop_to_upload' => 'Click or drop your files here to upload.',
107+
'paste_to_upload_hint' => 'Tip: You can paste images or videos to upload them.',
107108
'donation' => 'Donation',
108109
'donate_text' => 'If you like XBackBone, consider a donation to support development!',
109110
'custom_head_html' => 'Custom HTML Head content',

resources/templates/upload/web.twig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
<button type="button" class="btn btn-sm btn-outline-danger btn-block" onclick="Dropzone.instances[0].removeAllFiles(true)" data-toggle="tooltip" title="{{ lang('cancel') }}"><i class="fas fa-times"></i></button>
2222
</div>
2323
</div>
24+
<div class="row">
25+
<div class="col">
26+
<div class="text-muted small mb-2"><i class="fas fa-lightbulb"></i> {{ lang('paste_to_upload_hint') }}</div>
27+
</div>
28+
</div>
2429
<div class="row">
2530
<div class="col">
2631
<form action="{{ route('upload.web') }}" method="post" id="upload-dropzone" class="dropzone">

src/js/app.js

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,17 +258,46 @@ var app = {
258258
});
259259
},
260260
initClipboardPasteToUpload: function() {
261-
document.onpaste = function(event){
262-
if (event.clipboardData || event.originalEvent.clipboardData) {
263-
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
264-
items.forEach((item) => {
265-
if (item.kind === 'file') {
266-
// Add the file to the dropzone instance.
267-
Dropzone.forElement('.dropzone').addFile(item.getAsFile());
268-
}
269-
});
261+
// Attach a paste listener that uploads image/video files from the clipboard
262+
document.addEventListener('paste', function (event) {
263+
// Do not intercept paste if user is typing in an input/textarea/contenteditable
264+
var ae = document.activeElement;
265+
if (ae && ((ae.tagName && (ae.tagName.toLowerCase() === 'input' || ae.tagName.toLowerCase() === 'textarea')) || ae.isContentEditable)) {
266+
return;
267+
}
268+
269+
var clipboard = event.clipboardData || (event.originalEvent && event.originalEvent.clipboardData);
270+
if (!clipboard || !clipboard.items || clipboard.items.length === 0) {
271+
return;
272+
}
273+
274+
// Try to resolve the active Dropzone instance in a safe way
275+
var dz = null;
276+
try {
277+
dz = Dropzone.forElement('#upload-dropzone');
278+
} catch (e) {
279+
if (Dropzone && Dropzone.instances && Dropzone.instances.length > 0) {
280+
dz = Dropzone.instances[0];
281+
}
282+
}
283+
if (!dz) {
284+
return; // No Dropzone available; nothing to do
285+
}
286+
287+
// Iterate items in a cross-browser way (DataTransferItemList may not support forEach)
288+
for (var i = 0; i < clipboard.items.length; i++) {
289+
var item = clipboard.items[i];
290+
if (!item) continue;
291+
if (item.kind === 'file') {
292+
var file = item.getAsFile();
293+
if (!file) continue;
294+
// Only process images and videos to avoid surprising uploads
295+
if ((file.type && file.type.indexOf('image/') === 0) || (file.type && file.type.indexOf('video/') === 0)) {
296+
dz.addFile(file);
297+
}
298+
}
270299
}
271-
};
300+
});
272301
},
273302
};
274303

0 commit comments

Comments
 (0)