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

1
.gitignore vendored
View File

@@ -1,6 +1,5 @@
.vscode .vscode
main main
pdwebconf.toml pdwebconf.toml
go.sum
svelte/node_modules svelte/node_modules
res/static/svelte/* res/static/svelte/*

69
go.sum Normal file
View File

@@ -0,0 +1,69 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Fornaxian/config v0.0.0-20180915150834-ac41cf746a70 h1:yRkXab8h+BAWEphLE0qexJVxIOdPgw+3T9VSLuyPXus=
github.com/Fornaxian/config v0.0.0-20180915150834-ac41cf746a70/go.mod h1:Ig5am30IOP/eqsjogI1TuSlOTIeTPHoMOpYYM1bisww=
github.com/Fornaxian/log v0.0.0-20190617093801-1c7ce9a7c9b3 h1:PfKr7anK3z4kLG9V6BbbKOVFhVaGEAJi4HxXCAa+QeU=
github.com/Fornaxian/log v0.0.0-20190617093801-1c7ce9a7c9b3/go.mod h1:jdnyerqAlXJJpQmpyrdmSYMitRaRZ8RejEXuXz6n5QY=
github.com/Fornaxian/pd_mime_type v0.0.0-20200204165508-2815edf3a145 h1:2a8cFtVwEvK7NeimwAEoSUdf4hC80cXpnj3s4pHga+c=
github.com/Fornaxian/pd_mime_type v0.0.0-20200204165508-2815edf3a145/go.mod h1:Ew6h8nlacK6H8aABMDUYN3uaO4Rpw2HLKQZ2ntEybqM=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jhillyerd/enmime v0.8.4/go.mod h1:SWWlqKsXJMl00Nnuh04RSu6XoEo83B/VaujD/xzoBP0=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/scylladb/gocql v1.5.0 h1:tWaA08A8IprjgtBNPTVn8RkdiwEFUc85VcLA0E8JjH0=
github.com/scylladb/gocql v1.5.0/go.mod h1:S154F0u6zQlF3JjuHAidQIExQf9H45yT8z68h0FQYdU=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

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" }} {{ define "ad_headers" }}
{{ if eq .Other.AdBannerType 3 }} {{ if eq .Other.AdBannerType 3 }}
<!-- Adshares -->
<script type="text/javascript"> <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); !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> </script>
@@ -8,6 +9,7 @@
{{ define "banner_ads"}} {{ define "banner_ads"}}
{{ if eq .Other.AdBannerType 1 }} {{ 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 --> <!-- 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" <iframe class="sponsors_banner"
data-aa="73974" data-aa="73974"
@@ -44,6 +46,7 @@
<img src="/res/img/misc/banner_amarula_jobs.png" style="width: 100%; height: 100%" /> <img src="/res/img/misc/banner_amarula_jobs.png" style="width: 100%; height: 100%" />
</a> </a>
{{ else if eq .Other.AdBannerType 6 }} {{ else if eq .Other.AdBannerType 6 }}
<!-- Adsterra -->
<div class="sponsors_banner" style="display: inline-block; width: 728px; height: 90px;"> <div class="sponsors_banner" style="display: inline-block; width: 728px; height: 90px;">
<script type="text/javascript"> <script type="text/javascript">
atOptions = { atOptions = {
@@ -57,10 +60,12 @@
</script> </script>
</div> </div>
{{ else if eq .Other.AdBannerType 7 }} {{ 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"> <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%" /> <img src="/res/img/misc/brave-728x90.png" style="width: 100%; height: 100%" />
</a> </a>
{{ else if eq .Other.AdBannerType 8 }} {{ else if eq .Other.AdBannerType 8 }}
<!-- Pro ad 1 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"/> <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;"> <div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -69,6 +74,7 @@
</div> </div>
</div> </div>
{{ else if eq .Other.AdBannerType 9 }} {{ else if eq .Other.AdBannerType 9 }}
<!-- Pro ad 2 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"/> <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;"> <div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -77,6 +83,7 @@
</div> </div>
</div> </div>
{{ else if eq .Other.AdBannerType 10 }} {{ else if eq .Other.AdBannerType 10 }}
<!-- Pro ad 3 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"/> <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;"> <div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -85,6 +92,7 @@
</div> </div>
</div> </div>
{{ else if eq .Other.AdBannerType 11 }} {{ else if eq .Other.AdBannerType 11 }}
<!-- Pro ad 4 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"/> <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;"> <div style="margin: 4px; display: inline-block; vertical-align: middle;">
@@ -117,11 +125,13 @@
</script> </script>
</div> </div>
{{ else if eq .Other.AdBannerType 15 }} {{ else if eq .Other.AdBannerType 15 }}
<!-- Pixfuture -->
<!-- AuctionX Display platform tag START --> <!-- AuctionX Display platform tag START -->
<div id="27517x728x90x4605x_ADSLOT1" clickTrack="%%CLICK_URL_ESC%%" style="display: block; margin: auto;"></div> <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> <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 --> <!-- AuctionX Display platform tag END -->
{{ else if eq .Other.AdBannerType 16 }} {{ else if eq .Other.AdBannerType 16 }}
<!-- Publisherrest ad 1 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"> <a href="https://pixeldrain.com/vouchercodes/" class="button button_highlight" style="margin: 8px;">
<i class="icon">shopping_cart</i> <i class="icon">shopping_cart</i>
@@ -130,6 +140,7 @@
</a> </a>
</div> </div>
{{ else if eq .Other.AdBannerType 17 }} {{ else if eq .Other.AdBannerType 17 }}
<!-- Publisherrest ad 2 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"> <a href="https://pixeldrain.com/vouchercodes/" class="button button_highlight" style="margin: 8px;">
<i class="icon">shopping_cart</i> <i class="icon">shopping_cart</i>
@@ -138,6 +149,7 @@
</a> </a>
</div> </div>
{{ else if eq .Other.AdBannerType 18 }} {{ else if eq .Other.AdBannerType 18 }}
<!-- Publisherrest ad 3 -->
<div style="text-align: center; line-height: 1.4em; font-size: 22px;"> <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;"> <a href="https://pixeldrain.com/vouchercodes/" class="button button_highlight" style="margin: 8px;">
<i class="icon">shopping_cart</i> <i class="icon">shopping_cart</i>
@@ -147,34 +159,3 @@
</div> </div>
{{ end }} {{ end }}
{{ 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}}

View File

@@ -2,7 +2,6 @@ package webcontroller
import ( import (
"fmt" "fmt"
"math/rand"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@@ -24,113 +23,19 @@ func browserCompat(ua string) bool {
return strings.Contains(ua, "MSIE") || strings.Contains(ua, "Trident/7.0") return strings.Contains(ua, "MSIE") || strings.Contains(ua, "Trident/7.0")
} }
type viewerData struct { type fileViewerData struct {
Type string // file or list Type string `json:"type"` // file or list
CaptchaKey string APIResponse interface{} `json:"api_response"`
ViewToken string CaptchaKey string `json:"captcha_key"`
AdBannerType int ViewToken string `json:"view_token"`
AdSkyscraperType string Embedded bool `json:"embedded"`
AdFloaterType int UserAdsEnabled bool `json:"user_ads_enabled"`
AdPopupType int
FileAdsEnabled bool
UserAdsEnabled bool
Embedded bool
APIResponse interface{}
}
func (vd *viewerData) adType(files []pixelapi.ListFile) {
if len(files) == 0 {
return
} else if !vd.FileAdsEnabled || !vd.UserAdsEnabled {
return
}
var avgSize int64
for _, v := range files {
avgSize += v.Size
}
avgSize /= int64(len(files))
const (
// Banners
none = 0
aAds = 1
patreon = 2
adshares = 3
amarulaSolutions = 4
adMaven = 5
adSterra = 6
brave = 7
pdpro1 = 8
pdpro2 = 9
pdpro3 = 10
pdpro4 = 11
clickAduBanner = 12
amarulaElectronics = 13
adsPlus = 14
pixFuture = 15
publisherrest1 = 16
publisherrest2 = 17
publisherrest3 = 18
// Skyscrapers
aAdsSkyscraper = "a-ads"
pixfutureSkyscraper = "pixfuture"
adsPlusSkyscraper = "adsplus"
adsharesSkyscraper = "adshares"
// Floaters
// propellerFloat = 1
// adSterraFloat = 2
// adMavenFloat = 3
// Popunders
// clickAduPopup = 1
// propellerPopup = 2
)
// Intn returns a number up to n, but never n itself. So to get a random 0
// or 1 we need to give it n=2. We can use this function to make other
// splits like 1/3 1/4, etc
switch i := rand.Intn(20); i {
case 0:
vd.AdBannerType = publisherrest1 // 5%, total 5%
case 1:
vd.AdBannerType = publisherrest2 // 5%, total 10%
case 2:
vd.AdBannerType = publisherrest3 // 5%, total 15%
case 3, 4, 5:
vd.AdBannerType = brave // 15%, total 30%
case 6, 7, 8, 9, 10, 11, 12:
vd.AdBannerType = adsPlus // 35%, total 65%
case 13, 14, 15, 16, 17, 18, 19:
vd.AdBannerType = pixFuture // 35%, total 100%
default:
panic(fmt.Errorf("random number generator returned unrecognised number: %d", i))
}
switch i := rand.Intn(2); i {
case 0:
vd.AdSkyscraperType = aAdsSkyscraper
case 1:
vd.AdSkyscraperType = pixfutureSkyscraper
default:
panic(fmt.Errorf("random number generator returned unrecognised number: %d", i))
}
} }
// ServeFileViewer controller for GET /u/:id // ServeFileViewer controller for GET /u/:id
func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) { func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var err error
if p.ByName("id") == "demo" { if p.ByName("id") == "demo" {
wc.serveFileViewerDemo(w, r, 1, "a-ads") // Required for a-ads.com quality check wc.serveViewerDemo(w, r) // Required for a-ads.com quality check
return
} else if p.ByName("id") == "adsplus" {
wc.serveFileViewerDemo(w, r, 14, "")
return
} else if p.ByName("id") == "pixfuture" {
wc.serveFileViewerDemo(w, r, 15, "")
return return
} }
@@ -141,9 +46,9 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
return return
} }
var err error
var ids = strings.Split(p.ByName("id"), ",") var ids = strings.Split(p.ByName("id"), ",")
var templateData = wc.newTemplateData(w, r)
templateData := wc.newTemplateData(w, r)
var files []pixelapi.ListFile var files []pixelapi.ListFile
for _, id := range ids { for _, id := range ids {
@@ -166,13 +71,11 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
templateData.OGData = wc.metadataFromFile(files[0].FileInfo) templateData.OGData = wc.metadataFromFile(files[0].FileInfo)
var vd = viewerData{ var vd = fileViewerData{
CaptchaKey: wc.captchaKey(), CaptchaKey: wc.captchaKey(),
ViewToken: wc.viewTokenOrBust(), ViewToken: wc.viewTokenOrBust(),
FileAdsEnabled: files[0].ShowAds,
UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay), UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay),
} }
vd.adType(files)
if len(ids) > 1 { if len(ids) > 1 {
templateData.Title = fmt.Sprintf("%d files on pixeldrain", len(files)) templateData.Title = fmt.Sprintf("%d files on pixeldrain", len(files))
@@ -195,11 +98,6 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
templateData.Other = vd templateData.Other = vd
var templateName = "file_viewer"
if browserCompat(r.UserAgent()) {
templateName = "file_viewer_compat"
}
for _, file := range files { for _, file := range files {
if file.AbuseType != "" { if file.AbuseType != "" {
w.WriteHeader(http.StatusUnavailableForLegalReasons) w.WriteHeader(http.StatusUnavailableForLegalReasons)
@@ -207,47 +105,25 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
} }
} }
var templateName = "file_viewer_svelte"
if browserCompat(r.UserAgent()) {
templateName = "file_viewer_compat"
}
err = wc.templates.Get().ExecuteTemplate(w, templateName, templateData) err = wc.templates.Get().ExecuteTemplate(w, templateName, templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") { if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template file_viewer: %s", err) log.Error("Error executing template file_viewer: %s", err)
} }
} }
// ServeFileViewerDemo is a dummy API response that responds with info about a func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
// non-existent demo file. This is required by the a-ads ad network to allow for // If the user agent is Wget we redirect it to the API so that the file can
// automatic checking of the presence of the ad unit on this page. // be downloaded directly
func (wc *WebController) serveFileViewerDemo(w http.ResponseWriter, r *http.Request, banner int, scraper string) { if strings.HasPrefix(r.UserAgent(), "Wget/") {
templateData := wc.newTemplateData(w, r) http.Redirect(w, r, "/api/list/"+p.ByName("id")+"/zip", http.StatusSeeOther)
templateData.Other = viewerData{ return
Type: "file",
CaptchaKey: wc.captchaSiteKey,
AdBannerType: banner, // Always show a-ads on the demo page
AdSkyscraperType: scraper,
FileAdsEnabled: true,
UserAdsEnabled: true,
APIResponse: map[string]interface{}{
"id": "demo",
"name": "Demo file",
"date_upload": "2017-01-01 12:34:56",
"date_lastview": "2017-01-01 12:34:56",
"size": 123456789,
"views": 1,
"bandwidth_used": 123456789,
"mime_type": "text/demo",
"description": "A file to demonstrate the viewer page",
"mime_image": "/res/img/mime/text.png",
"thumbnail": "/res/img/mime/text.png",
"abuse_type": "",
},
}
err := wc.templates.Get().ExecuteTemplate(w, "file_viewer", templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error rendering demo file: %s", err)
}
} }
// ServeListViewer controller for GET /l/:id
func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var templateData = wc.newTemplateData(w, r) var templateData = wc.newTemplateData(w, r)
var list, err = templateData.PixelAPI.GetListID(p.ByName("id")) var list, err = templateData.PixelAPI.GetListID(p.ByName("id"))
if err != nil { if err != nil {
@@ -269,26 +145,19 @@ func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request,
templateData.Title = fmt.Sprintf("%s ~ pixeldrain", list.Title) templateData.Title = fmt.Sprintf("%s ~ pixeldrain", list.Title)
templateData.OGData = wc.metadataFromList(list) templateData.OGData = wc.metadataFromList(list)
var vd = viewerData{ var vd = fileViewerData{
Type: "list", Type: "list",
CaptchaKey: wc.captchaSiteKey, CaptchaKey: wc.captchaSiteKey,
ViewToken: wc.viewTokenOrBust(), ViewToken: wc.viewTokenOrBust(),
FileAdsEnabled: list.Files[0].ShowAds,
UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay), UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay),
APIResponse: list, APIResponse: list,
} }
vd.adType(list.Files)
if _, ok := r.URL.Query()["embed"]; ok { if _, ok := r.URL.Query()["embed"]; ok {
vd.Embedded = true vd.Embedded = true
} }
templateData.Other = vd templateData.Other = vd
var templateName = "file_viewer"
if browserCompat(r.UserAgent()) {
templateName = "file_viewer_compat"
}
for _, file := range list.Files { for _, file := range list.Files {
if file.AbuseType != "" { if file.AbuseType != "" {
w.WriteHeader(http.StatusUnavailableForLegalReasons) w.WriteHeader(http.StatusUnavailableForLegalReasons)
@@ -296,8 +165,44 @@ func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request,
} }
} }
var templateName = "file_viewer_svelte"
if browserCompat(r.UserAgent()) {
templateName = "file_viewer_compat"
}
err = wc.templates.Get().ExecuteTemplate(w, templateName, templateData) err = wc.templates.Get().ExecuteTemplate(w, templateName, templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") { if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template file_viewer: %s", err) log.Error("Error executing template file_viewer: %s", err)
} }
} }
// ServeFileViewerDemo is a dummy API response that responds with info about a
// non-existent demo file. This is required by the a-ads ad network to allow for
// automatic checking of the presence of the ad unit on this page.
func (wc *WebController) serveViewerDemo(w http.ResponseWriter, r *http.Request) {
templateData := wc.newTemplateData(w, r)
templateData.Other = fileViewerData{
Type: "file",
CaptchaKey: wc.captchaSiteKey,
UserAdsEnabled: true,
APIResponse: map[string]interface{}{
"id": "demo",
"name": "Demo file",
"date_upload": "2017-01-01 12:34:56",
"date_lastview": "2017-01-01 12:34:56",
"size": 123456789,
"views": 1,
"bandwidth_used": 123456789,
"mime_type": "text/demo",
"description": "A file to demonstrate the viewer page",
"mime_image": "/res/img/mime/text.png",
"thumbnail": "/res/img/mime/text.png",
"abuse_type": "",
"show_ads": true,
},
}
err := wc.templates.Get().ExecuteTemplate(w, "file_viewer_svelte", templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error rendering demo file: %s", err)
}
}

View File

@@ -1,196 +0,0 @@
package webcontroller
import (
"fmt"
"net/http"
"strings"
"time"
"fornaxian.tech/pixeldrain_api_client/pixelapi"
"github.com/Fornaxian/log"
"github.com/julienschmidt/httprouter"
)
type fileViewerData struct {
Type string `json:"type"` // file or list
APIResponse interface{} `json:"api_response"`
CaptchaKey string `json:"captcha_key"`
ViewToken string `json:"view_token"`
Embedded bool `json:"embedded"`
UserAdsEnabled bool `json:"user_ads_enabled"`
}
// ServeFileViewer controller for GET /u/:id
func (wc *WebController) serveSvelteFile(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
if p.ByName("id") == "demo" {
wc.serveSvelteViewerDemo(w, r) // Required for a-ads.com quality check
return
}
// If the user agent is Wget we redirect it to the API so that the file can
// be downloaded directly
if strings.HasPrefix(r.UserAgent(), "Wget/") {
http.Redirect(w, r, "/api/file/"+p.ByName("id"), http.StatusSeeOther)
return
}
var err error
var ids = strings.Split(p.ByName("id"), ",")
var templateData = wc.newTemplateData(w, r)
var files []pixelapi.ListFile
for _, id := range ids {
inf, err := templateData.PixelAPI.GetFileInfo(id)
if err != nil {
if pixelapi.ErrIsServerError(err) {
wc.templates.Get().ExecuteTemplate(w, "500", templateData)
return
}
continue
}
files = append(files, pixelapi.ListFile{FileInfo: inf})
}
if len(files) == 0 {
w.WriteHeader(http.StatusNotFound)
wc.templates.Get().ExecuteTemplate(w, "file_not_found", templateData)
return
}
templateData.OGData = wc.metadataFromFile(files[0].FileInfo)
var vd = fileViewerData{
CaptchaKey: wc.captchaKey(),
ViewToken: wc.viewTokenOrBust(),
UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay),
}
if len(ids) > 1 {
templateData.Title = fmt.Sprintf("%d files on pixeldrain", len(files))
vd.Type = "list"
vd.APIResponse = pixelapi.ListInfo{
Success: true,
Title: "Multiple files",
DateCreated: time.Now(),
Files: files,
}
} else {
templateData.Title = fmt.Sprintf("%s ~ pixeldrain", files[0].Name)
vd.Type = "file"
vd.APIResponse = files[0].FileInfo
}
if _, ok := r.URL.Query()["embed"]; ok {
vd.Embedded = true
}
templateData.Other = vd
for _, file := range files {
if file.AbuseType != "" {
w.WriteHeader(http.StatusUnavailableForLegalReasons)
break
}
}
var templateName = "file_viewer_svelte"
if browserCompat(r.UserAgent()) {
templateName = "file_viewer_compat"
}
err = wc.templates.Get().ExecuteTemplate(w, templateName, templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template file_viewer: %s", err)
}
}
func (wc *WebController) serveSvelteList(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
// If the user agent is Wget we redirect it to the API so that the file can
// be downloaded directly
if strings.HasPrefix(r.UserAgent(), "Wget/") {
http.Redirect(w, r, "/api/list/"+p.ByName("id")+"/zip", http.StatusSeeOther)
return
}
var templateData = wc.newTemplateData(w, r)
var list, err = templateData.PixelAPI.GetListID(p.ByName("id"))
if err != nil {
if err, ok := err.(pixelapi.Error); ok && err.Status == http.StatusNotFound {
w.WriteHeader(http.StatusNotFound)
wc.templates.Get().ExecuteTemplate(w, "list_not_found", templateData)
} else {
log.Error("API request error occurred: %s", err)
w.WriteHeader(http.StatusInternalServerError)
wc.templates.Get().ExecuteTemplate(w, "500", templateData)
}
return
}
if len(list.Files) == 0 {
w.WriteHeader(http.StatusNotFound)
wc.templates.Get().ExecuteTemplate(w, "list_not_found", templateData)
return
}
templateData.Title = fmt.Sprintf("%s ~ pixeldrain", list.Title)
templateData.OGData = wc.metadataFromList(list)
var vd = fileViewerData{
Type: "list",
CaptchaKey: wc.captchaSiteKey,
ViewToken: wc.viewTokenOrBust(),
UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay),
APIResponse: list,
}
if _, ok := r.URL.Query()["embed"]; ok {
vd.Embedded = true
}
templateData.Other = vd
for _, file := range list.Files {
if file.AbuseType != "" {
w.WriteHeader(http.StatusUnavailableForLegalReasons)
break
}
}
var templateName = "file_viewer_svelte"
if browserCompat(r.UserAgent()) {
templateName = "file_viewer_compat"
}
err = wc.templates.Get().ExecuteTemplate(w, templateName, templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template file_viewer: %s", err)
}
}
// ServeFileViewerDemo is a dummy API response that responds with info about a
// non-existent demo file. This is required by the a-ads ad network to allow for
// automatic checking of the presence of the ad unit on this page.
func (wc *WebController) serveSvelteViewerDemo(w http.ResponseWriter, r *http.Request) {
templateData := wc.newTemplateData(w, r)
templateData.Other = fileViewerData{
Type: "file",
CaptchaKey: wc.captchaSiteKey,
UserAdsEnabled: true,
APIResponse: map[string]interface{}{
"id": "demo",
"name": "Demo file",
"date_upload": "2017-01-01 12:34:56",
"date_lastview": "2017-01-01 12:34:56",
"size": 123456789,
"views": 1,
"bandwidth_used": 123456789,
"mime_type": "text/demo",
"description": "A file to demonstrate the viewer page",
"mime_image": "/res/img/mime/text.png",
"thumbnail": "/res/img/mime/text.png",
"abuse_type": "",
"show_ads": true,
},
}
err := wc.templates.Get().ExecuteTemplate(w, "file_viewer_svelte", templateData)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error rendering demo file: %s", err)
}
}