diff --git a/.gitignore b/.gitignore index e6deb28..0fcd276 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .vscode main pdwebconf.toml -go.sum svelte/node_modules res/static/svelte/* diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ede3de1 --- /dev/null +++ b/go.sum @@ -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= diff --git a/res/include/script/dependencies/drawGraph.js b/res/include/script/dependencies/drawGraph.js deleted file mode 100644 index 4252869..0000000 --- a/res/include/script/dependencies/drawGraph.js +++ /dev/null @@ -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 } - } - ] - } - } - } - ); -} diff --git a/res/include/script/file_viewer/AbuseReportWindow.js b/res/include/script/file_viewer/AbuseReportWindow.js deleted file mode 100644 index 4eb00bb..0000000 --- a/res/include/script/file_viewer/AbuseReportWindow.js +++ /dev/null @@ -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) - } -} diff --git a/res/include/script/file_viewer/DetailsWindow.js b/res/include/script/file_viewer/DetailsWindow.js deleted file mode 100644 index 18d7b6b..0000000 --- a/res/include/script/file_viewer/DetailsWindow.js +++ /dev/null @@ -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 = `Name${escapeHTML(file.name)} - URL${file.link} - Mime Type${escapeHTML(file.mime_type)} - ID${file.id} - Size${formatDataVolume(file.size, 4)} ( ${formatThousands(file.size)} B ) - Bandwidth${formatDataVolume(file.bandwidth_used, 4)} - Upload Date${printDate(file.date_created, true, true, true)} - Description${escapeHTML(desc)}` - - 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() - }) -} diff --git a/res/include/script/file_viewer/EditWindow.js b/res/include/script/file_viewer/EditWindow.js deleted file mode 100644 index cda01f3..0000000 --- a/res/include/script/file_viewer/EditWindow.js +++ /dev/null @@ -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) - } -} diff --git a/res/include/script/file_viewer/EmbedWindow.js b/res/include/script/file_viewer/EmbedWindow.js deleted file mode 100644 index b814602..0000000 --- a/res/include/script/file_viewer/EmbedWindow.js +++ /dev/null @@ -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 = `` -} - -EmbedWindow.prototype.copyHTML = function () { - if (copyText(this.textarea.value)) { - console.log('Text copied') - this.btnCopyHTML.innerHTML = `content_copy 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 -} diff --git a/res/include/script/file_viewer/IntroPopup.js b/res/include/script/file_viewer/IntroPopup.js deleted file mode 100644 index b196f3e..0000000 --- a/res/include/script/file_viewer/IntroPopup.js +++ /dev/null @@ -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) -} diff --git a/res/include/script/file_viewer/ListNavigator.js b/res/include/script/file_viewer/ListNavigator.js deleted file mode 100644 index 990331f..0000000 --- a/res/include/script/file_viewer/ListNavigator.js +++ /dev/null @@ -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 ☑" // Check icon - this.btnShuffle.classList.add("button_highlight") - }else{ - document.querySelector("#btn_shuffle > span").innerHTML = "Shuffle ☐" // 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 = "\""" - + escapeHTML(name) - - navigatorItems[i].innerHTML = itemHtml - } -} diff --git a/res/include/script/file_viewer/QRCodeWindow.js b/res/include/script/file_viewer/QRCodeWindow.js deleted file mode 100644 index b0d7cec..0000000 --- a/res/include/script/file_viewer/QRCodeWindow.js +++ /dev/null @@ -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 - } -} diff --git a/res/include/script/file_viewer/Skyscraper.js b/res/include/script/file_viewer/Skyscraper.js deleted file mode 100644 index 3406651..0000000 --- a/res/include/script/file_viewer/Skyscraper.js +++ /dev/null @@ -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 = `` - } 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) -} diff --git a/res/include/script/file_viewer/Toolbar.js b/res/include/script/file_viewer/Toolbar.js deleted file mode 100644 index d57a715..0000000 --- a/res/include/script/file_viewer/Toolbar.js +++ /dev/null @@ -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, - }) -} diff --git a/res/include/script/file_viewer/Viewer.js b/res/include/script/file_viewer/Viewer.js deleted file mode 100644 index 9f9a047..0000000 --- a/res/include/script/file_viewer/Viewer.js +++ /dev/null @@ -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, '&') - .replace(//g, '>') - .replace(/"/g, '"'); -} - -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 -} diff --git a/res/include/script/file_viewer/viewer_scripts/AbuseViewer.js b/res/include/script/file_viewer/viewer_scripts/AbuseViewer.js deleted file mode 100644 index 67fc836..0000000 --- a/res/include/script/file_viewer/viewer_scripts/AbuseViewer.js +++ /dev/null @@ -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 -} diff --git a/res/include/script/file_viewer/viewer_scripts/AudioViewer.js b/res/include/script/file_viewer/viewer_scripts/AudioViewer.js deleted file mode 100644 index 5d9caff..0000000 --- a/res/include/script/file_viewer/viewer_scripts/AudioViewer.js +++ /dev/null @@ -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) -} diff --git a/res/include/script/file_viewer/viewer_scripts/FileViewer.js b/res/include/script/file_viewer/viewer_scripts/FileViewer.js deleted file mode 100644 index d0fee0f..0000000 --- a/res/include/script/file_viewer/viewer_scripts/FileViewer.js +++ /dev/null @@ -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 = `save 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) -} diff --git a/res/include/script/file_viewer/viewer_scripts/ImageViewer.js b/res/include/script/file_viewer/viewer_scripts/ImageViewer.js deleted file mode 100644 index a1ba3d2..0000000 --- a/res/include/script/file_viewer/viewer_scripts/ImageViewer.js +++ /dev/null @@ -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 - } -} diff --git a/res/include/script/file_viewer/viewer_scripts/PDFViewer.js b/res/include/script/file_viewer/viewer_scripts/PDFViewer.js deleted file mode 100644 index 6d2c446..0000000 --- a/res/include/script/file_viewer/viewer_scripts/PDFViewer.js +++ /dev/null @@ -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) -} diff --git a/res/include/script/file_viewer/viewer_scripts/TextViewer.js b/res/include/script/file_viewer/viewer_scripts/TextViewer.js deleted file mode 100644 index d7210a6..0000000 --- a/res/include/script/file_viewer/viewer_scripts/TextViewer.js +++ /dev/null @@ -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) -} diff --git a/res/include/script/file_viewer/viewer_scripts/VideoViewer.js b/res/include/script/file_viewer/viewer_scripts/VideoViewer.js deleted file mode 100644 index 3a3fa8a..0000000 --- a/res/include/script/file_viewer/viewer_scripts/VideoViewer.js +++ /dev/null @@ -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 = `upgrade 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 = `save Download` - downloadBtn.addEventListener("click", () => { this.viewer.toolbar.download() }) - fileDetails.appendChild(downloadBtn) - - container.appendChild(fileDetails) - parent.appendChild(container) - } -} diff --git a/res/include/style/file_manager.css b/res/include/style/file_manager.css deleted file mode 100644 index 51b69fc..0000000 --- a/res/include/style/file_manager.css +++ /dev/null @@ -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; -} diff --git a/res/include/style/viewer.css b/res/include/style/viewer.css deleted file mode 100644 index 15b980d..0000000 --- a/res/include/style/viewer.css +++ /dev/null @@ -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; -} diff --git a/res/template/advertisements.html b/res/template/advertisements.html index fc53760..f4b3386 100644 --- a/res/template/advertisements.html +++ b/res/template/advertisements.html @@ -1,5 +1,6 @@ {{ define "ad_headers" }} {{ if eq .Other.AdBannerType 3 }} + @@ -8,6 +9,7 @@ {{ define "banner_ads"}} {{ if eq .Other.AdBannerType 1 }} + - - -
- Share on:
- - - - - -
- -
-
{{template "spinner.svg" .}}
-
- -
- -
-
- - -
- {{ if and .Other.FileAdsEnabled .Other.UserAdsEnabled }} - {{ template "banner_ads" . }} - {{ else if not .Other.UserAdsEnabled }} -
- Thank you for supporting pixeldrain! -
- {{ else if not .Other.FileAdsEnabled }} -
- The uploader of this file disabled advertisements. You can do the same for only €2 per month! -
- {{ end }} -
- - - - - - - - - - - - - - - - - - {{ if and .Other.FileAdsEnabled .Other.UserAdsEnabled }} - {{ template "analytics" }} - {{ template "floating_ads" . }} - {{ end }} - - -{{end}} diff --git a/webcontroller/file_viewer.go b/webcontroller/file_viewer.go index 9851d74..38d309d 100644 --- a/webcontroller/file_viewer.go +++ b/webcontroller/file_viewer.go @@ -2,7 +2,6 @@ package webcontroller import ( "fmt" - "math/rand" "net/http" "strings" "time" @@ -24,113 +23,19 @@ func browserCompat(ua string) bool { return strings.Contains(ua, "MSIE") || strings.Contains(ua, "Trident/7.0") } -type viewerData struct { - Type string // file or list - CaptchaKey string - ViewToken string - AdBannerType int - AdSkyscraperType string - AdFloaterType int - 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)) - } +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) serveFileViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - var err error if p.ByName("id") == "demo" { - wc.serveFileViewerDemo(w, r, 1, "a-ads") // 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, "") + wc.serveViewerDemo(w, r) // Required for a-ads.com quality check return } @@ -141,9 +46,9 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request, return } + var err error var ids = strings.Split(p.ByName("id"), ",") - - templateData := wc.newTemplateData(w, r) + var templateData = wc.newTemplateData(w, r) var files []pixelapi.ListFile 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) - var vd = viewerData{ + var vd = fileViewerData{ CaptchaKey: wc.captchaKey(), ViewToken: wc.viewTokenOrBust(), - FileAdsEnabled: files[0].ShowAds, UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay), } - vd.adType(files) if len(ids) > 1 { 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 - var templateName = "file_viewer" - if browserCompat(r.UserAgent()) { - templateName = "file_viewer_compat" - } - for _, file := range files { if file.AbuseType != "" { 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) 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) serveFileViewerDemo(w http.ResponseWriter, r *http.Request, banner int, scraper string) { - templateData := wc.newTemplateData(w, r) - templateData.Other = viewerData{ - 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) { + // 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 { @@ -269,26 +145,19 @@ func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request, templateData.Title = fmt.Sprintf("%s ~ pixeldrain", list.Title) templateData.OGData = wc.metadataFromList(list) - var vd = viewerData{ + var vd = fileViewerData{ Type: "list", CaptchaKey: wc.captchaSiteKey, ViewToken: wc.viewTokenOrBust(), - FileAdsEnabled: list.Files[0].ShowAds, UserAdsEnabled: !(templateData.Authenticated && templateData.User.Subscription.DisableAdDisplay), APIResponse: list, } - vd.adType(list.Files) if _, ok := r.URL.Query()["embed"]; ok { vd.Embedded = true } templateData.Other = vd - var templateName = "file_viewer" - if browserCompat(r.UserAgent()) { - templateName = "file_viewer_compat" - } - for _, file := range list.Files { if file.AbuseType != "" { 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) 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) 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) + } +} diff --git a/webcontroller/file_viewer_svelte.go b/webcontroller/file_viewer_svelte.go deleted file mode 100644 index 621ff48..0000000 --- a/webcontroller/file_viewer_svelte.go +++ /dev/null @@ -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) - } -}