Support clipboard upload on home page

Add shortcuts for all the buttons
Add abuse reporter page
Fix user buckets page
Add a-ads advertisement
This commit is contained in:
2021-02-16 19:13:15 +01:00
parent 443f8c1af5
commit 346fa355c4
14 changed files with 605 additions and 376 deletions

View File

@@ -1,4 +1,4 @@
function UploadProgressBar(uploadManager, queueDiv, file){
function UploadProgressBar(uploadManager, queueDiv, file) {
this.uploadManager = uploadManager
this.file = file
this.name = file.name
@@ -14,38 +14,38 @@ function UploadProgressBar(uploadManager, queueDiv, file){
this.file,
this.name,
(progress) => { this.onProgress(progress) },
(id) => { this.onFinished(id) },
(id) => { this.onFinished(id) },
(val, msg) => { this.onFailure(val, msg) }
)
// Browsers don't render the transition if the opacity is set and
// updated in the same frame. So we have to wait a frame (or more)
// before changing the opacity to make sure the transition triggers
window.setTimeout(() => {this.uploadDiv.style.opacity = "1"}, 100)
window.setTimeout(() => { this.uploadDiv.style.opacity = "1" }, 100)
}
UploadProgressBar.prototype.onProgress = function(progress){
this.uploadDiv.innerText = "Uploading... " + Math.round(progress*1000)/10 + "%\n" + this.name
UploadProgressBar.prototype.onProgress = function (progress) {
this.uploadDiv.innerText = "Uploading... " + Math.round(progress * 1000) / 10 + "%\n" + this.name
this.uploadDiv.style.background = 'linear-gradient('
+'to right, '
+'var(--layer_3_color) 0%, '
+'var(--highlight_color) '+ ((progress*100)) +'%, '
+'var(--layer_3_color) '+ ((progress*100)+1) +'%)'
+ 'to right, '
+ 'var(--layer_3_color) 0%, '
+ 'var(--highlight_color) ' + ((progress * 100)) + '%, '
+ 'var(--layer_3_color) ' + ((progress * 100) + 1) + '%)'
}
UploadProgressBar.prototype.onFinished = function(id){
console.log("Upload finished: "+this.file.name+" "+id)
UploadProgressBar.prototype.onFinished = function (id) {
console.log("Upload finished: " + this.file.name + " " + id)
this.uploadDiv.style.background = 'var(--layer_3_color)'
this.uploadDiv.href = '/u/'+id
this.uploadDiv.target= "_blank"
this.uploadDiv.href = '/u/' + id
this.uploadDiv.target = "_blank"
let fileImg = document.createElement("img")
fileImg.src = apiEndpoint+'/file/'+id+'/thumbnail'
fileImg.src = apiEndpoint + '/file/' + id + '/thumbnail'
fileImg.alt = this.file.name
let linkSpan = document.createElement("span")
linkSpan.classList = "file_button_title"
linkSpan.innerText = domainURL()+"/u/"+id
linkSpan.innerText = domainURL() + "/u/" + id
this.uploadDiv.innerHTML = "" // Remove uploading progress
this.uploadDiv.appendChild(fileImg)
@@ -53,7 +53,7 @@ UploadProgressBar.prototype.onFinished = function(id){
this.uploadDiv.appendChild(document.createElement("br"))
this.uploadDiv.appendChild(linkSpan)
}
UploadProgressBar.prototype.onFailure = function(val, msg) {
UploadProgressBar.prototype.onFailure = function (val, msg) {
if (val === "") {
val = "Could not connect to server"
}
@@ -62,22 +62,18 @@ UploadProgressBar.prototype.onFailure = function(val, msg) {
this.uploadDiv.style.background = 'var(--danger_color)'
this.uploadDiv.style.color = 'var(--highlight_text_color)'
this.uploadDiv.appendChild(document.createTextNode("Upload failed: "))
this.uploadDiv.appendChild(document.createTextNode(msg+" ("+val+")"))
this.uploadDiv.appendChild(document.createTextNode(msg + " (" + val + ")"))
this.uploadDiv.appendChild(document.createElement("br"))
this.uploadDiv.appendChild(document.createTextNode(this.file.name))
console.log(msg)
}
let uploader = null
let uploader = new UploadManager(apiEndpoint + "/file", uploadsFinished)
let shareTitle = ""
let shareLink = ""
let shareLink = ""
function handleUploads(files) {
if (uploader === null){
uploader = new UploadManager(apiEndpoint+"/file", uploadsFinished)
}
if (files.length === 0) {
return
}
@@ -101,8 +97,8 @@ function uploadsFinished() {
let uploadLog = uploader.finishedUploads()
if (uploadLog.length === 1) {
shareTitle = "Download "+uploadLog[0].fileName+" here"
shareLink = domainURL()+"/u/"+uploadLog[0].fileID
shareTitle = "Download " + uploadLog[0].fileName + " here"
shareLink = domainURL() + "/u/" + uploadLog[0].fileID
showShareButtons()
} else if (uploadLog.length > 1) {
@@ -111,13 +107,13 @@ function uploadsFinished() {
createList(
title, true,
).then(resp => {
console.log("Automatic list ID "+resp.id)
shareTitle = "View "+title+" here"
shareLink = domainURL()+"/l/"+resp.id
console.log("Automatic list ID " + resp.id)
shareTitle = "View " + title + " here"
shareLink = domainURL() + "/l/" + resp.id
showShareButtons()
}).catch(err => {
alert("Failed to generate link. Please check your internet connection and try again.\nError: "+err)
alert("Failed to generate link. Please check your internet connection and try again.\nError: " + err)
})
}
}
@@ -130,10 +126,10 @@ function createList(title, anonymous) {
}
return fetch(
apiEndpoint+"/list",
apiEndpoint + "/list",
{
method: "POST",
headers: {"Content-Type": "application/json; charset=UTF-8"},
headers: { "Content-Type": "application/json; charset=UTF-8" },
body: JSON.stringify({
"title": title,
"anonymous": anonymous,
@@ -142,7 +138,7 @@ function createList(title, anonymous) {
}
).then(resp => {
if (!resp.ok) {
return Promise.reject("HTTP error: "+resp.status)
return Promise.reject("HTTP error: " + resp.status)
}
return resp.json()
})
@@ -163,7 +159,7 @@ function showShareButtons() {
}
function copyLink() {
if(copyText(shareLink)) {
if (copyText(shareLink)) {
console.log('Text copied')
document.querySelector("#btn_copy_link>span").textContent = "Copied!"
document.getElementById("btn_copy_link").classList.add("button_highlight")
@@ -177,35 +173,47 @@ function copyLink() {
* Upload Handlers
*/
// Relay click event to hidden file field
document.getElementById("upload_file_button").onclick = function() {
// Relay click event to hidden file field
document.getElementById("upload_file_button").onclick = () => {
document.getElementById("file_input_field").click()
}
document.getElementById("file_input_field").onchange = function(evt){
document.getElementById("file_input_field").onchange = e => {
// Start uploading the files async
window.setTimeout(handleUploads(evt.target.files), 1)
window.setTimeout(handleUploads(e.target.files), 1)
// This resets the file input field
document.getElementById("file_input_field").nodeValue = ""
}
document.getElementById("upload_text_button").onclick = function() {
window.location.href = '/t/'
document.getElementById("upload_text_button").onclick = () => {
window.location.href = '/t'
}
/*
* Drag 'n Drop upload handlers
*/
document.ondragover = function(e) {
document.addEventListener("dragover", e => {
document.getElementById("file_drop_highlight").style.display = ""
e.preventDefault()
e.stopPropagation()
}
document.ondragenter = function(e) {
})
document.addEventListener("dragenter", e => {
document.getElementById("file_drop_highlight").style.display = ""
e.preventDefault()
e.stopPropagation()
}
document.addEventListener('drop', function(e){
})
document.addEventListener("dragleave", e => {
document.getElementById("file_drop_highlight").style.display = "none"
e.preventDefault()
e.stopPropagation()
})
document.addEventListener("drop", e => {
document.getElementById("file_drop_highlight").style.display = "none"
if (e.dataTransfer && e.dataTransfer.files.length > 0) {
e.preventDefault()
e.stopPropagation()
@@ -214,37 +222,41 @@ document.addEventListener('drop', function(e){
window.setTimeout(handleUploads(e.dataTransfer.files), 1)
}
})
document.addEventListener("paste", e => {
if (e.clipboardData.files[0]) {
e.preventDefault();
e.stopPropagation();
window.setTimeout(handleUploads(e.clipboardData.files), 1)
}
})
/*
* Share buttons
*/
document.getElementById("btn_social_share").addEventListener("click", function() {
window.navigator.share({
title: "Pixeldrain",
text: shareTitle,
url: shareLink
})
document.getElementById("btn_social_share").addEventListener("click", () => {
window.navigator.share({ title: "Pixeldrain", text: shareTitle, url: shareLink })
})
document.getElementById("btn_copy_link").addEventListener("click", function() {
document.getElementById("btn_copy_link").addEventListener("click", () => {
copyLink()
})
document.getElementById("btn_open_link").addEventListener("click", function() {
document.getElementById("btn_open_link").addEventListener("click", () => {
window.open(shareLink, '_blank')
})
document.getElementById("btn_social_email").addEventListener("click", function() {
document.getElementById("btn_social_email").addEventListener("click", () => {
window.open('mailto:please@set.address?subject=File%20on%20pixeldrain&body=' + shareLink)
})
document.getElementById("btn_social_twitter").addEventListener("click", function() {
document.getElementById("btn_social_twitter").addEventListener("click", () => {
window.open('https://twitter.com/share?url=' + shareLink)
})
document.getElementById("btn_social_facebook").addEventListener("click", function() {
document.getElementById("btn_social_facebook").addEventListener("click", () => {
window.open('http://www.facebook.com/sharer.php?u=' + shareLink)
})
document.getElementById("btn_social_reddit").addEventListener("click", function() {
document.getElementById("btn_social_reddit").addEventListener("click", () => {
window.open('https://www.reddit.com/submit?url=' + shareLink)
})
document.getElementById("btc_social_tumblr").addEventListener("click", function() {
document.getElementById("btn_social_tumblr").addEventListener("click", () => {
window.open('http://www.tumblr.com/share/link?url=' + shareLink)
})
@@ -253,18 +265,18 @@ document.getElementById("btc_social_tumblr").addEventListener("click", function(
*/
function renderListButton(apiURL, id, title, subtitle) {
let btn = document.createElement("a")
btn.classList = "file_button"
btn.href = "/l/"+id
btn.target = "_blank"
let thumbnail = document.createElement("img")
thumbnail.src = apiURL+"/list/"+id+"/thumbnail?width=80&height=80"
thumbnail.alt = title
let titleSpan = document.createElement("span")
titleSpan.classList = "file_button_title"
titleSpan.innerText = title
let br = document.createElement("br")
let subtitleSpan = document.createElement("span")
let btn = document.createElement("a")
btn.classList = "file_button"
btn.href = "/l/" + id
btn.target = "_blank"
let thumbnail = document.createElement("img")
thumbnail.src = apiURL + "/list/" + id + "/thumbnail?width=80&height=80"
thumbnail.alt = title
let titleSpan = document.createElement("span")
titleSpan.classList = "file_button_title"
titleSpan.innerText = title
let br = document.createElement("br")
let subtitleSpan = document.createElement("span")
subtitleSpan.classList = "file_button_subtitle"
subtitleSpan.innerText = subtitle
@@ -276,12 +288,12 @@ function renderListButton(apiURL, id, title, subtitle) {
}
// Create list button
document.getElementById("btn_create_list").addEventListener("click", function(evt) {
document.getElementById("btn_create_list").addEventListener("click", function (evt) {
let title = prompt(
"You are creating a list containing " + uploader.finishedUploads().length + " files.\n"
+ "What do you want to call it?", "My New Album"
)
if(title === null){
if (title === null) {
return
}
createList(title, false).then(resp => {
@@ -289,11 +301,11 @@ document.getElementById("btn_create_list").addEventListener("click", function(ev
renderListButton(
apiEndpoint,
resp.id,
domainURL()+'/l/'+resp.id,
domainURL() + '/l/' + resp.id,
"List creation finished!",
)
)
window.open('/l/'+resp.id, '_blank')
window.open('/l/' + resp.id, '_blank')
}).catch(err => {
let div = document.createElement("div")
div.className = "file_button"
@@ -305,55 +317,55 @@ document.getElementById("btn_create_list").addEventListener("click", function(ev
})
let btnCopyLinks = document.getElementById("btn_copy_links")
btnCopyLinks.addEventListener("click", function(){
btnCopyLinks.addEventListener("click", function () {
let text = ""
let uploads = uploader.finishedUploads()
// Add the text to the textarea
for (let i = 0; i < uploads.length; i++) {
// Example: https://pixeldrain.com/u/abcd1234: Some_file.png
text += domainURL()+"/u/"+uploads[i].fileID+" "+uploads[i].fileName+"\n"
text += domainURL() + "/u/" + uploads[i].fileID + " " + uploads[i].fileName + "\n"
}
if (shareLink.includes("/l/")) {
text += "\n"+shareLink+" All "+uploads.length+" files\n"
text += "\n" + shareLink + " All " + uploads.length + " files\n"
}
// Copy the selected text
if(copyText(text)){
if (copyText(text)) {
btnCopyLinks.classList.add("button_highlight")
btnCopyLinks.innerHTML = "Links copied to clipboard!"
}else{
} else {
btnCopyLinks.classList.add("button_red")
btnCopyLinks.innerHTML = "Copying links failed"
}
})
let btnCopyBBCode = document.getElementById("btn_copy_bbcode")
btnCopyBBCode.addEventListener("click", function(){
btnCopyBBCode.addEventListener("click", function () {
let text = ""
let uploads = uploader.finishedUploads()
// Add the text to the textarea
for (let i = 0; i < uploads.length; i++) {
// Example: [url=https://pixeldrain.com/u/abcd1234]Some_file.png[/url]
text += "[url="+domainURL()+"/u/"+uploads[i].fileID+"]"+uploads[i].fileName+"[/url]\n"
text += "[url=" + domainURL() + "/u/" + uploads[i].fileID + "]" + uploads[i].fileName + "[/url]\n"
}
if (shareLink.includes("/l/")) {
text += "\n[url="+shareLink+"]All "+uploads.length+" files[/url]\n"
text += "\n[url=" + shareLink + "]All " + uploads.length + " files[/url]\n"
}
// Copy the selected text
if(copyText(text)){
if (copyText(text)) {
btnCopyBBCode.classList.add("button_highlight")
btnCopyBBCode.innerHTML = "BBCode copied to clipboard!"
}else{
} else {
btnCopyBBCode.classList.add("button_red")
btnCopyBBCode.innerHTML = "Copying links failed"
}
})
let btnCopyMarkdown = document.getElementById("btn_copy_markdown")
btnCopyMarkdown.addEventListener("click", function(){
btnCopyMarkdown.addEventListener("click", function () {
let text = ""
let uploads = uploader.finishedUploads()
@@ -362,17 +374,17 @@ btnCopyMarkdown.addEventListener("click", function(){
// Example: * [Some_file.png](https://pixeldrain.com/u/abcd1234)
if (uploads.length > 1) { text += " * " }
text += "["+uploads[i].fileName+"]("+domainURL()+"/u/"+uploads[i].fileID+")\n"
text += "[" + uploads[i].fileName + "](" + domainURL() + "/u/" + uploads[i].fileID + ")\n"
}
if (shareLink.includes("/l/")) {
text += " * [All "+uploads.length+" files]("+shareLink+")\n"
text += " * [All " + uploads.length + " files](" + shareLink + ")\n"
}
// Copy the selected text
if(copyText(text)){
if (copyText(text)) {
btnCopyMarkdown.classList.add("button_highlight")
btnCopyMarkdown.innerHTML = "Markdown copied to clipboard!"
}else{
} else {
btnCopyMarkdown.classList.add("button_red")
btnCopyMarkdown.innerHTML = "Copying links failed"
}
@@ -382,19 +394,35 @@ btnCopyMarkdown.addEventListener("click", function(){
/*
* Keyboard shortcuts
*/
document.addEventListener("keydown", function(event){
if (event.ctrlKey || event.altKey || event.metaKey) {
document.addEventListener("keydown", e => {
if (e.ctrlKey || e.altKey || e.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts
}
if (event.keyCode === 67) { // c
// Copy links to clipboard
if (e.key === "c") {
document.getElementById("btn_copy_link").click()
} else if (event.keyCode === 85) { // u
// Click the upload button
} else if (e.key === "u") {
document.getElementById("file_input_field").click()
} else if (event.keyCode === 84) { // t
// Click the text button
} else if (e.key === "t") {
document.getElementById("upload_text_button").click()
} else if (e.key === "o") {
document.getElementById("btn_open_link").click()
} else if (e.key === "l") {
document.getElementById("btn_create_list").click()
} else if (e.key === "e") {
document.getElementById("btn_social_email").click()
} else if (e.key === "w") {
document.getElementById("btn_social_twitter").click()
} else if (e.key === "f") {
document.getElementById("btn_social_facebook").click()
} else if (e.key === "r") {
document.getElementById("btn_social_reddit").click()
} else if (e.key === "m") {
document.getElementById("btn_social_tumblr").click()
} else if (e.key === "a") {
document.getElementById("btn_copy_links").click()
} else if (e.key === "d") {
document.getElementById("btn_copy_markdown").click()
} else if (e.key === "b") {
document.getElementById("btn_copy_bbcode").click()
}
console.log(event.keyCode)
})

View File

@@ -10,8 +10,23 @@
<div class="page_content">
{{if and .Authenticated .User.IsAdmin}}
<div class="limit_width">
<a class="button" href="/admin/globals">Update global settings</a>
<a class="button" href="/admin/abuse">Block files</a>
<h3>Actions</h3>
</div>
<br/>
<a class="button" href="/admin/abuse">
<i class="icon">block</i>
Block files
</a>
<a class="button" href="/admin/abuse_reporters">
<i class="icon">report</i>
Manage abuse reporters
</a>
<a class="button" href="/admin/globals">
<i class="icon">edit</i>
Update global settings
</a>
<br/>
<div class="limit_width">
<h3>Bandwidth and views</h3>
</div>
<div class="highlight_dark">

View File

@@ -0,0 +1,19 @@
{{define "admin_abuse_reporters"}}<!DOCTYPE html>
<html lang="en">
<head>
{{template "meta_tags" "Abuse reporters"}}
{{template "user_style" .}}
<script>window.api_endpoint = '{{.APIEndpoint}}';</script>
<link rel='stylesheet' href='/res/svelte/admin_abuse_reporters.css'>
<script defer src='/res/svelte/admin_abuse_reporters.js'></script>
</head>
<body>
{{template "page_top" .}}
<h1>Abuse reporters</h1>
<div id="page_content" class="page_content"></div>
{{template "page_bottom" .}}
{{template "analytics"}}
</body>
</html>
{{end}}

View File

@@ -186,7 +186,15 @@
<a href="/click/7wy9gg2J?target=%2F%23pro" class="button button_highlight">Pixeldrain Pro: Only €2 per month</a>
</div>
</div>
{{end}}
{{ else }}
<!-- scrolling="no" is not allowed by the W3C, but overflow: hidden doesn't work in chrome, so I have no choice -->
<iframe class="sponsors_banner"
data-aa="73974"
src="//ad.a-ads.com/73974?size=728x90&background_color={{.Style.Layer1Color.RGB}}&text_color={{.Style.TextColor.RGB}}&title_color={{.Style.HighlightColor.RGB}}&title_hover_color={{.Style.HighlightColor.RGB}}&link_color={{.Style.HighlightColor.RGB}}&link_hover_color={{.Style.HighlightColor.RGB}}"
style="width:728px; height:90px; border:none; padding:0; overflow:hidden;"
scrolling="no">
</iframe>
{{ end }}
{{ else if not .Other.UserAdsEnabled }}
<div style="text-align: center; line-height: 1.3em; font-size: 13px;">
Thank you for supporting pixeldrain!
@@ -295,7 +303,7 @@
{{ if eq .Other.AdType 5 }}
<!-- AdMaven -->
<script data-cfasync="false" async src="//d227cncaprzd7y.cloudfront.net/?acncd=905608"></script>
{{ else if eq .Other.AdType 7 }}
{{ else if or (eq .Other.AdType 7) (eq .Other.AdType 8) (eq .Other.AdType 9) (eq .Other.AdType 10) (eq .Other.AdType 11) }}
<!-- PropellerAds -->
<script>
// Load fires when the page is completely finished loading,

View File

@@ -125,63 +125,37 @@
<img class="header_image" src="/res/img/header_orbitron_wide.png" alt="Header image">
</picture>
<br/>
<div class="highlight_blue">
<div class="limit_width">
<p>
Hello! Due to
<a href="https://nypost.com/2021/02/14/hundreds-of-thousands-without-power-after-oregon-ice-storm/" target="_blank">an ice storm in Oregon</a>
the datacenter where pixeldrain is hosted in North America
was cut off from the internet and has lost power. This
caused an outage of a few hours last saturday (it happened
when I was out ice skating, so I didn't notice it until I
got back).
</p>
<p>
I am now redirecting all American (north and south) traffic
to the UK, the server there has more than enough capacity to
handle it all. Due to the long distance the website might
feel a bit slower.
</p>
<p>
Files which were uploaded to the server in Oregon shortly
before the outage will not be available until the server is
back up. Normally all files are backed up in Finland, but
this process takes a while. Due to the abruptness of the
outage a lot of files could not make it over. When the
server comes back online your files will be downloadable
again.
</p>
<p>
I will post updates <a href="https://twitter.com/Fornax96"
target="_blank">on Twitter</a> when the situation changes.
</p>
</div>
</div>
<div class="page_content" style="padding-top: 0; margin-bottom: 100px;">
<div id="instruction_1" class="instruction_highlight" style="margin-top: 0;"><div class="limit_width">
<span class="big_number">1</span>
<span class="instruction_text">Select files to upload</span>
<br/>
You can also drop files anywhere on this page from your file
manager
<br/> You can also drop files on this page from your file
manager or paste an image from your clipboard
</div></div>
<input id="file_input_field" type="file" name="file" multiple="multiple"/>
<button id="upload_file_button" class="big_button button_highlight">
<i class="icon small">cloud_upload</i>
<u>U</u>pload Files</button>
<u>U</u>pload Files
</button>
<button id="upload_text_button" class="big_button button_highlight">
<i class="icon small">text_fields</i>
Upload <u>T</u>ext</button>
Upload <u>T</u>ext
</button>
<br/>
<p>
By uploading files to pixeldrain you acknowledge and accept our
<a href="/about#content-policy">content policy</a>.
<p>
<div id="instruction_2" class="instruction_highlight">
<div class="limit_width">
<span class="big_number">2</span><span class="instruction_text">Wait for the files to finish uploading</span>
</div>
</div>
<div id="file_drop_highlight" class="highlight_green" style="display: none;">
Gimme gimme gimme!<br/>
Drop your files to upload them
</div>
<div id="uploads_queue"></div>
<div id="instruction_3" class="instruction_highlight">
@@ -197,30 +171,30 @@
{{template `copy.svg` .}}<br/><span><u>C</u>opy link</span>
</button>
<button id="btn_open_link" class="social_button" style="display: inline-block">
{{template `open_in_new.svg` .}}<br/><span>Open link</span>
{{template `open_in_new.svg` .}}<br/><span><u>O</u>pen link</span>
</button>
<div id="social_buttons" style="display: inline-block">
<button id="btn_social_email" class="social_button">
{{template `email.svg` .}}<br/>E-Mail
{{template `email.svg` .}}<br/><u>E</u>-Mail
</button>
<button id="btn_social_twitter" class="social_button">
{{template `twitter.svg` .}}<br/>Twitter
{{template `twitter.svg` .}}<br/>T<u>w</u>itter
</button>
<button id="btn_social_facebook" class="social_button">
{{template `facebook.svg` .}}<br/>Facebook
{{template `facebook.svg` .}}<br/><u>F</u>acebook
</button>
<button id="btn_social_reddit" class="social_button">
{{template `reddit.svg` .}}<br/>Reddit
{{template `reddit.svg` .}}<br/><u>R</u>eddit
</button>
<button id="btc_social_tumblr" class="social_button">
{{template `tumblr.svg` .}}<br/>Tumblr
<button id="btn_social_tumblr" class="social_button">
{{template `tumblr.svg` .}}<br/>Tu<u>m</u>blr
</button>
</div>
<br/><br/>
<button id="btn_create_list"><i class="icon">list</i> Create list with uploaded files</button>
<button id="btn_copy_links"><i class="icon">content_copy</i> Copy all links to clipboard</button>
<button id="btn_copy_markdown"><i class="icon">content_copy</i> Copy markdown to clipboard</button>
<button id="btn_copy_bbcode"><i class="icon">content_copy</i> Copy BBCode to clipboard</button>
<button id="btn_create_list"><i class="icon">list</i> Create <u>l</u>ist with uploaded files</button>
<button id="btn_copy_links"><i class="icon">content_copy</i> Copy <u>a</u>ll links to clipboard</button>
<button id="btn_copy_markdown"><i class="icon">content_copy</i> Copy mark<u>d</u>own to clipboard</button>
<button id="btn_copy_bbcode"><i class="icon">content_copy</i> Copy <u>B</u>BCode to clipboard</button>
<br/>
<div id="created_lists"></div>
</div>