Move home page into svelte and remove some old junk
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
|
||||
let modal_global_index = 100
|
||||
|
||||
// Modal creates a new modal window and shows it. The width and height are will
|
||||
// be adjusted to the screen size. Content should be added to the `body`
|
||||
// property
|
||||
function Modal(parent, closeCallback, title, width, height) {
|
||||
this.parent = parent
|
||||
this.closeCallback = closeCallback
|
||||
this.title = title
|
||||
this.width = width
|
||||
this.height = height
|
||||
this.visible = false
|
||||
|
||||
this.background = document.createElement("div")
|
||||
this.background.classList = "modal_background"
|
||||
this.background.addEventListener("click", e => { this.close() })
|
||||
|
||||
this.window = document.createElement("div")
|
||||
this.window.classList = "modal_window"
|
||||
this.window.style.width = this.width
|
||||
this.window.style.height = this.height
|
||||
this.window.addEventListener("click", e => { e.stopPropagation() })
|
||||
|
||||
this.header = document.createElement("div")
|
||||
this.header.classList = "modal_header"
|
||||
|
||||
this.titleDiv = document.createElement("div")
|
||||
this.titleDiv.classList = "modal_title"
|
||||
this.titleDiv.innerText = this.title
|
||||
|
||||
this.btnClose = document.createElement("button")
|
||||
this.btnClose.classList = "modal_btn_close button_red round"
|
||||
this.btnClose.innerHTML = '<i class="icon">close</i>'
|
||||
this.btnClose.addEventListener("click", e => { this.close() })
|
||||
|
||||
this.body = document.createElement("div")
|
||||
this.body.classList = "modal_body"
|
||||
|
||||
// And add all the elements to eachother.
|
||||
this.header.append(this.titleDiv)
|
||||
this.header.append(this.btnClose)
|
||||
this.window.append(this.header)
|
||||
this.window.append(this.body)
|
||||
this.background.append(this.window)
|
||||
}
|
||||
|
||||
Modal.prototype.setTitle = function (title) {
|
||||
this.title = title
|
||||
this.titleDiv.innerText = title
|
||||
}
|
||||
|
||||
Modal.prototype.setBody = function (element) {
|
||||
this.body.innerHTML = ""
|
||||
this.body.append(element)
|
||||
}
|
||||
|
||||
Modal.prototype.cloneTemplate = function (templateID) {
|
||||
this.setBody(document.getElementById(templateID).content.cloneNode(true))
|
||||
}
|
||||
|
||||
Modal.prototype.open = function () {
|
||||
if (this.visible) { return }
|
||||
this.visible = true
|
||||
|
||||
console.debug("Showing modal " + this.title)
|
||||
|
||||
// Each time a modal is shown it gets a z-index which is one higher of the
|
||||
// previous one. This makes sure they are always shown and closed in order
|
||||
this.background.style.zIndex = modal_global_index
|
||||
modal_global_index++
|
||||
|
||||
this.parent.prepend(this.background)
|
||||
|
||||
// If an element is created and shown in the same frame it won't render the
|
||||
// transition. So here we wait for a few frames to make it visible
|
||||
this.background.style.display = ""
|
||||
setTimeout(() => { this.background.style.opacity = 1 }, 40)
|
||||
|
||||
// This is a workaround for a chrome bug which makes it so hidden
|
||||
// windows can't be scrolled after they are shown
|
||||
this.body.focus()
|
||||
}
|
||||
|
||||
Modal.prototype.close = function () {
|
||||
if (!this.visible) { return }
|
||||
this.visible = false
|
||||
|
||||
if (this.closeCallback) {
|
||||
this.closeCallback()
|
||||
}
|
||||
|
||||
// First we make it invisible with a transition. When we remove it so the
|
||||
// user can click through it
|
||||
this.background.style.opacity = 0
|
||||
|
||||
// Wait for the animation to finish and remove the window
|
||||
setTimeout(() => { this.parent.removeChild(this.background) }, 400)
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
function UploadManager(uploadEndpoint, uploadsFinished) {
|
||||
this.uploadEndpoint = uploadEndpoint;
|
||||
|
||||
// Callback function for when the queue is empty
|
||||
this.uploadsFinished = uploadsFinished;
|
||||
|
||||
// Counts the total number of upload jobs
|
||||
this.jobCounter = 0;
|
||||
|
||||
// Queue of files to be uploaded. Format:
|
||||
// {
|
||||
// jobID: number,
|
||||
// file: Blob,
|
||||
// name: string,
|
||||
// onProgress: function,
|
||||
// onFinished: function,
|
||||
// onFailure: function,
|
||||
// tries: number
|
||||
// }
|
||||
this.uploadQueue = [];
|
||||
|
||||
// Here we put successful jobs. The array should be sorted by job ID.
|
||||
// Format:
|
||||
// { jobID: number, fileID: string, fileName: string }
|
||||
this.uploadLog = [];
|
||||
|
||||
// Max number of uploading threads at once
|
||||
this.maxWorkers = 3;
|
||||
|
||||
// Threads which are currently uploading
|
||||
this.activeWorkers = 0;
|
||||
|
||||
// Total number of jobs accepted
|
||||
this.jobCounter = 0;
|
||||
}
|
||||
|
||||
UploadManager.prototype.finishedUploads = function () {
|
||||
this.uploadLog.sort((a, b) => {
|
||||
return a.jobID - b.jobID;
|
||||
})
|
||||
return this.uploadLog;
|
||||
}
|
||||
|
||||
UploadManager.prototype.addFile = function (
|
||||
file, // Blob
|
||||
name, // string
|
||||
onProgress, // func (progress: number)
|
||||
onFinished, // func (id: string)
|
||||
onFailure // func (errorID: string, errorMessage: string)
|
||||
) {
|
||||
this.uploadQueue.push({
|
||||
jobID: this.jobCounter,
|
||||
file: file,
|
||||
name: name,
|
||||
onProgress: onProgress,
|
||||
onFinished: onFinished,
|
||||
onFailure: onFailure,
|
||||
tries: 0
|
||||
});
|
||||
|
||||
// Increment the job counter
|
||||
this.jobCounter++
|
||||
|
||||
if (this.activeWorkers < this.maxWorkers) {
|
||||
// Run the upload function
|
||||
this.startUpload();
|
||||
}
|
||||
}
|
||||
|
||||
UploadManager.prototype.startUpload = function () {
|
||||
if (this.uploadQueue.length === 0) {
|
||||
return; // Nothing to upload
|
||||
}
|
||||
|
||||
if (this.activeWorkers < this.maxWorkers) {
|
||||
this.activeWorkers++;
|
||||
this.uploadThread();
|
||||
}
|
||||
}
|
||||
|
||||
UploadManager.prototype.finishUpload = function () {
|
||||
this.activeWorkers--;
|
||||
|
||||
if (
|
||||
this.uploadQueue.length === 0 &&
|
||||
this.activeWorkers === 0 &&
|
||||
typeof (this.uploadsFinished) === "function"
|
||||
) {
|
||||
this.uploadsFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the upload function for the next file
|
||||
this.startUpload();
|
||||
}
|
||||
|
||||
UploadManager.prototype.uploadThread = function () {
|
||||
let job = this.uploadQueue.shift(); // Get the first element of the array
|
||||
console.debug("Starting upload of " + job.name);
|
||||
|
||||
let form = new FormData();
|
||||
form.append('file', job.file, job.name);
|
||||
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", this.uploadEndpoint, true);
|
||||
xhr.timeout = 21600000; // 6 hours, to account for slow connections
|
||||
|
||||
// Report progress updates back to the caller
|
||||
xhr.upload.addEventListener("progress", evt => {
|
||||
if (evt.lengthComputable && typeof (job.onProgress) === "function") {
|
||||
job.onProgress(evt.loaded / evt.total);
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
// readystate 4 means the upload is done
|
||||
if (xhr.readyState !== 4) { return; }
|
||||
|
||||
if (xhr.status >= 100 && xhr.status < 400) {
|
||||
// Request is a success
|
||||
let resp = JSON.parse(xhr.response);
|
||||
addUploadHistory(resp.id)
|
||||
|
||||
// Log the successful job
|
||||
this.uploadLog.push({
|
||||
jobID: job.jobID,
|
||||
fileID: resp.id,
|
||||
fileName: job.name
|
||||
});
|
||||
|
||||
if (typeof (job.onFinished) === "function") {
|
||||
job.onFinished(resp.id);
|
||||
}
|
||||
|
||||
// Finish the upload job
|
||||
this.finishUpload();
|
||||
} else if (xhr.status >= 400) {
|
||||
// Request failed
|
||||
console.log("Upload error. status: " + xhr.status + " response: " + xhr.response);
|
||||
let resp = JSON.parse(xhr.response);
|
||||
if (resp.value == "file_too_large" || job.tries === 3) { // Upload failed
|
||||
job.onFailure(resp.value, resp.message);
|
||||
} else { // Try again
|
||||
job.tries++;
|
||||
this.uploadQueue.push(job);
|
||||
}
|
||||
|
||||
// Sleep the upload thread for 5 seconds
|
||||
window.setTimeout(() => { this.finishUpload(); }, 5000);
|
||||
} else {
|
||||
// Request did not arrive
|
||||
if (job.tries === 3) { // Upload failed
|
||||
if (typeof (job.onFailure) === "function") {
|
||||
job.onFailure(xhr.responseText, xhr.responseText);
|
||||
}
|
||||
} else { // Try again
|
||||
job.tries++;
|
||||
this.uploadQueue.push(job);
|
||||
}
|
||||
|
||||
// Sleep the upload thread for 5 seconds
|
||||
window.setTimeout(() => { this.finishUpload(); }, 5000);
|
||||
}
|
||||
};
|
||||
xhr.send(form);
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
function addUploadHistory(fileID) {
|
||||
// Make sure the user is not logged in, for privacy. This keeps the
|
||||
// files uploaded while logged in and anonymously uploaded files
|
||||
// separated
|
||||
if (document.cookie.includes("pd_auth_key")) { return; }
|
||||
|
||||
let uploads = localStorage.getItem("uploaded_files");
|
||||
if (uploads === null) { uploads = ""; }
|
||||
|
||||
// Check if there are not too many values stored
|
||||
if (uploads.length > 3600) {
|
||||
// 3600 characters is enough to store 400 file IDs. If we exceed that
|
||||
// number we'll drop the last two items
|
||||
uploads = uploads.substring(
|
||||
uploads.indexOf(",") + 1
|
||||
).substring(
|
||||
uploads.indexOf(",") + 1
|
||||
);
|
||||
}
|
||||
|
||||
// Save the new ID
|
||||
localStorage.setItem("uploaded_files", fileID + "," + uploads);
|
||||
}
|
||||
|
||||
function printDate(date, hours, minutes, seconds) {
|
||||
let dateStr = date.getFullYear()
|
||||
+ "-" + ("00" + (date.getMonth() + 1)).slice(-2)
|
||||
+ "-" + ("00" + date.getDate()).slice(-2)
|
||||
|
||||
if (hours) { dateStr += " " + ("00" + date.getHours()).slice(-2) }
|
||||
if (minutes) { dateStr += ":" + ("00" + date.getMinutes()).slice(-2) }
|
||||
if (seconds) { dateStr += ":" + ("00" + date.getMinutes()).slice(-2) }
|
||||
return dateStr
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function domainURL() {
|
||||
let url = window.location.protocol + "//" + window.location.hostname;
|
||||
if (window.location.port != "") {
|
||||
url = url + ":" + window.location.port;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function formatNumber(amt, precision) {
|
||||
if (precision < 3) { precision = 3; }
|
||||
if (amt >= 1e6) {
|
||||
return (amt / 1e6).toPrecision(precision) + "M";
|
||||
} else if (amt >= 1e3) {
|
||||
return (amt / 1e3).toPrecision(precision) + "k";
|
||||
}
|
||||
return amt
|
||||
}
|
||||
|
||||
function formatThousands(x) {
|
||||
// Inject a space every three digits
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||
}
|
||||
|
||||
function formatDataVolume(amt, precision) {
|
||||
if (precision < 3) { precision = 3; }
|
||||
if (amt >= 1e18) {
|
||||
return (amt / 1e18).toPrecision(precision) + " EB";
|
||||
} else if (amt >= 1e15) {
|
||||
return (amt / 1e15).toPrecision(precision) + " PB";
|
||||
} else if (amt >= 1e12) {
|
||||
return (amt / 1e12).toPrecision(precision) + " TB";
|
||||
} else if (amt >= 1e9) {
|
||||
return (amt / 1e9).toPrecision(precision) + " GB";
|
||||
} else if (amt >= 1e6) {
|
||||
return (amt / 1e6).toPrecision(precision) + " MB";
|
||||
} else if (amt >= 1e3) {
|
||||
return (amt / 1e3).toPrecision(precision) + " kB";
|
||||
}
|
||||
return Math.floor(amt) + " B"
|
||||
}
|
||||
|
||||
const second = 1000
|
||||
const minute = second * 60
|
||||
const hour = minute * 60
|
||||
const day = hour * 24
|
||||
|
||||
function formatDuration(ms) {
|
||||
let res = ""
|
||||
if (ms >= day) { res += Math.floor(ms / day) + "d " }
|
||||
if (ms >= hour) { res += Math.floor((ms % day) / hour) + "h " }
|
||||
if (ms >= minute) { res += Math.floor((ms % hour) / minute) + "m " }
|
||||
return res + ((ms % minute) / second).toFixed(3) + "s"
|
||||
}
|
@@ -1,16 +1,16 @@
|
||||
function renderFileButton(apiURL, id, title, subtitle) {
|
||||
let btn = document.createElement("a")
|
||||
btn.classList = "file_button"
|
||||
btn.href = "/u/"+id
|
||||
btn.target = "_blank"
|
||||
let thumbnail = document.createElement("img")
|
||||
thumbnail.src = apiURL+"/file/"+id+"/thumbnail?width=80&height=80"
|
||||
thumbnail.alt = title
|
||||
let titleSpan = document.createElement("span")
|
||||
titleSpan.classList = "file_button_title"
|
||||
titleSpan.innerText = title
|
||||
let br = document.createElement("br")
|
||||
let subtitleSpan = document.createElement("span")
|
||||
let btn = document.createElement("a")
|
||||
btn.classList = "file_button"
|
||||
btn.href = "/u/" + id
|
||||
btn.target = "_blank"
|
||||
let thumbnail = document.createElement("img")
|
||||
thumbnail.src = apiURL + "/file/" + id + "/thumbnail?width=80&height=80"
|
||||
thumbnail.alt = title
|
||||
let titleSpan = document.createElement("span")
|
||||
titleSpan.classList = "file_button_title"
|
||||
titleSpan.innerText = title
|
||||
let br = document.createElement("br")
|
||||
let subtitleSpan = document.createElement("span")
|
||||
subtitleSpan.classList = "file_button_subtitle"
|
||||
subtitleSpan.innerText = subtitle
|
||||
|
||||
@@ -26,6 +26,17 @@ function getCookie(name) {
|
||||
return result ? result[1] : null
|
||||
}
|
||||
|
||||
function printDate(date, hours, minutes, seconds) {
|
||||
let dateStr = date.getFullYear()
|
||||
+ "-" + ("00" + (date.getMonth() + 1)).slice(-2)
|
||||
+ "-" + ("00" + date.getDate()).slice(-2)
|
||||
|
||||
if (hours) { dateStr += " " + ("00" + date.getHours()).slice(-2) }
|
||||
if (minutes) { dateStr += ":" + ("00" + date.getMinutes()).slice(-2) }
|
||||
if (seconds) { dateStr += ":" + ("00" + date.getMinutes()).slice(-2) }
|
||||
return dateStr
|
||||
}
|
||||
|
||||
// Get the uploads from localstorage
|
||||
let uploadsStr = localStorage.getItem("uploaded_files")
|
||||
if (uploadsStr === null) { uploadsStr = "" }
|
||||
@@ -51,7 +62,7 @@ function getHistoryItem() {
|
||||
if (item === undefined || item === "") { return }
|
||||
|
||||
fetch(
|
||||
apiEndpoint+"/file/"+item+"/info"
|
||||
apiEndpoint + "/file/" + item + "/info"
|
||||
).then(resp => {
|
||||
if (!resp.ok) {
|
||||
return Promise.reject()
|
||||
@@ -68,7 +79,7 @@ function getHistoryItem() {
|
||||
)
|
||||
getHistoryItem()
|
||||
}).catch(err => {
|
||||
console.log("Fetch failed: "+err)
|
||||
console.log("Fetch failed: " + err)
|
||||
getHistoryItem()
|
||||
})
|
||||
}
|
||||
|
@@ -1,64 +0,0 @@
|
||||
.modal_background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
opacity: 0;
|
||||
transition: opacity .4s;
|
||||
}
|
||||
|
||||
.modal_window {
|
||||
position: absolute;
|
||||
z-index: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--layer_2_color);
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
top: 20%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -20%);
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
/* box-shadow: var(--shadow_color) 0px 0px 50px; */
|
||||
border-radius: 20px 20px 8px 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal_header {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background: var(--layer_1_color);
|
||||
color: var(--layer_1_text_color);
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.modal_title {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal_btn_close {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal_body {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
7
res/static/script/Chart.bundle.min.js
vendored
7
res/static/script/Chart.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
7
res/static/script/Chart.min.js
vendored
7
res/static/script/Chart.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,631 +0,0 @@
|
||||
/*
|
||||
* Behave.js
|
||||
*
|
||||
* Copyright 2013, Jacob Kelley - http://jakiestfu.com/
|
||||
* Released under the MIT Licence
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Github: http://github.com/jakiestfu/Behave.js/
|
||||
* Version: 1.5
|
||||
*/
|
||||
|
||||
(function(undefined){
|
||||
'use strict';
|
||||
|
||||
var BehaveHooks = BehaveHooks || (function(){
|
||||
var hooks = {};
|
||||
|
||||
return {
|
||||
add: function(hookName, fn){
|
||||
if(typeof hookName == "object"){
|
||||
var i;
|
||||
for(i=0; i<hookName.length; i++){
|
||||
var theHook = hookName[i];
|
||||
if(!hooks[theHook]){
|
||||
hooks[theHook] = [];
|
||||
}
|
||||
hooks[theHook].push(fn);
|
||||
}
|
||||
} else {
|
||||
if(!hooks[hookName]){
|
||||
hooks[hookName] = [];
|
||||
}
|
||||
hooks[hookName].push(fn);
|
||||
}
|
||||
},
|
||||
get: function(hookName){
|
||||
if(hooks[hookName]){
|
||||
return hooks[hookName];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})(),
|
||||
Behave = Behave || function (userOpts) {
|
||||
|
||||
if (typeof String.prototype.repeat !== 'function') {
|
||||
String.prototype.repeat = function(times) {
|
||||
if(times < 1){
|
||||
return '';
|
||||
}
|
||||
if(times % 2){
|
||||
return this.repeat(times - 1) + this;
|
||||
}
|
||||
var half = this.repeat(times / 2);
|
||||
return half + half;
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof Array.prototype.filter !== 'function') {
|
||||
Array.prototype.filter = function(func /*, thisp */) {
|
||||
if (this === null) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var t = Object(this),
|
||||
len = t.length >>> 0;
|
||||
if (typeof func != "function"){
|
||||
throw new TypeError();
|
||||
}
|
||||
var res = [],
|
||||
thisp = arguments[1];
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (i in t) {
|
||||
var val = t[i];
|
||||
if (func.call(thisp, val, i, t)) {
|
||||
res.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
var defaults = {
|
||||
textarea: null,
|
||||
replaceTab: true,
|
||||
softTabs: true,
|
||||
tabSize: 4,
|
||||
autoOpen: true,
|
||||
overwrite: true,
|
||||
autoStrip: true,
|
||||
autoIndent: true,
|
||||
fence: false
|
||||
},
|
||||
tab,
|
||||
newLine,
|
||||
charSettings = {
|
||||
|
||||
keyMap: [
|
||||
{ open: "\"", close: "\"", canBreak: false },
|
||||
{ open: "'", close: "'", canBreak: false },
|
||||
{ open: "(", close: ")", canBreak: false },
|
||||
{ open: "[", close: "]", canBreak: true },
|
||||
{ open: "{", close: "}", canBreak: true }
|
||||
]
|
||||
|
||||
},
|
||||
utils = {
|
||||
|
||||
_callHook: function(hookName, passData){
|
||||
var hooks = BehaveHooks.get(hookName);
|
||||
passData = typeof passData=="boolean" && passData === false ? false : true;
|
||||
|
||||
if(hooks){
|
||||
if(passData){
|
||||
var theEditor = defaults.textarea,
|
||||
textVal = theEditor.value,
|
||||
caretPos = utils.cursor.get(),
|
||||
i;
|
||||
|
||||
for(i=0; i<hooks.length; i++){
|
||||
hooks[i].call(undefined, {
|
||||
editor: {
|
||||
element: theEditor,
|
||||
text: textVal,
|
||||
levelsDeep: utils.levelsDeep()
|
||||
},
|
||||
caret: {
|
||||
pos: caretPos
|
||||
},
|
||||
lines: {
|
||||
current: utils.cursor.getLine(textVal, caretPos),
|
||||
total: utils.editor.getLines(textVal)
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for(i=0; i<hooks.length; i++){
|
||||
hooks[i].call(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
defineNewLine: function(){
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = "\n";
|
||||
|
||||
if(ta.value.length==2){
|
||||
newLine = "\r\n";
|
||||
} else {
|
||||
newLine = "\n";
|
||||
}
|
||||
},
|
||||
defineTabSize: function(tabSize){
|
||||
if(typeof defaults.textarea.style.OTabSize != "undefined"){
|
||||
defaults.textarea.style.OTabSize = tabSize; return;
|
||||
}
|
||||
if(typeof defaults.textarea.style.MozTabSize != "undefined"){
|
||||
defaults.textarea.style.MozTabSize = tabSize; return;
|
||||
}
|
||||
if(typeof defaults.textarea.style.tabSize != "undefined"){
|
||||
defaults.textarea.style.tabSize = tabSize; return;
|
||||
}
|
||||
},
|
||||
cursor: {
|
||||
getLine: function(textVal, pos){
|
||||
return ((textVal.substring(0,pos)).split("\n")).length;
|
||||
},
|
||||
get: function() {
|
||||
|
||||
if (typeof document.createElement('textarea').selectionStart==="number") {
|
||||
return defaults.textarea.selectionStart;
|
||||
} else if (document.selection) {
|
||||
var caretPos = 0,
|
||||
range = defaults.textarea.createTextRange(),
|
||||
rangeDupe = document.selection.createRange().duplicate(),
|
||||
rangeDupeBookmark = rangeDupe.getBookmark();
|
||||
range.moveToBookmark(rangeDupeBookmark);
|
||||
|
||||
while (range.moveStart('character' , -1) !== 0) {
|
||||
caretPos++;
|
||||
}
|
||||
return caretPos;
|
||||
}
|
||||
},
|
||||
set: function (start, end) {
|
||||
if(!end){
|
||||
end = start;
|
||||
}
|
||||
if (defaults.textarea.setSelectionRange) {
|
||||
defaults.textarea.focus();
|
||||
defaults.textarea.setSelectionRange(start, end);
|
||||
} else if (defaults.textarea.createTextRange) {
|
||||
var range = defaults.textarea.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveEnd('character', end);
|
||||
range.moveStart('character', start);
|
||||
range.select();
|
||||
}
|
||||
},
|
||||
selection: function(){
|
||||
var textAreaElement = defaults.textarea,
|
||||
start = 0,
|
||||
end = 0,
|
||||
normalizedValue,
|
||||
range,
|
||||
textInputRange,
|
||||
len,
|
||||
endRange;
|
||||
|
||||
if (typeof textAreaElement.selectionStart == "number" && typeof textAreaElement.selectionEnd == "number") {
|
||||
start = textAreaElement.selectionStart;
|
||||
end = textAreaElement.selectionEnd;
|
||||
} else {
|
||||
range = document.selection.createRange();
|
||||
|
||||
if (range && range.parentElement() == textAreaElement) {
|
||||
|
||||
normalizedValue = utils.editor.get();
|
||||
len = normalizedValue.length;
|
||||
|
||||
textInputRange = textAreaElement.createTextRange();
|
||||
textInputRange.moveToBookmark(range.getBookmark());
|
||||
|
||||
endRange = textAreaElement.createTextRange();
|
||||
endRange.collapse(false);
|
||||
|
||||
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
|
||||
start = end = len;
|
||||
} else {
|
||||
start = -textInputRange.moveStart("character", -len);
|
||||
start += normalizedValue.slice(0, start).split(newLine).length - 1;
|
||||
|
||||
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
|
||||
end = len;
|
||||
} else {
|
||||
end = -textInputRange.moveEnd("character", -len);
|
||||
end += normalizedValue.slice(0, end).split(newLine).length - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return start==end ? false : {
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
getLines: function(textVal){
|
||||
return (textVal).split("\n").length;
|
||||
},
|
||||
get: function(){
|
||||
return defaults.textarea.value.replace(/\r/g,'');
|
||||
},
|
||||
set: function(data){
|
||||
defaults.textarea.value = data;
|
||||
}
|
||||
},
|
||||
fenceRange: function(){
|
||||
if(typeof defaults.fence == "string"){
|
||||
|
||||
var data = utils.editor.get(),
|
||||
pos = utils.cursor.get(),
|
||||
hacked = 0,
|
||||
matchedFence = data.indexOf(defaults.fence),
|
||||
matchCase = 0;
|
||||
|
||||
while(matchedFence>=0){
|
||||
matchCase++;
|
||||
if( pos < (matchedFence+hacked) ){
|
||||
break;
|
||||
}
|
||||
|
||||
hacked += matchedFence+defaults.fence.length;
|
||||
data = data.substring(matchedFence+defaults.fence.length);
|
||||
matchedFence = data.indexOf(defaults.fence);
|
||||
|
||||
}
|
||||
|
||||
if( (hacked) < pos && ( (matchedFence+hacked) > pos ) && matchCase%2===0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
isEven: function(_this,i){
|
||||
return i%2;
|
||||
},
|
||||
levelsDeep: function(){
|
||||
var pos = utils.cursor.get(),
|
||||
val = utils.editor.get();
|
||||
|
||||
var left = val.substring(0, pos),
|
||||
levels = 0,
|
||||
i, j;
|
||||
|
||||
for(i=0; i<left.length; i++){
|
||||
for (j=0; j<charSettings.keyMap.length; j++) {
|
||||
if(charSettings.keyMap[j].canBreak){
|
||||
if(charSettings.keyMap[j].open == left.charAt(i)){
|
||||
levels++;
|
||||
}
|
||||
|
||||
if(charSettings.keyMap[j].close == left.charAt(i)){
|
||||
levels--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var toDecrement = 0,
|
||||
quoteMap = ["'", "\""];
|
||||
for(i=0; i<charSettings.keyMap.length; i++) {
|
||||
if(charSettings.keyMap[i].canBreak){
|
||||
for(j in quoteMap){
|
||||
toDecrement += left.split(quoteMap[j]).filter(utils.isEven).join('').split(charSettings.keyMap[i].open).length - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var finalLevels = levels - toDecrement;
|
||||
|
||||
return finalLevels >=0 ? finalLevels : 0;
|
||||
},
|
||||
deepExtend: function(destination, source) {
|
||||
for (var property in source) {
|
||||
if (source[property] && source[property].constructor &&
|
||||
source[property].constructor === Object) {
|
||||
destination[property] = destination[property] || {};
|
||||
utils.deepExtend(destination[property], source[property]);
|
||||
} else {
|
||||
destination[property] = source[property];
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
},
|
||||
addEvent: function addEvent(element, eventName, func) {
|
||||
if (element.addEventListener){
|
||||
element.addEventListener(eventName,func,false);
|
||||
} else if (element.attachEvent) {
|
||||
element.attachEvent("on"+eventName, func);
|
||||
}
|
||||
},
|
||||
removeEvent: function addEvent(element, eventName, func){
|
||||
if (element.addEventListener){
|
||||
element.removeEventListener(eventName,func,false);
|
||||
} else if (element.attachEvent) {
|
||||
element.detachEvent("on"+eventName, func);
|
||||
}
|
||||
},
|
||||
|
||||
preventDefaultEvent: function(e){
|
||||
if(e.preventDefault){
|
||||
e.preventDefault();
|
||||
} else {
|
||||
e.returnValue = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
intercept = {
|
||||
tabKey: function (e) {
|
||||
|
||||
if(!utils.fenceRange()){ return; }
|
||||
|
||||
if (e.keyCode == 9) {
|
||||
utils.preventDefaultEvent(e);
|
||||
|
||||
var toReturn = true;
|
||||
utils._callHook('tab:before');
|
||||
|
||||
var selection = utils.cursor.selection(),
|
||||
pos = utils.cursor.get(),
|
||||
val = utils.editor.get();
|
||||
|
||||
if(selection){
|
||||
|
||||
var tempStart = selection.start;
|
||||
while(tempStart--){
|
||||
if(val.charAt(tempStart)=="\n"){
|
||||
selection.start = tempStart + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var toIndent = val.substring(selection.start, selection.end),
|
||||
lines = toIndent.split("\n"),
|
||||
i;
|
||||
|
||||
if(e.shiftKey){
|
||||
for(i = 0; i<lines.length; i++){
|
||||
if(lines[i].substring(0,tab.length) == tab){
|
||||
lines[i] = lines[i].substring(tab.length);
|
||||
}
|
||||
}
|
||||
toIndent = lines.join("\n");
|
||||
|
||||
utils.editor.set( val.substring(0,selection.start) + toIndent + val.substring(selection.end) );
|
||||
utils.cursor.set(selection.start, selection.start+toIndent.length);
|
||||
|
||||
} else {
|
||||
for(i in lines){
|
||||
lines[i] = tab + lines[i];
|
||||
}
|
||||
toIndent = lines.join("\n");
|
||||
|
||||
utils.editor.set( val.substring(0,selection.start) + toIndent + val.substring(selection.end) );
|
||||
utils.cursor.set(selection.start, selection.start+toIndent.length);
|
||||
}
|
||||
} else {
|
||||
var left = val.substring(0, pos),
|
||||
right = val.substring(pos),
|
||||
edited = left + tab + right;
|
||||
|
||||
if(e.shiftKey){
|
||||
if(val.substring(pos-tab.length, pos) == tab){
|
||||
edited = val.substring(0, pos-tab.length) + right;
|
||||
utils.editor.set(edited);
|
||||
utils.cursor.set(pos-tab.length);
|
||||
}
|
||||
} else {
|
||||
utils.editor.set(edited);
|
||||
utils.cursor.set(pos + tab.length);
|
||||
toReturn = false;
|
||||
}
|
||||
}
|
||||
utils._callHook('tab:after');
|
||||
}
|
||||
return toReturn;
|
||||
},
|
||||
enterKey: function (e) {
|
||||
|
||||
if(!utils.fenceRange()){ return; }
|
||||
|
||||
if (e.keyCode == 13) {
|
||||
|
||||
utils.preventDefaultEvent(e);
|
||||
utils._callHook('enter:before');
|
||||
|
||||
var pos = utils.cursor.get(),
|
||||
val = utils.editor.get(),
|
||||
left = val.substring(0, pos),
|
||||
right = val.substring(pos),
|
||||
leftChar = left.charAt(left.length - 1),
|
||||
rightChar = right.charAt(0),
|
||||
numTabs = utils.levelsDeep(),
|
||||
ourIndent = "",
|
||||
closingBreak = "",
|
||||
finalCursorPos,
|
||||
i;
|
||||
if(!numTabs){
|
||||
finalCursorPos = 1;
|
||||
} else {
|
||||
while(numTabs--){
|
||||
ourIndent+=tab;
|
||||
}
|
||||
ourIndent = ourIndent;
|
||||
finalCursorPos = ourIndent.length + 1;
|
||||
|
||||
for(i=0; i<charSettings.keyMap.length; i++) {
|
||||
if (charSettings.keyMap[i].open == leftChar && charSettings.keyMap[i].close == rightChar){
|
||||
closingBreak = newLine;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var edited = left + newLine + ourIndent + closingBreak + (ourIndent.substring(0, ourIndent.length-tab.length) ) + right;
|
||||
utils.editor.set(edited);
|
||||
utils.cursor.set(pos + finalCursorPos);
|
||||
utils._callHook('enter:after');
|
||||
}
|
||||
},
|
||||
deleteKey: function (e) {
|
||||
|
||||
if(!utils.fenceRange()){ return; }
|
||||
|
||||
if(e.keyCode == 8){
|
||||
utils.preventDefaultEvent(e);
|
||||
|
||||
utils._callHook('delete:before');
|
||||
|
||||
var pos = utils.cursor.get(),
|
||||
val = utils.editor.get(),
|
||||
left = val.substring(0, pos),
|
||||
right = val.substring(pos),
|
||||
leftChar = left.charAt(left.length - 1),
|
||||
rightChar = right.charAt(0),
|
||||
i;
|
||||
|
||||
if( utils.cursor.selection() === false ){
|
||||
for(i=0; i<charSettings.keyMap.length; i++) {
|
||||
if (charSettings.keyMap[i].open == leftChar && charSettings.keyMap[i].close == rightChar) {
|
||||
var edited = val.substring(0,pos-1) + val.substring(pos+1);
|
||||
utils.editor.set(edited);
|
||||
utils.cursor.set(pos - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
var edited = val.substring(0,pos-1) + val.substring(pos);
|
||||
utils.editor.set(edited);
|
||||
utils.cursor.set(pos - 1);
|
||||
} else {
|
||||
var sel = utils.cursor.selection(),
|
||||
edited = val.substring(0,sel.start) + val.substring(sel.end);
|
||||
utils.editor.set(edited);
|
||||
utils.cursor.set(pos);
|
||||
}
|
||||
|
||||
utils._callHook('delete:after');
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
charFuncs = {
|
||||
openedChar: function (_char, e) {
|
||||
utils.preventDefaultEvent(e);
|
||||
utils._callHook('openChar:before');
|
||||
var pos = utils.cursor.get(),
|
||||
val = utils.editor.get(),
|
||||
left = val.substring(0, pos),
|
||||
right = val.substring(pos),
|
||||
edited = left + _char.open + _char.close + right;
|
||||
|
||||
defaults.textarea.value = edited;
|
||||
utils.cursor.set(pos + 1);
|
||||
utils._callHook('openChar:after');
|
||||
},
|
||||
closedChar: function (_char, e) {
|
||||
var pos = utils.cursor.get(),
|
||||
val = utils.editor.get(),
|
||||
toOverwrite = val.substring(pos, pos + 1);
|
||||
if (toOverwrite == _char.close) {
|
||||
utils.preventDefaultEvent(e);
|
||||
utils._callHook('closeChar:before');
|
||||
utils.cursor.set(utils.cursor.get() + 1);
|
||||
utils._callHook('closeChar:after');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
action = {
|
||||
filter: function (e) {
|
||||
|
||||
if(!utils.fenceRange()){ return; }
|
||||
|
||||
var theCode = e.which || e.keyCode;
|
||||
|
||||
if(theCode == 39 || theCode == 40 && e.which===0){ return; }
|
||||
|
||||
var _char = String.fromCharCode(theCode),
|
||||
i;
|
||||
|
||||
for(i=0; i<charSettings.keyMap.length; i++) {
|
||||
|
||||
if (charSettings.keyMap[i].close == _char) {
|
||||
var didClose = defaults.overwrite && charFuncs.closedChar(charSettings.keyMap[i], e);
|
||||
|
||||
if (!didClose && charSettings.keyMap[i].open == _char && defaults.autoOpen) {
|
||||
charFuncs.openedChar(charSettings.keyMap[i], e);
|
||||
}
|
||||
} else if (charSettings.keyMap[i].open == _char && defaults.autoOpen) {
|
||||
charFuncs.openedChar(charSettings.keyMap[i], e);
|
||||
}
|
||||
}
|
||||
},
|
||||
listen: function () {
|
||||
|
||||
if(defaults.replaceTab){ utils.addEvent(defaults.textarea, 'keydown', intercept.tabKey); }
|
||||
if(defaults.autoIndent){ utils.addEvent(defaults.textarea, 'keydown', intercept.enterKey); }
|
||||
if(defaults.autoStrip){ utils.addEvent(defaults.textarea, 'keydown', intercept.deleteKey); }
|
||||
|
||||
utils.addEvent(defaults.textarea, 'keypress', action.filter);
|
||||
|
||||
utils.addEvent(defaults.textarea, 'keydown', function(){ utils._callHook('keydown'); });
|
||||
utils.addEvent(defaults.textarea, 'keyup', function(){ utils._callHook('keyup'); });
|
||||
}
|
||||
},
|
||||
init = function (opts) {
|
||||
|
||||
if(opts.textarea){
|
||||
utils._callHook('init:before', false);
|
||||
utils.deepExtend(defaults, opts);
|
||||
utils.defineNewLine();
|
||||
|
||||
if (defaults.softTabs) {
|
||||
tab = " ".repeat(defaults.tabSize);
|
||||
} else {
|
||||
tab = "\t";
|
||||
|
||||
utils.defineTabSize(defaults.tabSize);
|
||||
}
|
||||
|
||||
action.listen();
|
||||
utils._callHook('init:after', false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.destroy = function(){
|
||||
utils.removeEvent(defaults.textarea, 'keydown', intercept.tabKey);
|
||||
utils.removeEvent(defaults.textarea, 'keydown', intercept.enterKey);
|
||||
utils.removeEvent(defaults.textarea, 'keydown', intercept.deleteKey);
|
||||
utils.removeEvent(defaults.textarea, 'keypress', action.filter);
|
||||
};
|
||||
|
||||
init(userOpts);
|
||||
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = Behave;
|
||||
}
|
||||
|
||||
if (typeof ender === 'undefined') {
|
||||
this.Behave = Behave;
|
||||
this.BehaveHooks = BehaveHooks;
|
||||
}
|
||||
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define("behave", [], function () {
|
||||
return Behave;
|
||||
});
|
||||
}
|
||||
|
||||
}).call(this);
|
@@ -1,64 +0,0 @@
|
||||
!function(){/*
|
||||
|
||||
Copyright (C) 2013 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Copyright (C) 2006 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
(function(){function aa(g){function r(){try{L.doScroll("left")}catch(ba){k.setTimeout(r,50);return}x("poll")}function x(r){if("readystatechange"!=r.type||"complete"==z.readyState)("load"==r.type?k:z)[B](n+r.type,x,!1),!l&&(l=!0)&&g.call(k,r.type||r)}var X=z.addEventListener,l=!1,E=!0,v=X?"addEventListener":"attachEvent",B=X?"removeEventListener":"detachEvent",n=X?"":"on";if("complete"==z.readyState)g.call(k,"lazy");else{if(z.createEventObject&&L.doScroll){try{E=!k.frameElement}catch(ba){}E&&r()}z[v](n+
|
||||
"DOMContentLoaded",x,!1);z[v](n+"readystatechange",x,!1);k[v](n+"load",x,!1)}}function T(){U&&aa(function(){var g=M.length;ca(g?function(){for(var r=0;r<g;++r)(function(g){k.setTimeout(function(){k.exports[M[g]].apply(k,arguments)},0)})(r)}:void 0)})}for(var k=window,z=document,L=z.documentElement,N=z.head||z.getElementsByTagName("head")[0]||L,B="",F=z.getElementsByTagName("script"),l=F.length;0<=--l;){var O=F[l],Y=O.src.match(/^[^?#]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(Y){B=Y[1]||"";O.parentNode.removeChild(O);
|
||||
break}}var U=!0,H=[],P=[],M=[];B.replace(/[?&]([^&=]+)=([^&]+)/g,function(g,r,x){x=decodeURIComponent(x);r=decodeURIComponent(r);"autorun"==r?U=!/^[0fn]/i.test(x):"lang"==r?H.push(x):"skin"==r?P.push(x):"callback"==r&&M.push(x)});l=0;for(B=H.length;l<B;++l)(function(){var g=z.createElement("script");g.onload=g.onerror=g.onreadystatechange=function(){!g||g.readyState&&!/loaded|complete/.test(g.readyState)||(g.onerror=g.onload=g.onreadystatechange=null,--S,S||k.setTimeout(T,0),g.parentNode&&g.parentNode.removeChild(g),
|
||||
g=null)};g.type="text/javascript";g.src="https://cdn.rawgit.com/google/code-prettify/master/loader/lang-"+encodeURIComponent(H[l])+".js";N.insertBefore(g,N.firstChild)})(H[l]);for(var S=H.length,F=[],l=0,B=P.length;l<B;++l)F.push("https://cdn.rawgit.com/google/code-prettify/master/loader/skins/"+encodeURIComponent(P[l])+".css");F.push("https://cdn.rawgit.com/google/code-prettify/master/loader/prettify.css");(function(g){function r(l){if(l!==x){var k=z.createElement("link");k.rel="stylesheet";k.type=
|
||||
"text/css";l+1<x&&(k.error=k.onerror=function(){r(l+1)});k.href=g[l];N.appendChild(k)}}var x=g.length;r(0)})(F);var ca=function(){"undefined"!==typeof window&&(window.PR_SHOULD_USE_CONTINUATION=!0);var g;(function(){function r(a){function d(e){var a=e.charCodeAt(0);if(92!==a)return a;var c=e.charAt(1);return(a=k[c])?a:"0"<=c&&"7">=c?parseInt(e.substring(1),8):"u"===c||"x"===c?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);
|
||||
return"\\"===e||"-"===e||"]"===e||"^"===e?"\\"+e:e}function c(e){var c=e.substring(1,e.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));e=[];var a="^"===c[0],b=["["];a&&b.push("^");for(var a=a?1:0,h=c.length;a<h;++a){var m=c[a];if(/\\[bdsw]/i.test(m))b.push(m);else{var m=d(m),p;a+2<h&&"-"===c[a+1]?(p=d(c[a+2]),a+=2):p=m;e.push([m,p]);65>p||122<m||(65>p||90<m||e.push([Math.max(65,m)|32,Math.min(p,90)|32]),97>p||122<m||
|
||||
e.push([Math.max(97,m)&-33,Math.min(p,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});c=[];h=[];for(a=0;a<e.length;++a)m=e[a],m[0]<=h[1]+1?h[1]=Math.max(h[1],m[1]):c.push(h=m);for(a=0;a<c.length;++a)m=c[a],b.push(f(m[0])),m[1]>m[0]&&(m[1]+1>m[0]&&b.push("-"),b.push(f(m[1])));b.push("]");return b.join("")}function g(e){for(var a=e.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)",
|
||||
"g")),b=a.length,d=[],h=0,m=0;h<b;++h){var p=a[h];"("===p?++m:"\\"===p.charAt(0)&&(p=+p.substring(1))&&(p<=m?d[p]=-1:a[h]=f(p))}for(h=1;h<d.length;++h)-1===d[h]&&(d[h]=++r);for(m=h=0;h<b;++h)p=a[h],"("===p?(++m,d[m]||(a[h]="(?:")):"\\"===p.charAt(0)&&(p=+p.substring(1))&&p<=m&&(a[h]="\\"+d[p]);for(h=0;h<b;++h)"^"===a[h]&&"^"!==a[h+1]&&(a[h]="");if(e.ignoreCase&&A)for(h=0;h<b;++h)p=a[h],e=p.charAt(0),2<=p.length&&"["===e?a[h]=c(p):"\\"!==e&&(a[h]=p.replace(/[a-zA-Z]/g,function(a){a=a.charCodeAt(0);
|
||||
return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var r=0,A=!1,q=!1,I=0,b=a.length;I<b;++I){var t=a[I];if(t.ignoreCase)q=!0;else if(/[a-z]/i.test(t.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){A=!0;q=!1;break}}for(var k={b:8,t:9,n:10,v:11,f:12,r:13},u=[],I=0,b=a.length;I<b;++I){t=a[I];if(t.global||t.multiline)throw Error(""+t);u.push("(?:"+g(t)+")")}return new RegExp(u.join("|"),q?"gi":"g")}function l(a,d){function f(a){var b=a.nodeType;if(1==b){if(!c.test(a.className)){for(b=
|
||||
a.firstChild;b;b=b.nextSibling)f(b);b=a.nodeName.toLowerCase();if("br"===b||"li"===b)g[q]="\n",A[q<<1]=r++,A[q++<<1|1]=a}}else if(3==b||4==b)b=a.nodeValue,b.length&&(b=d?b.replace(/\r\n?/g,"\n"):b.replace(/[ \t\r\n]+/g," "),g[q]=b,A[q<<1]=r,r+=b.length,A[q++<<1|1]=a)}var c=/(?:^|\s)nocode(?:\s|$)/,g=[],r=0,A=[],q=0;f(a);return{a:g.join("").replace(/\n$/,""),c:A}}function k(a,d,f,c,g){f&&(a={h:a,l:1,j:null,m:null,a:f,c:null,i:d,g:null},c(a),g.push.apply(g,a.g))}function z(a){for(var d=void 0,f=a.firstChild;f;f=
|
||||
f.nextSibling)var c=f.nodeType,d=1===c?d?a:f:3===c?S.test(f.nodeValue)?a:d:d;return d===a?void 0:d}function E(a,d){function f(a){for(var q=a.i,r=a.h,b=[q,"pln"],t=0,A=a.a.match(g)||[],u={},e=0,l=A.length;e<l;++e){var D=A[e],w=u[D],h=void 0,m;if("string"===typeof w)m=!1;else{var p=c[D.charAt(0)];if(p)h=D.match(p[1]),w=p[0];else{for(m=0;m<n;++m)if(p=d[m],h=D.match(p[1])){w=p[0];break}h||(w="pln")}!(m=5<=w.length&&"lang-"===w.substring(0,5))||h&&"string"===typeof h[1]||(m=!1,w="src");m||(u[D]=w)}p=t;
|
||||
t+=D.length;if(m){m=h[1];var C=D.indexOf(m),G=C+m.length;h[2]&&(G=D.length-h[2].length,C=G-m.length);w=w.substring(5);k(r,q+p,D.substring(0,C),f,b);k(r,q+p+C,m,F(w,m),b);k(r,q+p+G,D.substring(G),f,b)}else b.push(q+p,w)}a.g=b}var c={},g;(function(){for(var f=a.concat(d),q=[],k={},b=0,t=f.length;b<t;++b){var n=f[b],u=n[3];if(u)for(var e=u.length;0<=--e;)c[u.charAt(e)]=n;n=n[1];u=""+n;k.hasOwnProperty(u)||(q.push(n),k[u]=null)}q.push(/[\0-\uffff]/);g=r(q)})();var n=d.length;return f}function v(a){var d=
|
||||
[],f=[];a.tripleQuotedStrings?d.push(["str",/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""]):a.multiLineStrings?d.push(["str",/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):d.push(["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);a.verbatimStrings&&
|
||||
f.push(["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);var c=a.hashComments;c&&(a.cStyleComments?(1<c?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"]),f.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,
|
||||
null]));if(c=a.regexLiterals){var g=(c=1<c?"":"\n\r")?".":"[\\S\\s]";f.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+c+"])(?:[^/\\x5B\\x5C"+c+"]|\\x5C"+g+"|\\x5B(?:[^\\x5C\\x5D"+c+"]|\\x5C"+g+")*(?:\\x5D|$))+/")+")")])}(c=a.types)&&f.push(["typ",c]);c=(""+a.keywords).replace(/^ | $/g,"");c.length&&f.push(["kwd",
|
||||
new RegExp("^(?:"+c.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);c="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(c+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(c),null]);return E(d,f)}function B(a,d,f){function c(a){var b=
|
||||
a.nodeType;if(1==b&&!r.test(a.className))if("br"===a.nodeName.toLowerCase())g(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)c(a);else if((3==b||4==b)&&f){var e=a.nodeValue,d=e.match(n);d&&(b=e.substring(0,d.index),a.nodeValue=b,(e=e.substring(d.index+d[0].length))&&a.parentNode.insertBefore(q.createTextNode(e),a.nextSibling),g(a),b||a.parentNode.removeChild(a))}}function g(a){function c(a,b){var e=b?a.cloneNode(!1):a,p=a.parentNode;if(p){var p=c(p,1),d=a.nextSibling;
|
||||
p.appendChild(e);for(var f=d;f;f=d)d=f.nextSibling,p.appendChild(f)}return e}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=c(a.nextSibling,0);for(var e;(e=a.parentNode)&&1===e.nodeType;)a=e;b.push(a)}for(var r=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,q=a.ownerDocument,k=q.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var b=[k],t=0;t<b.length;++t)c(b[t]);d===(d|0)&&b[0].setAttribute("value",d);var l=q.createElement("ol");l.className="linenums";d=Math.max(0,d-1|0)||0;for(var t=
|
||||
0,u=b.length;t<u;++t)k=b[t],k.className="L"+(t+d)%10,k.firstChild||k.appendChild(q.createTextNode("\u00a0")),l.appendChild(k);a.appendChild(l)}function n(a,d){for(var f=d.length;0<=--f;){var c=d[f];V.hasOwnProperty(c)?Q.console&&console.warn("cannot override language handler %s",c):V[c]=a}}function F(a,d){a&&V.hasOwnProperty(a)||(a=/^\s*</.test(d)?"default-markup":"default-code");return V[a]}function H(a){var d=a.j;try{var f=l(a.h,a.l),c=f.a;a.a=c;a.c=f.c;a.i=0;F(d,c)(a);var g=/\bMSIE\s(\d+)/.exec(navigator.userAgent),
|
||||
g=g&&8>=+g[1],d=/\n/g,r=a.a,k=r.length,f=0,q=a.c,n=q.length,c=0,b=a.g,t=b.length,v=0;b[t]=k;var u,e;for(e=u=0;e<t;)b[e]!==b[e+2]?(b[u++]=b[e++],b[u++]=b[e++]):e+=2;t=u;for(e=u=0;e<t;){for(var x=b[e],z=b[e+1],w=e+2;w+2<=t&&b[w+1]===z;)w+=2;b[u++]=x;b[u++]=z;e=w}b.length=u;var h=a.h;a="";h&&(a=h.style.display,h.style.display="none");try{for(;c<n;){var m=q[c+2]||k,p=b[v+2]||k,w=Math.min(m,p),C=q[c+1],G;if(1!==C.nodeType&&(G=r.substring(f,w))){g&&(G=G.replace(d,"\r"));C.nodeValue=G;var Z=C.ownerDocument,
|
||||
W=Z.createElement("span");W.className=b[v+1];var B=C.parentNode;B.replaceChild(W,C);W.appendChild(C);f<m&&(q[c+1]=C=Z.createTextNode(r.substring(w,m)),B.insertBefore(C,W.nextSibling))}f=w;f>=m&&(c+=2);f>=p&&(v+=2)}}finally{h&&(h.style.display=a)}}catch(y){Q.console&&console.log(y&&y.stack||y)}}var Q="undefined"!==typeof window?window:{},J=["break,continue,do,else,for,if,return,while"],K=[[J,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],R=[K,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],L=[K,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
M=[K,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"],K=[K,"abstract,async,await,constructor,debugger,enum,eval,export,function,get,implements,instanceof,interface,let,null,set,undefined,var,with,yield,Infinity,NaN"],
|
||||
N=[J,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],O=[J,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],J=[J,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],P=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
|
||||
S=/\S/,T=v({keywords:[R,M,L,K,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",N,O,J],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),V={};n(T,["default-code"]);n(E([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
|
||||
/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));n(E([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
|
||||
["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);n(E([],[["atv",/^[\s\S]+/]]),["uq.val"]);n(v({keywords:R,hashComments:!0,cStyleComments:!0,types:P}),"c cc cpp cxx cyc m".split(" "));n(v({keywords:"null,true,false"}),["json"]);n(v({keywords:M,hashComments:!0,cStyleComments:!0,
|
||||
verbatimStrings:!0,types:P}),["cs"]);n(v({keywords:L,cStyleComments:!0}),["java"]);n(v({keywords:J,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);n(v({keywords:N,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);n(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),
|
||||
["perl","pl","pm"]);n(v({keywords:O,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);n(v({keywords:K,cStyleComments:!0,regexLiterals:!0}),["javascript","js","ts","typescript"]);n(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);n(E([],[["str",/^[\s\S]+/]]),
|
||||
["regex"]);var U=Q.PR={createSimpleLexer:E,registerLangHandler:n,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,f){f=f||!1;d=d||null;var c=document.createElement("div");c.innerHTML="<pre>"+a+"</pre>";c=c.firstChild;f&&B(c,f,!0);H({j:d,m:f,h:c,l:1,a:null,i:null,c:null,g:null});
|
||||
return c.innerHTML},prettyPrint:g=function(a,d){function f(){for(var c=Q.PR_SHOULD_USE_CONTINUATION?b.now()+250:Infinity;t<r.length&&b.now()<c;t++){for(var d=r[t],k=h,n=d;n=n.previousSibling;){var q=n.nodeType,l=(7===q||8===q)&&n.nodeValue;if(l?!/^\??prettify\b/.test(l):3!==q||/\S/.test(n.nodeValue))break;if(l){k={};l.replace(/\b(\w+)=([\w:.%+-]+)/g,function(a,b,c){k[b]=c});break}}n=d.className;if((k!==h||u.test(n))&&!e.test(n)){q=!1;for(l=d.parentNode;l;l=l.parentNode)if(w.test(l.tagName)&&l.className&&
|
||||
u.test(l.className)){q=!0;break}if(!q){d.className+=" prettyprinted";q=k.lang;if(!q){var q=n.match(v),A;!q&&(A=z(d))&&D.test(A.tagName)&&(q=A.className.match(v));q&&(q=q[1])}if(x.test(d.tagName))l=1;else var l=d.currentStyle,y=g.defaultView,l=(l=l?l.whiteSpace:y&&y.getComputedStyle?y.getComputedStyle(d,null).getPropertyValue("white-space"):0)&&"pre"===l.substring(0,3);y=k.linenums;(y="true"===y||+y)||(y=(y=n.match(/\blinenums\b(?::(\d+))?/))?y[1]&&y[1].length?+y[1]:!0:!1);y&&B(d,y,l);H({j:q,h:d,m:y,
|
||||
l:l,a:null,i:null,c:null,g:null})}}}t<r.length?Q.setTimeout(f,250):"function"===typeof a&&a()}for(var c=d||document.body,g=c.ownerDocument||document,c=[c.getElementsByTagName("pre"),c.getElementsByTagName("code"),c.getElementsByTagName("xmp")],r=[],k=0;k<c.length;++k)for(var n=0,l=c[k].length;n<l;++n)r.push(c[k][n]);var c=null,b=Date;b.now||(b={now:function(){return+new Date}});var t=0,v=/\blang(?:uage)?-([\w.]+)(?!\S)/,u=/\bprettyprint\b/,e=/\bprettyprinted\b/,x=/pre|xmp/i,D=/^code$/i,w=/^(?:pre|code|xmp)$/i,
|
||||
h={};f()}},R=Q.define;"function"===typeof R&&R.amd&&R("google-code-prettify",[],function(){return U})})();return g}();S||k.setTimeout(T,0)})();}()
|
@@ -23,7 +23,6 @@
|
||||
|
||||
<script>
|
||||
let apiEndpoint = '{{.APIEndpoint}}';
|
||||
{{template `util.js`}}
|
||||
{{template `history.js`}}
|
||||
</script>
|
||||
{{template "analytics"}}
|
||||
|
@@ -15,81 +15,6 @@
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
.header_image{
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
.feat_table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.feat_table > div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.feat_table > div > div:first-child {
|
||||
flex: 0 0 20%;
|
||||
max-width: 20%;
|
||||
}
|
||||
.feat_table > div > div {
|
||||
flex: 1 1 0;
|
||||
margin: 0.25em;
|
||||
padding: 0.5em;
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
.feat_table > div > .feat_label {
|
||||
border-top-left-radius: 0.5em;
|
||||
border-bottom-left-radius: 0.5em;
|
||||
background-color: var(--layer_1_color);
|
||||
color: var(--layer_1_text_color);
|
||||
}
|
||||
.feat_table > div > .feat_normal {
|
||||
background-color: var(--layer_3_color);
|
||||
box-shadow: 1px 1px 4px -2px var(--shadow_color);
|
||||
}
|
||||
.feat_table > div > .feat_pro {
|
||||
background-color: var(--layer_4_color);
|
||||
box-shadow: 1px 1px 4px -1px var(--shadow_color);
|
||||
}
|
||||
.feat_table > div > .feat_highlight {
|
||||
border: 1px solid var(--link_color)
|
||||
}
|
||||
.text_highlight {
|
||||
color: var(--link_color);
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.feat_table > div > .cell_background {
|
||||
flex: 0 0 33%;
|
||||
min-width: 33%;
|
||||
border-top-left-radius: 0.5em;
|
||||
border-bottom-left-radius: 0.5em;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
text-align: left;
|
||||
font-size: 1.2em;
|
||||
color: #ffffff;
|
||||
text-shadow: 1px 1px 3px #000000;
|
||||
}
|
||||
|
||||
.feat_table > div > div.round_tl { border-top-left-radius: 0.5em; }
|
||||
.feat_table > div > div.round_tr { border-top-right-radius: 0.5em; }
|
||||
.feat_table > div > div.round_br { border-bottom-right-radius: 0.5em; }
|
||||
.feat_table > div > div.round_bl { border-bottom-left-radius: 0.5em; }
|
||||
|
||||
.features_cell {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.features_cell > div {
|
||||
flex: 1 1 50%;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
{{ template `modal.css` }}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -101,533 +26,10 @@
|
||||
</head>
|
||||
<body>
|
||||
{{template "page_top" .}}
|
||||
<header style="padding-bottom: 80px; padding-top: 80px;">
|
||||
<picture>
|
||||
<source media="(max-width: 700px)" srcset="/res/img/header_orbitron.png">
|
||||
<img class="header_image" src="/res/img/header_orbitron_wide.png" alt="Header image">
|
||||
</picture>
|
||||
</header>
|
||||
|
||||
<!-- Svelte element -->
|
||||
<div id="uploader" class="page_content"></div>
|
||||
|
||||
<header>
|
||||
<h1>What is pixeldrain?</h1>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<p>
|
||||
Pixeldrain is a file sharing website built for speed and ease of
|
||||
use. You can upload files you want to share online to our
|
||||
servers and we will hold on to them for at least a month. During
|
||||
this time anyone with the link will be able to download your
|
||||
files. Pixeldrain is built to be as fast as possible, so you
|
||||
don't have to do any unnecessary waiting when downloading files.
|
||||
</p>
|
||||
<p>
|
||||
Files can be uploaded by clicking the big green upload
|
||||
button, or by dragging them onto this page from your file
|
||||
manager.
|
||||
</p>
|
||||
<p>
|
||||
If you uploaded multiple files at once you can also create a
|
||||
list, which is a collection of files with one single link. Like
|
||||
a photo album, a music record or a video compilation. Click the
|
||||
'Create list with uploaded files' button after your uploads are
|
||||
complete. The files will be saved in the order you uploaded
|
||||
them.
|
||||
</p>
|
||||
|
||||
</section>
|
||||
<header>
|
||||
<h1 id="pro">Getting more out of pixeldrain</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>
|
||||
By purchasing a subscription you support pixeldrain on its
|
||||
mission to make content sharing easier, safer and faster for
|
||||
everyone. The standard subscription plans use Patreon for
|
||||
payment processing. Check out our <a href="#prepaid">prepaid
|
||||
plans</a> if you would like to pay using cryptocurrencies.
|
||||
</p>
|
||||
<p>
|
||||
Pixeldrain uses
|
||||
<a href="https://en.wikipedia.org/wiki/Byte#Multiple-byte_units"
|
||||
target="_blank">SI standard units</a> for measuring file sizes.
|
||||
If you are using Microsoft Windows your files may appear smaller
|
||||
than they actually are.
|
||||
</p>
|
||||
<br/>
|
||||
<div class="feat_table">
|
||||
<div>
|
||||
<div></div>
|
||||
<div class="feat_normal round_tl">Free</div>
|
||||
<div class="feat_pro feat_highlight round_tr">Pro</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">Size limit per file</div>
|
||||
<div class="feat_normal">10 GB per file (9.31 GiB)</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">20 GB</span> per file (18.63 GiB)
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">
|
||||
File expiry
|
||||
</div>
|
||||
<div class="feat_normal">
|
||||
30 days after the last time it is viewed
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">90 days</span> after the last time it is viewed
|
||||
<br/>
|
||||
<button class="round" onclick="return explainFileExpiry()">
|
||||
<i class="icon">info</i>
|
||||
More information
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">
|
||||
Data transfer limit
|
||||
</div>
|
||||
<div class="feat_normal">
|
||||
Rate limiting mode will be enabled when a file has 3
|
||||
times more downloads than views
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
Transfer limit of <span class="text_highlight">1
|
||||
terabyte</span>. If the transfer limit is exceeded
|
||||
advertisements will be enabled again
|
||||
<br/>
|
||||
<button class="round" onclick="return explainDirectLinking()">
|
||||
<i class="icon">info</i>
|
||||
More information
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">
|
||||
Adver­tise­ments
|
||||
</div>
|
||||
<div class="feat_normal">
|
||||
Banner advertisements on the file viewer page
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">No ads</span> on files
|
||||
you share. No ads when viewing files uploaded by
|
||||
other users
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">Privacy</div>
|
||||
<div class="feat_normal">
|
||||
No trackers, but advertisers can see your IP address
|
||||
and browser fingerprint
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">Completely
|
||||
private</span>. No third party scripts and no
|
||||
logging
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">
|
||||
Storage space
|
||||
</div>
|
||||
<div class="feat_normal">
|
||||
500 gigabytes
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">1 terabyte</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">
|
||||
Download speed
|
||||
</div>
|
||||
<div class="feat_normal">
|
||||
Up to 4 MiB/s, may be slower during busy periods
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">High priority</span>
|
||||
bandwidth for files you download and files on your
|
||||
account
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="feat_label">
|
||||
Online file previews
|
||||
</div>
|
||||
<div class="feat_normal">
|
||||
View image, audio, PDF and text files directly in your
|
||||
web browser
|
||||
</div>
|
||||
<div class="feat_pro">
|
||||
<span class="text_highlight">Video streaming</span> in
|
||||
your web browser. Free users will also be able to watch
|
||||
videos you uploaded
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div></div>
|
||||
<div class="feat_normal round_bl">Free</div>
|
||||
<div class="feat_pro feat_highlight round_br">
|
||||
{{if eq .User.Subscription.ID "patreon_1"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
Only
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291427" class="button button_highlight round">
|
||||
€ 2 per month
|
||||
</a>
|
||||
or
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291427&cadence=12" class="button button_highlight round">
|
||||
€ 20 per year!
|
||||
</a>
|
||||
<br/>
|
||||
(Excluding tax)
|
||||
<br/>
|
||||
Subscription managed by Patreon
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div style="text-align: center;">
|
||||
Do you need even more time and space? Check out our other plans
|
||||
</div>
|
||||
<br/>
|
||||
<div class="feat_table">
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_5.webp');">
|
||||
Resolve<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_5"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5736701" class="button button_highlight round">
|
||||
€ 4
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell round_tr">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">180 days</span> file expiry</div>
|
||||
<div><span class="text_highlight">2 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">2 TB</span> storage space</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_2.webp');">
|
||||
Persistence<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_2"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291482" class="button button_highlight round">€ 8</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">360 days</span> file expiry</div>
|
||||
<div><span class="text_highlight">4 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">4 TB</span> storage space</div>
|
||||
<div>
|
||||
<span class="text_highlight">File viewer
|
||||
branding</span>: Set a custom theme and header,
|
||||
footer and background images on the download pages
|
||||
for your files
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_3.webp');">
|
||||
Tenacity<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_3"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291516" class="button button_highlight round">€ 16</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">8 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">8 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_4.webp');">
|
||||
Eternity<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_4"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291528" class="button button_highlight round">€ 32</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">16 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">16 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_6.webp');">
|
||||
Infinity<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_6"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=6573749" class="button button_highlight round">€ 64</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">32 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">32 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_7.webp');">
|
||||
Omnipotence<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_7"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=7732256" class="button button_highlight round">
|
||||
€ 96
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">48 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">48 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_8.webp');">
|
||||
Omnipresence<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_8"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=7732262" class="button button_highlight round">
|
||||
€ 128
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">64 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">64 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_9.webp');">
|
||||
Omniscience<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_9"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=7732266" class="button button_highlight round">
|
||||
€ 192
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">96 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">96 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cell_background" style="background-image: url('/res/img/benefit_10.webp');">
|
||||
Trinity<br/>
|
||||
{{if eq .User.Subscription.ID "patreon_10"}}
|
||||
You have this plan<br/>
|
||||
Thank you for supporting pixeldrain!
|
||||
{{else}}
|
||||
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=7732271" class="button button_highlight round">
|
||||
€ 256
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="feat_pro features_cell round_br">
|
||||
<div><span class="text_highlight">20 GB</span> max file size</div>
|
||||
<div><span class="text_highlight">Files never expire</span></div>
|
||||
<div><span class="text_highlight">128 TB</span> transfer limit</div>
|
||||
<div><span class="text_highlight">128 TB</span> storage space</div>
|
||||
<div><span class="text_highlight">File viewer branding</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 id="prepaid">Prepaid plans</h2>
|
||||
<p>
|
||||
You you need more bandwidth or storage space there's also
|
||||
prepaid plans. For prepaid we charge a base rate of €1 per
|
||||
month, the rest of the charges are usage based. We charge €4 per
|
||||
TB per month for storage space and €2 per TB for bandwidth
|
||||
usage. We accept Bitcoin, Lightning Network and Dogecoin
|
||||
payments.
|
||||
</p>
|
||||
<p>
|
||||
If €4 per TB of storage is too much we also have plans with
|
||||
cheaper storage and file expiry enabled. Your files will not
|
||||
expire as long as they generate traffic, so this can be a viable
|
||||
option if your files are accessed often.
|
||||
</p>
|
||||
<div style="text-align: center;">
|
||||
<img src="/res/img/coins.png" alt="supported coins" style="width: 250px;"/>
|
||||
</div>
|
||||
<p>
|
||||
To use prepaid you need to register a pixeldrain account. After
|
||||
logging in head to the <a href="/user/transactions">transactions
|
||||
page</a> to deposit your coins.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<template id="tpl_file_expiry">
|
||||
<p>
|
||||
Files on pixeldrain have to expire eventually. If we didn't do
|
||||
this the website would keep growing forever and we would run out
|
||||
of money pretty quickly.
|
||||
</p>
|
||||
<p>
|
||||
Unlike most other sharing sites pixeldrain uses a postponing
|
||||
system for expiring files. When a file is freshly uploaded it
|
||||
gets 30 days by default (90 days if you have the pro plan).
|
||||
After these 30 days we will check when the file was last viewed.
|
||||
Files which are regularly viewed could still bring new users to
|
||||
the platform, it would be rude to show these people a File Not
|
||||
Found page. So if the file was viewed in the last 30 days we
|
||||
will simply postpone the next check a month. If the file was not
|
||||
viewed however it will immediately be removed.
|
||||
</p>
|
||||
<p>
|
||||
Views are only counted when someone visits the download page in
|
||||
a web browser. This makes sure that users can see that the file
|
||||
comes from pixeldrain.
|
||||
</p>
|
||||
<p>
|
||||
This way we can minimize dead links, and you won't have to tell
|
||||
your friends to 'hurry and download this before it expires'.
|
||||
</p>
|
||||
</template>
|
||||
<template id="tpl_direct_linking">
|
||||
<p>
|
||||
Paying for bandwidth is the most expensive part of running
|
||||
pixeldrain. Because of this we have to limit what can be
|
||||
downloaded and by who.
|
||||
</p>
|
||||
<p>
|
||||
Normally when you view a file it's on pixeldrain's file viewer.
|
||||
The file viewer is the page with the download button, the name
|
||||
of the file and a frame where you can view the file if it's an
|
||||
image, video, audio, PDF or text file.
|
||||
</p>
|
||||
<h3>Rate limiting</h3>
|
||||
<p>
|
||||
It's also possible to link directly to a file instead of the
|
||||
download page. This circumvents our advertisers and branding and
|
||||
thus we lose money when people do this. That's why I added 'rate
|
||||
limiting mode' to files. This mode is enabled when a file has
|
||||
been downloaded three times more than it has been viewed through
|
||||
the file viewer. When rate limiting mode is activated a file
|
||||
cannot be downloaded through the API, the request needs to come
|
||||
from the file viewer page. On the file viewer you will see a
|
||||
CAPTCHA to fill in when you click the download button.
|
||||
</p>
|
||||
<p>
|
||||
More information about <a
|
||||
href="https://en.wikipedia.org/wiki/Inline_linking"
|
||||
target="_blank">Hotlinking on Wikipedia</a>.
|
||||
</p>
|
||||
<h3>Hotlinking with a Pro subscription</h3>
|
||||
<p>
|
||||
When you have a Pro subscription you will get a monthly data
|
||||
transfer limit for all the files on your account combined. Files
|
||||
you download from pixeldrain are subtracted from the data cap.
|
||||
If you have <a href="/user/subscription">Bandwidth sharing</a>
|
||||
enabled your data cap is also used when other people download
|
||||
your files.
|
||||
</p>
|
||||
<p>
|
||||
In principle there is always someone who pays for the bandwidth
|
||||
usage when a file is being downloaded:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
If the person downloading the file has a Pro subscription
|
||||
their data cap is used.
|
||||
</li>
|
||||
<li>
|
||||
If the person who uploaded the file has a Pro subscription
|
||||
and Bandwidth sharing is enabled on their account, then the
|
||||
uploader's data cap is used.
|
||||
</li>
|
||||
<li>
|
||||
If neither the uploader nor the downloader has a Pro
|
||||
subscription the download will be supported by
|
||||
advertisements on the download page.
|
||||
</li>
|
||||
</ol>
|
||||
<p>
|
||||
The bandwidth cap on your account is a 30 day rolling window.
|
||||
This means that bandwidth usage will expire 30 days after it was
|
||||
used. Your counter will not reset at the start of the next
|
||||
month.
|
||||
</p>
|
||||
<p>
|
||||
When a list of files is downloaded with the 'DL all files'
|
||||
button each file in the resulting zip file will be counted
|
||||
separately.
|
||||
</p>
|
||||
</template>
|
||||
<div id="page_content" class="page_content"></div>
|
||||
|
||||
{{template "page_bottom" .}}
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
{{template "Modal.js"}}
|
||||
|
||||
function explainFileExpiry() {
|
||||
let m = new Modal(
|
||||
document.body, null, "File Expiry Postponing", "600px", "auto"
|
||||
)
|
||||
m.cloneTemplate("tpl_file_expiry")
|
||||
m.open()
|
||||
return false
|
||||
}
|
||||
function explainDirectLinking() {
|
||||
let m = new Modal(
|
||||
document.body, null, "Hotlinking Bandwidth", "800px", "auto"
|
||||
)
|
||||
m.cloneTemplate("tpl_direct_linking")
|
||||
m.open()
|
||||
return false
|
||||
}
|
||||
|
||||
if (window.location.hash === "#direct_linking" || window.location.hash === "#hotlinking") {
|
||||
explainDirectLinking()
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "analytics"}}
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -1,156 +0,0 @@
|
||||
{{define "text_editor"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{{template "meta_tags" "Text Upload"}}
|
||||
{{template "user_style" .}}
|
||||
|
||||
<style>
|
||||
.text_editor {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.headerbar {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.headerbar > * {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.headerbar > .headerbar_spacer { flex: 1 1 auto; }
|
||||
.textarea_container {
|
||||
flex: 1 1 auto;
|
||||
margin: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
.textarea {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: var(--layer_1_color);
|
||||
color: var(--text_color);
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.textarea:focus { box-shadow: none; }
|
||||
|
||||
{{template `modal.css`}}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="text_editor" class="text_editor">
|
||||
<div id="headerbar" class="highlight_2 headerbar">
|
||||
<a href="/" class="button round">
|
||||
<i class="icon">arrow_back</i>
|
||||
</a>
|
||||
<div id="headerbar_spacer" class="headerbar_spacer"></div>
|
||||
<button class="button toolbar_button round" onclick="return helpModal();">
|
||||
<i class="icon">info</i> Information
|
||||
</button>
|
||||
<button class="button toolbar_button round button_highlight" onclick="uploadText();">
|
||||
<i class="icon">save</i> Save
|
||||
</button>
|
||||
</div>
|
||||
<div class="textarea_container">
|
||||
<textarea id="textarea" class="textarea" placeholder="Your text here..." autofocus="autofocus"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template id="tpl_text_editor_help">
|
||||
<p>
|
||||
You can type anything you want in here. When you're done press
|
||||
CTRL + S or click the Save button in the top right corner to
|
||||
upload your text file to pixeldrain.
|
||||
</p>
|
||||
<p>
|
||||
To show syntax highlighting on pixeldrain's file viewer you
|
||||
should save your file with a file extension like .js, .go,
|
||||
.java, etc. If you save your file with the extension .md or
|
||||
.markdown the result will be rendered as HTML on the file
|
||||
viewer.
|
||||
</p>
|
||||
<p>
|
||||
The text editor has been enhanced by Jacob Kelley's
|
||||
<a href="https://jakiestfu.github.io/Behave.js/"target="_blank">Behave.js</a>.
|
||||
|
||||
Many thanks to him for developing this plugin and putting it
|
||||
under the MIT license.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script src="/res/script/behave.js"></script>
|
||||
<script>
|
||||
let apiEndpoint = '{{.APIEndpoint}}';
|
||||
{{template "UploadManager.js"}}
|
||||
{{template "util.js"}}
|
||||
{{template "Modal.js"}}
|
||||
|
||||
let editor = new Behave({
|
||||
textarea: document.getElementById("textarea"),
|
||||
autoStrip: false,
|
||||
autoOpen: false,
|
||||
overwrite: false,
|
||||
autoIndent: false,
|
||||
replaceTab: true,
|
||||
softTabs: false,
|
||||
tabSize: 8
|
||||
});
|
||||
|
||||
function uploadText() {
|
||||
var text = document.getElementById("textarea").value;
|
||||
var blob = new Blob([text], {type: "text/plain"});
|
||||
var filename = prompt(
|
||||
"What do you want to call this piece of textual art?\n\n" +
|
||||
"Please add your own file extension, if you want.",
|
||||
"Text file.txt"
|
||||
);
|
||||
|
||||
if(filename === null){
|
||||
return; // User pressed cancel
|
||||
}
|
||||
|
||||
new UploadManager(apiEndpoint+"/file", null).addFile(
|
||||
blob,
|
||||
filename,
|
||||
null,
|
||||
function (id){
|
||||
addUploadHistory(id);
|
||||
setTimeout(window.location.href = "/u/" + id, 100);
|
||||
},
|
||||
function (response, error) {
|
||||
alert("File upload failed! The server told us this: " + response);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Upload the file when ctrl + s is pressed
|
||||
document.addEventListener("keydown", function(event) {
|
||||
if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) {
|
||||
event.preventDefault();
|
||||
uploadText();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
function helpModal() {
|
||||
let m = new Modal(
|
||||
document.body, null, "Text editor", "600px", "auto"
|
||||
)
|
||||
m.cloneTemplate("tpl_text_editor_help")
|
||||
m.open()
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "analytics"}}
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
17
res/template/text_upload.html
Normal file
17
res/template/text_upload.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{{define "text_upload"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{{template "meta_tags" "Text upload"}}
|
||||
{{template "user_style" .}}
|
||||
|
||||
<script>
|
||||
window.api_endpoint = '{{.APIEndpoint}}';
|
||||
</script>
|
||||
<link rel='stylesheet' href='/res/svelte/text_upload.css?v{{.CacheID}}'>
|
||||
<script defer src='/res/svelte/text_upload.js?v{{.CacheID}}'></script>
|
||||
</head>
|
||||
<body id="body"></body>
|
||||
{{template "analytics"}}
|
||||
</html>
|
||||
{{end}}
|
Reference in New Issue
Block a user