add compat file viewer

This commit is contained in:
2020-01-28 12:51:21 +01:00
parent cb4082fd44
commit 8151aaa18e
23 changed files with 552 additions and 702 deletions

View File

@@ -14,7 +14,7 @@ type List struct {
Title string `json:"title"` Title string `json:"title"`
DateCreated time.Time `json:"date_created"` DateCreated time.Time `json:"date_created"`
FileCount int `json:"file_count"` FileCount int `json:"file_count"`
Files []ListFile `json:"files,omitempty"` Files []ListFile `json:"files"`
} }
// ListFile information object from the pixeldrain API // ListFile information object from the pixeldrain API
@@ -26,11 +26,6 @@ type ListFile struct {
// 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
// List.Error. Standard error checks apply. // List.Error. Standard error checks apply.
func (p *PixelAPI) GetList(id string) (resp *List, err error) { func (p *PixelAPI) GetList(id string) (resp List, err error) {
resp = &List{} return resp, p.jsonRequest("GET", p.apiEndpoint+"/list/"+id, &resp)
err = p.jsonRequest("GET", p.apiEndpoint+"/list/"+id, resp)
if err != nil {
return nil, err
}
return resp, nil
} }

View File

@@ -1,12 +1,11 @@
function UploadManager(apiAddress, uploadsFinished) {let um = this; function UploadManager(apiAddress, uploadsFinished) {
this.apiAddress = apiAddress;
um.apiAddress = apiAddress;
// Callback function for when the queue is empty // Callback function for when the queue is empty
um.uploadsFinished = uploadsFinished; this.uploadsFinished = uploadsFinished;
// Counts the total number of upload jobs // Counts the total number of upload jobs
um.jobCounter = 0; this.jobCounter = 0;
// Queue of files to be uploaded. Format: // Queue of files to be uploaded. Format:
// { // {
@@ -18,28 +17,28 @@ function UploadManager(apiAddress, uploadsFinished) {let um = this;
// onFailure: function, // onFailure: function,
// tries: number // tries: number
// } // }
um.uploadQueue = []; this.uploadQueue = [];
// Here we put successful jobs. The array should be sorted by job ID. // Here we put successful jobs. The array should be sorted by job ID.
// Format: // Format:
// { jobID: number, fileID: string, fileName: string } // { jobID: number, fileID: string, fileName: string }
um.uploadLog = []; this.uploadLog = [];
// Max number of uploading threads at once // Max number of uploading threads at once
um.maxWorkers = 3; this.maxWorkers = 3;
// Threads which are currently uploading // Threads which are currently uploading
um.activeWorkers = 0; this.activeWorkers = 0;
// Total number of jobs accepted // Total number of jobs accepted
um.jobCounter = 0; this.jobCounter = 0;
} }
UploadManager.prototype.finishedUploads = function() {let um = this; UploadManager.prototype.finishedUploads = function() {
um.uploadLog.sort(function(a, b) { this.uploadLog.sort((a, b) => {
return a.jobID - b.jobID; return a.jobID - b.jobID;
}) })
return um.uploadLog; return this.uploadLog;
} }
UploadManager.prototype.addFile = function( UploadManager.prototype.addFile = function(
@@ -48,9 +47,9 @@ UploadManager.prototype.addFile = function(
onProgress, // func (progress: number) onProgress, // func (progress: number)
onFinished, // func (id: string) onFinished, // func (id: string)
onFailure // func (errorID: string, errorMessage: string) onFailure // func (errorID: string, errorMessage: string)
) {let um = this; ) {
um.uploadQueue.push({ this.uploadQueue.push({
jobID: um.jobCounter, jobID: this.jobCounter,
file: file, file: file,
name: name, name: name,
onProgress: onProgress, onProgress: onProgress,
@@ -60,43 +59,43 @@ UploadManager.prototype.addFile = function(
}); });
// Increment the job counter // Increment the job counter
um.jobCounter++ this.jobCounter++
if (um.activeWorkers < um.maxWorkers) { if (this.activeWorkers < this.maxWorkers) {
// Run the upload function // Run the upload function
um.startUpload(); this.startUpload();
} }
} }
UploadManager.prototype.startUpload = function() {let um = this; UploadManager.prototype.startUpload = function() {
if (um.uploadQueue.length === 0) { if (this.uploadQueue.length === 0) {
return; // Nothing to upload return; // Nothing to upload
} }
if (um.activeWorkers < um.maxWorkers) { if (this.activeWorkers < this.maxWorkers) {
um.activeWorkers++; this.activeWorkers++;
um.uploadThread(); this.uploadThread();
} }
} }
UploadManager.prototype.finishUpload = function() {let um = this; UploadManager.prototype.finishUpload = function() {
um.activeWorkers--; this.activeWorkers--;
if ( if (
um.uploadQueue.length === 0 && this.uploadQueue.length === 0 &&
um.activeWorkers === 0 && this.activeWorkers === 0 &&
typeof(um.uploadsFinished) === "function" typeof(this.uploadsFinished) === "function"
) { ) {
um.uploadsFinished(); this.uploadsFinished();
return; return;
} }
// Run the upload function for the next file // Run the upload function for the next file
um.startUpload(); this.startUpload();
} }
UploadManager.prototype.uploadThread = function() {let um = this; UploadManager.prototype.uploadThread = function() {
let job = um.uploadQueue.shift(); // Get the first element of the array let job = this.uploadQueue.shift(); // Get the first element of the array
console.debug("Starting upload of " + job.name); console.debug("Starting upload of " + job.name);
let form = new FormData(); let form = new FormData();
@@ -104,17 +103,17 @@ UploadManager.prototype.uploadThread = function() {let um = this;
form.append('file', job.file); form.append('file', job.file);
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open("POST", um.apiAddress + "/file", true); xhr.open("POST", this.apiAddress + "/file", true);
xhr.timeout = 21600000; // 6 hours, to account for slow connections xhr.timeout = 21600000; // 6 hours, to account for slow connections
// Report progress updates back to the caller // Report progress updates back to the caller
xhr.upload.addEventListener("progress", function (evt) { xhr.upload.addEventListener("progress", evt => {
if (evt.lengthComputable && typeof(job.onProgress) === "function") { if (evt.lengthComputable && typeof(job.onProgress) === "function") {
job.onProgress(evt.loaded / evt.total); job.onProgress(evt.loaded / evt.total);
} }
}); });
xhr.onreadystatechange = function () { xhr.onreadystatechange = () => {
// readystate 4 means the upload is done // readystate 4 means the upload is done
if (xhr.readyState !== 4) { return; } if (xhr.readyState !== 4) { return; }
@@ -124,7 +123,7 @@ UploadManager.prototype.uploadThread = function() {let um = this;
addUploadHistory(resp.id) addUploadHistory(resp.id)
// Log the successful job // Log the successful job
um.uploadLog.push({ this.uploadLog.push({
jobID: job.jobID, jobID: job.jobID,
fileID: resp.id, fileID: resp.id,
fileName: job.name fileName: job.name
@@ -135,7 +134,7 @@ UploadManager.prototype.uploadThread = function() {let um = this;
} }
// Finish the upload job // Finish the upload job
um.finishUpload(); this.finishUpload();
} else if (xhr.status >= 400) { } else if (xhr.status >= 400) {
// Request failed // Request failed
console.log("Upload error. status: " + xhr.status + " response: " + xhr.response); console.log("Upload error. status: " + xhr.status + " response: " + xhr.response);
@@ -144,11 +143,11 @@ UploadManager.prototype.uploadThread = function() {let um = this;
job.onFailure(resp.value, resp.message); job.onFailure(resp.value, resp.message);
} else { // Try again } else { // Try again
job.tries++; job.tries++;
um.uploadQueue.push(job); this.uploadQueue.push(job);
} }
// Sleep the upload thread for 5 seconds // Sleep the upload thread for 5 seconds
window.setTimeout(() => { um.finishUpload(); }, 5000); window.setTimeout(() => { this.finishUpload(); }, 5000);
} else { } else {
// Request did not arrive // Request did not arrive
if (job.tries === 3) { // Upload failed if (job.tries === 3) { // Upload failed
@@ -157,11 +156,11 @@ UploadManager.prototype.uploadThread = function() {let um = this;
} }
} else { // Try again } else { // Try again
job.tries++; job.tries++;
um.uploadQueue.push(job); this.uploadQueue.push(job);
} }
// Sleep the upload thread for 5 seconds // Sleep the upload thread for 5 seconds
window.setTimeout(() => { um.finishUpload(); }, 5000); window.setTimeout(() => { this.finishUpload(); }, 5000);
} }
}; };
xhr.send(form); xhr.send(form);

View File

@@ -1,48 +1,48 @@
function DetailsWindow(viewer) {let dw = this; function DetailsWindow(viewer) {
dw.viewer = viewer; this.viewer = viewer;
dw.visible = false; this.visible = false;
dw.fileID = ""; this.fileID = "";
dw.graph = 0; this.graph = 0;
dw.divPopup = document.getElementById("details_popup"); this.divPopup = document.getElementById("details_popup");
dw.btnDetails = document.getElementById("btn_details"); this.btnDetails = document.getElementById("btn_details");
dw.btnCloseDetails = document.getElementById("btn_close_details"); this.btnCloseDetails = document.getElementById("btn_close_details");
dw.divFileDetails = document.getElementById("info_file_details"); this.divFileDetails = document.getElementById("info_file_details");
dw.btnDetails.addEventListener("click", () => { dw.toggle(); }); this.btnDetails.addEventListener("click", () => { this.toggle(); });
dw.btnCloseDetails.addEventListener("click", () => { dw.toggle(); }); this.btnCloseDetails.addEventListener("click", () => { this.toggle(); });
} }
DetailsWindow.prototype.toggle = function() {let dw = this; DetailsWindow.prototype.toggle = function() {
if (dw.visible) { if (this.visible) {
dw.divPopup.style.opacity = "0"; this.divPopup.style.opacity = "0";
dw.divPopup.style.visibility = "hidden"; this.divPopup.style.visibility = "hidden";
dw.btnDetails.classList.remove("button_highlight"); this.btnDetails.classList.remove("button_highlight");
dw.visible = false; this.visible = false;
} else { } else {
dw.divPopup.style.opacity = "1"; this.divPopup.style.opacity = "1";
dw.divPopup.style.visibility = "visible"; this.divPopup.style.visibility = "visible";
dw.btnDetails.classList.add("button_highlight"); this.btnDetails.classList.add("button_highlight");
dw.visible = true; this.visible = true;
// This is a workaround for a chrome bug which makes it so hidden // This is a workaround for a chrome bug which makes it so hidden
// windows can't be scrolled after they are shown // windows can't be scrolled after they are shown
dw.divPopup.focus(); this.divPopup.focus();
if (dw.graph === 0) { if (this.graph === 0) {
dw.renderGraph(); this.renderGraph();
} }
dw.updateGraph(dw.fileID); this.updateGraph(this.fileID);
} }
} }
DetailsWindow.prototype.setDetails = function(file) {let dw = this; DetailsWindow.prototype.setDetails = function(file) {
let desc = ""; let desc = "";
if (dw.viewer.isList) { if (this.viewer.isList) {
desc = file.description; desc = file.description;
} }
dw.fileID = file.id; this.fileID = file.id;
dw.divFileDetails.innerHTML = "<table>" this.divFileDetails.innerHTML = "<table>"
+ "<tr><td>Name<td><td>" + escapeHTML(file.name) + "</td></tr>" + "<tr><td>Name<td><td>" + escapeHTML(file.name) + "</td></tr>"
+ "<tr><td>URL<td><td><a href=\"/u/" + file.id + "\">"+domainURL()+"/u/" + file.id + "</a></td></tr>" + "<tr><td>URL<td><td><a href=\"/u/" + file.id + "\">"+domainURL()+"/u/" + file.id + "</a></td></tr>"
+ "<tr><td>Mime Type<td><td>" + escapeHTML(file.mime_type) + "</td></tr>" + "<tr><td>Mime Type<td><td>" + escapeHTML(file.mime_type) + "</td></tr>"
@@ -53,25 +53,25 @@ DetailsWindow.prototype.setDetails = function(file) {let dw = this;
+ "<tr><td>Description<td><td>" + escapeHTML(desc) + "</td></tr>" + "<tr><td>Description<td><td>" + escapeHTML(desc) + "</td></tr>"
+ "</table>"; + "</table>";
if(dw.visible) { if(this.visible) {
dw.updateGraph(file.id); this.updateGraph(file.id);
} }
} }
DetailsWindow.prototype.updateGraph = function(fileID) {let dw = this; DetailsWindow.prototype.updateGraph = function(fileID) {
console.log("updating graph "+fileID); console.log("updating graph "+fileID);
fetch(apiEndpoint+"/file/" + fileID + "/timeseries?interval=60?days=14").then(resp => { fetch(apiEndpoint+"/file/" + fileID + "/timeseries?interval=60?days=14").then(resp => {
if (!resp.ok) {return null;} if (!resp.ok) {return null;}
return resp.json(); return resp.json();
}).then(resp => { }).then(resp => {
dw.graph.data.labels = resp.labels; this.graph.data.labels = resp.labels;
dw.graph.data.datasets[0].data = resp.downloads; this.graph.data.datasets[0].data = resp.downloads;
dw.graph.data.datasets[1].data = resp.views; this.graph.data.datasets[1].data = resp.views;
dw.graph.update(); this.graph.update();
}) })
} }
DetailsWindow.prototype.renderGraph = function() {let dw = this; DetailsWindow.prototype.renderGraph = function() {
console.log("rendering graph"); console.log("rendering graph");
Chart.defaults.global.defaultFontColor = "#b3b3b3"; Chart.defaults.global.defaultFontColor = "#b3b3b3";
Chart.defaults.global.defaultFontSize = 15; Chart.defaults.global.defaultFontSize = 15;
@@ -81,7 +81,7 @@ DetailsWindow.prototype.renderGraph = function() {let dw = this;
Chart.defaults.global.tooltips.mode = "index"; Chart.defaults.global.tooltips.mode = "index";
Chart.defaults.global.tooltips.axis = "x"; Chart.defaults.global.tooltips.axis = "x";
Chart.defaults.global.tooltips.intersect = false; Chart.defaults.global.tooltips.intersect = false;
dw.graph = new Chart( this.graph = new Chart(
document.getElementById('bandwidth_chart'), document.getElementById('bandwidth_chart'),
{ {
type: 'line', type: 'line',

View File

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

View File

@@ -1,73 +1,73 @@
function Toolbar(viewer) {let t = this; function Toolbar(viewer) {
t.viewer = viewer; this.viewer = viewer;
t.visible = false; this.visible = false;
t.sharebarVisible = false; this.sharebarVisible = false;
t.divToolbar = document.getElementById("toolbar"); this.divToolbar = document.getElementById("toolbar");
t.divFilePreview = document.getElementById("filepreview"); this.divFilePreview = document.getElementById("filepreview");
t.downloadFrame = document.getElementById("download_frame"); this.downloadFrame = document.getElementById("download_frame");
t.spanViews = document.getElementById("stat_views"); this.spanViews = document.getElementById("stat_views");
t.spanDownloads = document.getElementById("stat_downloads"); this.spanDownloads = document.getElementById("stat_downloads");
t.spanSize = document.getElementById("stat_size"); this.spanSize = document.getElementById("stat_size");
t.btnToggleToolbar = document.getElementById("btn_toggle_toolbar"); this.btnToggleToolbar = document.getElementById("btn_toggle_toolbar");
t.btnDownload = document.getElementById("btn_download"); this.btnDownload = document.getElementById("btn_download");
t.btnCopyLink = document.getElementById("btn_copy"); this.btnCopyLink = document.getElementById("btn_copy");
t.spanCopyLink = document.querySelector("#btn_copy > span"); this.spanCopyLink = document.querySelector("#btn_copy > span");
t.btnShare = document.getElementById("btn_share"); this.btnShare = document.getElementById("btn_share");
t.divSharebar = document.getElementById("sharebar"); this.divSharebar = document.getElementById("sharebar");
t.btnToggleToolbar.addEventListener("click", () => { t.toggle(); }); this.btnToggleToolbar.addEventListener("click", () => { this.toggle(); });
t.btnDownload.addEventListener("click", () => { t.download(); }); this.btnDownload.addEventListener("click", () => { this.download(); });
t.btnCopyLink.addEventListener("click", () => { t.copyUrl(); }); this.btnCopyLink.addEventListener("click", () => { this.copyUrl(); });
t.btnShare.addEventListener("click", () => { t.toggleSharebar(); }); this.btnShare.addEventListener("click", () => { this.toggleSharebar(); });
} }
Toolbar.prototype.toggle = function() {let t = this; Toolbar.prototype.toggle = function() {
if (t.visible) { if (this.visible) {
if (t.sharebarVisible) { t.toggleSharebar(); } if (this.sharebarVisible) { this.toggleSharebar(); }
t.divToolbar.style.left = "-8em"; this.divToolbar.style.left = "-8em";
t.divFilePreview.style.left = "0px"; this.divFilePreview.style.left = "0px";
t.btnToggleToolbar.classList.remove("button_highlight"); this.btnToggleToolbar.classList.remove("button_highlight");
t.visible = false; this.visible = false;
} else { } else {
t.divToolbar.style.left = "0px"; this.divToolbar.style.left = "0px";
t.divFilePreview.style.left = "8em"; this.divFilePreview.style.left = "8em";
t.btnToggleToolbar.classList.add("button_highlight"); this.btnToggleToolbar.classList.add("button_highlight");
t.visible = true; this.visible = true;
} }
} }
Toolbar.prototype.toggleSharebar = function() {let t = this; Toolbar.prototype.toggleSharebar = function() {
if (navigator.share) { if (navigator.share) {
navigator.share({ navigator.share({
title: t.viewer.title, title: this.viewer.title,
text: "Download " + t.viewer.title + " here", text: "Download " + this.viewer.title + " here",
url: window.location.href url: window.location.href
}); });
return; return;
} }
if(t.sharebarVisible){ if(this.sharebarVisible){
t.divSharebar.style.left = "-8em"; this.divSharebar.style.left = "-8em";
t.btnShare.classList.remove("button_highlight") this.btnShare.classList.remove("button_highlight")
t.sharebarVisible = false; this.sharebarVisible = false;
}else{ }else{
t.divSharebar.style.left = "8em"; this.divSharebar.style.left = "8em";
t.btnShare.classList.add("button_highlight") this.btnShare.classList.add("button_highlight")
t.sharebarVisible = true; this.sharebarVisible = true;
} }
} }
Toolbar.prototype.download = function() {let t = this; Toolbar.prototype.download = function() {
let triggerDL = (captchaResp = "") => { let triggerDL = (captchaResp = "") => {
if (captchaResp === "") { if (captchaResp === "") {
t.downloadFrame.src = apiEndpoint+"/file/"+ this.downloadFrame.src = apiEndpoint+"/file/"+
t.viewer.currentFile+"?download"; this.viewer.currentFile+"?download";
} else { } else {
t.downloadFrame.src = apiEndpoint+"/file/"+ this.downloadFrame.src = apiEndpoint+"/file/"+
t.viewer.currentFile+"?download&recaptcha_response="+captchaResp; this.viewer.currentFile+"?download&recaptcha_response="+captchaResp;
} }
} }
@@ -87,7 +87,7 @@ Toolbar.prototype.download = function() {let t = this;
return; return;
} }
fetch(apiEndpoint+"/file/"+t.viewer.currentFile+"/availability").then(resp => { fetch(apiEndpoint+"/file/"+this.viewer.currentFile+"/availability").then(resp => {
return resp.json(); return resp.json();
}).then(resp => { }).then(resp => {
let popupDiv = document.getElementById("captcha_popup"); let popupDiv = document.getElementById("captcha_popup");
@@ -130,28 +130,28 @@ Toolbar.prototype.download = function() {let t = this;
}); });
} }
Toolbar.prototype.copyUrl = function() {let t = this; Toolbar.prototype.copyUrl = function() {
if(copyText(window.location.href)) { if(copyText(window.location.href)) {
console.log('Text copied'); console.log('Text copied');
t.spanCopyLink.innerText = "Copied!"; this.spanCopyLink.innerText = "Copied!";
t.btnCopyLink.classList.add("button_highlight") this.btnCopyLink.classList.add("button_highlight")
} else { } else {
console.log('Copying not supported'); console.log('Copying not supported');
t.spanCopyLink.innerText = "Error!"; this.spanCopyLink.innerText = "Error!";
alert("Your browser does not support copying text."); alert("Your browser does not support copying text.");
} }
// Return to normal // Return to normal
setTimeout(() => { setTimeout(() => {
t.spanCopyLink.innerText = "Copy"; this.spanCopyLink.innerText = "Copy";
t.btnCopyLink.classList.remove("button_highlight") this.btnCopyLink.classList.remove("button_highlight")
}, 60000); }, 60000);
} }
Toolbar.prototype.setStats = function(file) {let t = this; Toolbar.prototype.setStats = function(file) {
t.spanViews.innerText = file.views this.spanViews.innerText = file.views
t.spanDownloads.innerText = Math.round((file.bandwidth_used/file.size)*10)/10; this.spanDownloads.innerText = Math.round((file.bandwidth_used/file.size)*10)/10;
t.spanSize.innerText = formatDataVolume(file.size, 3); this.spanSize.innerText = formatDataVolume(file.size, 3);
} }

View File

@@ -1,27 +1,27 @@
function Viewer(type, viewToken, data) {let v = this; function Viewer(type, viewToken, data) {
// Set defaults // Set defaults
v.toolbar = null; this.toolbar = null;
v.listNavigator = null; this.listNavigator = null;
v.detailsWindow = null; this.detailsWindow = null;
v.divFilepreview = null; this.divFilepreview = null;
v.currentFile = ""; this.currentFile = "";
v.title = ""; // Contains either the file name or list title this.title = ""; // Contains either the file name or list title
v.listId = ""; this.listId = "";
v.viewToken = ""; this.viewToken = "";
v.isList = false; this.isList = false;
v.isFile = false; this.isFile = false;
v.initialized = false; this.initialized = false;
v.viewToken = viewToken; this.viewToken = viewToken;
v.toolbar = new Toolbar(v); this.toolbar = new Toolbar(this);
v.detailsWindow = new DetailsWindow(v); this.detailsWindow = new DetailsWindow(this);
v.divFilepreview = document.getElementById("filepreview"); this.divFilepreview = document.getElementById("filepreview");
// On small screens the toolbar takes too much space, so it collapses // On small screens the toolbar takes too much space, so it collapses
// automatically // automatically
if (v.divFilepreview.clientWidth > 600 && !v.toolbar.visible) { if (this.divFilepreview.clientWidth > 600 && !this.toolbar.visible) {
v.toolbar.toggle(); this.toolbar.toggle();
} }
// The close button only works if the window has an opener. So we hide // The close button only works if the window has an opener. So we hide
@@ -31,41 +31,41 @@ function Viewer(type, viewToken, data) {let v = this;
} }
if (type === "file") { if (type === "file") {
v.isFile = true; this.isFile = true;
v.currentFile = data.id; this.currentFile = data.id;
v.title = data.name; this.title = data.name;
v.setFile(data); this.setFile(data);
} else if (type === "list") { } else if (type === "list") {
v.isList = true; this.isList = true;
v.listId = data.id; this.listId = data.id;
v.title = data.title; this.title = data.title;
v.listNavigator = new ListNavigator(v, data.data); this.listNavigator = new ListNavigator(this, data.files);
} }
v.renderSponsors(); this.renderSponsors();
window.addEventListener("resize", e => { v.renderSponsors(e); }); window.addEventListener("resize", e => { this.renderSponsors(e); });
// Register keyboard shortcuts // Register keyboard shortcuts
document.addEventListener("keydown", e => { v.keyboardEvent(e); }); document.addEventListener("keydown", e => { this.keyboardEvent(e); });
v.initialized = true; this.initialized = true;
} }
Viewer.prototype.setFile = function(file) {let v = this; Viewer.prototype.setFile = function(file) {
v.currentFile = file.id; this.currentFile = file.id;
if (v.isList) { if (this.isList) {
document.getElementById("file_viewer_headerbar_title").style.lineHeight = "1em"; document.getElementById("file_viewer_headerbar_title").style.lineHeight = "1em";
document.getElementById("file_viewer_list_title").innerText = this.title; document.getElementById("file_viewer_list_title").innerText = this.title;
document.getElementById("file_viewer_file_title").innerText = file.name; document.getElementById("file_viewer_file_title").innerText = file.name;
document.title = v.title + " ~ " + file.name + " ~ pixeldrain"; document.title = this.title + " ~ " + file.name + " ~ pixeldrain";
} else { } else {
document.getElementById("file_viewer_file_title").innerText = file.name; document.getElementById("file_viewer_file_title").innerText = file.name;
document.title = file.name + " ~ pixeldrain"; document.title = file.name + " ~ pixeldrain";
} }
// Update the file details // Update the file details
v.detailsWindow.setDetails(file); this.detailsWindow.setDetails(file);
v.toolbar.setStats(file); this.toolbar.setStats(file);
// Register a new view. We don't care what this returns becasue we can't // Register a new view. We don't care what this returns becasue we can't
// do anything about it anyway // do anything about it anyway
@@ -73,47 +73,47 @@ Viewer.prototype.setFile = function(file) {let v = this;
{ {
method: "POST", method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"}, headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "token="+v.viewToken body: "token="+this.viewToken
} }
); );
// Clear the canvas // Clear the canvas
v.divFilepreview.innerHTML = ""; this.divFilepreview.innerHTML = "";
let nextItem = () => { let nextItem = () => {
if (v.listNavigator !== null) { if (this.listNavigator !== null) {
v.listNavigator.nextItem(); this.listNavigator.nextItem();
} }
}; };
if ( if (
file.mime_type.startsWith("image") file.mime_type.startsWith("image")
) { ) {
new ImageViewer(v, file).render(v.divFilepreview); new ImageViewer(this, file).render(this.divFilepreview);
} else if ( } else if (
file.mime_type.startsWith("video") || file.mime_type.startsWith("video") ||
file.mime_type === "application/matroska" || file.mime_type === "application/matroska" ||
file.mime_type === "application/x-matroska" file.mime_type === "application/x-matroska"
) { ) {
new VideoViewer(v, file, nextItem).render(v.divFilepreview); new VideoViewer(this, file, nextItem).render(this.divFilepreview);
} else if ( } else if (
file.mime_type.startsWith("audio") || file.mime_type.startsWith("audio") ||
file.mime_type === "application/ogg" || file.mime_type === "application/ogg" ||
file.name.endsWith(".mp3") file.name.endsWith(".mp3")
) { ) {
new AudioViewer(v, file, nextItem).render(v.divFilepreview); new AudioViewer(this, file, nextItem).render(this.divFilepreview);
} else if ( } else if (
file.mime_type === "application/pdf" || file.mime_type === "application/pdf" ||
file.mime_type === "application/x-pdf" file.mime_type === "application/x-pdf"
) { ) {
new PDFViewer(v, file).render(v.divFilepreview); new PDFViewer(this, file).render(this.divFilepreview);
} else if ( } else if (
file.mime_type.startsWith("text") || file.mime_type.startsWith("text") ||
file.id === "demo" file.id === "demo"
) { ) {
new TextViewer(v, file).render(v.divFilepreview); new TextViewer(this, file).render(this.divFilepreview);
} else { } else {
new FileViewer(v, file).render(v.divFilepreview); new FileViewer(this, file).render(this.divFilepreview);
} }
} }
@@ -152,7 +152,7 @@ Viewer.prototype.renderSponsors = function() {
} }
} }
Viewer.prototype.keyboardEvent = function(evt) {let v = this; Viewer.prototype.keyboardEvent = function(evt) {
if (evt.ctrlKey || evt.altKey) { if (evt.ctrlKey || evt.altKey) {
return // prevent custom shortcuts from interfering with system shortcuts return // prevent custom shortcuts from interfering with system shortcuts
} }
@@ -160,33 +160,33 @@ Viewer.prototype.keyboardEvent = function(evt) {let v = this;
switch (evt.keyCode) { switch (evt.keyCode) {
case 65: // A or left arrow key go to previous file case 65: // A or left arrow key go to previous file
case 37: case 37:
if (v.listNavigator != null) { if (this.listNavigator != null) {
v.listNavigator.previousItem(); this.listNavigator.previousItem();
} }
break; break;
case 68: // D or right arrow key go to next file case 68: // D or right arrow key go to next file
case 39: case 39:
if (v.listNavigator != null) { if (this.listNavigator != null) {
v.listNavigator.nextItem(); this.listNavigator.nextItem();
} }
break; break;
case 83: case 83:
if (evt.shiftKey) { if (evt.shiftKey) {
v.listNavigator.downloadList(); // SHIFT + S downloads all files in list this.listNavigator.downloadList(); // SHIFT + S downloads all files in list
} else { } else {
v.toolbar.download(); // S to download the current file this.toolbar.download(); // S to download the current file
} }
break; break;
case 82: // R to toggle list shuffle case 82: // R to toggle list shuffle
if (v.listNavigator != null) { if (this.listNavigator != null) {
v.listNavigator.toggleShuffle(); this.listNavigator.toggleShuffle();
} }
break; break;
case 67: // C to copy to clipboard case 67: // C to copy to clipboard
v.toolbar.copyUrl(); this.toolbar.copyUrl();
break; break;
case 73: // I to open the details window case 73: // I to open the details window
v.detailsWindow.toggle(); this.detailsWindow.toggle();
break; break;
case 81: // Q to close the window case 81: // Q to close the window
window.close(); window.close();

View File

@@ -1,33 +1,33 @@
function AudioViewer(viewer, file, next) {let v = this; function AudioViewer(viewer, file, next) {
v.viewer = viewer; this.viewer = viewer;
v.file = file; this.file = file;
v.next = next; this.next = next;
v.container = document.createElement("div"); this.container = document.createElement("div");
v.container.classList = "image-container"; this.container.classList = "image-container";
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.icon = document.createElement("img"); this.icon = document.createElement("img");
v.icon.src = "/res/img/mime/audio.png"; this.icon.src = "/res/img/mime/audio.png";
v.container.appendChild(v.icon); this.container.appendChild(this.icon);
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.container.appendChild(document.createTextNode(file.name)); this.container.appendChild(document.createTextNode(file.name));
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.element = document.createElement("audio"); this.element = document.createElement("audio");
v.element.autoplay = "autoplay"; this.element.autoplay = "autoplay";
v.element.controls = "controls"; this.element.controls = "controls";
v.element.style.width = "90%"; this.element.style.width = "90%";
v.element.addEventListener("ended", () => { v.next(); }, false); this.element.addEventListener("ended", () => { this.next(); }, false);
v.source = document.createElement("source"); this.source = document.createElement("source");
v.source.src = apiEndpoint+"/file/"+v.file.id; this.source.src = apiEndpoint+"/file/"+this.file.id;
v.element.appendChild(v.source); this.element.appendChild(this.source);
v.container.appendChild(v.element); this.container.appendChild(this.element);
} }
AudioViewer.prototype.render = function(parent) {let v = this; AudioViewer.prototype.render = function(parent) {
parent.appendChild(v.container); parent.appendChild(this.container);
} }

View File

@@ -1,27 +1,27 @@
function FileViewer(viewer, file, next) {let v = this; function FileViewer(viewer, file, next) {
v.viewer = viewer; this.viewer = viewer;
v.file = file; this.file = file;
v.next = next; this.next = next;
v.container = document.createElement("div"); this.container = document.createElement("div");
v.container.classList = "image-container"; this.container.classList = "image-container";
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.icon = document.createElement("img"); this.icon = document.createElement("img");
v.icon.src = apiEndpoint+"/"+file.thumbnail_href; this.icon.src = apiEndpoint+"/"+file.thumbnail_href;
v.container.appendChild(v.icon); this.container.appendChild(this.icon);
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.container.appendChild(document.createTextNode(file.name)); this.container.appendChild(document.createTextNode(file.name));
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.container.appendChild(document.createTextNode("Type: "+file.mime_type)); this.container.appendChild(document.createTextNode("Type: "+file.mime_type));
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.container.appendChild(document.createElement("br")); this.container.appendChild(document.createElement("br"));
v.container.appendChild(document.createTextNode( this.container.appendChild(document.createTextNode(
"Press the 'Download' button in the menu to download this file" "Press the 'Download' button in the menu to download this file"
)); ));
} }
FileViewer.prototype.render = function(parent) {let v = this; FileViewer.prototype.render = function(parent) {
parent.appendChild(v.container); parent.appendChild(this.container);
} }

View File

@@ -1,48 +1,48 @@
function ImageViewer(viewer, file) {let v = this; function ImageViewer(viewer, file) {
v.viewer = viewer; this.viewer = viewer;
v.file = file; this.file = file;
v.zoomed = false; this.zoomed = false;
v.x = 0; this.x = 0;
v.y = 0; this.y = 0;
v.dragging = false; this.dragging = false;
v.container = document.createElement("dv"); this.container = document.createElement("dv");
v.container.classList = "image-container"; this.container.classList = "image-container";
// v.container.style.lineHeight = "0"; // this.container.style.lineHeight = "0";
v.element = document.createElement("img"); this.element = document.createElement("img");
v.element.classList = "pannable center drop-shadow"; this.element.classList = "pannable center drop-shadow";
v.element.src = apiEndpoint+"/file/"+v.file.id; this.element.src = apiEndpoint+"/file/"+this.file.id;
v.element.addEventListener("dblclick", (e) => { return v.doubleclick(e); }); this.element.addEventListener("dblclick", (e) => { return this.doubleclick(e); });
v.element.addEventListener("doubletap", (e) => { return v.doubleclick(e); }); this.element.addEventListener("doubletap", (e) => { return this.doubleclick(e); });
v.element.addEventListener("mousedown", (e) => { return v.mousedown(e); }); this.element.addEventListener("mousedown", (e) => { return this.mousedown(e); });
document.addEventListener("mousemove", (e) => { return v.mousemove(e); }); document.addEventListener("mousemove", (e) => { return this.mousemove(e); });
document.addEventListener("mouseup", (e) => { return v.mouseup(e); }); document.addEventListener("mouseup", (e) => { return this.mouseup(e); });
v.container.appendChild(v.element); this.container.appendChild(this.element);
} }
ImageViewer.prototype.render = function(parent) {let v = this; ImageViewer.prototype.render = function(parent) {
parent.appendChild(v.container); parent.appendChild(this.container);
} }
ImageViewer.prototype.doubleclick = function(e) {let v = this; ImageViewer.prototype.doubleclick = function(e) {
if (v.zoomed) { if (this.zoomed) {
v.element.style.maxWidth = "100%"; this.element.style.maxWidth = "100%";
v.element.style.maxHeight = "100%"; this.element.style.maxHeight = "100%";
v.element.style.top = "50%"; this.element.style.top = "50%";
v.element.style.left = "auto"; this.element.style.left = "auto";
v.element.style.transform = "translateY(-50%)"; this.element.style.transform = "translateY(-50%)";
v.container.style.overflow = "hidden"; this.container.style.overflow = "hidden";
v.zoomed = false; this.zoomed = false;
} else { } else {
v.element.style.maxWidth = "none"; this.element.style.maxWidth = "none";
v.element.style.maxHeight = "none"; this.element.style.maxHeight = "none";
v.element.style.top = "0"; this.element.style.top = "0";
v.element.style.left = ""; this.element.style.left = "";
v.element.style.transform = "none"; this.element.style.transform = "none";
v.container.style.overflow = "scroll"; this.container.style.overflow = "scroll";
v.zoomed = true; this.zoomed = true;
} }
e.preventDefault(); e.preventDefault();
@@ -50,11 +50,11 @@ ImageViewer.prototype.doubleclick = function(e) {let v = this;
return false; return false;
} }
ImageViewer.prototype.mousedown = function(e) {let v = this; ImageViewer.prototype.mousedown = function(e) {
if (!v.dragging && e.which === 1 && v.zoomed) { if (!this.dragging && e.which === 1 && this.zoomed) {
v.x = e.pageX; this.x = e.pageX;
v.y = e.pageY; this.y = e.pageY;
v.dragging = true; this.dragging = true;
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@@ -62,13 +62,13 @@ ImageViewer.prototype.mousedown = function(e) {let v = this;
} }
} }
ImageViewer.prototype.mousemove = function(e) {let v = this; ImageViewer.prototype.mousemove = function(e) {
if (v.dragging) { if (this.dragging) {
v.container.scrollLeft = v.container.scrollLeft - (e.pageX - v.x); this.container.scrollLeft = this.container.scrollLeft - (e.pageX - this.x);
v.container.scrollTop = v.container.scrollTop - (e.pageY - v.y); this.container.scrollTop = this.container.scrollTop - (e.pageY - this.y);
v.x = e.pageX; this.x = e.pageX;
v.y = e.pageY; this.y = e.pageY;
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@@ -77,9 +77,9 @@ ImageViewer.prototype.mousemove = function(e) {let v = this;
} }
ImageViewer.prototype.mouseup = function(e) {let v = this; ImageViewer.prototype.mouseup = function(e) {
if (v.dragging) { if (this.dragging) {
v.dragging = false; this.dragging = false;
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();

View File

@@ -1,13 +1,13 @@
function PDFViewer(viewer, file) {let v = this; function PDFViewer(viewer, file) {
v.viewer = viewer; this.viewer = viewer;
v.file = file; this.file = file;
v.container = document.createElement("iframe"); this.container = document.createElement("iframe");
v.container.classList = "image-container"; this.container.classList = "image-container";
v.container.style.border = "none"; this.container.style.border = "none";
v.container.src = "/res/misc/pdf-viewer/web/viewer.html?file="+apiEndpoint+"/file/"+file.id; this.container.src = "/res/misc/pdf-viewer/web/viewer.html?file="+apiEndpoint+"/file/"+file.id;
} }
PDFViewer.prototype.render = function(parent) {let v = this; PDFViewer.prototype.render = function(parent) {
parent.appendChild(v.container); parent.appendChild(this.container);
} }

View File

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

View File

@@ -1,24 +1,24 @@
function VideoViewer(viewer, file, next) {let v = this; function VideoViewer(viewer, file, next) {
v.viewer = viewer; this.viewer = viewer;
v.file = file; this.file = file;
v.next = next; this.next = next;
v.vidContainer = document.createElement("div"); this.vidContainer = document.createElement("div");
v.vidContainer.classList = "image-container"; this.vidContainer.classList = "image-container";
v.vidElement = document.createElement("video"); this.vidElement = document.createElement("video");
v.vidElement.autoplay = "autoplay"; this.vidElement.autoplay = "autoplay";
v.vidElement.controls = "controls"; this.vidElement.controls = "controls";
v.vidElement.classList = "center drop-shadow"; this.vidElement.classList = "center drop-shadow";
v.vidElement.addEventListener("ended", () => { v.next(); }, false); this.vidElement.addEventListener("ended", () => { this.next(); }, false);
v.videoSource = document.createElement("source"); this.videoSource = document.createElement("source");
v.videoSource.src = apiEndpoint+"/file/"+v.file.id; this.videoSource.src = apiEndpoint+"/file/"+this.file.id;
v.vidElement.appendChild(v.videoSource); this.vidElement.appendChild(this.videoSource);
v.vidContainer.appendChild(v.vidElement); this.vidContainer.appendChild(this.vidElement);
} }
VideoViewer.prototype.render = function(parent) {let v = this; VideoViewer.prototype.render = function(parent) {
parent.appendChild(v.vidContainer); parent.appendChild(this.vidContainer);
} }

View File

@@ -1,28 +1,27 @@
function UploadProgressBar(uploadManager, queueDiv, file){let upb = this; function UploadProgressBar(uploadManager, queueDiv, file){
upb.uploadManager = uploadManager; this.uploadManager = uploadManager;
upb.file = file; this.file = file;
upb.name = file.name; this.name = file.name;
upb.uploadDiv = document.createElement("a"); this.uploadDiv = document.createElement("a");
upb.uploadDiv.classList.add("file_button"); this.uploadDiv.classList.add("file_button");
upb.uploadDiv.style.opacity = "0"; this.uploadDiv.style.opacity = "0";
upb.uploadDiv.innerText = "Queued\n" + upb.file.name; this.uploadDiv.innerText = "Queued\n" + this.file.name;
queueDiv.appendChild(upb.uploadDiv); queueDiv.appendChild(this.uploadDiv);
// Start uploading the file // Start uploading the file
upb.uploadManager.addFile( this.uploadManager.addFile(
upb.file, this.file,
upb.name, this.name,
function(progress) { upb.onProgress(progress); }, (progress) => { this.onProgress(progress); },
function(id) { upb.onFinished(id); }, (id) => { this.onFinished(id); },
function(val, msg) { upb.onFailure(val, msg); } (val, msg) => { this.onFailure(val, msg); }
); );
// Browsers don't render the transition if the opacity is set and // Browsers don't render the transition if the opacity is set and
// updated in the same frame. So we have to wait a frame (or more) // updated in the same frame. So we have to wait a frame (or more)
// before changing the opacity to make sure the transition triggers // before changing the opacity to make sure the transition triggers
var d = this.uploadDiv // `this` stops working after constructor ends window.setTimeout(() => {this.uploadDiv.style.opacity = "1";}, 100)
window.setTimeout(function(){d.style.opacity = "1";}, 100)
} }
UploadProgressBar.prototype.onProgress = function(progress){ UploadProgressBar.prototype.onProgress = function(progress){

View File

@@ -25,13 +25,13 @@ html, body {
overflow-x: hidden; overflow-x: hidden;
} }
body{ body{
background-color: #0d0d0d; /* Fallback */ background-color: #111111; /* Fallback */
background-color: var(--body_color); background-color: var(--body_color);
background-repeat: repeat; background-repeat: repeat;
font-family: sans-serif; font-family: sans-serif;
margin: 0; margin: 0;
line-height: 1.5em; line-height: 1.5em;
color: #bfbfbf; /* Fallback */ color: #b2b2b2; /* Fallback */
color: var(--text_color); color: var(--text_color);
padding: 0; padding: 0;
} }
@@ -54,9 +54,11 @@ body{
padding: 10px 20px 15px 10px; padding: 10px 20px 15px 10px;
font-size: 2em; font-size: 2em;
margin: 0; margin: 0;
background: #3f3f3f;
background: var(--input_color); background: var(--input_color);
border-radius: 0; border-radius: 0;
border-bottom-right-radius: 90%; border-bottom-right-radius: 90%;
box-shadow: 2px 2px 8px -3px #000000;
box-shadow: 2px 2px 8px -3px var(--shadow_color); box-shadow: 2px 2px 8px -3px var(--shadow_color);
} }
.page_navigation { .page_navigation {
@@ -67,12 +69,14 @@ body{
height: 100%; height: 100%;
left: 0; left: 0;
float: left; float: left;
background-color: #1c1c1c;
background-color: var(--layer_1_color); background-color: var(--layer_1_color);
padding: 20px 0 0.5em 0; padding: 20px 0 0.5em 0;
box-sizing: border-box; box-sizing: border-box;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
text-align: left; text-align: left;
box-shadow: inset -10px 0px 10px -10px #000000;
box-shadow: inset -10px 0px 10px -10px var(--shadow_color); box-shadow: inset -10px 0px 10px -10px var(--shadow_color);
transition: left 0.5s; transition: left 0.5s;
} }
@@ -107,7 +111,9 @@ body{
width: 100%; width: 100%;
height: auto; height: auto;
padding: 20px 0 20px 0; padding: 20px 0 20px 0;
background-color: #212121;
background-color: var(--layer_2_color); background-color: var(--layer_2_color);
box-shadow: 1px 1px 20px 0 #000000;
box-shadow: 1px 1px 20px 0 var(--shadow_color); box-shadow: 1px 1px 20px 0 var(--shadow_color);
box-sizing: border-box; box-sizing: border-box;
clear: both; clear: both;
@@ -125,6 +131,7 @@ body{
} }
.page_body > h1 { .page_body > h1 {
text-shadow: 1px 1px 25px #000000; text-shadow: 1px 1px 25px #000000;
text-shadow: 1px 1px 25px var(--shadow_color);
} }
/* Page contents */ /* Page contents */
@@ -140,7 +147,7 @@ body{
float: none; float: none;
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
color: #bfbfbf; /* Fallback */ color: #b2b2b2; /* Fallback */
color: var(--text_color); color: var(--text_color);
text-align: center; text-align: center;
padding: 6px 6px; padding: 6px 6px;
@@ -153,12 +160,15 @@ body{
border-radius: 5px; border-radius: 5px;
} }
.page_navigation a:hover { .page_navigation a:hover {
background-color: #3f3f3f;
background-color: var(--input_color); background-color: var(--input_color);
color: #ffffff;
color: var(--input_text_color); color: var(--input_text_color);
text-decoration: none; text-decoration: none;
} }
.inset { .inset {
box-shadow: inset 1px 1px 20px 0 #000000;
box-shadow: inset 1px 1px 20px 0 var(--shadow_color); box-shadow: inset 1px 1px 20px 0 var(--shadow_color);
} }
.checkers { .checkers {

View File

@@ -1,87 +0,0 @@
/*
Created on : Jul 30, 2015, 12:46:39 PM
Author : Fornax
*/
.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;
}
#listNavigator{
position: absolute;
left: 0;
right: 0;
top: -100px;
height: 58px;
background-color: #000;
border-bottom: 2px ridge #9FCF6C;
text-align: center;
overflow-x: hidden;
overflow-y: hidden;
z-index: 1000;
}
#listNavigatorItems{
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
white-space: nowrap;
overflow-x: scroll;
overflow-y: hidden;
border: none;
padding: 0 40px;
z-index: 1001;
}
.listItem{
display: inline-block;
position: relative;
height: 92%;
width: 100px;
margin-right: 5px;
text-align: center;
border: #333 solid 2px;
font-size: 12px;
overflow: hidden;
cursor: pointer;
}
.listItemThumbnail{
position: relative;
max-width: 100%;
max-height: 74%;
margin: 0;
}
#arrow-left{
position: fixed;
height: 58px;
width: 25px;
left: 0;
z-index: 1002;
}
#arrow-right{
position: fixed;
height: 58px;
width: 25px;
right: 0;
z-index: 1002;
}

View File

@@ -1,128 +0,0 @@
/*
Created on : May 22, 2015, 1:20:02 PM
Author : Fornax
*/
body{
background-color: #111;
background-image: url("img/checker.png");
background-repeat: repeat;
color: #eeeeee;
font-size: 16px;
font-family: 'Ubuntu', sans-serif;
margin: 0;
overflow: hidden;
}
a{
color: #9FCF6C;
text-decoration: none;
}
a:hover{
text-decoration: underline;
color: #9FCF6C;
}
::-webkit-scrollbar{
width: 10px; /* for vertical scrollbars */
height: 10px; /* for horizontal scrollbars */
}
::-webkit-scrollbar-track{
background: #000;
}
::-webkit-scrollbar-thumb{
background-color: #444;
}
::-webkit-scrollbar-corner{
background: transparent;
}
button:active{
background-color: #111;
}
button:hover{
background-color: #333;
}
#filepreview{
position: absolute;
display: inline-block;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-height: 100px;
min-width: 100px;
text-align: center;
vertical-align: middle;
}
/* ===========================
== START FILE CONTAINERS ==
===========================*/
.image-container{
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
}
.image{
position: relative;
display: block;
margin: auto;
max-width: 100%;
max-height: 100%;
}
.text-container{
background: #333 none;
position: absolute;
overflow-y: scroll;
overflow-x: auto;
text-align: left;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 5px 5px 5px 20px;
box-sizing: border-box;
}
.pre-container{
white-space: pre-wrap;
word-wrap: break-word;
}
.pannable{
position: relative;
display: inline-block;
margin: auto;
max-width: 100%;
max-height: 100%;
cursor: move;
top: 50%;
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
}
.center{
position: relative;
display: block;
margin: auto;
max-width: 100%;
max-height: 100%;
top: 50%;
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
}
.drop-shadow{
box-shadow: #000 10px 10px 50px;
}

View File

@@ -25,5 +25,5 @@
{{template "page_bottom"}} {{template "page_bottom"}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>
</html lang="en"> </html>
{{end}} {{end}}

View File

@@ -0,0 +1,66 @@
{{define "file_viewer_compat"}}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{.Title}}</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{template "user_style" .}}
<link rel="shortcut icon" href="/res/img/tray32.png"/>
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain.png"/>
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_big.png"/>
<meta name="theme-color" content="#75AD38"/>
<style>
{{template `viewer.css`}}
</style>
{{.OGData}}
</head>
<body>
{{template "page_top" .}}
<h1>{{.Title}}</h1>
<div class="page_content"><div class="limit_width">
{{if eq .Other.Type "file"}}
Download <a href="{{.APIEndpoint}}/file/{{.Other.APIResponse.ID}}?download">{{.Other.APIResponse.Name}}</a> here.
{{else}}
<ul>
{{$totalSize := 0}}
{{range $file := .Other.APIResponse.Files}}
{{$totalSize = add $totalSize $file.Size}}
<li><a href="{{$.APIEndpoint}}/file/{{$file.ID}}?download">{{$file.Name}}</a> ({{formatData $file.Size}})</li>
{{end}}
</ul>
{{if ne .Other.APIResponse.ID ""}}
<a href="{{.APIEndpoint}}/list/{{.Other.APIResponse.ID}}/zip">Download all files</a> (~{{formatData $totalSize}})
{{end}}
{{end}}
<h2>Compatibility file viewer</h2>
<p>
Welcome to the compatibility version of the file viewer. This
page is only shown to web browsers which can't properly display
the regular file viewer.
</p>
<p>
This page only allows you to download the linked files. If you
want more features like online video / audio players, an image
viewer, a PDF viewer or a text file viewer, please download any
of the supported web browsers:
</p>
<ul>
<li><a href="https://brave.com/pix009">Brave</a> (Linux, Mac OS, Windows)</li>
<li><a href="https://www.mozilla.org/en-US/firefox/">Firefox</a> (Linux, Mac OS, Windows)</li>
<li><a href="https://www.google.com/chrome/">Chrome</a> / <a href="https://www.chromium.org/Home">Chromium</a> (Linux, Mac OS, Windows)</li>
<li><a href="https://vivaldi.com/">Vivaldi</a> (Linux, Mac OS, Windows)</li>
<li><a href="https://www.opera.com/">Opera</a> (Linux, Mac OS, Windows)</li>
<li><a href="https://wiki.gnome.org/Apps/Web/">GNOME Web</a> (Linux)</li>
<li><a href="https://www.palemoon.org/">Pale Moon</a> (Linux, Windows)</li>
<li><a href="https://www.apple.com/safari/">Safari</a> (Mac OS)</li>
<li><a href="https://www.microsoft.com/en-us/edge">Edge</a> (Windows)</li>
</ul>
</div></div>
{{template "analytics"}}
</body>
</html>
{{end}}

View File

@@ -24,7 +24,7 @@
<input type="text" autocomplete="username" value="{{.Username}}" style="display: none;" readonly="readonly"/> <input type="text" autocomplete="username" value="{{.Username}}" style="display: none;" readonly="readonly"/>
{{end}} {{end}}
<table class="form"> <table class="form">
{{range $index, $field := .Fields}} {{range $field := .Fields}}
<tr class="form"> <tr class="form">
{{if eq $field.Type "textarea"}} {{if eq $field.Type "textarea"}}
<td colspan="2"> <td colspan="2">

View File

@@ -1,6 +1,6 @@
{{define "user_style"}} {{define "user_style"}}
<style> <style>
{{.UserStyle}} {{.UserStyle}}
{{template "layout.css"}} {{template "layout.css" .}}
</style> </style>
{{end}} {{end}}

View File

@@ -44,7 +44,7 @@
<body> <body>
<div id="toolbar"> <div id="toolbar">
<button class="toolbar_button button_full_width" onClick="uploadText();"> <button class="toolbar_button button_full_width button_highlight" onclick="uploadText();">
{{template `upload.svg` .}} {{template `upload.svg` .}}
<span>Upload</span> <span>Upload</span>
</button> </button>

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
"time"
"fornaxian.com/pixeldrain-web/pixelapi" "fornaxian.com/pixeldrain-web/pixelapi"
"github.com/Fornaxian/log" "github.com/Fornaxian/log"
@@ -18,6 +19,10 @@ func viewTokenOrBust(api *pixelapi.PixelAPI) (t string) {
return t return t
} }
func browserCompat(ua string) bool {
return strings.Contains(ua, "MSIE") || strings.Contains(ua, "Trident/7.0")
}
type viewerData struct { type viewerData struct {
Type string // file or list Type string // file or list
CaptchaKey string CaptchaKey string
@@ -33,23 +38,17 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
return return
} }
var list = strings.Contains(p.ByName("id"), ",") var ids = strings.Split(p.ByName("id"), ",")
var ids []string
if list {
ids = strings.Split(p.ByName("id"), ",")
} else {
ids = append(ids, p.ByName("id"))
}
templateData := wc.newTemplateData(w, r) templateData := wc.newTemplateData(w, r)
var finfo []pixelapi.FileInfo var finfo []pixelapi.ListFile
for _, id := range ids { for _, id := range ids {
inf, err := templateData.PixelAPI.GetFileInfo(id) inf, err := templateData.PixelAPI.GetFileInfo(id)
if err != nil { if err != nil {
continue continue
} }
finfo = append(finfo, inf) finfo = append(finfo, pixelapi.ListFile{FileInfo: inf})
} }
if len(finfo) == 0 { if len(finfo) == 0 {
@@ -58,19 +57,18 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
return return
} }
templateData.OGData = metadataFromFile(finfo[0]) templateData.OGData = metadataFromFile(finfo[0].FileInfo)
if list { if len(ids) > 1 {
templateData.Title = fmt.Sprintf("%d files on pixeldrain", len(finfo)) templateData.Title = fmt.Sprintf("%d files on pixeldrain", len(finfo))
templateData.Other = viewerData{ templateData.Other = viewerData{
Type: "list", Type: "list",
CaptchaKey: wc.captchaKey(), CaptchaKey: wc.captchaKey(),
ViewToken: viewTokenOrBust(templateData.PixelAPI), ViewToken: viewTokenOrBust(templateData.PixelAPI),
APIResponse: map[string]interface{}{ APIResponse: pixelapi.List{
"data": finfo, Success: true,
"date_created": "now", Title: "Multiple files",
"title": "Multiple files", DateCreated: time.Now(),
"date_lastview": "now", Files: finfo,
"views": 0,
}, },
} }
} else { } else {
@@ -79,10 +77,16 @@ func (wc *WebController) serveFileViewer(w http.ResponseWriter, r *http.Request,
Type: "file", Type: "file",
CaptchaKey: wc.captchaKey(), CaptchaKey: wc.captchaKey(),
ViewToken: viewTokenOrBust(templateData.PixelAPI), ViewToken: viewTokenOrBust(templateData.PixelAPI),
APIResponse: finfo[0], APIResponse: finfo[0].FileInfo,
} }
} }
err = wc.templates.Get().ExecuteTemplate(w, "file_viewer", templateData)
var templateName = "file_viewer"
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") { if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template file_viewer: %s", err) log.Error("Error executing template file_viewer: %s", err)
} }
@@ -118,13 +122,11 @@ func (wc *WebController) serveFileViewerDemo(w http.ResponseWriter, r *http.Requ
// ServeListViewer controller for GET /l/:id // ServeListViewer controller for GET /l/:id
func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) { func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var api = pixelapi.New(wc.apiURLInternal, "")
var list, err = api.GetList(p.ByName("id"))
var templateData = wc.newTemplateData(w, r) var templateData = wc.newTemplateData(w, r)
var list, err = templateData.PixelAPI.GetList(p.ByName("id"))
if err != nil { if err != nil {
if err, ok := err.(pixelapi.Error); ok && err.ReqError { if err, ok := err.(pixelapi.Error); ok && err.ReqError {
log.Error("API request error occurred: %s", err.Value) log.Error("API request error occurred: %s", err.Value)
} }
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
wc.templates.Get().ExecuteTemplate(w, "list_not_found", templateData) wc.templates.Get().ExecuteTemplate(w, "list_not_found", templateData)
@@ -132,20 +134,20 @@ func (wc *WebController) serveListViewer(w http.ResponseWriter, r *http.Request,
} }
templateData.Title = fmt.Sprintf("%s ~ pixeldrain", list.Title) templateData.Title = fmt.Sprintf("%s ~ pixeldrain", list.Title)
templateData.OGData = metadataFromList(*list) templateData.OGData = metadataFromList(list)
templateData.Other = viewerData{ templateData.Other = viewerData{
Type: "list", Type: "list",
CaptchaKey: wc.captchaSiteKey, CaptchaKey: wc.captchaSiteKey,
ViewToken: viewTokenOrBust(templateData.PixelAPI), ViewToken: viewTokenOrBust(templateData.PixelAPI),
APIResponse: map[string]interface{}{ APIResponse: list,
"id": list.ID,
"data": list.Files,
"date_created": list.DateCreated,
"title": list.Title,
"views": 0,
},
} }
err = wc.templates.Get().ExecuteTemplate(w, "file_viewer", templateData)
var templateName = "file_viewer"
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") { if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template file_viewer: %s", err) log.Error("Error executing template file_viewer: %s", err)
} }

View File

@@ -227,8 +227,8 @@ func (tm *TemplateManager) add(a, b interface{}) int {
func (tm *TemplateManager) sub(a, b interface{}) int { func (tm *TemplateManager) sub(a, b interface{}) int {
return detectInt(a) - detectInt(b) return detectInt(a) - detectInt(b)
} }
func (tm *TemplateManager) formatData(i int) string { func (tm *TemplateManager) formatData(i interface{}) string {
return util.FormatData(uint64(i)) return util.FormatData(uint64(detectInt(i)))
} }
func detectInt(i interface{}) int { func detectInt(i interface{}) int {