add viewer scripts
This commit is contained in:
@@ -19,14 +19,9 @@ type List struct {
|
|||||||
|
|
||||||
// ListFile information object from the pixeldrain API
|
// ListFile information object from the pixeldrain API
|
||||||
type ListFile struct {
|
type ListFile struct {
|
||||||
ID string `json:"id"`
|
DetailHREF string `json:"detail_href"`
|
||||||
DetailHREF string `json:"detail_href"`
|
Description string `json:"description"`
|
||||||
Name string `json:"name"`
|
FileInfo `json:""`
|
||||||
Description string `json:"description"`
|
|
||||||
DateCreated time.Time `json:"date_created"`
|
|
||||||
DateLastView time.Time `json:"date_last_view"`
|
|
||||||
Views int64 `json:"views"`
|
|
||||||
BandwidthUsed uint64 `json:"bandwidth_used"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetList get a List from the pixeldrain API. Errors will be available through
|
// GetList get a List from the pixeldrain API. Errors will be available through
|
||||||
|
@@ -1,182 +0,0 @@
|
|||||||
/* global Viewer */
|
|
||||||
|
|
||||||
var ListNavigator = {
|
|
||||||
length: 0,
|
|
||||||
position: 0,
|
|
||||||
data: [],
|
|
||||||
history: [],
|
|
||||||
shuffle: false,
|
|
||||||
|
|
||||||
nextItem: function(){
|
|
||||||
if(!Viewer.isList){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(this.shuffle){
|
|
||||||
this.randItem();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.position >= this.length){
|
|
||||||
this.position = 0;
|
|
||||||
}else{
|
|
||||||
this.position++;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setItem(this.position);
|
|
||||||
},
|
|
||||||
|
|
||||||
previousItem: function(){
|
|
||||||
if(!Viewer.isList){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.position === 0){
|
|
||||||
this.position = this.length - 1;
|
|
||||||
}else{
|
|
||||||
this.position--;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setItem(this.position);
|
|
||||||
},
|
|
||||||
|
|
||||||
randItem: function(){
|
|
||||||
if(!Viewer.isList){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid viewing the same file multiple times
|
|
||||||
var rand;
|
|
||||||
do {
|
|
||||||
rand = Math.round(Math.random() * this.length);
|
|
||||||
console.log("rand is " + rand);
|
|
||||||
} while(this.inHistory(rand));
|
|
||||||
|
|
||||||
this.setItem(rand);
|
|
||||||
},
|
|
||||||
|
|
||||||
setItem: function(index){
|
|
||||||
if(index >= this.length){
|
|
||||||
this.position = 0;
|
|
||||||
}else{
|
|
||||||
this.position = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the URL hash
|
|
||||||
location.hash = "item=" + this.position;
|
|
||||||
Viewer.setFile(this.data[this.position]);
|
|
||||||
|
|
||||||
this.addToHistory(index);
|
|
||||||
|
|
||||||
$("#list_navigator").find("*").removeClass("file_button_selected");
|
|
||||||
var selectedItem = $("#list_navigator div").eq(this.position);
|
|
||||||
selectedItem.addClass("file_button_selected");
|
|
||||||
var itemWidth = selectedItem.outerWidth(true);
|
|
||||||
|
|
||||||
// This centers the scroll bar exactly on the selected item
|
|
||||||
$("#list_navigator").animate(
|
|
||||||
{scrollLeft: ((this.position * itemWidth) + (itemWidth / 2)) - ($("#list_navigator").width() / 2)},
|
|
||||||
{duration: 1000, queue: false}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.loadThumbnails(index);
|
|
||||||
},
|
|
||||||
|
|
||||||
addToHistory: function(index){
|
|
||||||
if(this.history.length >= (this.length - 6)){
|
|
||||||
this.history.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.history.push(index);
|
|
||||||
},
|
|
||||||
|
|
||||||
inHistory: function(index){
|
|
||||||
var i = $.inArray(index, this.history); // Returns -1 when the item is not found
|
|
||||||
|
|
||||||
return (i !== -1); // Return false when it's not in the array
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleShuffle: function(){
|
|
||||||
this.shuffle = !this.shuffle; // :P
|
|
||||||
|
|
||||||
if(this.shuffle){
|
|
||||||
$("#btnShuffle > span").html("Shuffle ☑"); // Check icon
|
|
||||||
$("#btnShuffle").addClass("button_highlight");
|
|
||||||
}else{
|
|
||||||
$("#btnShuffle > span").html("Shuffle ☐"); // Empty checkbox
|
|
||||||
$("#btnShuffle").removeClass("button_highlight");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loadThumbnails: function(index){
|
|
||||||
var startPos = +index - 50;
|
|
||||||
var 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;
|
|
||||||
}
|
|
||||||
console.log(endPos);
|
|
||||||
|
|
||||||
var navigatorItems = document.getElementById("list_navigator").children
|
|
||||||
|
|
||||||
for (i = startPos; i <= endPos; i++){
|
|
||||||
if (navigatorItems[i].innerHTML.includes("list_item_thumbnail")) {
|
|
||||||
continue; // Thumbnail already loaded
|
|
||||||
}
|
|
||||||
|
|
||||||
var thumb = "/api/file/" + this.data[i].id + "/thumbnail?width=48&height=48";
|
|
||||||
var name = this.data[i].name;
|
|
||||||
|
|
||||||
var itemHtml = "<img src=\"" + thumb + "\" "
|
|
||||||
+ "class=\"list_item_thumbnail\" alt=\"" + escapeHTML(name) + "\"/>"
|
|
||||||
+ escapeHTML(name);
|
|
||||||
|
|
||||||
navigatorItems[i].innerHTML = itemHtml;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(data){
|
|
||||||
this.data = data;
|
|
||||||
this.length = data.length;
|
|
||||||
|
|
||||||
var listHTML = "";
|
|
||||||
data.forEach(function(item, i){
|
|
||||||
var filename;
|
|
||||||
if(item.name !== "null"){
|
|
||||||
filename = item.name;
|
|
||||||
}else{
|
|
||||||
filename = "Removed File";
|
|
||||||
}
|
|
||||||
|
|
||||||
listHTML += "<div class=\"file_button list_item\" "
|
|
||||||
+ "onClick=\"ListNavigator.setItem('" + i + "')\">"
|
|
||||||
+ escapeHTML(filename) + "<br>"
|
|
||||||
+ "</div>";
|
|
||||||
});
|
|
||||||
document.getElementById("list_navigator").innerHTML = listHTML;
|
|
||||||
|
|
||||||
document.getElementById("btnDownloadList").style.display = "";
|
|
||||||
document.getElementById("btnShuffle").style.display = "";
|
|
||||||
|
|
||||||
// Make the navigator visible
|
|
||||||
document.getElementById("list_navigator").style.display = "inline-block";
|
|
||||||
|
|
||||||
// Skip to the file defined in the link hash
|
|
||||||
if(Number.isInteger(parseInt(getHashValue("item")))){
|
|
||||||
this.setItem(parseInt(getHashValue("item")));
|
|
||||||
}else{
|
|
||||||
this.setItem(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Misc function, don't really know where else to put it
|
|
||||||
function getHashValue(key) {
|
|
||||||
var matches = location.hash.match(new RegExp(key + '=([^&]*)'));
|
|
||||||
return matches ? matches[1] : null;
|
|
||||||
}
|
|
@@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* Time for a more Java-like approach.
|
|
||||||
*
|
|
||||||
* Feel free to use this of course
|
|
||||||
*
|
|
||||||
* Made by Fornax
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global Viewer */
|
|
||||||
|
|
||||||
var Toolbar = {
|
|
||||||
visible: false,
|
|
||||||
toggle: function () {
|
|
||||||
if (this.visible) {
|
|
||||||
if (Sharebar.visible) {
|
|
||||||
Sharebar.toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("toolbar").style.left = "-8em";
|
|
||||||
document.getElementById("filepreview").style.left = "0px";
|
|
||||||
document.getElementById("button_toggle_toolbar").classList.remove("button_highlight");
|
|
||||||
|
|
||||||
this.visible = false;
|
|
||||||
} else {
|
|
||||||
document.getElementById("toolbar").style.left = "0px";
|
|
||||||
document.getElementById("filepreview").style.left = "8em";
|
|
||||||
document.getElementById("button_toggle_toolbar").classList.add("button_highlight");
|
|
||||||
|
|
||||||
this.visible = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
download: function () {
|
|
||||||
var triggerDL = function(){
|
|
||||||
document.getElementById("download_frame").src = "/api/file/" + Viewer.currentFile + "?download";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (captchaKey === "a"){
|
|
||||||
// If the server doesn't support captcha there's no use in checking
|
|
||||||
// availability
|
|
||||||
triggerDL();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$.getJSON(
|
|
||||||
apiEndpoint + "/file/" + Viewer.currentFile + "/availability"
|
|
||||||
).done(function(data){
|
|
||||||
if(data.success === true){
|
|
||||||
// Downloading is allowed, start the download
|
|
||||||
triggerDL();
|
|
||||||
}
|
|
||||||
}).fail(function(data){
|
|
||||||
if(data.responseJSON.success === false) {
|
|
||||||
var popupDiv = document.getElementById("captcha_popup");
|
|
||||||
var popupTitle = document.getElementById("captcha_popup_title");
|
|
||||||
var popupContent = document.getElementById("captcha_popup_content");
|
|
||||||
var popupCaptcha = document.getElementById("captcha_popup_captcha");
|
|
||||||
|
|
||||||
if(data.responseJSON.value === "file_rate_limited_captcha_required") {
|
|
||||||
popupTitle.innerText = "Rate limiting enabled!";
|
|
||||||
popupContent.innerText = "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(data.responseJSON.value === "virus_detected_captcha_required"){
|
|
||||||
popupTitle.innerText = "Malware warning!";
|
|
||||||
popupContent.innerText = "According to our scanning "+
|
|
||||||
"systems this file may contain a virus of type '"+
|
|
||||||
data.responseJSON.extra+"'. You can continue "+
|
|
||||||
"downloading this file at your own risk, but you will "+
|
|
||||||
"have to prove that you're a human first.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the recaptcha script with a load function
|
|
||||||
$.getScript("https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit");
|
|
||||||
|
|
||||||
popupDiv.style.opacity = "1";
|
|
||||||
popupDiv.style.visibility = "visible";
|
|
||||||
}else{
|
|
||||||
// No JSON, try download anyway
|
|
||||||
triggerDL();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
downloadList: function(){
|
|
||||||
if(!Viewer.isList){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.getElementById("download_frame").src = "/api/list/" + Viewer.listId + "/zip";
|
|
||||||
},
|
|
||||||
copyUrl: function () {
|
|
||||||
if(copyText(window.location.href)) {
|
|
||||||
console.log('Text copied');
|
|
||||||
$("#btnCopy>span").text("Copied!");
|
|
||||||
document.getElementById("btnCopy").classList.add("button_highlight")
|
|
||||||
} else {
|
|
||||||
console.log('Copying not supported');
|
|
||||||
$("#btnCopy>span").text("Error!");
|
|
||||||
alert("Your browser does not support copying text.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return to normal
|
|
||||||
setTimeout(function(){
|
|
||||||
$("#btnCopy>span").text("Copy");
|
|
||||||
document.getElementById("btnCopy").classList.remove("button_highlight")
|
|
||||||
}, 60000);
|
|
||||||
},
|
|
||||||
setStats: function(views, downloads){
|
|
||||||
document.getElementById("stat_views").innerText = views
|
|
||||||
document.getElementById("stat_downloads").innerText = Math.round(downloads*10)/10;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var Sharebar = {
|
|
||||||
visible: false,
|
|
||||||
|
|
||||||
toggle: function(){
|
|
||||||
if (navigator.share) {
|
|
||||||
navigator.share({
|
|
||||||
title: Viewer.title,
|
|
||||||
text: "Download " + Viewer.title + " here",
|
|
||||||
url: window.location.href
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Toolbar.visible){
|
|
||||||
Toolbar.toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.visible){
|
|
||||||
document.getElementById("sharebar").style.left = "-8em";
|
|
||||||
document.getElementById("btnShare").classList.remove("button_highlight")
|
|
||||||
this.visible = false;
|
|
||||||
}else{
|
|
||||||
document.getElementById("sharebar").style.left = "8em";
|
|
||||||
document.getElementById("btnShare").classList.add("button_highlight")
|
|
||||||
this.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function copyText(text) {
|
|
||||||
// Create a textarea to copy the text from
|
|
||||||
var ta = document.createElement("textarea");
|
|
||||||
ta.setAttribute("readonly", "readonly")
|
|
||||||
ta.style.position = "absolute";
|
|
||||||
ta.style.left = "-9999px";
|
|
||||||
ta.value = text; // Put the text in the textarea
|
|
||||||
|
|
||||||
// Add the textarea to the DOM so it can be seleted by the user
|
|
||||||
document.body.appendChild(ta);
|
|
||||||
ta.select(); // Select the contents of the textarea
|
|
||||||
var success = document.execCommand("copy"); // Copy the selected text
|
|
||||||
document.body.removeChild(ta); // Remove the textarea
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadCaptcha(){
|
|
||||||
grecaptcha.render("captcha_popup_captcha", {
|
|
||||||
sitekey: captchaKey,
|
|
||||||
theme: "dark",
|
|
||||||
callback: function(token){
|
|
||||||
document.getElementById("download_frame").src = "/api/file/" + Viewer.currentFile +
|
|
||||||
"?download&recaptcha_response="+token;
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
var popupDiv = document.getElementById("captcha_popup");
|
|
||||||
popupDiv.style.opacity = "0";
|
|
||||||
popupDiv.style.visibility = "hidden";
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDataVolume(amt) {
|
|
||||||
if (amt > 1e12) {
|
|
||||||
return Math.round(amt/1e9)/1e3 + " TB";
|
|
||||||
} else if (amt > 1e9) {
|
|
||||||
return Math.round(amt/1e6)/1e3 + " GB";
|
|
||||||
} else if (amt > 1e6) {
|
|
||||||
return Math.round(amt/1e3)/1e3 + " MB";
|
|
||||||
} else if (amt > 1e3) {
|
|
||||||
return Math.round(amt)/1e3 + " kB";
|
|
||||||
}
|
|
||||||
return amt + " B"
|
|
||||||
}
|
|
||||||
|
|
||||||
var DetailsWindow = {
|
|
||||||
visible: false,
|
|
||||||
fileID: "",
|
|
||||||
graph: 0,
|
|
||||||
popupDiv: document.getElementById("details_popup"),
|
|
||||||
detailsButton: document.getElementById("btnDetails"),
|
|
||||||
toggle: function () {
|
|
||||||
if (this.visible) {
|
|
||||||
this.popupDiv.style.opacity = "0"
|
|
||||||
this.popupDiv.style.visibility = "hidden"
|
|
||||||
this.detailsButton.classList.remove("button_highlight")
|
|
||||||
this.visible = false;
|
|
||||||
} else {
|
|
||||||
this.popupDiv.style.opacity = "1"
|
|
||||||
this.popupDiv.style.visibility = "visible"
|
|
||||||
this.detailsButton.classList.add("button_highlight")
|
|
||||||
this.visible = true;
|
|
||||||
|
|
||||||
// This is a workaround for a chrome bug which makes it so hidden
|
|
||||||
// windows can't be scrolled after they are shown
|
|
||||||
this.popupDiv.focus();
|
|
||||||
|
|
||||||
if (this.graph === 0) {
|
|
||||||
this.renderGraph();
|
|
||||||
}
|
|
||||||
this.updateGraph(this.fileID);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setDetails: function (file) {
|
|
||||||
var that = this;
|
|
||||||
if (Viewer.isList) {
|
|
||||||
// Lists give incomplete file information, so we have to request
|
|
||||||
// more details in the background. File descriptions only exist in
|
|
||||||
// lists, so for that we use the data provided in the page source
|
|
||||||
$.ajax({
|
|
||||||
dataType: "json",
|
|
||||||
url: apiEndpoint + "/file/" + file.id + "/info",
|
|
||||||
success: function(data){
|
|
||||||
that.fileID = data.id;
|
|
||||||
$("#info_file_details").html(
|
|
||||||
"<table>"
|
|
||||||
+ "<tr><td>Name<td><td>" + escapeHTML(data.name) + "</td></tr>"
|
|
||||||
+ "<tr><td>URL<td><td><a href=\"/u/" + data.id + "\">/u/" + data.id + "</a></td></tr>"
|
|
||||||
+ "<tr><td>Mime Type<td><td>" + escapeHTML(data.mime_type) + "</td></tr>"
|
|
||||||
+ "<tr><td>ID<td><td>" + data.id + "</td></tr>"
|
|
||||||
+ "<tr><td>Size<td><td>" + formatDataVolume(data.size) + "</td></tr>"
|
|
||||||
+ "<tr><td>Bandwidth<td><td>" + formatDataVolume(data.bandwidth_used) + "</td></tr>"
|
|
||||||
+ "<tr><td>Upload Date<td><td>" + data.date_upload + "</td></tr>"
|
|
||||||
+ "<tr><td>Description<td><td>" + escapeHTML(file.description) + "</td></tr>"
|
|
||||||
+ "</table>"
|
|
||||||
);
|
|
||||||
Toolbar.setStats(data.views, data.bandwidth_used/data.size);
|
|
||||||
if(that.visible) {
|
|
||||||
that.updateGraph(that.fileID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.fileID = file.id;
|
|
||||||
$("#info_file_details").html(
|
|
||||||
"<table>"
|
|
||||||
+ "<tr><td>Name<td><td>" + escapeHTML(file.name) + "</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) + "</td></tr>"
|
|
||||||
+ "<tr><td>Bandwidth<td><td>" + formatDataVolume(file.bandwidth_used) + "</td></tr>"
|
|
||||||
+ "<tr><td>Upload Date<td><td>" + file.date_upload + "</td></tr>"
|
|
||||||
+ "</table>"
|
|
||||||
);
|
|
||||||
Toolbar.setStats(file.views, file.bandwidth_used/file.size);
|
|
||||||
if(this.visible) {
|
|
||||||
this.updateGraph(file.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateGraph: function(fileID) {
|
|
||||||
var that = this;
|
|
||||||
console.log("updating graph "+fileID);
|
|
||||||
$.get(apiEndpoint+"/file/" + fileID + "/timeseries?interval=60?days=14", function(response){
|
|
||||||
console.log(response);
|
|
||||||
if (response.success) {
|
|
||||||
that.graph.data.labels = response.labels;
|
|
||||||
that.graph.data.datasets[0].data = response.downloads;
|
|
||||||
that.graph.data.datasets[1].data = response.views;
|
|
||||||
that.graph.update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
renderGraph: function() {
|
|
||||||
console.log("rendering graph");
|
|
||||||
Chart.defaults.global.defaultFontColor = "#b3b3b3";
|
|
||||||
Chart.defaults.global.defaultFontSize = 15;
|
|
||||||
Chart.defaults.global.defaultFontFamily = "Ubuntu";
|
|
||||||
Chart.defaults.global.aspectRatio = 2.5;
|
|
||||||
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;
|
|
||||||
this.graph = new Chart(
|
|
||||||
document.getElementById('bandwidth_chart'),
|
|
||||||
{
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Downloads",
|
|
||||||
backgroundColor: "rgba(64, 255, 64, .05)",
|
|
||||||
borderColor: "rgba(128, 255, 128, 1)",
|
|
||||||
borderWidth: 1.5,
|
|
||||||
lineTension: 0.1,
|
|
||||||
fill: true,
|
|
||||||
yAxisID: "y_bandwidth",
|
|
||||||
}, {
|
|
||||||
label: "Views",
|
|
||||||
backgroundColor: "rgba(64, 64, 255, .1)",
|
|
||||||
borderColor: "rgba(128, 128, 255, 1)",
|
|
||||||
borderWidth: 1.5,
|
|
||||||
lineTension: 0.1,
|
|
||||||
fill: true,
|
|
||||||
yAxisID: "y_views",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
yAxes: [
|
|
||||||
{
|
|
||||||
type: "linear",
|
|
||||||
display: true,
|
|
||||||
position: "left",
|
|
||||||
id: "y_bandwidth",
|
|
||||||
scaleLabel: {
|
|
||||||
display: true,
|
|
||||||
labelString: "Downloads"
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
color: "rgba(100, 255, 100, .1)"
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
type: "linear",
|
|
||||||
display: true,
|
|
||||||
position: "right",
|
|
||||||
id: "y_views",
|
|
||||||
scaleLabel: {
|
|
||||||
display: true,
|
|
||||||
labelString: "Views"
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
color: "rgba(128, 128, 255, .2)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
xAxes: [
|
|
||||||
{
|
|
||||||
ticks: {
|
|
||||||
maxRotation: 20
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
display: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,147 +0,0 @@
|
|||||||
/* global ListNavigator, Toolbar, DetailsWindow */
|
|
||||||
|
|
||||||
var Viewer = {
|
|
||||||
currentFile: "",
|
|
||||||
title: "", // Contains either the file name or list title
|
|
||||||
listId: "",
|
|
||||||
isList: false,
|
|
||||||
isFile: false,
|
|
||||||
initialized: false,
|
|
||||||
|
|
||||||
init: function(type, data){
|
|
||||||
if(this.initialized){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// On small screens the toolbar takes too much space, so it collapses automatically
|
|
||||||
if($("#filepreview").width() > 600 && !Toolbar.visible){
|
|
||||||
Toolbar.toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The close button only works if the window has an opener. So we hide
|
|
||||||
// the button if it does not
|
|
||||||
if (window.opener === null && window.history.length !== 1) {
|
|
||||||
$("#button_close_file_viewer").remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(type === "file"){
|
|
||||||
this.isFile = true;
|
|
||||||
this.currentFile = data.id;
|
|
||||||
this.title = data.name;
|
|
||||||
this.setFile(data);
|
|
||||||
} else if (type === "list") {
|
|
||||||
this.isList = true;
|
|
||||||
this.listId = data.id;
|
|
||||||
this.title = data.title;
|
|
||||||
ListNavigator.init(data.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSponsors();
|
|
||||||
|
|
||||||
this.initialized = true;
|
|
||||||
},
|
|
||||||
setFile: function(file){
|
|
||||||
this.currentFile = file.id;
|
|
||||||
var title = "";
|
|
||||||
if (this.isList) {
|
|
||||||
document.getElementById("file_viewer_headerbar_title").style.lineHeight = "1em";
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
$.get("/u/" + file.id + "/preview", function(response){
|
|
||||||
$("#filepreview").html(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
DetailsWindow.setDetails(file);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Against XSS attacks
|
|
||||||
function escapeHTML(str) {
|
|
||||||
return String(str)
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register keyboard shortcuts
|
|
||||||
document.addEventListener("keydown", function(event){
|
|
||||||
if (event.ctrlKey || event.altKey) {
|
|
||||||
return // prevent custom shortcuts from interfering with system shortcuts
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event.which) {
|
|
||||||
case 65: // A or left arrow key go to previous file
|
|
||||||
case 37:
|
|
||||||
ListNavigator.previousItem();
|
|
||||||
break;
|
|
||||||
case 68: // D or right arrow key go to next file
|
|
||||||
case 39:
|
|
||||||
ListNavigator.nextItem();
|
|
||||||
break;
|
|
||||||
case 83:
|
|
||||||
if (event.shiftKey) {
|
|
||||||
Toolbar.downloadList(); // SHIFT + S downloads all files in list
|
|
||||||
} else {
|
|
||||||
Toolbar.download(); // S to download the current file
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 82: // R to toggle list shuffle
|
|
||||||
ListNavigator.toggleShuffle();
|
|
||||||
break;
|
|
||||||
case 67: // C to copy to clipboard
|
|
||||||
Toolbar.copyUrl();
|
|
||||||
break;
|
|
||||||
case 73: // I to open the details window
|
|
||||||
DetailsWindow.toggle();
|
|
||||||
break;
|
|
||||||
case 81: // Q to close the window
|
|
||||||
window.close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener("resize", renderSponsors);
|
|
||||||
function renderSponsors() {
|
|
||||||
var scale = 1;
|
|
||||||
var scaleWidth = 1;
|
|
||||||
var scaleHeight = 1;
|
|
||||||
var minWidth = 728;
|
|
||||||
var minHeight = 800;
|
|
||||||
|
|
||||||
if (window.innerWidth < minWidth) {
|
|
||||||
scaleWidth = window.innerWidth/minWidth;
|
|
||||||
}
|
|
||||||
if (window.innerHeight < minHeight) {
|
|
||||||
scaleHeight = window.innerHeight/minHeight;
|
|
||||||
}
|
|
||||||
scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
|
|
||||||
|
|
||||||
console.log(scale, scaleWidth, scaleHeight);
|
|
||||||
|
|
||||||
// Because of the scale transformation the automatic margins don't work
|
|
||||||
// anymore. So we have to maunally calculate the margin. Where 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 calcualted
|
|
||||||
// to account for the smaller size.
|
|
||||||
var offset = (window.innerWidth - (minWidth*scale)) / 2
|
|
||||||
if (offset < 0) {
|
|
||||||
offset = 0
|
|
||||||
}
|
|
||||||
document.querySelector(".sponsors > iframe").style.marginLeft = offset+"px";
|
|
||||||
|
|
||||||
if (scale == 1) {
|
|
||||||
document.querySelector(".sponsors > iframe").style.transform = "none";
|
|
||||||
document.querySelector(".sponsors").style.height = "90px";
|
|
||||||
} else {
|
|
||||||
document.querySelector(".sponsors > iframe").style.transform = "scale("+scale+")";
|
|
||||||
document.querySelector(".sponsors").style.height = (scale*90)+"px";
|
|
||||||
}
|
|
||||||
}
|
|
@@ -66,3 +66,19 @@ function addUploadHistory(fileID) {
|
|||||||
// Save the new ID
|
// Save the new ID
|
||||||
localStorage.setItem("uploaded_files", fileID + "," + uploads);
|
localStorage.setItem("uploaded_files", fileID + "," + uploads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyText(text) {
|
||||||
|
// Create a textarea to copy the text from
|
||||||
|
let ta = document.createElement("textarea");
|
||||||
|
ta.setAttribute("readonly", "readonly")
|
||||||
|
ta.style.position = "absolute";
|
||||||
|
ta.style.left = "-9999px";
|
||||||
|
ta.value = text; // Put the text in the textarea
|
||||||
|
|
||||||
|
// Add the textarea to the DOM so it can be seleted by the user
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.select() // Select the contents of the textarea
|
||||||
|
let success = document.execCommand("copy"); // Copy the selected text
|
||||||
|
document.body.removeChild(ta); // Remove the textarea
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
184
res/include/script/file_viewer/DetailsWindow.js
Normal file
184
res/include/script/file_viewer/DetailsWindow.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
class DetailsWindow {
|
||||||
|
viewer = null;
|
||||||
|
|
||||||
|
visible = false;
|
||||||
|
fileID = "";
|
||||||
|
graph = 0;
|
||||||
|
|
||||||
|
divPopup = null;
|
||||||
|
btnDetails = null;
|
||||||
|
btnCloseDetails = null;
|
||||||
|
divFileDetails = null;
|
||||||
|
|
||||||
|
constructor(viewer) {let dw = this;
|
||||||
|
dw.viewer = viewer;
|
||||||
|
|
||||||
|
dw.divPopup = document.getElementById("details_popup");
|
||||||
|
dw.btnDetails = document.getElementById("btn_details");
|
||||||
|
dw.btnCloseDetails = document.getElementById("btn_close_details");
|
||||||
|
dw.divFileDetails = document.getElementById("info_file_details");
|
||||||
|
|
||||||
|
dw.btnDetails.addEventListener("click", () => { dw.toggle(); });
|
||||||
|
dw.btnCloseDetails.addEventListener("click", () => { dw.toggle(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {let dw = this;
|
||||||
|
if (dw.visible) {
|
||||||
|
dw.divPopup.style.opacity = "0";
|
||||||
|
dw.divPopup.style.visibility = "hidden";
|
||||||
|
dw.btnDetails.classList.remove("button_highlight");
|
||||||
|
dw.visible = false;
|
||||||
|
} else {
|
||||||
|
dw.divPopup.style.opacity = "1";
|
||||||
|
dw.divPopup.style.visibility = "visible";
|
||||||
|
dw.btnDetails.classList.add("button_highlight");
|
||||||
|
dw.visible = true;
|
||||||
|
|
||||||
|
// This is a workaround for a chrome bug which makes it so hidden
|
||||||
|
// windows can't be scrolled after they are shown
|
||||||
|
dw.divPopup.focus();
|
||||||
|
|
||||||
|
if (dw.graph === 0) {
|
||||||
|
dw.renderGraph();
|
||||||
|
}
|
||||||
|
dw.updateGraph(dw.fileID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDetails(file) {let dw = this;
|
||||||
|
if (dw.viewer.isList) {
|
||||||
|
// Lists give incomplete file information, so we have to request
|
||||||
|
// more details in the background. File descriptions only exist in
|
||||||
|
// lists, so for that we use the data provided in the page source
|
||||||
|
fetch(apiEndpoint + "/file/" + file.id + "/info").then(resp => {
|
||||||
|
if (!resp.ok) {return;}
|
||||||
|
return resp.json();
|
||||||
|
}).then(resp => {
|
||||||
|
dw.fileID = resp.id;
|
||||||
|
dw.divFileDetails.innerHTML = "<table>"
|
||||||
|
+ "<tr><td>Name<td><td>" + escapeHTML(resp.name) + "</td></tr>"
|
||||||
|
+ "<tr><td>URL<td><td><a href=\"/u/" + resp.id + "\">/u/" + resp.id + "</a></td></tr>"
|
||||||
|
+ "<tr><td>Mime Type<td><td>" + escapeHTML(resp.mime_type) + "</td></tr>"
|
||||||
|
+ "<tr><td>ID<td><td>" + resp.id + "</td></tr>"
|
||||||
|
+ "<tr><td>Size<td><td>" + formatDataVolume(resp.size) + "</td></tr>"
|
||||||
|
+ "<tr><td>Bandwidth<td><td>" + formatDataVolume(resp.bandwidth_used) + "</td></tr>"
|
||||||
|
+ "<tr><td>Upload Date<td><td>" + resp.date_upload + "</td></tr>"
|
||||||
|
+ "<tr><td>Description<td><td>" + escapeHTML(file.description) + "</td></tr>"
|
||||||
|
+ "</table>";
|
||||||
|
|
||||||
|
dw.viewer.toolbar.setStats(resp.views, resp.bandwidth_used/resp.size);
|
||||||
|
if(dw.visible) {
|
||||||
|
dw.updateGraph(dw.fileID);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
dw.fileID = file.id;
|
||||||
|
dw.divFileDetails.innerHTML = "<table>"
|
||||||
|
+ "<tr><td>Name<td><td>" + escapeHTML(file.name) + "</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) + "</td></tr>"
|
||||||
|
+ "<tr><td>Bandwidth<td><td>" + formatDataVolume(file.bandwidth_used) + "</td></tr>"
|
||||||
|
+ "<tr><td>Upload Date<td><td>" + file.date_upload + "</td></tr>"
|
||||||
|
+ "</table>";
|
||||||
|
|
||||||
|
dw.viewer.toolbar.setStats(file.views, file.bandwidth_used/file.size);
|
||||||
|
if(dw.visible) {
|
||||||
|
dw.updateGraph(file.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGraph(fileID) {let dw = this;
|
||||||
|
console.log("updating graph "+fileID);
|
||||||
|
fetch(apiEndpoint+"/file/" + fileID + "/timeseries?interval=60?days=14").then(resp => {
|
||||||
|
if (!resp.ok) {return null;}
|
||||||
|
return resp.json();
|
||||||
|
}).then(resp => {
|
||||||
|
dw.graph.data.labels = resp.labels;
|
||||||
|
dw.graph.data.datasets[0].data = resp.downloads;
|
||||||
|
dw.graph.data.datasets[1].data = resp.views;
|
||||||
|
dw.graph.update();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGraph() {let dw = this;
|
||||||
|
console.log("rendering graph");
|
||||||
|
Chart.defaults.global.defaultFontColor = "#b3b3b3";
|
||||||
|
Chart.defaults.global.defaultFontSize = 15;
|
||||||
|
Chart.defaults.global.defaultFontFamily = "Ubuntu";
|
||||||
|
Chart.defaults.global.aspectRatio = 2.5;
|
||||||
|
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;
|
||||||
|
dw.graph = new Chart(
|
||||||
|
document.getElementById('bandwidth_chart'),
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Downloads",
|
||||||
|
backgroundColor: "rgba(64, 255, 64, .05)",
|
||||||
|
borderColor: "rgba(128, 255, 128, 1)",
|
||||||
|
borderWidth: 1.5,
|
||||||
|
lineTension: 0.1,
|
||||||
|
fill: true,
|
||||||
|
yAxisID: "y_bandwidth",
|
||||||
|
}, {
|
||||||
|
label: "Views",
|
||||||
|
backgroundColor: "rgba(64, 64, 255, .1)",
|
||||||
|
borderColor: "rgba(128, 128, 255, 1)",
|
||||||
|
borderWidth: 1.5,
|
||||||
|
lineTension: 0.1,
|
||||||
|
fill: true,
|
||||||
|
yAxisID: "y_views",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
type: "linear",
|
||||||
|
display: true,
|
||||||
|
position: "left",
|
||||||
|
id: "y_bandwidth",
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: "Downloads"
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
color: "rgba(100, 255, 100, .1)"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: "linear",
|
||||||
|
display: true,
|
||||||
|
position: "right",
|
||||||
|
id: "y_views",
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: "Views"
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
color: "rgba(128, 128, 255, .2)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
ticks: {
|
||||||
|
maxRotation: 20
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
193
res/include/script/file_viewer/ListNavigator.js
Normal file
193
res/include/script/file_viewer/ListNavigator.js
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
class ListNavigator {
|
||||||
|
viewer = null;
|
||||||
|
length = 0;
|
||||||
|
position = 0;
|
||||||
|
data = [];
|
||||||
|
history = [];
|
||||||
|
shuffle = false;
|
||||||
|
|
||||||
|
divListNavigator = null;
|
||||||
|
btnDownloadList = null;
|
||||||
|
btnShuffle = null;
|
||||||
|
|
||||||
|
constructor(viewer, data){let ln = this;
|
||||||
|
ln.viewer = viewer;
|
||||||
|
ln.data = data;
|
||||||
|
ln.length = data.length;
|
||||||
|
|
||||||
|
ln.divListNavigator = document.getElementById("list_navigator");
|
||||||
|
|
||||||
|
ln.btnDownloadList = document.getElementById("btn_download_list");
|
||||||
|
ln.btnDownloadList.style.display = "";
|
||||||
|
ln.btnDownloadList.addEventListener("click", () => { ln.downloadList(); });
|
||||||
|
|
||||||
|
ln.btnShuffle = document.getElementById("btn_shuffle");
|
||||||
|
ln.btnShuffle.style.display = "";
|
||||||
|
ln.btnShuffle.addEventListener("click", () => { ln.toggleShuffle(); });
|
||||||
|
|
||||||
|
// Render list contents in list navigator div
|
||||||
|
data.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", () => { ln.setItem(i); });
|
||||||
|
d.innerText = filename;
|
||||||
|
ln.divListNavigator.appendChild(d);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make the navigator visible
|
||||||
|
ln.divListNavigator.style.display = "inline-block";
|
||||||
|
|
||||||
|
// Skip to the file defined in the link hash
|
||||||
|
if(Number.isInteger(parseInt(getHashValue("item")))){
|
||||||
|
ln.setItem(parseInt(getHashValue("item")));
|
||||||
|
}else{
|
||||||
|
ln.setItem(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextItem(){let ln = this;
|
||||||
|
if(ln.shuffle){
|
||||||
|
ln.randItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ln.position >= ln.length) {
|
||||||
|
ln.position = 0;
|
||||||
|
} else {
|
||||||
|
ln.position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ln.setItem(ln.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousItem(){let ln = this;
|
||||||
|
if(ln.position === 0){
|
||||||
|
ln.position = ln.length - 1;
|
||||||
|
}else{
|
||||||
|
ln.position--;
|
||||||
|
}
|
||||||
|
|
||||||
|
ln.setItem(ln.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
randItem(){let ln = this;
|
||||||
|
// Avoid viewing the same file multiple times
|
||||||
|
let rand;
|
||||||
|
do {
|
||||||
|
rand = Math.round(Math.random() * ln.length);
|
||||||
|
console.log("rand is " + rand);
|
||||||
|
} while(ln.history.indexOf(index) > -1);
|
||||||
|
|
||||||
|
ln.setItem(rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(index){let ln = this;
|
||||||
|
if(index >= ln.length){
|
||||||
|
ln.position = 0;
|
||||||
|
}else{
|
||||||
|
ln.position = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the URL hash
|
||||||
|
location.hash = "item=" + ln.position;
|
||||||
|
ln.viewer.setFile(ln.data[ln.position]);
|
||||||
|
|
||||||
|
ln.addToHistory(index);
|
||||||
|
ln.loadThumbnails(index);
|
||||||
|
|
||||||
|
document.querySelectorAll("#list_navigator > .file_button_selected").forEach(el => {
|
||||||
|
el.classList.remove("file_button_selected");
|
||||||
|
});
|
||||||
|
|
||||||
|
let selectedItem = ln.divListNavigator.children[ln.position];
|
||||||
|
selectedItem.classList.add("file_button_selected");
|
||||||
|
|
||||||
|
let cst = window.getComputedStyle(selectedItem);
|
||||||
|
let itemWidth = selectedItem.offsetWidth + parseInt(cst.marginLeft) + parseInt(cst.marginRight);
|
||||||
|
|
||||||
|
let start = ln.divListNavigator.scrollLeft;
|
||||||
|
let end = ((ln.position * itemWidth) + (itemWidth / 2)) - (ln.divListNavigator.clientWidth / 2);
|
||||||
|
let steps = 60; // One second
|
||||||
|
let stepSize = (end - start)/steps;
|
||||||
|
|
||||||
|
let animateScroll = (pos, step) => {
|
||||||
|
ln.divListNavigator.scrollLeft = pos;
|
||||||
|
|
||||||
|
if (step < steps) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
animateScroll(pos+stepSize, step+1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
animateScroll(start, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadList(){let ln = this;
|
||||||
|
document.getElementById("download_frame").src = "/api/list/" + ln.viewer.listId + "/zip";
|
||||||
|
}
|
||||||
|
|
||||||
|
addToHistory(index){let ln = this;
|
||||||
|
if(ln.history.length >= (ln.length - 6)){
|
||||||
|
ln.history.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
ln.history.push(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShuffle(){let ln = this;
|
||||||
|
ln.shuffle = !ln.shuffle; // :P
|
||||||
|
|
||||||
|
if(ln.shuffle){
|
||||||
|
document.querySelector("#btn_shuffle > span").innerHTML = "Shuffle ☑"; // Check icon
|
||||||
|
ln.btnShuffle.classList.add("button_highlight");
|
||||||
|
}else{
|
||||||
|
document.querySelector("#btn_shuffle > span").innerHTML = "Shuffle ☐"; // Empty checkbox
|
||||||
|
ln.btnShuffle.classList.remove("button_highlight");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadThumbnails(index){let ln = this;
|
||||||
|
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 >= ln.length){
|
||||||
|
endPos = ln.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/" + ln.data[i].id + "/thumbnail?width=48&height=48";
|
||||||
|
let name = ln.data[i].name;
|
||||||
|
|
||||||
|
let itemHtml = "<img src=\"" + thumb + "\" "
|
||||||
|
+ "class=\"list_item_thumbnail\" alt=\"" + escapeHTML(name) + "\"/>"
|
||||||
|
+ escapeHTML(name);
|
||||||
|
|
||||||
|
navigatorItems[i].innerHTML = itemHtml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Misc function, don't really know where else to put it
|
||||||
|
function getHashValue(key) {
|
||||||
|
let matches = location.hash.match(new RegExp(key + '=([^&]*)'));
|
||||||
|
return matches ? matches[1] : null;
|
||||||
|
}
|
186
res/include/script/file_viewer/Toolbar.js
Normal file
186
res/include/script/file_viewer/Toolbar.js
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
class Toolbar {
|
||||||
|
viewer = null;
|
||||||
|
|
||||||
|
visible = false;
|
||||||
|
sharebarVisible = false;
|
||||||
|
|
||||||
|
// Elements
|
||||||
|
divToolbar = null;
|
||||||
|
divFilePreview = null;
|
||||||
|
downloadFrame = null
|
||||||
|
|
||||||
|
spanViews = null;
|
||||||
|
spanDownloads = null;
|
||||||
|
|
||||||
|
btnToggleToolbar = null;
|
||||||
|
btnDownload = null;
|
||||||
|
btnCopyLink = null;
|
||||||
|
spanCopyLink = null;
|
||||||
|
btnShare = null;
|
||||||
|
divSharebar = null;
|
||||||
|
|
||||||
|
constructor(viewer) {let t = this;
|
||||||
|
this.viewer = viewer;
|
||||||
|
|
||||||
|
t.divToolbar = document.getElementById("toolbar");
|
||||||
|
t.divFilePreview = document.getElementById("filepreview");
|
||||||
|
t.downloadFrame = document.getElementById("download_frame");
|
||||||
|
t.spanViews = document.getElementById("stat_views");
|
||||||
|
t.spanDownloads = document.getElementById("stat_downloads");
|
||||||
|
|
||||||
|
t.btnToggleToolbar = document.getElementById("btn_toggle_toolbar");
|
||||||
|
t.btnDownload = document.getElementById("btn_download");
|
||||||
|
t.btnCopyLink = document.getElementById("btn_copy");
|
||||||
|
t.spanCopyLink = document.querySelector("#btn_copy > span");
|
||||||
|
t.btnShare = document.getElementById("btn_share");
|
||||||
|
t.divSharebar = document.getElementById("sharebar");
|
||||||
|
|
||||||
|
t.btnToggleToolbar.addEventListener("click", () => { t.toggle(); });
|
||||||
|
t.btnDownload.addEventListener("click", () => { t.download(); });
|
||||||
|
t.btnCopyLink.addEventListener("click", () => { t.copyUrl(); });
|
||||||
|
t.btnShare.addEventListener("click", () => { t.toggleSharebar(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {let t = this;
|
||||||
|
if (t.visible) {
|
||||||
|
if (t.sharebarVisible) { t.toggleSharebar(); }
|
||||||
|
|
||||||
|
t.divToolbar.style.left = "-8em";
|
||||||
|
t.divFilePreview.style.left = "0px";
|
||||||
|
t.btnToggleToolbar.classList.remove("button_highlight");
|
||||||
|
t.visible = false;
|
||||||
|
} else {
|
||||||
|
t.divToolbar.style.left = "0px";
|
||||||
|
t.divFilePreview.style.left = "8em";
|
||||||
|
t.btnToggleToolbar.classList.add("button_highlight");
|
||||||
|
t.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSharebar(){let t = this;
|
||||||
|
if (navigator.share) {
|
||||||
|
navigator.share({
|
||||||
|
title: t.viewer.title,
|
||||||
|
text: "Download " + t.viewer.title + " here",
|
||||||
|
url: window.location.href
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t.sharebarVisible){
|
||||||
|
t.divSharebar.style.left = "-8em";
|
||||||
|
t.btnShare.classList.remove("button_highlight")
|
||||||
|
t.sharebarVisible = false;
|
||||||
|
}else{
|
||||||
|
t.divSharebar.style.left = "8em";
|
||||||
|
t.btnShare.classList.add("button_highlight")
|
||||||
|
t.sharebarVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
download() {let t = this;
|
||||||
|
let triggerDL = function(){
|
||||||
|
t.downloadFrame.src = apiEndpoint+"/file/"+t.viewer.currentFile+"?download";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (captchaKey === "a"){
|
||||||
|
// If the server doesn't support captcha there's no use in checking
|
||||||
|
// availability
|
||||||
|
triggerDL();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(apiEndpoint+"/file/"+t.viewer.currentFile+"/availability").then(resp => {
|
||||||
|
return resp.json();
|
||||||
|
}).then(resp => {
|
||||||
|
let popupDiv = document.getElementById("captcha_popup");
|
||||||
|
let popupTitle = document.getElementById("captcha_popup_title");
|
||||||
|
let popupContent = document.getElementById("captcha_popup_content");
|
||||||
|
|
||||||
|
let showCaptcha = function() {
|
||||||
|
// Load the recaptcha script with a load function
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.src = "https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit";
|
||||||
|
document.appendChild(script);
|
||||||
|
// $.getScript("https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit");
|
||||||
|
popupDiv.style.opacity = "1";
|
||||||
|
popupDiv.style.visibility = "visible";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.value === "file_rate_limited_captcha_required") {
|
||||||
|
popupTitle.innerText = "Rate limiting enabled!";
|
||||||
|
popupContent.innerText = "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.";
|
||||||
|
showCaptcha();
|
||||||
|
} else if (resp.value === "virus_detected_captcha_required") {
|
||||||
|
popupTitle.innerText = "Malware warning!";
|
||||||
|
popupContent.innerText = "According to our scanning "+
|
||||||
|
"systems this file may contain a virus of type '"+
|
||||||
|
resp.extra+"'. You can continue downloading this file at "+
|
||||||
|
"your own risk, but you will have to prove that you're a "+
|
||||||
|
"human first.";
|
||||||
|
showCaptcha();
|
||||||
|
} else {
|
||||||
|
triggerDL();
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
triggerDL();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
copyUrl() {let t = this;
|
||||||
|
if(copyText(window.location.href)) {
|
||||||
|
console.log('Text copied');
|
||||||
|
t.spanCopyLink.innerText = "Copied!";
|
||||||
|
t.btnCopyLink.classList.add("button_highlight")
|
||||||
|
} else {
|
||||||
|
console.log('Copying not supported');
|
||||||
|
t.spanCopyLink.innerText = "Error!";
|
||||||
|
alert("Your browser does not support copying text.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return to normal
|
||||||
|
setTimeout(function(){
|
||||||
|
t.spanCopyLink.innerText = "Copy";
|
||||||
|
t.btnCopyLink.classList.remove("button_highlight")
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStats(views, downloads) {let t = this;
|
||||||
|
t.spanViews.innerText = views
|
||||||
|
t.spanDownloads.innerText = Math.round(downloads*10)/10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by the google recaptcha script
|
||||||
|
function loadCaptcha(){
|
||||||
|
grecaptcha.render("captcha_popup_captcha", {
|
||||||
|
sitekey: captchaKey,
|
||||||
|
theme: "dark",
|
||||||
|
callback: function(token){
|
||||||
|
document.getElementById("download_frame").src = "/api/file/" + Viewer.currentFile +
|
||||||
|
"?download&recaptcha_response="+token;
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
let popupDiv = document.getElementById("captcha_popup");
|
||||||
|
popupDiv.style.opacity = "0";
|
||||||
|
popupDiv.style.visibility = "hidden";
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDataVolume(amt) {
|
||||||
|
if (amt > 1e12) {
|
||||||
|
return Math.round(amt/1e9)/1e3 + " TB";
|
||||||
|
} else if (amt > 1e9) {
|
||||||
|
return Math.round(amt/1e6)/1e3 + " GB";
|
||||||
|
} else if (amt > 1e6) {
|
||||||
|
return Math.round(amt/1e3)/1e3 + " MB";
|
||||||
|
} else if (amt > 1e3) {
|
||||||
|
return Math.round(amt)/1e3 + " kB";
|
||||||
|
}
|
||||||
|
return amt + " B"
|
||||||
|
}
|
189
res/include/script/file_viewer/Viewer.js
Normal file
189
res/include/script/file_viewer/Viewer.js
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
class Viewer {
|
||||||
|
// Child components
|
||||||
|
toolbar = null;
|
||||||
|
listNavigator = null;
|
||||||
|
detailsWindow = null;
|
||||||
|
|
||||||
|
divFilepreview = null;
|
||||||
|
|
||||||
|
currentFile = "";
|
||||||
|
title = ""; // Contains either the file name or list title
|
||||||
|
listId = "";
|
||||||
|
isList = false;
|
||||||
|
isFile = false;
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
|
constructor(type, data) {let v = this;
|
||||||
|
if(v.initialized){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
v.toolbar = new Toolbar(v);
|
||||||
|
v.detailsWindow = new DetailsWindow(v);
|
||||||
|
|
||||||
|
v.divFilepreview = document.getElementById("filepreview");
|
||||||
|
|
||||||
|
// On small screens the toolbar takes too much space, so it collapses
|
||||||
|
// automatically
|
||||||
|
if(v.divFilepreview.clientWidth > 600 && !v.toolbar.visible){
|
||||||
|
v.toolbar.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The close button only works if the window has an opener. So we hide
|
||||||
|
// the button if it does not
|
||||||
|
if (window.opener === null && window.history.length !== 1) {
|
||||||
|
document.getElementById("button_close_file_viewer").remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(type === "file"){
|
||||||
|
v.isFile = true;
|
||||||
|
v.currentFile = data.id;
|
||||||
|
v.title = data.name;
|
||||||
|
v.setFile(data);
|
||||||
|
} else if (type === "list") {
|
||||||
|
v.isList = true;
|
||||||
|
v.listId = data.id;
|
||||||
|
v.title = data.title;
|
||||||
|
v.listNavigator = new ListNavigator(v, data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
v.renderSponsors();
|
||||||
|
window.addEventListener("resize", e => { v.renderSponsors(e); });
|
||||||
|
|
||||||
|
// Register keyboard shortcuts
|
||||||
|
document.addEventListener("keydown", e => { v.keyboardEvent(e); });
|
||||||
|
|
||||||
|
v.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFile(file) {let v = this;
|
||||||
|
v.currentFile = file.id;
|
||||||
|
if (v.isList) {
|
||||||
|
document.getElementById("file_viewer_headerbar_title").style.lineHeight = "1em";
|
||||||
|
document.getElementById("file_viewer_list_title").innerText = this.title;
|
||||||
|
document.getElementById("file_viewer_file_title").innerText = file.name;
|
||||||
|
document.title = v.title + " ~ " + file.name + " ~ pixeldrain";
|
||||||
|
} else {
|
||||||
|
document.getElementById("file_viewer_file_title").innerText = file.name;
|
||||||
|
document.title = file.name + " ~ pixeldrain";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the file details
|
||||||
|
v.detailsWindow.setDetails(file);
|
||||||
|
|
||||||
|
// Clear the canvas
|
||||||
|
v.divFilepreview.innerHTML = "";
|
||||||
|
|
||||||
|
if (file.mime_type.startsWith("image")) {
|
||||||
|
new ImageViewer(v, file).render(v.divFilepreview);
|
||||||
|
} else if (file.mime_type.startsWith("video")) {
|
||||||
|
new VideoViewer(v, file, () => {
|
||||||
|
if (v.listNavigator !== null) {
|
||||||
|
v.listNavigator.nextItem();
|
||||||
|
}
|
||||||
|
}).render(v.divFilepreview);
|
||||||
|
} else if (file.mime_type.startsWith("audio") || file.mime_type === "application/ogg") {
|
||||||
|
new AudioViewer(v, file, () => {
|
||||||
|
if (v.listNavigator !== null) {
|
||||||
|
v.listNavigator.nextItem();
|
||||||
|
}
|
||||||
|
}).render(v.divFilepreview);
|
||||||
|
} else {
|
||||||
|
fetch("/u/"+file.id+"/preview").then(resp => {
|
||||||
|
if (!resp.ok) { return Promise.reject(resp.status); }
|
||||||
|
return resp.text();
|
||||||
|
}).then(resp => {
|
||||||
|
v.divFilepreview.innerHTML = resp;
|
||||||
|
}).catch(err => {
|
||||||
|
v.divFilepreview.innerText = "Error loading file: "+err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSponsors() {
|
||||||
|
let scale = 1;
|
||||||
|
let scaleWidth = 1;
|
||||||
|
let scaleHeight = 1;
|
||||||
|
let minWidth = 728;
|
||||||
|
let minHeight = 800;
|
||||||
|
|
||||||
|
if (window.innerWidth < minWidth) {
|
||||||
|
scaleWidth = window.innerWidth/minWidth;
|
||||||
|
}
|
||||||
|
if (window.innerHeight < minHeight) {
|
||||||
|
scaleHeight = window.innerHeight/minHeight;
|
||||||
|
}
|
||||||
|
scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
|
||||||
|
|
||||||
|
// Because of the scale transformation the automatic margins don't work
|
||||||
|
// anymore. So we have to maunally calculate the margin. Where 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 calcualted
|
||||||
|
// to account for the smaller size.
|
||||||
|
let offset = (window.innerWidth - (minWidth*scale)) / 2
|
||||||
|
if (offset < 0) {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
document.querySelector(".sponsors > iframe").style.marginLeft = offset+"px";
|
||||||
|
|
||||||
|
if (scale == 1) {
|
||||||
|
document.querySelector(".sponsors > iframe").style.transform = "none";
|
||||||
|
document.querySelector(".sponsors").style.height = "90px";
|
||||||
|
} else {
|
||||||
|
document.querySelector(".sponsors > iframe").style.transform = "scale("+scale+")";
|
||||||
|
document.querySelector(".sponsors").style.height = (scale*90)+"px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardEvent(evt) {let v = this;
|
||||||
|
if (evt.ctrlKey || evt.altKey) {
|
||||||
|
return // prevent custom shortcuts from interfering with system shortcuts
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (evt.which) {
|
||||||
|
case 65: // A or left arrow key go to previous file
|
||||||
|
case 37:
|
||||||
|
if (v.listNavigator != null) {
|
||||||
|
v.listNavigator.previousItem();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 68: // D or right arrow key go to next file
|
||||||
|
case 39:
|
||||||
|
if (v.listNavigator != null) {
|
||||||
|
v.listNavigator.nextItem();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 83:
|
||||||
|
if (evt.shiftKey) {
|
||||||
|
v.toolbar.downloadList(); // SHIFT + S downloads all files in list
|
||||||
|
} else {
|
||||||
|
v.toolbar.download(); // S to download the current file
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 82: // R to toggle list shuffle
|
||||||
|
if (v.listNavigator != null) {
|
||||||
|
v.listNavigator.toggleShuffle();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 67: // C to copy to clipboard
|
||||||
|
v.toolbar.copyUrl();
|
||||||
|
break;
|
||||||
|
case 73: // I to open the details window
|
||||||
|
v.detailsWindow.toggle();
|
||||||
|
break;
|
||||||
|
case 81: // Q to close the window
|
||||||
|
window.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Against XSS attacks
|
||||||
|
function escapeHTML(str) {
|
||||||
|
return String(str)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
}
|
44
res/include/script/file_viewer/viewer_scripts/AudioViewer.js
Normal file
44
res/include/script/file_viewer/viewer_scripts/AudioViewer.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
class AudioViewer {
|
||||||
|
viewer = null;
|
||||||
|
file = null;
|
||||||
|
next = null;
|
||||||
|
|
||||||
|
container = null;
|
||||||
|
icon = null;
|
||||||
|
element = null;
|
||||||
|
source = null;
|
||||||
|
|
||||||
|
constructor(viewer, file, next) {let av = this;
|
||||||
|
av.viewer = viewer;
|
||||||
|
av.file = file;
|
||||||
|
av.next = next;
|
||||||
|
|
||||||
|
av.container = document.createElement("div");
|
||||||
|
av.container.classList = "image-container";
|
||||||
|
av.container.appendChild(document.createElement("br"));
|
||||||
|
|
||||||
|
av.icon = document.createElement("img");
|
||||||
|
av.icon.src = "/res/img/mime/audio.png";
|
||||||
|
av.container.appendChild(av.icon);
|
||||||
|
|
||||||
|
av.container.appendChild(document.createElement("br"));
|
||||||
|
av.container.appendChild(document.createTextNode(file.name));
|
||||||
|
av.container.appendChild(document.createElement("br"));
|
||||||
|
av.container.appendChild(document.createElement("br"));
|
||||||
|
|
||||||
|
av.element = document.createElement("audio");
|
||||||
|
av.element.autoplay = "autoplay";
|
||||||
|
av.element.controls = "controls";
|
||||||
|
av.element.style.width = "90%";
|
||||||
|
av.element.addEventListener("ended", () => { av.next(); }, false);
|
||||||
|
|
||||||
|
av.source = document.createElement("source");
|
||||||
|
av.source.src = apiEndpoint+"/file/"+av.file.id;
|
||||||
|
av.element.appendChild(av.source);
|
||||||
|
av.container.appendChild(av.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(parent) {let av = this;
|
||||||
|
parent.appendChild(av.container);
|
||||||
|
}
|
||||||
|
}
|
156
res/include/script/file_viewer/viewer_scripts/ImageViewer.js
Normal file
156
res/include/script/file_viewer/viewer_scripts/ImageViewer.js
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
class ImageViewer {
|
||||||
|
viewer = null;
|
||||||
|
file = null;
|
||||||
|
|
||||||
|
imgContainer = null;
|
||||||
|
imgElement = null;
|
||||||
|
|
||||||
|
zoomed = false;
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
dragging = false;
|
||||||
|
|
||||||
|
constructor(viewer, file) {let iv = this;
|
||||||
|
iv.viewer = viewer;
|
||||||
|
iv.file = file;
|
||||||
|
|
||||||
|
iv.imgContainer = document.createElement("div");
|
||||||
|
iv.imgContainer.classList = "image-container";
|
||||||
|
|
||||||
|
iv.imgElement = document.createElement("img");
|
||||||
|
iv.imgElement.classList = "pannable drop-shadow";
|
||||||
|
iv.imgElement.src = apiEndpoint+"/file/"+iv.file.id;
|
||||||
|
iv.imgElement.addEventListener("mousedown", (e) => { return iv.mousedown(e); });
|
||||||
|
iv.imgElement.addEventListener("dblclick", (e) => { return iv.doubleclick(e); });
|
||||||
|
iv.imgElement.addEventListener("doubletap", (e) => { return iv.doubleclick(e); });
|
||||||
|
document.addEventListener("mousemove", (e) => { return iv.mousemove(e); });
|
||||||
|
document.addEventListener("mouseup", (e) => { return iv.mouseup(e); });
|
||||||
|
|
||||||
|
iv.imgContainer.appendChild(iv.imgElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(parent) {let iv = this;
|
||||||
|
parent.appendChild(iv.imgContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
doubleclick(e) {let iv = this;
|
||||||
|
if (iv.zoomed) {
|
||||||
|
iv.imgElement.style.maxWidth = "100%";
|
||||||
|
iv.imgElement.style.maxHeight = "100%";
|
||||||
|
iv.imgElement.style.top = "50%";
|
||||||
|
iv.imgElement.style.left = "auto";
|
||||||
|
iv.imgElement.style.transform = "translateY(-50%)";
|
||||||
|
iv.imgContainer.style.overflow = "hidden";
|
||||||
|
iv.zoomed = false;
|
||||||
|
} else {
|
||||||
|
iv.imgElement.style.maxWidth = "none";
|
||||||
|
iv.imgElement.style.maxHeight = "none";
|
||||||
|
iv.imgElement.style.top = "0";
|
||||||
|
iv.imgElement.style.left = "";
|
||||||
|
iv.imgElement.style.transform = "none";
|
||||||
|
iv.imgContainer.style.overflow = "scroll";
|
||||||
|
iv.zoomed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mousedown(e) {let iv = this;
|
||||||
|
if (!iv.dragging && e.which === 1 && iv.zoomed) {
|
||||||
|
iv.x = e.pageX;
|
||||||
|
iv.y = e.pageY;
|
||||||
|
iv.dragging = true;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mousemove(e) {let iv = this;
|
||||||
|
if (iv.dragging) {
|
||||||
|
iv.imgContainer.scrollLeft = iv.imgContainer.scrollLeft - (e.pageX - iv.x);
|
||||||
|
iv.imgContainer.scrollTop = iv.imgContainer.scrollTop - (e.pageY - iv.y);
|
||||||
|
|
||||||
|
iv.x = e.pageX;
|
||||||
|
iv.y = e.pageY;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseup(e) {let iv = this;
|
||||||
|
if (iv.dragging) {
|
||||||
|
iv.dragging = false;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// var zoomed = false;
|
||||||
|
// // When a user clicks the image
|
||||||
|
// $("#displayImg").on("dblclick doubletap", function (event) {
|
||||||
|
// if (zoomed) {
|
||||||
|
// $("#displayImg").css("max-width", "100%");
|
||||||
|
// $("#displayImg").css("max-height", "100%");
|
||||||
|
// $("#displayImg").css("top", "50%");
|
||||||
|
// $("#displayImg").css("left", "auto");
|
||||||
|
// $("#displayImg").css("transform", "translateY(-50%)");
|
||||||
|
// $(".image-container").css("overflow", "hidden");
|
||||||
|
// zoomed = false;
|
||||||
|
// } else {
|
||||||
|
// $("#displayImg").css("max-width", "none");
|
||||||
|
// $("#displayImg").css("max-height", "none");
|
||||||
|
// $("#displayImg").css("transform", "none");
|
||||||
|
// $(".pannable").css("top", "0");
|
||||||
|
// $(".image-container").css("overflow", "scroll");
|
||||||
|
// zoomed = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Image dragging around the screen
|
||||||
|
|
||||||
|
// var drag = {
|
||||||
|
// x: 0,
|
||||||
|
// y: 0,
|
||||||
|
// state: false
|
||||||
|
// };
|
||||||
|
|
||||||
|
// $(".pannable").on("mousedown", function (e) {
|
||||||
|
// if (!drag.state && e.which === 1 && zoomed) {
|
||||||
|
// drag.x = e.pageX;
|
||||||
|
// drag.y = e.pageY;
|
||||||
|
// drag.state = true;
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// var img = $(".image-container");
|
||||||
|
|
||||||
|
// $(document).on("mousemove", function (e) {
|
||||||
|
// if (drag.state) {
|
||||||
|
// img.scrollLeft(img.scrollLeft() - (e.pageX - drag.x));
|
||||||
|
// img.scrollTop(img.scrollTop() - (e.pageY - drag.y));
|
||||||
|
|
||||||
|
// drag.x = e.pageX;
|
||||||
|
// drag.y = e.pageY;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// $(document).on("mouseup", function () {
|
||||||
|
// if (drag.state) {
|
||||||
|
// drag.state = false;
|
||||||
|
// }
|
||||||
|
// });
|
34
res/include/script/file_viewer/viewer_scripts/VideoViewer.js
Normal file
34
res/include/script/file_viewer/viewer_scripts/VideoViewer.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
class VideoViewer {
|
||||||
|
viewer = null;
|
||||||
|
file = null;
|
||||||
|
next = null;
|
||||||
|
|
||||||
|
vidContainer = null;
|
||||||
|
vidElement = null;
|
||||||
|
videoSource = null;
|
||||||
|
|
||||||
|
constructor(viewer, file, next) {let vv = this;
|
||||||
|
vv.viewer = viewer;
|
||||||
|
vv.file = file;
|
||||||
|
vv.next = next;
|
||||||
|
|
||||||
|
vv.vidContainer = document.createElement("div");
|
||||||
|
vv.vidContainer.classList = "image-container";
|
||||||
|
|
||||||
|
vv.vidElement = document.createElement("video");
|
||||||
|
vv.vidElement.autoplay = "autoplay";
|
||||||
|
vv.vidElement.controls = "controls";
|
||||||
|
vv.vidElement.classList = "center drop-shadow";
|
||||||
|
vv.vidElement.addEventListener("ended", () => { vv.next(); }, false);
|
||||||
|
|
||||||
|
vv.videoSource = document.createElement("source");
|
||||||
|
vv.videoSource.src = apiEndpoint+"/file/"+vv.file.id;
|
||||||
|
|
||||||
|
vv.vidElement.appendChild(vv.videoSource);
|
||||||
|
vv.vidContainer.appendChild(vv.vidElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(parent) {let vv = this;
|
||||||
|
parent.appendChild(vv.vidContainer);
|
||||||
|
}
|
||||||
|
}
|
@@ -178,22 +178,6 @@ function copyLink() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyText(text) {
|
|
||||||
// Create a textarea to copy the text from
|
|
||||||
let ta = document.createElement("textarea");
|
|
||||||
ta.setAttribute("readonly", "readonly")
|
|
||||||
ta.style.position = "absolute";
|
|
||||||
ta.style.left = "-9999px";
|
|
||||||
ta.value = text; // Put the text in the textarea
|
|
||||||
|
|
||||||
// Add the textarea to the DOM so it can be seleted by the user
|
|
||||||
document.body.appendChild(ta);
|
|
||||||
ta.select() // Select the contents of the textarea
|
|
||||||
let success = document.execCommand("copy"); // Copy the selected text
|
|
||||||
document.body.removeChild(ta); // Remove the textarea
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upload Handlers
|
* Upload Handlers
|
||||||
*/
|
*/
|
||||||
@@ -344,6 +328,32 @@ btnCopyBBCode.addEventListener("click", function(){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let btnCopyMarkdown = document.getElementById("btn_copy_markdown");
|
||||||
|
btnCopyMarkdown.addEventListener("click", function(){
|
||||||
|
let text = "";
|
||||||
|
let uploads = uploader.finishedUploads();
|
||||||
|
|
||||||
|
// Add the text to the textarea
|
||||||
|
for (let i = 0; i < uploads.length; i++) {
|
||||||
|
// Example: * [Some_file.png](https://pixeldrain.com/u/abcd1234)
|
||||||
|
|
||||||
|
if (uploads.length > 1) { text += " * "; }
|
||||||
|
text += "["+uploads[i].fileName+"]("+domainURL()+"/u/"+uploads[i].fileID+")\n";
|
||||||
|
}
|
||||||
|
if (shareLink.includes("/l/")) {
|
||||||
|
text += " * [All "+uploads.length+" files]("+shareLink+")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the selected text
|
||||||
|
if(copyText(text)){
|
||||||
|
btnCopyMarkdown.classList.add("button_highlight");
|
||||||
|
btnCopyMarkdown.innerHTML = "Markdown copied to clipboard!"
|
||||||
|
}else{
|
||||||
|
btnCopyMarkdown.classList.add("button_red");
|
||||||
|
btnCopyMarkdown.innerHTML = "Copying links failed"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keyboard shortcuts
|
* Keyboard shortcuts
|
||||||
|
@@ -347,6 +347,7 @@ pre{
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.file_button:hover,
|
.file_button:hover,
|
||||||
.file_button_selected {
|
.file_button_selected {
|
||||||
|
4
res/static/script/jquery-2.1.4.min.js
vendored
4
res/static/script/jquery-2.1.4.min.js
vendored
File diff suppressed because one or more lines are too long
4
res/static/script/jquery-3.2.1.min.js
vendored
4
res/static/script/jquery-3.2.1.min.js
vendored
File diff suppressed because one or more lines are too long
114
res/static/script/jquery-cookie.js
vendored
114
res/static/script/jquery-cookie.js
vendored
@@ -1,114 +0,0 @@
|
|||||||
/*!
|
|
||||||
* jQuery Cookie Plugin v1.4.1
|
|
||||||
* https://github.com/carhartl/jquery-cookie
|
|
||||||
*
|
|
||||||
* Copyright 2006, 2014 Klaus Hartl
|
|
||||||
* Released under the MIT license
|
|
||||||
*/
|
|
||||||
(function (factory) {
|
|
||||||
if (typeof define === 'function' && define.amd) {
|
|
||||||
// AMD (Register as an anonymous module)
|
|
||||||
define(['jquery'], factory);
|
|
||||||
} else if (typeof exports === 'object') {
|
|
||||||
// Node/CommonJS
|
|
||||||
module.exports = factory(require('jquery'));
|
|
||||||
} else {
|
|
||||||
// Browser globals
|
|
||||||
factory(jQuery);
|
|
||||||
}
|
|
||||||
}(function ($) {
|
|
||||||
|
|
||||||
var pluses = /\+/g;
|
|
||||||
|
|
||||||
function encode(s) {
|
|
||||||
return config.raw ? s : encodeURIComponent(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decode(s) {
|
|
||||||
return config.raw ? s : decodeURIComponent(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringifyCookieValue(value) {
|
|
||||||
return encode(config.json ? JSON.stringify(value) : String(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseCookieValue(s) {
|
|
||||||
if (s.indexOf('"') === 0) {
|
|
||||||
// This is a quoted cookie as according to RFC2068, unescape...
|
|
||||||
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Replace server-side written pluses with spaces.
|
|
||||||
// If we can't decode the cookie, ignore it, it's unusable.
|
|
||||||
// If we can't parse the cookie, ignore it, it's unusable.
|
|
||||||
s = decodeURIComponent(s.replace(pluses, ' '));
|
|
||||||
return config.json ? JSON.parse(s) : s;
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function read(s, converter) {
|
|
||||||
var value = config.raw ? s : parseCookieValue(s);
|
|
||||||
return $.isFunction(converter) ? converter(value) : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = $.cookie = function (key, value, options) {
|
|
||||||
|
|
||||||
// Write
|
|
||||||
|
|
||||||
if (arguments.length > 1 && !$.isFunction(value)) {
|
|
||||||
options = $.extend({}, config.defaults, options);
|
|
||||||
|
|
||||||
if (typeof options.expires === 'number') {
|
|
||||||
var days = options.expires, t = options.expires = new Date();
|
|
||||||
t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (document.cookie = [
|
|
||||||
encode(key), '=', stringifyCookieValue(value),
|
|
||||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
|
||||||
options.path ? '; path=' + options.path : '',
|
|
||||||
options.domain ? '; domain=' + options.domain : '',
|
|
||||||
options.secure ? '; secure' : ''
|
|
||||||
].join(''));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read
|
|
||||||
|
|
||||||
var result = key ? undefined : {},
|
|
||||||
// To prevent the for loop in the first place assign an empty array
|
|
||||||
// in case there are no cookies at all. Also prevents odd result when
|
|
||||||
// calling $.cookie().
|
|
||||||
cookies = document.cookie ? document.cookie.split('; ') : [],
|
|
||||||
i = 0,
|
|
||||||
l = cookies.length;
|
|
||||||
|
|
||||||
for (; i < l; i++) {
|
|
||||||
var parts = cookies[i].split('='),
|
|
||||||
name = decode(parts.shift()),
|
|
||||||
cookie = parts.join('=');
|
|
||||||
|
|
||||||
if (key === name) {
|
|
||||||
// If second argument (value) is a function it's a converter...
|
|
||||||
result = read(cookie, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent storing a cookie that we couldn't decode.
|
|
||||||
if (!key && (cookie = read(cookie)) !== undefined) {
|
|
||||||
result[name] = cookie;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.defaults = {};
|
|
||||||
|
|
||||||
$.removeCookie = function (key, options) {
|
|
||||||
// Must not alter options, thus extending a fresh object...
|
|
||||||
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
|
|
||||||
return !$.cookie(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
}));
|
|
1
res/static/script/jquery.js
vendored
1
res/static/script/jquery.js
vendored
@@ -1 +0,0 @@
|
|||||||
jquery-3.2.1.min.js
|
|
@@ -1,79 +0,0 @@
|
|||||||
// Image zoom-in script
|
|
||||||
|
|
||||||
var zoomed = false;
|
|
||||||
// When a user clicks the image
|
|
||||||
$("#displayImg").on("contextmenu", function (event) {
|
|
||||||
// Trigger on a right click
|
|
||||||
if (event.which === 3) {
|
|
||||||
if (zoomed) {
|
|
||||||
$(".pannable").css("max-width", "100%");
|
|
||||||
$(".pannable").css("max-height", "100%");
|
|
||||||
$(".pannable").css("top", "50%");
|
|
||||||
$(".pannable").css("left", "auto");
|
|
||||||
$(".pannable").css("transform", "translateY(-50%)");
|
|
||||||
zoomed = false;
|
|
||||||
} else {
|
|
||||||
$(".pannable").css("max-width", "none");
|
|
||||||
$(".pannable").css("max-height", "none");
|
|
||||||
$(".pannable").css("transform", "none");
|
|
||||||
$(".pannable").css("top", "0");
|
|
||||||
zoomed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Image dragging around the screen
|
|
||||||
// Grabbed this script from stackoverflow, thanks Alexander Mistakidis!
|
|
||||||
// http://stackoverflow.com/questions/22187130/panning-div-element-around-using-javascript
|
|
||||||
// Edited to fit my needs
|
|
||||||
var deltaX = 0;
|
|
||||||
var deltaY = 0;
|
|
||||||
var scale = 1.0;
|
|
||||||
|
|
||||||
var drag = {
|
|
||||||
elem: null,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
state: false
|
|
||||||
};
|
|
||||||
var delta = {
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
$(".pannable").on("mousedown", function (e) {
|
|
||||||
if (!drag.state && e.which === 1) {
|
|
||||||
drag.elem = $('.pannable');
|
|
||||||
drag.x = e.pageX;
|
|
||||||
drag.y = e.pageY;
|
|
||||||
drag.state = true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$(document).on("mousemove", function (e) {
|
|
||||||
if (drag.state) {
|
|
||||||
delta.x = e.pageX - drag.x;
|
|
||||||
delta.y = e.pageY - drag.y;
|
|
||||||
|
|
||||||
var cur_offset = $(drag.elem).offset();
|
|
||||||
|
|
||||||
$(drag.elem).offset({
|
|
||||||
left: (cur_offset.left + delta.x),
|
|
||||||
top: (cur_offset.top + delta.y)
|
|
||||||
});
|
|
||||||
|
|
||||||
drag.x = e.pageX;
|
|
||||||
drag.y = e.pageY;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("mouseup", function () {
|
|
||||||
if (drag.state) {
|
|
||||||
drag.state = false;
|
|
||||||
}
|
|
||||||
});
|
|
@@ -26,7 +26,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/res/script/Chart.min.js"></script>
|
<script src="/res/script/Chart.min.js"></script>
|
||||||
<script src="/res/script/jquery.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
var apiEndpoint = '{{.APIEndpoint}}';
|
var apiEndpoint = '{{.APIEndpoint}}';
|
||||||
|
|
||||||
@@ -80,11 +79,11 @@
|
|||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(value, index, values) {
|
callback: function(value, index, values) {
|
||||||
if (value > 1e12) {
|
if (value >= 1e12) {
|
||||||
return Math.round(value/1e9)/1e3 + " TB";
|
return Math.round(value/1e9)/1e3 + " TB";
|
||||||
} else if (value > 1e9) {
|
} else if (value >= 1e9) {
|
||||||
return Math.round(value/1e6)/1e3 + " GB";
|
return Math.round(value/1e6)/1e3 + " GB";
|
||||||
} else if (value > 1e6) {
|
} else if (value >= 1e6) {
|
||||||
return Math.round(value/1e3)/1e3 + " MB";
|
return Math.round(value/1e3)/1e3 + " MB";
|
||||||
}
|
}
|
||||||
return value/1e3 + " kB";
|
return value/1e3 + " kB";
|
||||||
@@ -123,15 +122,19 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
function setData(){
|
function setData(){
|
||||||
$.get(apiEndpoint+"/admin/files/timeseries?days="+days+"&interval="+interval, function(response){
|
fetch(apiEndpoint+"/admin/files/timeseries?days="+days+"&interval="+interval).then(resp => {
|
||||||
console.log(response);
|
if (!resp.ok) { return Promise.reject("Error: "+resp.status);}
|
||||||
if (response.success) {
|
return resp.json();
|
||||||
window.graph.data.labels = response.labels;
|
}).then(resp => {
|
||||||
window.graph.data.datasets[0].data = response.downloads;
|
if (resp.success) {
|
||||||
window.graph.data.datasets[1].data = response.views;
|
window.graph.data.labels = resp.labels;
|
||||||
|
window.graph.data.datasets[0].data = resp.downloads;
|
||||||
|
window.graph.data.datasets[1].data = resp.views;
|
||||||
window.graph.update();
|
window.graph.update();
|
||||||
}
|
}
|
||||||
});
|
}).catch(e => {
|
||||||
|
alert("Error requesting time series: "+e);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setData();
|
setData();
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="file_viewer" class="file_viewer">
|
<div id="file_viewer" class="file_viewer">
|
||||||
<div id="file_viewer_headerbar" class="highlight_1 file_viewer_headerbar">
|
<div id="file_viewer_headerbar" class="highlight_1 file_viewer_headerbar">
|
||||||
<button id="button_toggle_toolbar" class="button_toggle_toolbar" onClick="Toolbar.toggle();">☰</button>
|
<button id="btn_toggle_toolbar" class="button_toggle_toolbar">☰</button>
|
||||||
<a href="/" id="button_home" class="button button_home">
|
<a href="/" id="button_home" class="button button_home">
|
||||||
<img src="{{template `pixeldrain_icon.png`}}"
|
<img src="{{template `pixeldrain_icon.png`}}"
|
||||||
alt="Back to the Home page"
|
alt="Back to the Home page"
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<div id="file_viewer_list_title"></div>
|
<div id="file_viewer_list_title"></div>
|
||||||
<div id="file_viewer_file_title">{{.Title}}</div>
|
<div id="file_viewer_file_title">{{.Title}}</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="button_close_file_viewer" class="button_close_file_viewer button_red" onClick="window.close();">{{template `close.svg` .}}</button>
|
<button id="button_close_file_viewer" class="button_close_file_viewer button_red" onclick="window.close();">{{template `close.svg` .}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="list_navigator" class="list_navigator"></div>
|
<div id="list_navigator" class="list_navigator"></div>
|
||||||
<div id="file_viewer_window" class="file_viewer_window">
|
<div id="file_viewer_window" class="file_viewer_window">
|
||||||
@@ -42,27 +42,27 @@
|
|||||||
<div id="stat_downloads_label" class="toolbar_label">Downloads</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_downloads" style="text-align: center;">N/A</div>
|
||||||
|
|
||||||
<button id="btnDownload" class="toolbar_button button_full_width" onClick="Toolbar.download();">
|
<button id="btn_download" class="toolbar_button button_full_width">
|
||||||
{{template `save.svg` .}}
|
{{template `save.svg` .}}
|
||||||
<span>Download</span>
|
<span>Download</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="btnDownloadList" class="toolbar_button button_full_width" style="display: none" onClick="Toolbar.downloadList()">
|
<button id="btn_download_list" class="toolbar_button button_full_width" style="display: none">
|
||||||
{{template `save.svg` .}}
|
{{template `save.svg` .}}
|
||||||
<span>DL all files</span>
|
<span>DL all files</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="btnCopy" class="toolbar_button button_full_width" onClick="Toolbar.copyUrl();">
|
<button id="btn_copy" class="toolbar_button button_full_width">
|
||||||
{{template `copy.svg` .}}
|
{{template `copy.svg` .}}
|
||||||
<span><u>C</u>opy Link</span>
|
<span><u>C</u>opy Link</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="btnShare" class="toolbar_button button_full_width" onClick="Sharebar.toggle();">
|
<button id="btn_share" class="toolbar_button button_full_width">
|
||||||
{{template `share.svg` .}}
|
{{template `share.svg` .}}
|
||||||
<span>Share</span>
|
<span>Share</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="btnShuffle" class="toolbar_button button_full_width" style="display: none" onClick="ListNavigator.toggleShuffle();">
|
<button id="btn_shuffle" class="toolbar_button button_full_width" style="display: none">
|
||||||
{{template `shuffle.svg` .}}
|
{{template `shuffle.svg` .}}
|
||||||
<span>Shuffle ☐</span>
|
<span>Shuffle ☐</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="btnDetails" class="toolbar_button button_full_width" onClick="DetailsWindow.toggle();">
|
<button id="btn_details" class="toolbar_button button_full_width">
|
||||||
{{template `help.svg` .}}
|
{{template `help.svg` .}}
|
||||||
<span>Deta<u>i</u>ls</span>
|
<span>Deta<u>i</u>ls</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
<div id="details_popup" class="popup details_popup">
|
<div id="details_popup" class="popup details_popup">
|
||||||
<div id="details_popup_title" class="highlight_1">
|
<div id="details_popup_title" class="highlight_1">
|
||||||
File Info
|
File Info
|
||||||
<button style="position: absolute; top: 3px; right: 3px;" class="button_red" onclick="DetailsWindow.toggle();">{{template `close.svg` .}}</button>
|
<button id="btn_close_details" style="position: absolute; top: 3px; right: 3px;" class="button_red">{{template `close.svg` .}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content_area">
|
<div class="content_area">
|
||||||
<div id="info_file_details"></div>
|
<div id="info_file_details"></div>
|
||||||
@@ -150,19 +150,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/res/script/Chart.min.js"></script>
|
<script src="/res/script/Chart.min.js"></script>
|
||||||
<script src="/res/script/jquery.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
var apiEndpoint = '{{.APIEndpoint}}';
|
var apiEndpoint = '{{.APIEndpoint}}';
|
||||||
var captchaKey = '{{.Other.CaptchaKey}}';
|
var captchaKey = '{{.Other.CaptchaKey}}';
|
||||||
|
|
||||||
|
{{template `util.js`}}
|
||||||
{{template `Toolbar.js`}}
|
{{template `Toolbar.js`}}
|
||||||
{{template `Viewer.js`}}
|
{{template `DetailsWindow.js`}}
|
||||||
{{template `ListNavigator.js`}}
|
{{template `ListNavigator.js`}}
|
||||||
|
{{template `Viewer.js`}}
|
||||||
|
|
||||||
|
{{template `ImageViewer.js`}}
|
||||||
|
{{template `VideoViewer.js`}}
|
||||||
|
{{template `AudioViewer.js`}}
|
||||||
|
|
||||||
// This info gets filled in on the server side to prevent having to make an API call right after the page loads.
|
// 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 :)
|
// Just to slice another few milliseconds from the load time :)
|
||||||
window.addEventListener("load", function(){
|
window.addEventListener("load", function(){
|
||||||
Viewer.init('{{.Other.Type}}', {{.Other.APIResponse}});
|
new Viewer('{{.Other.Type}}', {{.Other.APIResponse}});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -106,6 +106,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<button id="btn_create_list">Create list with uploaded files</button>
|
<button id="btn_create_list">Create list with uploaded files</button>
|
||||||
<button id="btn_copy_links">Copy all links to clipboard</button>
|
<button id="btn_copy_links">Copy all links to clipboard</button>
|
||||||
|
<button id="btn_copy_markdown">Copy markdown to clipboard</button>
|
||||||
<button id="btn_copy_bbcode">Copy BBCode to clipboard</button>
|
<button id="btn_copy_bbcode">Copy BBCode to clipboard</button>
|
||||||
<br/>
|
<br/>
|
||||||
<div id="created_lists"></div>
|
<div id="created_lists"></div>
|
||||||
|
Reference in New Issue
Block a user