Remove old file viewer

This commit is contained in:
2021-11-02 10:23:22 +01:00
parent 0d78078f8e
commit 5d18840a2f
26 changed files with 141 additions and 2940 deletions

View File

@@ -1,65 +0,0 @@
Chart.defaults.global.defaultFontColor = "#b3b3b3";
Chart.defaults.global.defaultFontSize = 15;
Chart.defaults.global.defaultFontFamily = "system-ui, sans-serif";
Chart.defaults.global.maintainAspectRatio = false;
Chart.defaults.global.elements.point.radius = 0;
Chart.defaults.global.tooltips.mode = "index";
Chart.defaults.global.tooltips.axis = "x";
Chart.defaults.global.tooltips.intersect = false;
Chart.defaults.global.animation.duration = 500;
Chart.defaults.global.animation.easing = "linear";
function drawGraph(element, label, dataType) {
return new Chart(
element,
{
type: 'line',
data: {
datasets: [
{
label: label,
backgroundColor: "#" + window.style.highlightColor,
borderWidth: 0,
lineTension: 0,
fill: true,
yAxisID: "ax_1"
}
]
},
options: {
legend: { display: false },
scales: {
yAxes: [
{
type: "linear",
display: true,
position: "left",
id: "ax_1",
ticks: {
callback: function (value, index, values) {
if (dataType == "bytes") {
return formatDataVolume(value, 3);
}
return formatNumber(value, 3);
},
beginAtZero: true
},
gridLines: { display: true },
}
],
xAxes: [
{
ticks: {
sampleSize: 1,
padding: 4,
minRotation: 0,
maxRotation: 0
},
gridLines: { display: false }
}
]
}
}
}
);
}

View File

@@ -1,80 +0,0 @@
function AbuseReportWindow(viewer) {
this.viewer = viewer
this.visible = false
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"Report abuse", "650px", "auto",
)
this.btnReportAbuse = document.getElementById("btn_report_abuse")
this.btnReportAbuse.addEventListener("click", () => { this.toggle() })
let clone = document.getElementById("tpl_report_abuse_popup").content.cloneNode(true)
this.form = clone.querySelector(".abuse_type_form")
// this.emailField = clone.querySelector(".abuse_email_field")
this.notification = clone.querySelector(".abuse_report_notification")
this.modal.setBody(clone)
this.form.addEventListener("submit", e => { this.submit(e) })
}
AbuseReportWindow.prototype.toggle = function () {
if (this.visible) {
this.modal.close()
this.btnReportAbuse.classList.remove("button_highlight")
this.visible = false
} else {
this.modal.open()
this.btnReportAbuse.classList.add("button_highlight")
this.visible = true
}
}
AbuseReportWindow.prototype.notify = function (success, content) {
this.notification.style.display = ""
this.notification.classList = "abuse_report_notification " + (success ? "highlight_green" : "highlight_red")
this.notification.innerHTML = content
}
AbuseReportWindow.prototype.submit = async function (e) {
e.preventDefault()
let abuseType = ""
this.form.querySelectorAll('[name="abuse_type"]').forEach(elem => {
if (elem.checked) {
abuseType = elem.value
}
})
if (abuseType === "") {
this.notify(false, "Please select an abuse type")
return
}
const form = new FormData()
form.append("type", abuseType)
// form.append("email", this.emailField.value)
try {
const resp = await fetch(
this.viewer.file.get_href + "/report_abuse",
{ method: "POST", body: form }
);
if (resp.status >= 400) {
let json = await resp.json()
if (json.value === "resource_already_exists") {
throw "You have already reported this file"
} else if (json.value === "file_already_blocked") {
throw "This file has already been blocked"
} else if (json.value === "multiple_errors") {
throw json.errors[0].message
}
throw json.message
}
this.notify(true, "Report has been sent")
} catch (err) {
this.notify(false, "Failed to send report: " + err)
}
}

View File

@@ -1,103 +0,0 @@
function DetailsWindow(viewer) {
this.viewer = viewer
this.visible = false
this.file = null
this.graphsInitialized = false
this.graphViews = 0
this.graphDownloads = 0
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"File Details", "1200px", "1000px",
)
let clone = document.getElementById("tpl_details_popup").content.cloneNode(true)
this.divFileDetails = clone.querySelector(".info_file_details")
this.modal.setBody(clone)
this.btnDetails = document.getElementById("btn_details")
this.btnDetails.addEventListener("click", () => { this.toggle() })
}
DetailsWindow.prototype.toggle = function () {
if (this.visible) {
this.modal.close()
this.btnDetails.classList.remove("button_highlight")
this.visible = false
} else {
this.modal.open()
this.btnDetails.classList.add("button_highlight")
this.visible = true
if (!this.graphsInitialized) {
this.renderGraphs()
this.graphsInitialized = true
}
this.updateGraph(this.file)
}
}
DetailsWindow.prototype.setFile = function (file) {
this.file = file
let desc = ""
if (this.viewer.isList) {
desc = file.description
}
this.divFileDetails.innerHTML = `<tr><td>Name<td><td>${escapeHTML(file.name)}</td></tr>
<tr><td>URL<td><td><a href="${file.link}">${file.link}</a></td></tr>
<tr><td>Mime Type<td><td>${escapeHTML(file.mime_type)}</td></tr>
<tr><td>ID<td><td>${file.id}</td></tr>
<tr><td>Size<td><td>${formatDataVolume(file.size, 4)} ( ${formatThousands(file.size)} B )</td></tr>
<tr><td>Bandwidth<td><td>${formatDataVolume(file.bandwidth_used, 4)}</td></tr>
<tr><td>Upload Date<td><td>${printDate(file.date_created, true, true, true)}</td></tr>
<tr><td>Description<td><td>${escapeHTML(desc)}</td></tr>`
if (this.visible && file.timeseries_href !== "") {
this.updateGraph(file)
}
}
DetailsWindow.prototype.renderGraphs = function () {
console.log("rendering graphs")
this.graphDownloads = drawGraph(
document.getElementById("downloads_chart"), "Downloads", "number",
);
this.graphViews = drawGraph(
document.getElementById("views_chart"), "Views", "number",
);
}
DetailsWindow.prototype.updateGraph = function (file) {
console.log("updating graph")
let today = new Date()
let start = new Date()
start.setDate(start.getDate() - 90)
fetch(
file.timeseries_href +
"?start=" + start.toISOString() +
"&end=" + today.toISOString() +
"&interval=" + 60
).then(resp => {
if (!resp.ok) { return null }
return resp.json()
}).then(resp => {
resp.views.timestamps.forEach((val, idx) => {
let date = new Date(val);
let dateStr = ("00" + (date.getMonth() + 1)).slice(-2);
dateStr += "-" + ("00" + date.getDate()).slice(-2);
dateStr += " " + ("00" + date.getHours()).slice(-2) + "h";
resp.views.timestamps[idx] = " " + dateStr + " "; // Poor man's padding
});
resp.bandwidth.amounts.forEach((val, idx) => {
resp.bandwidth.amounts[idx] = Math.round(val / file.size);
});
this.graphDownloads.data.labels = resp.views.timestamps
this.graphViews.data.labels = resp.views.timestamps
this.graphDownloads.data.datasets[0].data = resp.bandwidth.amounts
this.graphViews.data.datasets[0].data = resp.views.amounts
this.graphDownloads.update()
this.graphViews.update()
})
}

View File

@@ -1,87 +0,0 @@
function EditWindow() {
this.visible = false
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"Edit File", "1000px", "auto",
)
let clone = document.getElementById("tpl_edit_file").content.cloneNode(true)
this.notification = clone.querySelector(".edit_file_notification")
this.fileNameField = clone.querySelector(".edit_file_name_field")
clone.querySelector(".btn_delete_file").addEventListener("click", () => { this.deleteFile() })
clone.querySelector(".edit_file_name_form").addEventListener("submit", e => { this.renameFile(e) })
this.modal.setBody(clone)
this.btnEdit = document.getElementById("btn_edit")
this.btnEdit.addEventListener("click", () => { this.toggle() })
}
EditWindow.prototype.toggle = function () {
if (this.visible) {
this.modal.close()
this.btnEdit.classList.remove("button_highlight")
this.visible = false
} else if (!this.visible && this.file.can_edit) {
this.modal.open()
this.btnEdit.classList.add("button_highlight")
this.visible = true
}
}
EditWindow.prototype.setFile = function (file) {
this.file = file
this.modal.setTitle("Editing " + file.name)
this.notification.style.display = "none"
this.fileNameField.value = file.name
if (this.file.can_edit) {
this.btnEdit.style.display = ""
} else {
this.btnEdit.style.display = "none"
}
}
EditWindow.prototype.notify = function (success, content) {
this.notification.style.display = ""
this.notification.classList = "edit_file_notification " + (success ? "highlight_green" : "highlight_red")
this.notification.innerHTML = content
}
EditWindow.prototype.deleteFile = async function () {
if (!confirm("Are you sure you want to delete '" + this.file.name + "'?")) {
return
}
try {
const resp = await fetch(this.file.get_href, { method: "DELETE" });
if (resp.status >= 400) {
throw (await resp.json()).message
}
this.notify(true, "This file has been deleted, you can close the page")
} catch (err) {
this.notify(false, "Could not delete file: " + err)
}
}
EditWindow.prototype.renameFile = async function (e) {
e.preventDefault()
const form = new FormData()
form.append("action", "rename")
form.append("name", this.fileNameField.value)
try {
const resp = await fetch(this.file.get_href, { method: "POST", body: form });
if (resp.status >= 400) {
throw (await resp.json()).message
}
this.notify(true, "File name has been changed. Reload the page to see the changes")
} catch (err) {
this.notify(false, "Could not change file name: " + err)
}
}

View File

@@ -1,66 +0,0 @@
function EmbedWindow(viewer) {
this.viewer = viewer
this.visible = false
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"Embed file", "850px", "auto",
)
let clone = document.getElementById("tpl_embed_popup").content.cloneNode(true)
this.textarea = clone.querySelector(".embed_html_code")
this.previewArea = clone.querySelector(".embed_preview_area")
this.btnCopyHTML = clone.querySelector(".embed_copy_html")
this.btnShowPreview = clone.querySelector(".embed_show_preview")
this.modal.setBody(clone)
this.btnCopyHTML.addEventListener("click", () => { this.copyHTML() })
this.btnShowPreview.addEventListener("click", () => { this.showPreview() })
this.btnEmbed = document.getElementById("btn_embed")
this.btnEmbed.addEventListener("click", () => { this.toggle() })
this.updateCode()
}
EmbedWindow.prototype.toggle = function () {
if (this.visible) {
this.modal.close()
this.btnEmbed.classList.remove("button_highlight")
this.visible = false
} else {
this.modal.open()
this.btnEmbed.classList.add("button_highlight")
this.visible = true
}
}
EmbedWindow.prototype.updateCode = function () {
let url
if (this.viewer.isFile) {
url = domainURL() + "/u/" + this.viewer.file.id + "?embed"
} else {
url = domainURL() + "/l/" + this.viewer.listId + "?embed"
}
this.textarea.value = `<iframe ` +
`src="${url}" ` +
`style="border: none; width: 800px; max-width: 100%; height: 500px; border-radius: 16px;"` +
`></iframe>`
}
EmbedWindow.prototype.copyHTML = function () {
if (copyText(this.textarea.value)) {
console.log('Text copied')
this.btnCopyHTML.innerHTML = `<i class="icon">content_copy</i> Copied!`
this.btnCopyHTML.classList.add("button_highlight")
} else {
console.log('Copying not supported')
this.btnCopyHTML.innerText = "Error!"
alert("Your browser does not support copying text.")
}
}
EmbedWindow.prototype.showPreview = function () {
this.previewArea.innerHTML = this.textarea.value
}

View File

@@ -1,34 +0,0 @@
function IntroPopup() {
if (localStorage.getItem("viewer_intro_popup_dismissed") === "🍆") {
return
}
let btnHome = document.getElementById("button_home")
this.popup = document.createElement("div")
this.popup.classList = "intro_popup"
this.moveToElement(btnHome)
let clone = document.getElementById("intro_popup").content.cloneNode(true)
clone.querySelector(".intro_popup_close").addEventListener("click", () => { this.close() })
this.popup.append(clone)
document.getElementById("file_viewer").append(this.popup)
// Sometimes the layout is not correctly calculated yet when this script
// runs. We adjust the values later
setTimeout(() => { this.moveToElement(btnHome) }, 400)
}
IntroPopup.prototype.moveToElement = function (el) {
let rect = el.getBoundingClientRect()
this.popup.style.top = (rect.top + el.offsetHeight + 20) + "px"
this.popup.style.left = (rect.left + (el.clientWidth / 2) - 40) + "px"
}
IntroPopup.prototype.close = function () {
localStorage.setItem("viewer_intro_popup_dismissed", "🍆")
this.popup.style.opacity = 0
setTimeout(() => { this.popup.remove() }, 500)
}

View File

@@ -1,178 +0,0 @@
function ListNavigator(viewer, files) {
this.viewer = viewer
this.files = files
this.length = files.length
this.position = 0
this.history = []
this.shuffle = false
this.divListNavigator = document.getElementById("list_navigator")
this.btnDownloadList = document.getElementById("btn_download_list")
if (files.id !== "") {
this.btnDownloadList.style.display = ""
this.btnDownloadList.addEventListener("click", () => { this.downloadList() })
}
this.btnShuffle = document.getElementById("btn_shuffle")
this.btnShuffle.style.display = ""
this.btnShuffle.addEventListener("click", () => { this.toggleShuffle() })
// Render list contents in list navigator div
files.forEach((item, i) => {
let filename
if(item.name !== "null"){
filename = item.name
}else{
filename = "Removed File"
}
let d = document.createElement("div")
d.classList = "file_button list_item"
d.addEventListener("click", () => { this.setItem(i) })
d.innerText = filename
this.divListNavigator.appendChild(d)
})
// Make the navigator visible
this.divListNavigator.style.display = "inline-block"
// Skip to the file defined in the link hash
let matches = location.hash.match(new RegExp('item=([^&]*)'))
let hashID = matches ? matches[1] : null
if(Number.isInteger(parseInt(hashID))){
this.setItem(parseInt(hashID))
}else{
this.setItem(0)
}
}
ListNavigator.prototype.nextItem = function() {
if(this.shuffle){
this.randItem()
return
}
if (this.position >= this.length) {
this.position = 0
} else {
this.position++
}
this.setItem(this.position)
}
ListNavigator.prototype.previousItem = function() {
if(this.position === 0){
this.position = this.length - 1
}else{
this.position--
}
this.setItem(this.position)
}
ListNavigator.prototype.randItem = function() {
// Avoid viewing the same file multiple times
let rand
do {
rand = Math.round(Math.random() * this.length)
console.log("rand is " + rand)
} while(this.history.indexOf(rand) > -1)
this.setItem(rand)
}
ListNavigator.prototype.setItem = function(index) {
if(index >= this.length){
this.position = 0
}else{
this.position = index
}
// Set the URL hash
location.hash = "item=" + this.position
this.viewer.setFile(this.files[this.position])
this.addToHistory(index)
this.loadThumbnails(index)
document.querySelectorAll("#list_navigator > .file_button_selected").forEach(el => {
el.classList.remove("file_button_selected")
})
let selectedItem = this.divListNavigator.children[this.position]
selectedItem.classList.add("file_button_selected")
let cst = window.getComputedStyle(selectedItem)
let itemWidth = selectedItem.offsetWidth + parseInt(cst.marginLeft) + parseInt(cst.marginRight)
let start = this.divListNavigator.scrollLeft
let end = ((this.position * itemWidth) + (itemWidth / 2)) - (this.divListNavigator.clientWidth / 2)
let steps = 60 // One second
let stepSize = (end - start)/steps
let animateScroll = (pos, step) => {
this.divListNavigator.scrollLeft = pos
if (step < steps) {
requestAnimationFrame(() => {
animateScroll(pos+stepSize, step+1)
})
}
}
animateScroll(start, 0)
}
ListNavigator.prototype.downloadList = function() {
document.getElementById("download_frame").src = "/api/list/" + this.viewer.listId + "/zip"
}
ListNavigator.prototype.addToHistory = function(index) {
if(this.history.length >= (this.length - 6)){
this.history.shift()
}
this.history.push(index)
}
ListNavigator.prototype.toggleShuffle = function() {
this.shuffle = !this.shuffle // :P
if(this.shuffle){
document.querySelector("#btn_shuffle > span").innerHTML = "Shuffle&nbsp;&#x2611;" // Check icon
this.btnShuffle.classList.add("button_highlight")
}else{
document.querySelector("#btn_shuffle > span").innerHTML = "Shuffle&nbsp;&#x2610;" // Empty checkbox
this.btnShuffle.classList.remove("button_highlight")
}
}
ListNavigator.prototype.loadThumbnails = function(index) {
let startPos = +index - 50
let endPos = +index + 50
// fyi, the + is to let javascript know it's actually a number instead of a string
if(startPos < 0){
startPos = 0
}
if(endPos >= this.length){
endPos = this.length - 1
}
let navigatorItems = document.getElementById("list_navigator").children
for (let i = startPos; i <= endPos; i++){
if (navigatorItems[i].innerHTML.includes("list_item_thumbnail")) {
continue // Thumbnail already loaded
}
let thumb = "/api/file/" + this.files[i].id + "/thumbnail?width=48&height=48"
let name = this.files[i].name
let itemHtml = "<img src=\"" + thumb + "\" "
+ "class=\"list_item_thumbnail\" alt=\"" + escapeHTML(name) + "\"/>"
+ escapeHTML(name)
navigatorItems[i].innerHTML = itemHtml
}
}

View File

@@ -1,41 +0,0 @@
function QRCodeWindow(viewer) {
this.viewer = viewer
this.visible = false
this.src = ""
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"QR code", "500px", "auto",
)
this.img = document.createElement("img")
this.img.style.display = "block"
this.img.style.width = "100%"
this.modal.setBody(this.img)
this.btnQRCode = document.getElementById("btn_qr_code")
this.btnQRCode.addEventListener("click", () => { this.toggle() })
this.setFile()
}
QRCodeWindow.prototype.toggle = function () {
if (this.visible) {
this.modal.close()
this.btnQRCode.classList.remove("button_highlight")
this.visible = false
} else {
this.modal.open()
this.btnQRCode.classList.add("button_highlight")
this.visible = true
this.setFile()
}
}
QRCodeWindow.prototype.setFile = function () {
this.src = "/api/misc/qr?text=" + encodeURIComponent(window.location.href)
if (this.visible) {
this.img.src = this.src
}
}

View File

@@ -1,68 +0,0 @@
function Skyscraper() {
this.visible = false
this.divSkyscraper = document.getElementById("skyscraper")
this.divAdSpace = document.getElementById("skyscraper_ad_space")
this.divFilePreview = document.getElementById("filepreview")
this.btnClose = document.getElementById("btn_skyscraper_close")
this.btnClose.addEventListener("click", () => { this.close() })
}
Skyscraper.prototype.open = function () {
// If the ad popup was dismissed less than 24 hours ago we don't show it
let dismissal = +localStorage.getItem("viewer_skyscraper_ad_dismissed")
let now = new Date().getTime()
if (dismissal > 0 && now - dismissal < 1000 * 60 * 60 * 24) {
console.log("Skyscraper dismissed")
return
}
if (skyscraperType === "a-ads") {
let adsColours = "&background_color=" + window.style.layer2Color +
"&text_color=" + window.style.textColor +
"&title_color=" + window.style.highlightColor +
"&title_hover_color=" + window.style.highlightColor +
"&link_color=" + window.style.highlightColor +
"&link_hover_color=" + window.style.highlightColor
this.divAdSpace.innerHTML = `<iframe
data-aa="1811738"
src="//ad.a-ads.com/1811738?size=160x600${adsColours}"
style="width:160px; height:600px; border:0px; padding:0; overflow:hidden; background-color: transparent;">
</iframe>`
} else if (skyscraperType === "pixfuture") {
let div = document.createElement("div")
div.id = "27513x160x600x4605x_ADSLOT1"
div.setAttribute("clickTrack", "%%CLICK_URL_ESC%%")
this.divAdSpace.appendChild(div)
let script = document.createElement("script")
script.async = "async"
script.type = "text/javascript"
script.src = "https:\/\/served-by.pixfuture.com/www/delivery/headerbid.js"
script.setAttribute("slotId", "27513x160x600x4605x_ADSLOT1")
script.setAttribute("refreshTime", "5")
script.setAttribute("refreshInterval", "60")
document.head.appendChild(script)
} else {
return
}
this.divSkyscraper.style.right = "0"
this.divFilePreview.style.right = this.divSkyscraper.offsetWidth + "px"
this.visible = true
}
Skyscraper.prototype.close = function () {
this.divSkyscraper.style.right = -this.divSkyscraper.offsetWidth + "px"
this.divFilePreview.style.right = "0"
this.visible = false
localStorage.setItem("viewer_skyscraper_ad_dismissed", new Date().getTime())
// Remove the ad from the DOM to save memory
setTimeout(() => { this.divSkyscraper.remove() }, 1000)
}

View File

@@ -1,205 +0,0 @@
function Toolbar(viewer) {
this.viewer = viewer
this.visible = false
this.sharebarVisible = false
this.currentFile = null
this.editWindow = null
this.views = 0
this.downloads = 0
this.statsWebsocket = null
this.divToolbar = document.getElementById("toolbar")
this.divFilePreview = document.getElementById("filepreview")
this.downloadFrame = document.getElementById("download_frame")
this.spanViews = document.getElementById("stat_views")
this.spanDownloads = document.getElementById("stat_downloads")
this.spanSize = document.getElementById("stat_size")
this.btnToggleToolbar = document.getElementById("btn_toggle_toolbar")
this.btnDownload = document.getElementById("btn_download")
this.btnCopyLink = document.getElementById("btn_copy")
this.spanCopyLink = document.querySelector("#btn_copy > span")
this.btnShare = document.getElementById("btn_share")
this.divSharebar = document.getElementById("sharebar")
this.btnToggleToolbar.addEventListener("click", () => { this.toggle() })
this.btnDownload.addEventListener("click", () => { this.download() })
this.btnCopyLink.addEventListener("click", () => { this.copyUrl() })
this.btnShare.addEventListener("click", () => { this.toggleSharebar() })
}
Toolbar.prototype.setFile = function (file) {
this.currentFile = file
this.spanSize.innerText = formatDataVolume(file.size, 3)
this.setStats()
}
Toolbar.prototype.setStats = function () {
let size = this.currentFile.size
this.spanViews.innerText = "loading..."
this.spanDownloads.innerText = "loading..."
if (this.statsWebsocket !== null) {
this.statsWebsocket.close()
}
this.statsWebsocket = new WebSocket(
location.origin.replace(/^http/, 'ws') + "/api/file/" + this.currentFile.id + "/stats"
)
this.statsWebsocket.onmessage = (msg) => {
let j = JSON.parse(msg.data)
console.debug("WS update", j)
this.views = j.views
this.downloads = Math.round(j.bandwidth / size)
this.spanViews.innerText = formatThousands(this.views)
this.spanDownloads.innerText = formatThousands(this.downloads)
}
this.statsWebsocket.onerror = (err) => {
console.error("WS error", err)
this.statsWebsocket.close()
this.statsWebsocket = null
this.spanViews.innerText = "error"
this.spanDownloads.innerText = "retrying..."
window.setTimeout(() => {
if (this.statsWebsocket === null) {
this.setStats()
}
}, 5000)
}
}
Toolbar.prototype.toggle = function () {
if (this.visible) {
if (this.sharebarVisible) { this.toggleSharebar() }
this.divToolbar.style.left = "-8em"
this.divFilePreview.style.left = "0px"
this.btnToggleToolbar.classList.remove("button_highlight")
this.visible = false
} else {
this.divToolbar.style.left = "0px"
this.divFilePreview.style.left = "8em"
this.btnToggleToolbar.classList.add("button_highlight")
this.visible = true
}
}
Toolbar.prototype.toggleSharebar = function () {
if (navigator.share) {
navigator.share({
title: this.viewer.title,
text: "Download " + this.viewer.title + " here",
url: window.location.href
})
return
}
if (this.sharebarVisible) {
this.divSharebar.style.left = "-8em"
this.btnShare.classList.remove("button_highlight")
this.sharebarVisible = false
} else {
this.divSharebar.style.left = "8em"
this.btnShare.classList.add("button_highlight")
this.sharebarVisible = true
}
}
Toolbar.prototype.download = function () {
if (captchaKey === "none" || captchaKey === "") {
console.debug("Server doesn't support captcha, starting download")
this.downloadFrame.src = this.currentFile.download_href
return
}
if (this.currentFile.availability === "") {
console.debug("File is available, starting download")
this.downloadFrame.src = this.currentFile.download_href
} else {
console.debug("File is not readily available, showing captcha dialog")
let showCaptcha = (title, text) => {
// Create the modal
this.captchaModal = new Modal(
document.getElementById("file_viewer"),
null, title, "500px", "auto",
)
// Clone the popup contents and insert them into the popup
let clone = document.getElementById("tpl_captcha_popup").content.cloneNode(true)
clone.querySelector(".captcha_text").innerText = text
recaptchaElement = clone.querySelector(".captcha_popup_captcha")
this.captchaModal.setBody(clone)
// Set the callback function
recaptchaCallback = token => {
// Download the file using the recaptcha token
this.downloadFrame.src = this.currentFile.download_href + "&recaptcha_response=" + token
this.captchaModal.close()
}
// Load the recaptcha script with a load function
let script = document.createElement("script")
script.src = "https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit"
document.body.appendChild(script)
// Show the popup
this.captchaModal.open()
}
console.log(this.currentFile.availability)
if (this.currentFile.availability === "file_rate_limited_captcha_required") {
console.debug("Showing rate limiting captcha")
showCaptcha(
"Rate limiting enabled!",
"This file is using a suspicious amount of bandwidth relative " +
"to its popularity. To continue downloading this file you " +
"will have to prove that you're a human first.",
)
} else if (this.currentFile.availability === "virus_detected_captcha_required") {
console.debug("Showing virus captcha")
showCaptcha(
"Malware warning!",
"According to our scanning systems this file may contain a " +
"virus of type '" + this.currentFile.availability_name + "'. You " +
"can continue downloading this file at your own risk, but you " +
"will have to prove that you're a human first.",
)
}
}
}
Toolbar.prototype.copyUrl = function () {
if (copyText(window.location.href)) {
console.log('Text copied')
this.spanCopyLink.innerText = "Copied!"
this.btnCopyLink.classList.add("button_highlight")
} else {
console.log('Copying not supported')
this.spanCopyLink.innerText = "Error!"
alert("Your browser does not support copying text.")
}
// Return to normal
setTimeout(() => {
this.spanCopyLink.innerText = "Copy"
this.btnCopyLink.classList.remove("button_highlight")
}, 60000)
}
// Called by the google recaptcha script
let recaptchaElement = null
let recaptchaCallback = null
function loadCaptcha() {
grecaptcha.render(recaptchaElement, {
sitekey: captchaKey,
theme: "dark",
callback: recaptchaCallback,
})
}

View File

@@ -1,364 +0,0 @@
function Viewer(type, viewToken, data) {
// Set defaults
this.toolbar = null
this.listNavigator = null
this.detailsWindow = null
this.divFilepreview = null
this.file = null
this.title = "" // Contains either the file name or list title
this.listId = ""
this.viewToken = viewToken
this.isList = false
this.isFile = false
this.initialized = false
this.viewerScript = null
this.fullscreen = false
this.toolbar = new Toolbar(this)
this.detailsWindow = new DetailsWindow(this)
this.editWindow = new EditWindow()
this.qrCodeWindow = new QRCodeWindow(this)
this.skyscraper = new Skyscraper()
this.divFilepreview = document.getElementById("filepreview")
// On small screens the toolbar takes too much space, so it collapses
// automatically
if (this.divFilepreview.clientWidth > 600 && !this.toolbar.visible) {
this.toolbar.toggle()
}
if (this.divFilepreview.clientWidth > 800) {
this.skyscraper.open()
}
if (embeddedViewer) {
// Remove padding from the headerbar
document.getElementById("file_viewer_headerbar").classList += " file_viewer_headerbar_embedded"
// Hide toolbar by default
if (this.toolbar.visible) {
this.toolbar.toggle()
}
// Alter home button to open in a new tab
document.getElementById("button_home").setAttribute("target", "_blank")
// Remove sponsor bar if ads are disabled
if (!data.show_ads) {
document.getElementById("sponsors").remove()
}
// Show fullscreen button if this browser supports it
if (document.documentElement.requestFullscreen) {
let btnFullscreen = document.getElementById("btn_fullscreen")
btnFullscreen.style.display = ""
btnFullscreen.addEventListener("click", () => { this.toggleFullscreen() })
}
}
if (type === "file") {
this.isFile = true
this.title = data.name
this.setFile(fileFromAPIResp(data))
} else if (type === "list") {
this.isList = true
this.listId = data.id
this.title = data.title
let files = []
for (let i in data.files) {
files.push(fileFromAPIResp(data.files[i]))
}
this.listNavigator = new ListNavigator(this, files)
} else if (type === "skylink") {
this.isFile = true
this.title = data.name
document.getElementById("btn_details").remove()
document.getElementById("stat_views_label").remove()
document.getElementById("stat_views").remove()
document.getElementById("stat_downloads_label").remove()
document.getElementById("stat_downloads").remove()
this.setFile(fileFromSkyNet(data))
}
this.embedWindow = new EmbedWindow(this)
this.abuseReportWindow = new AbuseReportWindow(this)
if (userAuthenticated && !this.file.can_edit) {
let btnGrab = document.getElementById("btn_grab")
btnGrab.style.display = ""
btnGrab.addEventListener("click", () => { this.grabFile() })
}
this.renderSponsors()
window.addEventListener("resize", e => { this.renderSponsors() })
// Register keyboard shortcuts
document.addEventListener("keydown", e => { this.keyboardEvent(e) })
// Show introduction popup
new IntroPopup()
// Delete all the garbage that advertisers leave behind in my cookies and
// localstorage
// window.addEventListener("beforeunload", () => {
// document.cookie.split("; ").forEach(cookie => {
// let name = cookie.split("=")[0]
// if (name !== "pd_auth_key" && name !== "style") {
// document.cookie = name + "=; Max-Age=0; expires=Thu, 01 Jan 1970 00:00:00 UTC; SameSite=Lax; path=/;"
// }
// })
// let keys = []
// for (let i = 0; i < localStorage.length; i++) {
// keys.push(localStorage.key(i))
// }
// keys.forEach(key => {
// if (key !== "viewer_intro_popup_dismissed" && key !== "uploaded_files") {
// localStorage.removeItem(key)
// }
// })
// })
this.initialized = true
}
Viewer.prototype.getFile = function () {
return this.file
}
Viewer.prototype.setFile = function (file) {
this.file = file
if (this.isList) {
document.getElementById("file_viewer_list_title").innerText = this.title
document.getElementById("file_viewer_file_title").innerText = file.name
document.title = this.title + " ~ " + file.name + " ~ pixeldrain"
} else {
document.getElementById("file_viewer_file_title").innerText = file.name
document.title = file.name + " ~ pixeldrain"
}
// Relay the file change event to all components
this.detailsWindow.setFile(file)
this.editWindow.setFile(file)
this.toolbar.setFile(file)
this.qrCodeWindow.setFile()
// Register a new view. We don't care what this returns becasue we can't
// do anything about it anyway
if (file.view_href !== "") {
fetch(file.view_href, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "token=" + this.viewToken
})
}
// Clean up the previous viewer if possible
if (this.viewerScript !== null && typeof this.viewerScript.destroy === "function") {
this.viewerScript.destroy()
}
// Clear the canvas
this.divFilepreview.innerHTML = ""
// This function can be used by the viewer scripts to navigate to the next
// file when a songs has ended for example
let nextItem = () => {
if (this.listNavigator !== null) {
this.listNavigator.nextItem()
}
}
if (file.abuse_type !== "") {
this.viewerScript = new AbuseViewer(this, file)
} else if (file.mime_type.startsWith("image")) {
this.viewerScript = new ImageViewer(this, file)
} else if (
file.mime_type.startsWith("video") ||
file.mime_type === "application/matroska" ||
file.mime_type === "application/x-matroska"
) {
this.viewerScript = new VideoViewer(this, file, nextItem)
} else if (
file.mime_type.startsWith("audio") ||
file.mime_type === "application/ogg" ||
file.name.endsWith(".mp3")
) {
this.viewerScript = new AudioViewer(this, file, nextItem)
} else if (
file.mime_type === "application/pdf" ||
file.mime_type === "application/x-pdf"
) {
this.viewerScript = new PDFViewer(this, file)
} else if (
file.mime_type.startsWith("text") ||
file.id === "demo"
) {
this.viewerScript = new TextViewer(this, file)
} else {
this.viewerScript = new FileViewer(this, file)
}
this.viewerScript.render(this.divFilepreview)
}
Viewer.prototype.renderSponsors = function () {
// Check if the ad is enabled
if (document.querySelector(".sponsors_banner") == null) { return }
let scaleWidth = 1
let scaleHeight = 1
let minWindowHeight = 800
let bannerWidth = document.querySelector(".sponsors_banner").offsetWidth
let bannerHeight = document.querySelector(".sponsors_banner").offsetHeight
if (window.innerWidth < bannerWidth) {
scaleWidth = window.innerWidth / bannerWidth
}
if (window.innerHeight < minWindowHeight) {
scaleHeight = window.innerHeight / minWindowHeight
}
// The smaller scale is the scale we'll use
let scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight
// Because of the scale transformation the automatic margins don't work
// anymore. So we have to manually calculate the margin. Here we take the
// width of the viewport - the width of the ad to calculate the amount of
// pixels around the ad. We multiply the ad size by the scale we calculated
// to account for the smaller size.
let offset = (window.innerWidth - (bannerWidth * scale)) / 2
document.querySelector(".sponsors").style.height = (bannerHeight * scale) + "px"
document.querySelector(".sponsors_banner").style.marginLeft = offset + "px"
document.querySelector(".sponsors_banner").style.transform = "scale(" + scale + ")"
}
Viewer.prototype.keyboardEvent = function (evt) {
if (evt.ctrlKey || evt.altKey || evt.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts
}
if (document.activeElement.type && document.activeElement.type === "text") {
return // Prevent shortcuts from interfering with input fields
}
console.debug("Key pressed: " + evt.keyCode)
switch (evt.keyCode) {
case 65: // A or left arrow key go to previous file
case 37:
if (this.listNavigator != null) {
this.listNavigator.previousItem()
}
break
case 68: // D or right arrow key go to next file
case 39:
if (this.listNavigator != null) {
this.listNavigator.nextItem()
}
break
case 83:
if (evt.shiftKey) {
this.listNavigator.downloadList() // SHIFT + S downloads all files in list
} else {
this.toolbar.download() // S to download the current file
}
break
case 82: // R to toggle list shuffle
if (this.listNavigator != null) {
this.listNavigator.toggleShuffle()
}
break
case 67: // C to copy to clipboard
this.toolbar.copyUrl()
break
case 73: // I to open the details window
this.detailsWindow.toggle()
break
case 69: // E to open the edit window
this.editWindow.toggle()
break
case 77: // M to open the embed window
this.embedWindow.toggle()
break
case 71: // G to grab this file
this.grabFile()
break
case 81: // Q to close the window
window.close()
break
}
}
Viewer.prototype.toggleFullscreen = function () {
if (!this.fullscreen) {
document.documentElement.requestFullscreen()
document.getElementById("btn_fullscreen_icon").innerText = "fullscreen_exit"
this.fullscreen = true
} else {
document.exitFullscreen()
document.getElementById("btn_fullscreen_icon").innerText = "fullscreen"
this.fullscreen = false
}
}
Viewer.prototype.grabFile = async function () {
if (!userAuthenticated || this.file.can_edit) {
return
}
const form = new FormData()
form.append("grab_file", this.file.id)
console.log(this.file.id)
try {
const resp = await fetch(apiEndpoint + "/file", { method: "POST", body: form });
if (resp.status >= 400) {
throw (await resp.json()).message
}
window.open(domainURL() + "/u/" + (await resp.json()).id, "_blank")
} catch (err) {
alert("Failed to grab file: " + err)
return
}
}
// Against XSS attacks
function escapeHTML(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function fileFromAPIResp(resp) {
resp.date_created = new Date(resp.date_upload)
resp.date_last_view = new Date(resp.date_last_view)
resp.icon_href = apiEndpoint + "/file/" + resp.id + "/thumbnail"
resp.get_href = apiEndpoint + "/file/" + resp.id
resp.download_href = apiEndpoint + "/file/" + resp.id + "?download"
resp.view_href = apiEndpoint + "/file/" + resp.id + "/view"
resp.timeseries_href = apiEndpoint + "/file/" + resp.id + "/timeseries"
resp.stats_href = apiEndpoint + "/file/" + resp.id + "/stats"
resp.link = domainURL() + "/u/" + resp.id
if (resp.description === undefined) {
resp.description = ""
}
return resp
}
function fileFromSkyNet(resp) {
let file = fileFromAPIResp(resp)
file.icon_href = "/res/img/mime/empty.png"
file.get_href = "https://siasky.net/file/" + resp.id
file.download_href = "https://siasky.net/file/" + resp.id + "?attachment=1"
file.view_href = ""
file.timeseries_href = ""
file.stats_href = ""
file.link = domainURL() + "/s/" + resp.id
return file
}

View File

@@ -1,36 +0,0 @@
function AbuseViewer(viewer, file, next) {
this.viewer = viewer
this.file = file
this.next = next
this.container = document.createElement("div")
this.container.classList = "image-container"
this.container.appendChild(document.createElement("br"))
this.title = document.createElement("h1")
this.title.innerText = "Unavailable For Legal Reasons"
this.container.appendChild(this.title)
this.description = document.createElement("p")
this.description.innerText = "This file has received an abuse report and " +
"was taken down."
this.container.appendChild(this.description)
this.description2 = document.createElement("p")
this.description2.innerText = "Type of abuse: " + file.abuse_type + ". " +
"Reporter: " + file.abuse_reporter_name + "."
this.container.appendChild(this.description2)
}
AbuseViewer.prototype.render = function (parent) {
parent.appendChild(this.container)
// Disable the download button
this.btnDownloadDisplay = this.viewer.toolbar.btnDownload.style.display
this.viewer.toolbar.btnDownload.style.display = "none"
}
AbuseViewer.prototype.destroy = function (parent) {
// Restore the download button
this.viewer.toolbar.btnDownload.style.display = this.btnDownloadDisplay
}

View File

@@ -1,35 +0,0 @@
function AudioViewer(viewer, file, next) {
this.viewer = viewer
this.file = file
this.next = next
this.container = document.createElement("div")
this.container.classList = "image-container"
this.container.appendChild(document.createElement("br"))
this.icon = document.createElement("img")
this.icon.src = "/api/file/" + this.file.id + "/thumbnail"
this.container.appendChild(this.icon)
this.container.appendChild(document.createElement("br"))
this.container.appendChild(document.createTextNode(this.file.name))
this.container.appendChild(document.createElement("br"))
this.container.appendChild(document.createElement("br"))
this.element = document.createElement("audio")
this.element.controls = "controls"
this.element.style.width = "90%"
this.element.addEventListener("ended", () => { this.next() }, false)
if (!embeddedViewer) {
this.element.autoplay = "autoplay"
}
this.source = document.createElement("source")
this.source.src = this.file.get_href
this.element.appendChild(this.source)
this.container.appendChild(this.element)
}
AudioViewer.prototype.render = function (parent) {
parent.appendChild(this.container)
}

View File

@@ -1,46 +0,0 @@
function FileViewer(viewer, file, next) {
this.viewer = viewer
this.file = file
this.next = next
this.container = document.createElement("div")
this.container.classList = "image-container"
this.container.appendChild(document.createElement("br"))
this.title = document.createElement("h1")
this.title.innerText = "You are viewing a file on pixeldrain"
this.container.appendChild(this.title)
this.icon = document.createElement("img")
this.icon.style.display = "inline-block"
this.icon.style.verticalAlign = "middle"
this.icon.src = this.file.icon_href
this.container.appendChild(this.icon)
this.fileDetails = document.createElement("div")
this.fileDetails.style.display = "inline-block"
this.fileDetails.style.textAlign = "left"
this.fileDetails.style.paddingLeft = "8px"
this.fileDetails.style.verticalAlign = "middle"
this.fileDetails.appendChild(document.createTextNode("Name: " + file.name))
this.fileDetails.appendChild(document.createElement("br"))
this.fileDetails.appendChild(document.createTextNode("Type: " + file.mime_type))
this.fileDetails.appendChild(document.createElement("br"))
this.fileDetails.appendChild(document.createTextNode(
"No preview is available for this file type. Download to view it locally"
))
this.fileDetails.appendChild(document.createElement("br"))
this.btnDL = document.createElement("button")
this.btnDL.addEventListener("click", () => { viewer.toolbar.download() })
this.btnDL.innerHTML = `<i class="icon">save</i> Download`
this.btnDL.classList = "button_highlight"
this.fileDetails.appendChild(this.btnDL)
this.container.appendChild(this.fileDetails)
}
FileViewer.prototype.render = function (parent) {
parent.appendChild(this.container)
}

View File

@@ -1,107 +0,0 @@
function ImageViewer(viewer, file) {
this.viewer = viewer
this.file = file
this.zoomed = false
this.x = 0
this.y = 0
this.dragging = false
this.zoom = 1
this.container = document.createElement("dv")
this.container.classList = "image-container"
this.element = document.createElement("img")
this.element.classList = "pannable center drop_shadow"
this.element.src = this.file.get_href
this.element.addEventListener("dblclick", (e) => { return this.doubleclick(e) })
this.element.addEventListener("doubletap", (e) => { return this.doubleclick(e) })
this.element.addEventListener("mousedown", (e) => { return this.mousedown(e) })
this.element.addEventListener("mousedown", (e) => { return this.mousedown(e) })
// this.element.addEventListener("wheel", (e) => { return this.scroll(e) })
document.addEventListener("mousemove", (e) => { return this.mousemove(e) })
document.addEventListener("mouseup", (e) => { return this.mouseup(e) })
this.container.appendChild(this.element)
}
ImageViewer.prototype.render = function(parent) {
parent.appendChild(this.container)
}
ImageViewer.prototype.scroll = function(e) {
if (!this.zoomed) {
this.doubleclick(e)
}
console.log(e.deltaY)
this.zoom = this.zoom - (e.deltaY * 0.01);
if (this.zoom < 1) { this.zoom = 1 }
if (this.zoom > 10) { this.zoom = 10 }
this.element.style.width = this.zoom * 100 + "%"
this.element.style.height = "auto"
this.element.style.maxWidth = "1000%"
this.element.style.maxHeight = "1000%"
e.preventDefault()
e.stopPropagation()
return false
}
ImageViewer.prototype.doubleclick = function(e) {
if (this.zoomed) {
this.element.removeAttribute("style")
this.container.style.overflow = ""
this.zoomed = false
} else {
this.element.style.maxWidth = "none"
this.element.style.maxHeight = "none"
this.element.style.top = "0"
this.element.style.left = ""
this.element.style.transform = "none"
this.container.style.overflow = "scroll"
this.zoomed = true
}
e.preventDefault()
e.stopPropagation()
return false
}
ImageViewer.prototype.mousedown = function(e) {
if (!this.dragging && e.which === 1 && this.zoomed) {
this.x = e.pageX
this.y = e.pageY
this.dragging = true
e.preventDefault()
e.stopPropagation()
return false
}
}
ImageViewer.prototype.mousemove = function(e) {
if (this.dragging) {
this.container.scrollLeft = this.container.scrollLeft - (e.pageX - this.x)
this.container.scrollTop = this.container.scrollTop - (e.pageY - this.y)
this.x = e.pageX
this.y = e.pageY
e.preventDefault()
e.stopPropagation()
return false
}
}
ImageViewer.prototype.mouseup = function(e) {
if (this.dragging) {
this.dragging = false
e.preventDefault()
e.stopPropagation()
return false
}
}

View File

@@ -1,12 +0,0 @@
function PDFViewer(viewer, file) {
this.viewer = viewer
this.file = file
this.container = document.createElement("iframe")
this.container.classList = "image-container"
this.container.style.border = "none"
this.container.src = "/res/misc/pdf-viewer/web/viewer.html?file="+encodeURIComponent(this.file.get_href)
}
PDFViewer.prototype.render = function(parent) {
parent.appendChild(this.container)
}

View File

@@ -1,80 +0,0 @@
function TextViewer(viewer, file) {
this.viewer = viewer
this.file = file
this.pre = null
this.prettyprint = null
this.container = document.createElement("div")
this.container.classList = "text-container"
if (this.file.name.endsWith(".md") || this.file.name.endsWith(".markdown") || file.mime_type === "text/demo") {
this.getMarkdown()
} else if (this.file.name.endsWith(".txt")) {
this.getText()
} else {
this.getCode()
}
}
TextViewer.prototype.getCode = function () {
this.pre = document.createElement("pre")
this.pre.classList = "pre-container prettyprint linenums"
this.pre.innerText = "Loading..."
this.container.appendChild(this.pre)
if (this.file.size > 1 << 20) { // File larger than 1 MiB
this.pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return
}
fetch(this.file.get_href).then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
this.pre.innerText = resp
// Load prettyprint script
this.prettyprint = document.createElement("script")
this.prettyprint.src = `{{noescape "https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=desert"}}`
this.container.appendChild(this.prettyprint)
}).catch(err => {
this.pre.innerText = "Error loading file: " + err
})
}
TextViewer.prototype.getText = function () {
this.pre = document.createElement("pre")
this.pre.innerText = "Loading..."
this.container.appendChild(this.pre)
if (this.file.size > 1 << 20) { // File larger than 1 MiB
this.pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return
}
fetch(this.file.get_href).then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
this.pre.innerText = resp
}).catch(err => {
this.pre.innerText = "Error loading file: " + err
})
}
TextViewer.prototype.getMarkdown = function () {
fetch(
domainURL() + window.location.pathname + "/preview"
).then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
this.container.innerHTML = resp
}).catch(err => {
this.container.innerText = "Error loading file: " + err
})
}
TextViewer.prototype.render = function (parent) {
parent.appendChild(this.container)
}

View File

@@ -1,75 +0,0 @@
function VideoViewer(viewer, file, next) {
this.viewer = viewer
this.file = file
this.next = next
}
VideoViewer.prototype.render = function (parent) {
if (this.file.allow_video_player) {
let vidContainer = document.createElement("div")
vidContainer.classList = "image-container"
let vidElement = document.createElement("video")
vidElement.controls = "controls"
vidElement.playsInline = "playsInline"
vidElement.classList = "center drop_shadow"
vidElement.addEventListener("ended", () => { this.next() }, false)
if (!embeddedViewer) {
vidElement.autoplay = "autoplay"
}
let videoSource = document.createElement("source")
videoSource.src = this.file.get_href
videoSource.type = this.file.mime_type
vidElement.appendChild(videoSource)
vidContainer.appendChild(vidElement)
parent.appendChild(vidContainer)
// Possible fix for ios 15 video bug?
videoSource.src = this.file.get_href
videoSource.type = this.file.mime_type
} else {
let container = document.createElement("div")
container.classList = "image-container"
container.appendChild(document.createElement("br"))
let title = document.createElement("h1")
title.innerText = "This is a video file on pixeldrain"
container.appendChild(title)
let icon = document.createElement("img")
icon.style.display = "inline-block"
icon.style.verticalAlign = "middle"
icon.src = this.file.icon_href
container.appendChild(icon)
let fileDetails = document.createElement("div")
fileDetails.style.display = "inline-block"
fileDetails.style.textAlign = "left"
fileDetails.style.paddingLeft = "8px"
fileDetails.style.verticalAlign = "middle"
fileDetails.style.maxWidth = "600px"
fileDetails.appendChild(document.createTextNode(
`The online video player on pixeldrain has been disabled due to
repeated abuse. You can still watch videos online by upgrading to
Pro. Or download the video and watch it locally on your computer.`,
))
fileDetails.appendChild(document.createElement("br"))
let upgradeBtn = document.createElement("a")
upgradeBtn.innerHTML = `<i class="icon">upgrade</i> Upgrade to Pro`
upgradeBtn.classList = "button button_highlight"
upgradeBtn.href = `{{noescape "https://www.patreon.com/join/pixeldrain/checkout?rid=5291427&cadence=12"}}`
fileDetails.appendChild(upgradeBtn)
let downloadBtn = document.createElement("button")
downloadBtn.innerHTML = `<i class="icon">save</i> Download`
downloadBtn.addEventListener("click", () => { this.viewer.toolbar.download() })
fileDetails.appendChild(downloadBtn)
container.appendChild(fileDetails)
parent.appendChild(container)
}
}

View File

@@ -1,155 +0,0 @@
#page_body {
height: 100%;
padding: 0;
}
/* Override the menu button so it doesn't overlap the file manager when the menu
is collapsed */
#button_toggle_navigation {
display: none;
}
.file_manager {
position: absolute;
padding: 0;
background-color: var(--layer_2_color);
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.file_manager > .nav_bar {
flex-shrink: 0;
display: flex;
flex-direction: row;
}
.file_manager > .nav_bar > button {
flex-shrink: 0;
}
.file_manager > .nav_bar > .spacer {width: 8px;}
.file_manager > .nav_bar > .breadcrumbs {
flex-grow: .7;
flex-shrink: 1;
min-width: 0;
}
.file_manager > .nav_bar > .input_search {
flex-grow: .3;
flex-shrink: 1;
min-width: 100px;
}
.file_manager > .directory_area > .directory_sorters {
display: flex;
flex-direction: row;
position: sticky;
overflow: hidden;
top: 0;
z-index: 1;
background-color: var(--layer_2_color);
min-width: 850px;
}
.file_manager > .directory_area > .directory_sorters > div {
display: inline-block;
margin: 4px 10px;
padding: 4px;
box-sizing: border-box;
border-bottom: 1px solid var(--input_color);
cursor: pointer;
}
.file_manager > .directory_area > .directory_sorters > :first-child,
.node > :first-child {
flex-shrink: 1;
flex-grow: 1;
}
.file_manager > .directory_area > .directory_sorters > :not(:first-child),
.node > :not(:first-child) {
flex-shrink: 0;
flex-grow: 0;
}
.file_manager > .directory_area {
flex-shrink: 1;
flex-grow: 1;
margin: 0;
padding: 0;
overflow-x: auto;
text-align: left;
box-sizing: border-box;
}
.file_manager > .directory_area > .directory_node_container {
/* Required because we use padding for moving the nodes down when items
above are out of view*/
box-sizing: border-box;
display: block;
min-width: 850px;
}
.file_manager > .status_bar {
flex-shrink: 0;
text-align: left;
}
.node {
display: flex;
flex-direction: row;
position: relative;
height: 40px;
overflow: hidden;
/* I use padding instead of margin here because it goves me more precise
control over the size.
Check out https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing*/
margin: 0;
box-sizing: border-box;
color: var(--text_color);
text-decoration: none;
}
.node:hover:not(.node_selected) {
background-color: var(--input_color_dark);
color: var(--input_text_color);
text-decoration: none;
}
.node_selected {
background-color: var(--highlight_color);
color: var(--highlight_text_color);
}
.node > div {
height: 100%;
overflow: hidden;
margin: auto 10px;
padding: 4px;
box-sizing: border-box;
display: inline-block;
text-overflow: ellipsis;
white-space: nowrap;
}
.node > div > span {
margin: auto;
box-sizing: border-box;
display: block;
text-overflow: ellipsis;
white-space: nowrap;
}
.node > div > img {
max-height: 100%;
margin-right: 6px;
width: auto;
min-width: auto;
float: left;
display: block;
}
.spinner {
position: absolute;
display: block;
margin: auto;
max-width: 100%;
max-height: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
}

View File

@@ -1,350 +0,0 @@
/*
Created on : May 22, 2015, 1:20:02 PM
Author : Fornax
*/
/* Viewer container */
.file_viewer {
position: absolute;
display: flex;
flex-direction: column;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
background-color: var(--layer_2_color);
}
/* Headerbar (row 1) */
.file_viewer > .file_viewer_headerbar {
flex-grow: 0;
flex-shrink: 0;
display: flex;
flex-direction: row;
text-align: left;
z-index: 10;
box-shadow: none;
padding: 4px;
}
.file_viewer > .file_viewer_headerbar_embedded {
padding: 2px;
}
/* Headerbar components */
.file_viewer > .file_viewer_headerbar > * {
flex-grow: 0;
flex-shrink: 0;
margin-left: 4px;
margin-right: 4px;
display: inline;
}
.file_viewer > .file_viewer_headerbar > .file_viewer_headerbar_title {
flex-grow: 1;
flex-shrink: 1;
display: flex;
flex-direction: column;
overflow: hidden;
line-height: 1.2em; /* When the page is a list there will be two lines. Dont's want to stretch the container*/
white-space: nowrap;
text-overflow: ellipsis;
justify-content: center;
}
.file_viewer > .file_viewer_headerbar > .button_toggle_toolbar > .icon {
font-size: 1.6em;
}
.file_viewer > .file_viewer_headerbar > .button_home > svg {
height: 1.6em;
width: 1.6em;
margin: 0;
}
.file_viewer > .file_viewer_headerbar > .button_home::after {
content: "pixeldrain";
}
@media (max-width: 600px) {
.file_viewer > .file_viewer_headerbar > .button_home::after {
content: "pd";
}
}
.file_viewer > .file_viewer_headerbar > .file_viewer_headerbar_title > span {
flex-grow: 0;
flex-shrink: 0;
margin-right: 1em;
}
/* List Navigator (row 2) */
.file_viewer > .list_navigator {
flex-grow: 0;
flex-shrink: 0;
position: relative;
display: none; /* Becomes visible if the page is a list */
width: 100%;
border-top: 1px solid var(--layer_2_color_border);
text-align: center;
line-height: 1em;
overflow-x: auto;
overflow-y: hidden;
z-index: 50;
white-space: nowrap;
}
.file_viewer > .list_navigator > .list_item {
height: 2.6em !important;
width: 220px !important;
}
/* File preview area (row 3) */
.file_viewer > .file_viewer_window {
flex-grow: 1;
flex-shrink: 1;
position: relative;
display: inline-block;
width: auto;
height: auto;
margin: 0;
z-index: 9;
}
.file_viewer > .file_viewer_window > .file_viewer_file_preview {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: inline-block;
min-height: 100px;
min-width: 100px;
text-align: center;
vertical-align: middle;
transition: left 0.5s, right 0.5s;
overflow: hidden;
box-shadow: inset 2px 2px 10px 2px var(--shadow_color);
border-radius: 16px;
}
/* Sponsors (row 4) */
.file_viewer > .sponsors {
font-size: 0;
line-height: 0;
}
.file_viewer > .sponsors > .sponsors_banner {
display: block;
margin: auto;
transform-origin: 0 0;
}
/* Pop-up warning (row 5) */
.file_viewer > .popup_warn {
background-color: var(--layer_1_color);
border-top: 1px solid var(--layer_1_color_border);
text-align: center;
}
/* Toolbars */
.file_viewer > .file_viewer_window > .file_viewer_toolbar {
position: absolute;
width: 8em;
z-index: 49;
overflow: hidden;
left: -9em;
bottom: 0;
top: 0;
padding: 0;
text-align: left;
transition: left 0.5s;
background-color: var(--layer_2_color);
}
.file_viewer > .file_viewer_window > .file_viewer_sharebar {
position: absolute;
width: 7em;
left: -8em;
bottom: 0;
top: 0;
overflow-y: scroll;
overflow-x: hidden;
box-shadow: inset 1px 1px 5px var(--shadow_color);
background-color: var(--layer_1_color);
border-radius: 16px;
text-align: center;
z-index: 48;
overflow: hidden;
transition: left 0.5s;
}
.file_viewer > .file_viewer_window > .skyscraper {
position: absolute;
width: 160px;
z-index: 49;
overflow: hidden;
right: -170px;
bottom: 0;
top: 0;
padding: 0;
text-align: center;
transition: right 0.5s;
background-color: var(--layer_2_color);
}
.file_viewer > .file_viewer_window > .skyscraper > .skyscraper_ad_space {
width: 100%;
height: 100%;
}
/* =====================
== FILE CONTAINERS ==
=====================*/
.image-container{
position: relative;
display: block;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
}
.text-container{
background: #333 none;
position: relative;
text-align: left;
height: 100%;
width: 100%;
font-size: 0.9em;
line-height: 1.5em;
padding: 5px 5px 5px 20px;
box-sizing: border-box;
overflow-y: scroll;
overflow-x: hidden;
}
.text-container > pre {
white-space: pre-wrap;
overflow: hidden;
}
.center{
position: relative;
display: block;
margin: auto;
max-width: 100%;
max-height: 100%;
top: 50%;
transform: translateY(-50%);
}
.pannable { cursor: move; }
.drop_shadow { box-shadow: 1px 1px 5px var(--shadow_color); }
/* ========================
|| TOOLBAR COMPONENTS ||
======================== */
/* Workaround to hide the scrollbar in non webkit browsers, it's really ugly' */
.file_viewer_toolbar > div {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: -30px;
overflow-y: scroll;
overflow-x: hidden;
}
.file_viewer_toolbar > div > div {
position: absolute;
left: 0;
top: 0;
width: 8em;
height: auto;
text-align: center;
}
.toolbar_button{
text-align: left;
}
.toolbar_button > img {
width: 24px;
height: 24px;
vertical-align: middle;
}
.toolbar_button > span {
vertical-align: middle;
}
.toolbar_label {
text-align: left;
padding-left: 10px;
font-size: 0.8em;
line-height: 0.7em;
margin-top: 0.5em;
}
/* =========================
|| SHAREBAR COMPONENTS ||
========================= */
.sharebar-button {text-align: center;}
.sharebar-button > svg,
.sharebar-button > img {
width: 40px;
height: 40px;
}
/* =====================
|| MISC COMPONENTS ||
===================== */
.captcha_popup_captcha > div {
display: inline-block;
}
table {width: auto !important;}
/* Abuse report label*/
.abuse_type_form > label {
display: block;
border-bottom: 1px var(--layer_2_color_border) solid;
padding: 0.5em;
}
.abuse_type_form > label:last-of-type {
border-bottom: none;
}
/* ====================
|| LIST NAVIGATOR ||
==================== */
.file-container{
position: absolute;
top: 100px;
left: 0px;
right: 0px;
bottom: 0px;
width: 100%;
overflow: hidden;
border: none;
}
.file-container-frame{
position: absolute;
width: 100%;
height: 100%;
border: none;
}
.intro_popup {
position: absolute;
width: 380px;
max-width: 80%;
height: auto;
background-color: var(--layer_4_color);
box-shadow: 1px 1px 10px var(--shadow_color);
border-radius: 20px;
z-index: 50;
transition: opacity .4s, left .5s, top .5s;
}
.intro_popup:before {
content: "";
display: block;
position: absolute;
left: 30px;
top: -15px;
border-bottom: 15px solid var(--layer_4_color);
border-left: 15px solid transparent;
border-right: 15px solid transparent;
}

View File

@@ -1,5 +1,6 @@
{{ define "ad_headers" }}
{{ if eq .Other.AdBannerType 3 }}
<!-- Adshares -->
<script type="text/javascript">
!function(n){var t,e=function(n,t){var e=[["a","e","i","o","u","y"],["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"]];if(t)for(var r=0;r<=t.length-1;r++)n=n*t.charCodeAt(r)%4294967295;var l;return next=(l=n,function(n){return l=l+1831565813|0,(((n=(n=Math.imul(l^l>>>15,1|l))+Math.imul(n^n>>>7,61|n)^n)^n>>>14)>>>0)/Math.pow(2,32)}),function(n,t){for(var r=[],l=null,o=0;o<=n-1;o++){var a=void 0;null===l?a=e[0].concat(e[1]):1==l?(a=e[0],l=0):(a=e[1],l=1);var u=a[Math.floor(next()*a.length)];r.push(u),null===l&&(l=-1!=e[0].indexOf(u)?0:1)}return r.push("."+t),r.join("")}}((t=new Date,(t/=1e3)-t%1209600),"_fa7cdd4c68507744")(8,"xyz");if(null===n)console.log("https://"+e);else{var r=n.createElement("script");r.src="https://"+e+"/main.js",(n.body||n.head).appendChild(r)}}("undefined"!=typeof document?document:null);
</script>
@@ -8,6 +9,7 @@
{{ define "banner_ads"}}
{{ if eq .Other.AdBannerType 1 }}
<!-- A-ads -->
<!-- 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"
@@ -44,6 +46,7 @@
<img src="/res/img/misc/banner_amarula_jobs.png" style="width: 100%; height: 100%" />
</a>
{{ else if eq .Other.AdBannerType 6 }}
<!-- Adsterra -->
<div class="sponsors_banner" style="display: inline-block; width: 728px; height: 90px;">
<script type="text/javascript">
atOptions = {
@@ -57,10 +60,12 @@
</script>
</div>
{{ else if eq .Other.AdBannerType 7 }}
<!-- Brave -->
<a class="sponsors_banner" style="display: inline-block; width: 728px; height: 90px;" href="/click/MdUXxSov?target=https%3A%2F%2Fbrave.com%2Fpix009">
<img src="/res/img/misc/brave-728x90.png" style="width: 100%; height: 100%" />
</a>
{{ else if eq .Other.AdBannerType 8 }}
<!-- Pro ad 1 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<img src="/res/img/pixeldrain_128.png" style="height: 2.4em; vertical-align: middle; margin: 4px;"/>
<div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -69,6 +74,7 @@
</div>
</div>
{{ else if eq .Other.AdBannerType 9 }}
<!-- Pro ad 2 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<img src="/res/img/pixeldrain_128.png" style="height: 2.4em; vertical-align: middle; margin: 4px;"/>
<div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -77,6 +83,7 @@
</div>
</div>
{{ else if eq .Other.AdBannerType 10 }}
<!-- Pro ad 3 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<img src="/res/img/pixeldrain_128.png" style="height: 2.4em; vertical-align: middle; margin: 4px;"/>
<div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -85,6 +92,7 @@
</div>
</div>
{{ else if eq .Other.AdBannerType 11 }}
<!-- Pro ad 4 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<img src="/res/img/pixeldrain_128.png" style="height: 2.4em; vertical-align: middle; margin: 4px;"/>
<div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -117,11 +125,13 @@
</script>
</div>
{{ else if eq .Other.AdBannerType 15 }}
<!-- Pixfuture -->
<!-- AuctionX Display platform tag START -->
<div id="27517x728x90x4605x_ADSLOT1" clickTrack="%%CLICK_URL_ESC%%" style="display: block; margin: auto;"></div>
<script type="text/javascript" async src="https://served-by.pixfuture.com/www/delivery/headerbid.js" slotId="27517x728x90x4605x_ADSLOT1" refreshTime="5" refreshInterval="60"></script>
<!-- AuctionX Display platform tag END -->
{{ else if eq .Other.AdBannerType 16 }}
<!-- Publisherrest ad 1 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<a href="https://pixeldrain.com/vouchercodes/" class="button button_highlight" style="margin: 8px;">
<i class="icon">shopping_cart</i>
@@ -130,6 +140,7 @@
</a>
</div>
{{ else if eq .Other.AdBannerType 17 }}
<!-- Publisherrest ad 2 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<a href="https://pixeldrain.com/vouchercodes/" class="button button_highlight" style="margin: 8px;">
<i class="icon">shopping_cart</i>
@@ -138,6 +149,7 @@
</a>
</div>
{{ else if eq .Other.AdBannerType 18 }}
<!-- Publisherrest ad 3 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;">
<a href="https://pixeldrain.com/vouchercodes/" class="button button_highlight" style="margin: 8px;">
<i class="icon">shopping_cart</i>
@@ -147,34 +159,3 @@
</div>
{{ end }}
{{ end }}
{{ define "floating_ads"}}
{{ if eq .Other.AdFloaterType 3 }}
<!-- AdMaven -->
<script data-cfasync="false" async src="//d227cncaprzd7y.cloudfront.net/?acncd=905608"></script>
{{ else if eq .Other.AdFloaterType 2 }}
<!-- AdSterra -->
<script type='text/javascript' async src='//concoursestrickenpuppy.com/4c/4d/90/4c4d90a2e79b0e049f811a663f3e3c07.js'></script>
{{ else if eq .Other.AdFloaterType 1 }}
<!-- PropellerAds -->
<script type="text/javascript" src="//optyruntchan.com/400/4189877" data-cfasync="false" async="async"></script>
</script>
{{ end }}
{{ if eq .Other.AdPopupType 1 }}
<script data-cfasync="false" type="text/javascript" async src="//depirsmandk5.com/t/9/fret/meow4/1846775/brt.js"></script>
{{ else if eq .Other.AdPopupType 2 }}
<script>
window.addEventListener("load", () => {
(function(s,u,z,p){
s.src=u,s.setAttribute('data-zone',z),p.appendChild(s);
})(
document.createElement('script'),
'https://iclickcdn.com/tag.min.js',
3750089,
document.body||document.documentElement,
)
})
</script>
{{ end }}
{{ end }}

View File

@@ -1,370 +0,0 @@
{{define "file_viewer"}}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{.Title}}</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{ template "opengraph" .OGData }}
{{ template "user_style" . }}
{{ template "user_style_js" . }}
<link rel="icon" sizes="32x32" href="/res/img/pixeldrain_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/pixeldrain_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/pixeldrain_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<meta name="theme-color" content="#220735"/>
<style>
{{ template `viewer.css` }}
{{ template `modal.css` }}
</style>
{{ template `ad_headers` . }}
</head>
<body>
<div id="file_viewer" class="file_viewer">
<div id="file_viewer_headerbar" class="file_viewer_headerbar">
<button id="btn_toggle_toolbar" class="button_toggle_toolbar round">
<i class="icon">menu</i>
</button>
<a href="/" id="button_home" class="button button_home round">
{{template `pixeldrain.svg` .}}
</a>
<div id="file_viewer_headerbar_title" class="file_viewer_headerbar_title">
<div id="file_viewer_list_title"></div>
<div id="file_viewer_file_title">loading...</div>
</div>
<!-- fullscreen button becomes visible on embedded viewer -->
<button id="btn_fullscreen" class="btn_fullscreen round" style="display: none;">
<i class="icon" id="btn_fullscreen_icon">fullscreen</i>
</button>
</div>
<div id="list_navigator" class="list_navigator"></div>
<div id="file_viewer_window" class="file_viewer_window">
<div id="toolbar" class="file_viewer_toolbar"><div><div>
<div id="stat_views_label" class="toolbar_label">Views</div>
<div id="stat_views" style="text-align: center;">N/A</div>
<div id="stat_downloads_label" class="toolbar_label">Downloads</div>
<div id="stat_downloads" style="text-align: center;">N/A</div>
<div id="stat_size_label" class="toolbar_label">Size</div>
<div id="stat_size" style="text-align: center;">N/A</div>
<hr/>
<button id="btn_download" class="toolbar_button button_full_width">
<i class="icon">save</i>
<span>Download</span>
</button>
<button id="btn_download_list" class="toolbar_button button_full_width" style="display: none;">
<i class="icon">save</i>
<span>DL all files</span>
</button>
<button id="btn_copy" class="toolbar_button button_full_width">
<i class="icon">content_copy</i>
<span><u>C</u>opy link</span>
</button>
<button id="btn_share" class="toolbar_button button_full_width">
<i class="icon">share</i>
<span>Share</span>
</button>
<button id="btn_qr_code" class="toolbar_button button_full_width">
<i class="icon">qr_code</i>
<span>QR code</span>
</button>
<button id="btn_shuffle" class="toolbar_button button_full_width" style="display: none;" title="Randomize the order of the files in this list">
<i class="icon">shuffle</i>
<span>Shuffle &#x2610;</span>
</button>
<button id="btn_details" class="toolbar_button button_full_width">
<i class="icon">help</i>
<span>Deta<u>i</u>ls</span>
</button>
<hr/>
<button id="btn_edit" class="toolbar_button button_full_width" style="display: none;">
<i class="icon">edit</i>
<span><u>E</u>dit</span>
</button>
<button id="btn_grab" class="toolbar_button button_full_width" style="display: none;" title="Copy this file to your own pixeldrain account">
<i class="icon">save_alt</i>
<span><u>G</u>rab file</span>
</button>
<button id="btn_embed" class="toolbar_button button_full_width" title="Include this file in your own webpages">
<i class="icon">code</i>
<span>E<u>m</u>bed</span>
</button>
<button id="btn_report_abuse" class="toolbar_button button_full_width" title="Report abuse in this file">
<i class="icon">flag</i>
<span>Report</span>
</button>
<br/>
{{ if and .Other.FileAdsEnabled .Other.UserAdsEnabled }}
<hr/>
Tired of ads?<br/>
Files expiring too soon?<br/>
<a class="button button_highlight" href="/click/7wy9gg2J?target=%2F%23pro">
<svg style="float: left; width: 2em; height: 2em; fill: currentColor; margin-right: 4px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<g fill-rule="evenodd">
<path d="M64.1102,0.1004 C44.259,0.1004 28.1086,16.2486 28.1086,36.0986 C28.1086,55.8884 44.259,71.989 64.1102,71.989 C83.9,71.989 100,55.8884 100,36.0986 C100,16.2486 83.9,0.1004 64.1102,0.1004"/>
<polygon points=".012 95.988 17.59 95.988 17.59 .1 .012 .1"/>
</g>
</svg>
Become a Patron!
</a>
{{ end }}
<!-- This frame will load the download URL when a download button is pressed -->
<iframe id="download_frame" style="display: none; width: 1px; height: 1px;"></iframe>
</div></div></div>
<div id="sharebar" class="file_viewer_sharebar">
Share on:<br/>
<button class="sharebar-button button_full_width" onclick="window.open('mailto:please@set.address?subject=File%20on%20pixeldrain&body=' + window.location.href);">
{{template `email.svg` .}}<br/>E-Mail
</button>
<button class="sharebar-button button_full_width" onclick="window.open('https://www.reddit.com/submit?url=' + window.location.href);">
{{template `reddit.svg` .}}<br/>Reddit
</button>
<button class="sharebar-button button_full_width" onclick="window.open('https://twitter.com/share?url=' + window.location.href);">
{{template `twitter.svg` .}}<br/>Twitter
</button>
<button class="sharebar-button button_full_width" onclick="window.open('http://www.facebook.com/sharer.php?u=' + window.location.href);">
{{template `facebook.svg` .}}<br/>Facebook
</button>
<button class="sharebar-button button_full_width" onclick="window.open('http://www.tumblr.com/share/link?url=' + window.location.href);">
{{template `tumblr.svg` .}}<br/>Tumblr
</button>
</div>
<div id="filepreview" class="file_viewer_file_preview checkers">
<div class="center" style="width: 100px; height: 100px;">{{template "spinner.svg" .}}</div>
</div>
<div id="skyscraper" class="skyscraper">
<button id="btn_skyscraper_close" class="round">
<i class="icon">close</i> Close ad
</button>
<div id="skyscraper_ad_space" class="skyscraper_ad_space"></div>
</div>
</div>
<div id="sponsors" class="sponsors">
{{ if and .Other.FileAdsEnabled .Other.UserAdsEnabled }}
{{ template "banner_ads" . }}
{{ else if not .Other.UserAdsEnabled }}
<div style="text-align: center; line-height: 1.3em; font-size: 13px;">
Thank you for supporting pixeldrain!
</div>
{{ else if not .Other.FileAdsEnabled }}
<div style="text-align: center; line-height: 1.3em; font-size: 13px;">
The uploader of this file disabled advertisements. You can do the same for <a href="/#pro">only €2 per month</a>!
</div>
{{ end }}
</div>
</div>
<template id="tpl_details_popup">
<table class="info_file_details" style="min-width: 100%;"></table>
<div class="info_about">
<h3>Downloads</h3>
<div class="chart-container" style="position: relative; width: 100%; height: 150px;">
<canvas id="downloads_chart"></canvas>
</div>
<h3>Views</h3>
<div class="chart-container" style="position: relative; width: 100%; height: 150px;">
<canvas id="views_chart"></canvas>
</div>
<p style="text-align: center">
Charts rendered by the amazing <a href="https://www.chartjs.org/" target="_blank">Chart.js</a>.
</p>
<h3>About</h3>
Pixeldrain is a file sharing platform.
<a href="/" target="_blank">Visit the home page for more information.</a>
<h3>Keyboard Controls</h3>
<table style="max-width: 100%;">
<tr><td colspan="2">File Shortcuts</td></tr>
<tr><td>c</td><td> = Copy URL of this page</td></tr>
<tr><td>i</td><td> = Toggle details window (this window) (<b><u>i</u></b>nfo)</td></tr>
<tr><td>s</td><td> = Download the file you are currently viewing (<b><u>s</u></b>ave)</td></tr>
<tr><td>q</td><td> = Close the window (<b><u>q</u></b>uit)</td></tr>
<tr><td colspan="2">List Shortcuts</td></tr>
<tr><td>a or &#8592;</td><td> = View previous item in list</td></tr>
<tr><td>d or &#8594;</td><td> = View next item in list</td></tr>
<tr><td>r</td><td> = Toggle shuffle (<b><u>r</u></b>andom)</td></tr>
<tr><td>SHIFT + s</td><td> = Download all the files in the list as a zip archive</td></tr>
</table>
</div>
</template>
<template id="tpl_edit_file">
<div class="edit_file_notification" style="display: none;"></div>
<h3>Rename</h3>
<form class="edit_file_name_form" style="display: flex; width: 100%">
<input class="edit_file_name_field" type="text" style="flex: 1 1 auto"/>
<button class="edit_file_name_submit" role="submit" style="flex: 0 0 auto">
<i class="icon">save</i> Save
</button>
</form>
<h3>Delete</h3>
<p>
When you delete a file it cannot be recovered.
Nobody will be able to download it and the link will
stop working. The file will also disappear from any
lists it's contained in.
</p>
<div style="text-align: center;">
<button class="button_red btn_delete_file">
<i class="icon small">delete</i> Delete this file
</button>
</div>
</template>
<template id="tpl_captcha_popup">
<div class="captcha_text"></div>
<br/>
<div class="captcha_popup_captcha" style="text-align: center;"></div>
</template>
<template id="tpl_embed_popup">
<p>
You can embed pixeldrain's file viewer in your own web pages. We
have created a special HTML code which renders a minimalistic
version of the file viewer where the title bar is a bit thinner and
the toolbar is collapsed by default.
</p>
<p>
Unless it was uploaded using a pixeldrain Pro account the embedded
file will also show advertisements.
</p>
<h3>Code</h3>
<textarea class="embed_html_code" style="width: 100%; height: 4em; margin: 0;"></textarea>
<br/>
<button class="embed_copy_html"><i class="icon">content_copy</i> Copy HTML</button>
<button class="embed_show_preview"><i class="icon">visibility</i> Show example</button>
<h3>Example</h3>
<div class="embed_preview_area" style="text-align: center;"></div>
</template>
<template id="tpl_report_abuse_popup">
<p>
If you think this file violates pixeldrain's
<a href="/about#content-policy">content policy</a> you can
report it for moderation with this form. You cannot report
copyright abuse with this form, send a formal DMCA notification
to the
<a href="/about#content-policy">abuse e-mail address</a>
instead.
</p>
<form class="abuse_type_form" style="width: 100%">
<h3>Abuse type</h3>
<p>
Which type of abuse is shown in this file? Pick the most
appropriate one.
</p>
<label for="abuse_type_terrorism">
<input type="radio" id="abuse_type_terrorism" name="abuse_type" value="terrorism">
<b>Terrorism</b>: Videos, images or audio fragments showing
or promoting the use of intentional violence to achieve
political aims.
</label>
<label for="abuse_type_gore">
<input type="radio" id="abuse_type_gore" name="abuse_type" value="gore">
<b>Gore</b>: Graphic and shocking videos or images depicting
severe harm to humans (or animals).
</label>
<label for="abuse_type_child_abuse">
<input type="radio" id="abuse_type_child_abuse" name="abuse_type" value="child_abuse">
<b>Child abuse</b>: Videos or images depicting inappropriate
touching or nudity of minors.
</label>
<label for="abuse_type_malware">
<input type="radio" id="abuse_type_malware" name="abuse_type" value="malware">
<b>Malware</b>: Software programs designed to cause harm to
computer systems.
</label>
<h3>Send</h3>
<div class="abuse_report_notification" style="display: none;"></div>
<p>
Abuse reports are manually reviewed. Normally this shouldn't
take more than 24 hours. During busy periods it can take
longer.
</p>
<div style="text-align: right;">
<button class="button_highlight abuse_report_submit" role="submit">
<i class="icon">send</i> Send
</button>
</div>
</form>
</template>
<template id="intro_popup">
<h3>Upload your own files here!</h3>
<p>
On pixeldrain you can share your files with large or small
groups of people. The sky is the limit!
</p>
<button class="intro_popup_close button_highlight round" style="float: right; margin: 0 10px 10px 0;">
<i class="icon">check</i> Got it!
</button>
</template>
<script src="/res/script/Chart.min.js"></script>
<script>
'use strict';
let apiEndpoint = '{{.APIEndpoint}}';
let captchaKey = '{{.Other.CaptchaKey}}';
let embeddedViewer = {{.Other.Embedded}};
let userAuthenticated = {{.Authenticated}};
let skyscraperType = "{{.Other.AdSkyscraperType}}"
{{template `util.js`}}
{{template `drawGraph.js`}}
{{template `Modal.js`}}
{{template `Toolbar.js`}}
{{template `Skyscraper.js`}}
{{template `EditWindow.js`}}
{{template `EmbedWindow.js`}}
{{template `QRCodeWindow.js`}}
{{template `DetailsWindow.js`}}
{{template `AbuseReportWindow.js`}}
{{template `ListNavigator.js`}}
{{template `IntroPopup.js`}}
{{template `Viewer.js`}}
{{template `AbuseViewer.js`}}
{{template `ImageViewer.js`}}
{{template `VideoViewer.js`}}
{{template `AudioViewer.js`}}
{{template `PDFViewer.js`}}
{{template `TextViewer.js`}}
{{template `FileViewer.js`}}
// DOMContentLoaded fires when the DOM tree has been parsed, but
// before the assets have loaded
window.addEventListener("DOMContentLoaded", () => {
// This info gets filled in on the server side to prevent having to
// make an API call right after the page loads. Just to slice
// another few milliseconds from the load time :)
new Viewer('{{.Other.Type}}', '{{.Other.ViewToken}}', {{.Other.APIResponse}})
})
</script>
{{ if and .Other.FileAdsEnabled .Other.UserAdsEnabled }}
{{ template "analytics" }}
{{ template "floating_ads" . }}
{{ end }}
</body>
</html>
{{end}}