From 3d501c980f64986ffb53f73060b69c3eabd33737 Mon Sep 17 00:00:00 2001
From: zneix <44851575+zneix@users.noreply.github.com>
Date: Sat, 5 Sep 2020 18:13:49 +0200
Subject: [PATCH] Made app.js proper Class, added support for non-absolute path
Something that would solve relevant upstream issues/prs: seejohnrun/haste-server#53 seejohnrun/haste-server#140
---
static/application.js | 642 +++++++++++++++++++++---------------------
static/index.html | 15 +-
2 files changed, 329 insertions(+), 328 deletions(-)
diff --git a/static/application.js b/static/application.js
index 006a73b..57bb751 100644
--- a/static/application.js
+++ b/static/application.js
@@ -1,160 +1,332 @@
/* global $, hljs, window, document */
-
///// represents a single document
-
-let haste_document = function(){
- this.locked = false;
-};
-
-// Escapes HTML tag characters
-haste_document.prototype.htmlEscape = function(s){
- return s
- .replace(/&/g, '&')
- .replace(/>/g, '>')
- .replace(//g, '>')
+ .replace(/${msg}`);
- $('#messages').prepend(msgBox);
- setTimeout(function(){
- msgBox.slideUp('fast', function(){ $(this).remove(); });
- }, 3000);
-};
-
-// Show the light key
-haste.prototype.lightKey = function(){
- this.configureKey(['new', 'save']);
-};
-
-// Show the full key
-haste.prototype.fullKey = function(){
- this.configureKey(['new', 'duplicate', 'twitter', 'raw']);
-};
-
-// Set the key up for certain things to be enabled
-haste.prototype.configureKey = enable => {
- let $this;
- $('#box2 .function').each(function(){
- $this = $(this);
- for (const el of enable){
- if ($this.hasClass(el)){
- $this.addClass('enabled');
- return true;
- }
- }
- $this.removeClass('enabled');
- });
-};
-
-// Remove the current document (if there is one)
-// and set up for a new one
-haste.prototype.newDocument = function(hideHistory){
- this.$box.hide();
- this.doc = new haste_document();
- if (!hideHistory){
- window.history.pushState(null, this.appName, '/');
+class haste {
+ constructor(appName, options){
+ this.appName = appName;
+ this.$textarea = $('textarea');
+ this.$box = $('#box');
+ this.$code = $('#box code');
+ this.$linenos = $('#linenos');
+ this.options = options;
+ this.configureShortcuts();
+ this.configureButtons();
+ // If twitter is disabled, hide the button
+ if (!options.twitter) $('#box2 .twitter').hide();
+ this.baseUrl = options.baseUrl || '/';
}
- this.setTitle();
- this.lightKey();
- this.$textarea.val('').show('fast', function(){
- this.focus();
- });
- this.removeLineNumbers();
-};
+ // Set the page title - include the appName
+ setTitle(ext){
+ document.title = `${this.appName}${ext ? ` - ${ext}` : ''}`;
+ }
+ // Show a message box
+ showMessage(msg, cls){
+ let msgBox = $(`
${msg}`);
+ $('#messages').prepend(msgBox);
+ setTimeout(function (){
+ msgBox.slideUp('fast', function (){ $(this).remove(); });
+ }, 3000);
+ }
+ // Show the light key
+ lightKey(){
+ this.configureKey(['new', 'save']);
+ }
+ // Show the full key
+ fullKey(){
+ this.configureKey(['new', 'duplicate', 'twitter', 'raw']);
+ }
+ // Set the key up for certain things to be enabled
+ configureKey(enable){
+ let $this;
+ $('#box2 .function').each(function (){
+ $this = $(this);
+ for (const el of enable){
+ if ($this.hasClass(el)){
+ $this.addClass('enabled');
+ return true;
+ }
+ }
+ $this.removeClass('enabled');
+ });
+ }
+ // Remove the current document (if there is one)
+ // and set up for a new one
+ newDocument(hideHistory){
+ this.$box.hide();
+ this.doc = new haste_document(this);
+ if (!hideHistory){
+ window.history.pushState(null, this.appName, this.baseUrl);
+ }
+ this.setTitle();
+ this.lightKey();
+ this.$textarea.val('').show('fast', function (){
+ this.focus();
+ });
+ this.removeLineNumbers();
+ }
+ // Look up the extension preferred for a type
+ // If not found, return the type itself - which we'll place as the extension
+ lookupExtensionByType(type){
+ for (let key in haste.extensionMap){
+ if (haste.extensionMap[key] == type)
+ return key;
+ }
+ return type;
+ }
+ // Look up the type for a given extension
+ // If not found, return the extension - which we'll attempt to use as the type
+ lookupTypeByExtension(ext){
+ return haste.extensionMap[ext] || ext;
+ }
+ // Add line numbers to the document
+ // For the specified number of lines
+ addLineNumbers(lineCount){
+ let h = '';
+ for (let i = 0; i < lineCount; i++){
+ h += (i + 1).toString() + '
';
+ }
+ $('#linenos').html(h);
+ }
+ // Remove the line numbers
+ removeLineNumbers(){
+ $('#linenos').html('>');
+ }
+ // Load a document and show it
+ loadDocument(key){
+ // Split the key up
+ let parts = key.split('.', 2);
+ // Ask for what we want
+ let _this = this;
+ _this.doc = new haste_document(this);
+ _this.doc.load(parts[0], function (ret){
+ if (ret){
+ _this.$code.html(ret.value);
+ _this.setTitle(ret.key);
+ _this.fullKey();
+ _this.$textarea.val('').hide();
+ _this.$box.show().focus();
+ _this.addLineNumbers(ret.lineCount);
+ }
+ else {
+ _this.newDocument();
+ }
+ }, this.lookupTypeByExtension(parts[1]));
+ }
+ // Duplicate the current document - only if locked
+ duplicateDocument(){
+ if (this.doc.locked){
+ let currentData = this.doc.data;
+ this.newDocument();
+ this.$textarea.val(currentData);
+ }
+ }
+ // Lock the current document
+ lockDocument(){
+ let _this = this;
+ this.doc.save(this.$textarea.val(), function (err, ret){
+ if (err){
+ _this.showMessage(err.message, 'error');
+ }
+ else if (ret){
+ _this.$code.html(ret.value);
+ _this.setTitle(ret.key);
+ let file = _this.baseUrl + ret.key;
+ if (ret.language){
+ file += `.${_this.lookupExtensionByType(ret.language)}`;
+ }
+ window.history.pushState(null, `${_this.appName}-${ret.key}`, file);
+ _this.fullKey();
+ _this.$textarea.val('').hide();
+ _this.$box.show().focus();
+ _this.addLineNumbers(ret.lineCount);
+ }
+ });
+ }
+ configureButtons(){
+ let _this = this;
+ this.buttons = [
+ {
+ $where: $('#box2 .save'),
+ label: 'Save',
+ shortcutDescription: 'control + s',
+ shortcut: function (evt){
+ return evt.ctrlKey && (evt.keyCode == 83);
+ },
+ action: function (){
+ if (_this.$textarea.val().replace(/^\s+|\s+$/g, '') != ''){
+ _this.lockDocument();
+ }
+ }
+ },
+ {
+ $where: $('#box2 .new'),
+ label: 'New',
+ shortcut: function (evt){
+ return evt.ctrlKey && evt.keyCode == 78;
+ },
+ shortcutDescription: 'control + n',
+ action: function (){
+ _this.newDocument(!_this.doc.key);
+ }
+ },
+ {
+ $where: $('#box2 .duplicate'),
+ label: 'Duplicate & Edit',
+ shortcut: function (evt){
+ return _this.doc.locked && evt.ctrlKey && evt.keyCode == 68;
+ },
+ shortcutDescription: 'control + d',
+ action: function (){
+ _this.duplicateDocument();
+ }
+ },
+ {
+ $where: $('#box2 .raw'),
+ label: 'Just Text',
+ shortcut: function (evt){
+ return evt.ctrlKey && evt.shiftKey && evt.keyCode == 82;
+ },
+ shortcutDescription: 'control + shift + r',
+ action: function (){
+ window.location.href = `${_this.baseUrl}raw/${_this.doc.key}`;
+ }
+ },
+ {
+ $where: $('#box2 .twitter'),
+ label: 'Twitter',
+ shortcut: function (evt){
+ return _this.options.twitter && _this.doc.locked && evt.shiftKey && evt.ctrlKey && evt.keyCode == 84;
+ },
+ shortcutDescription: 'control + shift + t',
+ action: function (){
+ window.open(`https://twitter.com/share?url=${encodeURI(window.location.href)}`);
+ }
+ }
+ ];
+ for (const button of this.buttons){
+ this.configureButton(button);
+ }
+ }
+ configureButton(options){
+ // Handle the click action
+ options.$where.click(function (evt){
+ evt.preventDefault();
+ if (!options.clickDisabled && $(this).hasClass('enabled')){
+ options.action();
+ }
+ });
+ // Show the label
+ options.$where.mouseenter(function (){
+ $('#box3 .label').text(options.label);
+ $('#box3 .shortcut').text(options.shortcutDescription || '');
+ $('#box3').show();
+ $(this).append($('#pointer').remove().show());
+ });
+ // Hide the label
+ options.$where.mouseleave(function (){
+ $('#box3').hide();
+ $('#pointer').hide();
+ });
+ }
+ // Configure keyboard shortcuts for the textarea
+ configureShortcuts(){
+ let _this = this;
+ $(document.body).keydown(function (evt){
+ for (const button of _this.buttons){
+ if (button.shortcut && button.shortcut(evt)){
+ evt.preventDefault();
+ button.action();
+ return;
+ }
+ }
+ });
+ }
+}
// Map of common extensions
// Note: this list does not need to include anything that IS its extension,
@@ -170,191 +342,15 @@ haste.extensionMap = {
swift: 'swift'
};
-// Look up the extension preferred for a type
-// If not found, return the type itself - which we'll place as the extension
-haste.prototype.lookupExtensionByType = function(type){
- for (let key in haste.extensionMap){
- if (haste.extensionMap[key] == type) return key;
- }
- return type;
-};
-// Look up the type for a given extension
-// If not found, return the extension - which we'll attempt to use as the type
-haste.prototype.lookupTypeByExtension = function(ext){
- return haste.extensionMap[ext] || ext;
-};
-// Add line numbers to the document
-// For the specified number of lines
-haste.prototype.addLineNumbers = function(lineCount){
- let h = '';
- for (let i = 0; i < lineCount; i++){
- h += (i + 1).toString() + '
';
- }
- $('#linenos').html(h);
-};
-// Remove the line numbers
-haste.prototype.removeLineNumbers = function(){
- $('#linenos').html('>');
-};
-// Load a document and show it
-haste.prototype.loadDocument = function(key){
- // Split the key up
- let parts = key.split('.', 2);
- // Ask for what we want
- let _this = this;
- _this.doc = new haste_document();
- _this.doc.load(parts[0], function(ret){
- if (ret){
- _this.$code.html(ret.value);
- _this.setTitle(ret.key);
- _this.fullKey();
- _this.$textarea.val('').hide();
- _this.$box.show().focus();
- _this.addLineNumbers(ret.lineCount);
- }
- else {
- _this.newDocument();
- }
- }, this.lookupTypeByExtension(parts[1]));
-};
-// Duplicate the current document - only if locked
-haste.prototype.duplicateDocument = function(){
- if (this.doc.locked){
- let currentData = this.doc.data;
- this.newDocument();
- this.$textarea.val(currentData);
- }
-};
-// Lock the current document
-haste.prototype.lockDocument = function(){
- let _this = this;
- this.doc.save(this.$textarea.val(), function(err, ret){
- if (err){
- _this.showMessage(err.message, 'error');
- }
- else if (ret){
- _this.$code.html(ret.value);
- _this.setTitle(ret.key);
- let file = `/${ret.key}`;
- if (ret.language){
- file += `.${_this.lookupExtensionByType(ret.language)}`;
- }
- window.history.pushState(null, `${_this.appName}-${ret.key}`, file);
- _this.fullKey();
- _this.$textarea.val('').hide();
- _this.$box.show().focus();
- _this.addLineNumbers(ret.lineCount);
- }
- });
-};
-haste.prototype.configureButtons = function(){
- let _this = this;
- this.buttons = [
- {
- $where: $('#box2 .save'),
- label: 'Save',
- shortcutDescription: 'control + s',
- shortcut: function(evt){
- return evt.ctrlKey && (evt.keyCode == 83);
- },
- action: function(){
- if (_this.$textarea.val().replace(/^\s+|\s+$/g, '') != ''){
- _this.lockDocument();
- }
- }
- },
- {
- $where: $('#box2 .new'),
- label: 'New',
- shortcut: function(evt){
- return evt.ctrlKey && evt.keyCode == 78;
- },
- shortcutDescription: 'control + n',
- action: function(){
- _this.newDocument(!_this.doc.key);
- }
- },
- {
- $where: $('#box2 .duplicate'),
- label: 'Duplicate & Edit',
- shortcut: function(evt){
- return _this.doc.locked && evt.ctrlKey && evt.keyCode == 68;
- },
- shortcutDescription: 'control + d',
- action: function(){
- _this.duplicateDocument();
- }
- },
- {
- $where: $('#box2 .raw'),
- label: 'Just Text',
- shortcut: function(evt){
- return evt.ctrlKey && evt.shiftKey && evt.keyCode == 82;
- },
- shortcutDescription: 'control + shift + r',
- action: function(){
- window.location.href = `/raw/${_this.doc.key}`;
- }
- },
- {
- $where: $('#box2 .twitter'),
- label: 'Twitter',
- shortcut: function(evt){
- return _this.options.twitter && _this.doc.locked && evt.shiftKey && evt.ctrlKey && evt.keyCode == 84;
- },
- shortcutDescription: 'control + shift + t',
- action: function(){
- window.open(`https://twitter.com/share?url=${encodeURI(window.location.href)}`);
- }
- }
- ];
- for (const button of this.buttons){
- this.configureButton(button);
- }
-};
-haste.prototype.configureButton = function(options){
- // Handle the click action
- options.$where.click(function(evt){
- evt.preventDefault();
- if (!options.clickDisabled && $(this).hasClass('enabled')){
- options.action();
- }
- });
- // Show the label
- options.$where.mouseenter(function(){
- $('#box3 .label').text(options.label);
- $('#box3 .shortcut').text(options.shortcutDescription || '');
- $('#box3').show();
- $(this).append($('#pointer').remove().show());
- });
- // Hide the label
- options.$where.mouseleave(function(){
- $('#box3').hide();
- $('#pointer').hide();
- });
-};
-// Configure keyboard shortcuts for the textarea
-haste.prototype.configureShortcuts = function(){
- let _this = this;
- $(document.body).keydown(function(evt){
- for (const button of _this.buttons){
- if (button.shortcut && button.shortcut(evt)){
- evt.preventDefault();
- button.action();
- return;
- }
- }
- });
-};
///// Tab behavior in the textarea - 2 spaces per tab
$(function(){
diff --git a/static/index.html b/static/index.html
index ff41e78..11e230c 100644
--- a/static/index.html
+++ b/static/index.html
@@ -17,9 +17,9 @@
let app = null;
// Handle pops
let handlePop = function(evt){
- let path = evt.target.location.pathname;
- if (path == '/'){ app.newDocument(true); }
- else { app.loadDocument(path.substring(1, path.length)); }
+ let path = evt.target.location.href;
+ if (path == app.baseUrl) app.newDocument(true);
+ else app.loadDocument(path.split('/').slice(-1)[0]);
};
// Set up the pop state to handle loads, skipping the first load
// to make chrome behave like others:
@@ -31,7 +31,12 @@
}, 1000);
// Construct app and load initial path
$(function(){
- app = new haste('hastebin', { twitter: true });
+ let baseUrl = window.location.href.split('/');
+ console.log(baseUrl);
+ baseUrl = baseUrl.slice(0, baseUrl.length - 1).join('/') + '/';
+ console.log(baseUrl);
+ // baseUrl = 'https://plazatest.zneix.eu/haste/';
+ app = new haste('hastebin', { twitter: true, baseUrl: baseUrl });
handlePop({ target: window });
});
@@ -44,7 +49,7 @@