/* 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(/${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 (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, '/'); } this.setTitle(); this.lightKey(); this.$textarea.val('').show('fast', function(){ this.focus(); }); this.removeLineNumbers(); }; // Map of common extensions // Note: this list does not need to include anything that IS its extension, // due to the behavior of lookupTypeByExtension and lookupExtensionByType // Note: optimized for lookupTypeByExtension haste.extensionMap = { rb: 'ruby', py: 'python', pl: 'perl', php: 'php', scala: 'scala', go: 'go', xml: 'xml', html: 'xml', htm: 'xml', css: 'css', js: 'javascript', vbs: 'vbscript', lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec', vala: 'vala', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini', diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell', md: 'markdown', txt: '', coffee: 'coffee', json: 'javascript', 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 (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 (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(){ $('textarea').keydown(function(evt){ if (evt.keyCode == 9){ evt.preventDefault(); let myValue = ' '; // http://stackoverflow.com/questions/946534/insert-text-into-textarea-with-jquery // For browsers like Internet Explorer if (document.selection){ this.focus(); let sel = document.selection.createRange(); sel.text = myValue; this.focus(); } // Mozilla and Webkit else if (this.selectionStart || this.selectionStart == '0'){ let startPos = this.selectionStart; let endPos = this.selectionEnd; let scrollTop = this.scrollTop; this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos,this.value.length); this.focus(); this.selectionStart = startPos + myValue.length; this.selectionEnd = startPos + myValue.length; this.scrollTop = scrollTop; } else { this.value += myValue; this.focus(); } } }); });