diff --git a/lib/document_handler.js b/lib/document_handler.js index b65683c..0ecc773 100644 --- a/lib/document_handler.js +++ b/lib/document_handler.js @@ -55,7 +55,7 @@ DocumentHandler.prototype.handlePost = function(request, response) { if (!buffer) { response.writeHead(200, { 'content-type': 'application/json' }); } - buffer += JSON.parse(data.toString()).data; + buffer += data.toString(); if (_this.maxLength && buffer.length > _this.maxLength) { cancelled = true; winston.warn('document >maxLength', { maxLength: _this.maxLength }); diff --git a/static/application.css b/static/application.css index 1725140..55e9469 100644 --- a/static/application.css +++ b/static/application.css @@ -1,201 +1,168 @@ -html, body, div, pre, textarea, header, h1, a, nav, ul, li { - margin: 0; - padding: 0; -} - body { - font: 13px monospace; + background: #002B36; + padding: 20px 50px; + margin: 0px; } -header { - position: fixed; - top: 0; - right: 0; - z-index: 1000; +/* textarea */ + +textarea { + background: transparent; + border: 0px; + color: #fff; + padding: 0px; + width: 100%; + height: 100%; + font-family: monospace; + outline: none; + resize: none; + font-size: 13px; } -header h1 { - background: #00222b; - padding: 5px 22px; +/* the line numbers */ + +#linenos { + color: #7d7d7d; + z-index: -1000; + position: absolute; + top: 20px; + left: 0px; + width: 30px; /* 30 to get 20 away from box */ + font-size: 13px; + font-family: monospace; + text-align: right; } -header h1 a { - background: transparent url('logo.png') no-repeat top center; - display: block; - overflow: hidden; - text-indent: -9999px; - width: 126px; - height: 42px; +/* code box when locked */ + +#box { + padding: 0px; + margin: 0px; + width: 100%; + border: 0px; + outline: none; + font-size: 13px; } -header h1 a:hover { - background-position: bottom center; +#box code { + padding: 0px; + background: transparent !important; /* don't hide hastebox */ } -header ul { - background: #08323c; - font-size: 0; - list-style: none; - /*overflow: hidden;*/ - text-align: center; +/* key */ + +#key { + position: fixed; + top: 0px; + right: 0px; + z-index: +1000; /* watch out */ } -header ul li { - display: inline-block; - position: relative; +#box1 { + padding: 5px; + text-align: center; + background: #00222b; } -header ul li .pointer { - background: transparent url('hover-dropdown-tip.png') no-repeat; - display: inline-block; - text-align: center; - width: 10px; - height: 5px; +#box2 { + background: #08323c; + font-size: 0px; + padding: 0px 5px; } -header ul li a { - background: transparent url('function-icons.png'); - display: block; - overflow: hidden; - text-indent: -9999px; - width: 32px; - height: 37px; +#box1 a.logo, #box1 a.logo:visited { + display: inline-block; + background: url(logo.png); + width: 126px; + height: 42px; } -header ul li a.disabled { - cursor: default; +#box1 a.logo:hover { + background-position: 0 bottom; } -header li a.save { background-position: -5px center; } -header li a.save:hover { background-position: -5px bottom; } -header li a.save.disabled { background-position: -5px top; } - -header li a.new { background-position: -42px center; } -header li a.new:hover { background-position: -42px bottom; } -header li a.new.disabled { background-position: -42px top; } - -header li a.edit { background-position: -79px center; } -header li a.edit:hover { background-position: -79px bottom; } -header li a.edit.disabled { background-position: -79px top; } - -header li a.raw { background-position: -116px center; } -header li a.raw:hover { background-position: -116px bottom; } -header li a.raw.disabled { background-position: -116px top; } - -header li a.twitter { background-position: -153px center; } -header li a.twitter:hover { background-position: -153px bottom; } -header li a.twitter.disabled { background-position: -153px top; } - -#editor { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; +#box2 .function { + background: url(function-icons.png); + width: 32px; + height: 37px; + display: inline-block; + position: relative; } -.CodeMirror { - line-height: 1em; - height: 100%; +#box2 .link embed { + vertical-align: bottom; /* fix for zeroClipboard style */ } -.CodeMirror-scroll { - height: 100%; - overflow: auto; - position: relative; +#box2 .function.enabled:hover { + cursor: hand; + cursor: pointer; } -.CodeMirror-gutter { - height: 100%; - min-width: 2em; - position: absolute; - top: 0; - left: 0; +#pointer { + display: block; + height: 5px; + width: 10px; + background: url(hover-dropdown-tip.png); + bottom: 0px; + position: absolute; + margin: auto; + left: 0px; + right: 0px; } -.CodeMirror-gutter-text { - text-align: right; - padding: 0.4em 0.2em 0.4em 0.4em; - white-space: pre; +#box3, #messages li { + background: #173e48; + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 14px; + padding: 10px 15px; } -.CodeMirror-lines { - padding: 0.4em; +#box3 .label, #messages li { + color: #fff; + font-weight: bold; } -.CodeMirror textarea { - outline: 0; +#box3 .shortcut { + color: #c4dce3; + font-weight: normal; } -.CodeMirror pre.CodeMirror-cursor { - position: absolute; - visibility: hidden; - z-index: 10; +#box2 .function.save { background-position: -5px top; } +#box2 .function.enabled.save { background-position: -5px center; } +#box2 .function.enabled.save:hover { background-position: -5px bottom; } + +#box2 .function.new { background-position: -42px top; } +#box2 .function.enabled.new { background-position: -42px center; } +#box2 .function.enabled.new:hover { background-position: -42px bottom; } + +#box2 .function.duplicate { background-position: -79px top; } +#box2 .function.enabled.duplicate { background-position: -79px center; } +#box2 .function.enabled.duplicate:hover { background-position: -79px bottom; } + +#box2 .function.raw { background-position: -116px top; } +#box2 .function.enabled.raw { background-position: -116px center; } +#box2 .function.enabled.raw:hover { background-position: -116px bottom; } + +#box2 .function.twitter { background-position: -153px top; } +#box2 .function.enabled.twitter { background-position: -153px center; } +#box2 .function.enabled.twitter:hover { background-position: -153px bottom; } + +#messages { + position:fixed; + top:0px; + right:138px; + margin:0; + padding:0; + width:400px; } -.CodeMirror-focused pre.CodeMirror-cursor { - visibility: visible; +#messages li { + background:rgba(23,62,72,0.8); + margin:0 auto; + list-style:none; } -span.cm-header, span.cm-strong { - font-weight: bold; +#messages li.error { + background:rgba(102,8,0,0.8); } - -span.cm-em { - font-style: italic; -} - -span.cm-emstrong { - font-style: italic; font-weight: bold; -} - -span.cm-link { - text-decoration: underline; -} - -/* Solarized (dark) theme */ - -.cm-s-solarized-dark { - background: #002b36; - color: #839496; -} - -.cm-s-solarized-dark div.CodeMirror-selected { - background: #586e75; -} - -.cm-s-solarized-dark .CodeMirror-gutter { - background: #073642; -} - -.cm-s-solarized-dark .CodeMirror-gutter-text { - color: #586e75; -} - -.cm-s-solarized-dark .CodeMirror-cursor { - border-left: 1px solid #839496; -} - -.cm-s-solarized-dark span.cm-keyword { color: #268bd2; } -.cm-s-solarized-dark span.cm-atom { color: #b58900; } -.cm-s-solarized-dark span.cm-number { color: #2aa198; } -.cm-s-solarized-dark span.cm-def { color: #839496; } -.cm-s-solarized-dark span.cm-variable { color: #839496; } -.cm-s-solarized-dark span.cm-variable-2 { color: #b58900; } -.cm-s-solarized-dark span.cm-variable-3 { color: #268bd2; } -.cm-s-solarized-dark span.cm-property { color: #859900; } -.cm-s-solarized-dark span.cm-operator { color: #2aa198; } -.cm-s-solarized-dark span.cm-comment { color: #586e75; } -.cm-s-solarized-dark span.cm-string { color: #2aa198; } -.cm-s-solarized-dark span.cm-string-2 { color: #2aa198; } -.cm-s-solarized-dark span.cm-meta { color: #586e75; } -.cm-s-solarized-dark span.cm-error { color: #dc322f; } -.cm-s-solarized-dark span.cm-qualifier { color: #268bd2; } -.cm-s-solarized-dark span.cm-builtin { color: #b58900; } -.cm-s-solarized-dark span.cm-bracket { color: #dc322f; } -.cm-s-solarized-dark span.cm-tag { color: #268bd2; } -.cm-s-solarized-dark span.cm-attribute { color: #839496; } -.cm-s-solarized-dark span.cm-header { color: #cb4b16; } -.cm-s-solarized-dark span.cm-quote { color: #586e75; } -.cm-s-solarized-dark span.cm-hr { color: #cb4b16; } -.cm-s-solarized-dark span.cm-link { color: #6c71c4; } diff --git a/static/application.js b/static/application.js index 96e6753..7afb014 100644 --- a/static/application.js +++ b/static/application.js @@ -1,171 +1,396 @@ -window.Haste = { - Models: {}, - Views: {}, - Routers: {}, +///// represents a single document - extensionMap: { - clj: 'clojure', coffee: 'coffeescript', css: 'css', diff: 'diff', go: 'go', - hs: 'haskell', html: 'htmlmixed', js: 'javascript', lua: 'lua', - md: 'markdown', markdown: 'markdown', sql: 'mysql', pl: 'perl', php: 'php', - py: 'python', r: 'r', rb: 'ruby', scm: 'scheme', xml: 'xml', yml: 'yaml' - }, +var haste_document = function() { + this.locked = false; +}; - init: function() { - new Haste.Routers.Document(); - Backbone.history.start({ pushState: true }); +// 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); +}; - initialize: function() { - this.editor = new Haste.Views.EditorView(); - }, +// Show the light key +haste.prototype.lightKey = function() { + this.configureKey(['new', 'save']); +}; - show: function(id, extension) { - this.editor.load(id, extension); - }, +// Show the full key +haste.prototype.fullKey = function() { + this.configureKey(['new', 'duplicate', 'twitter', 'raw']); +}; - new: function() { - this.editor.new(); - } -}); - -Haste.Views.ActionsView = Backbone.View.extend({ - el: 'header', - - events: { - 'click .new': 'new', - 'click .save': 'save', - 'click .edit': 'edit', - 'click .raw': 'raw', - 'click .twitter': 'raw' - }, - - initialize: function() { - this.parent = this.options.parent; - }, - - toggleActions: function() { - var klass = 'disabled'; - - if (this.parent.model.isNew()) { - $('.save', this.el).removeClass(klass); - $('.edit, .raw, .twitter', this.el).addClass(klass); - } else { - $('.save', this.el).addClass(klass); - $('.edit, .raw, .twitter', this.el).removeClass(klass); - } - - this.setLink('.raw', 'raw/' + this.parent.model.id); - this.setLink('.twitter', 'https://twitter.com/share?url=' + encodeURI(window.location.href)); - }, - - setLink: function(el, href) { - if (this.parent.model.isNew()) { - href = '#'; - } - - $(el, this.el).attr('href', href); - }, - - new: function(event) { - event.preventDefault(); - this.parent.new(); - Backbone.history.navigate(''); - }, - - save: function(event) { - event.preventDefault(); - - if (!this.parent.model.isNew()) { return; } - - this.parent.save(); - }, - - edit: function(event) { - event.preventDefault(); - - if (this.parent.model.isNew()) { return; } - - this.parent.model.set('key', null); - Backbone.history.navigate('/'); - }, - - raw: function(event) { - if (this.model.isNew()) { - event.preventDefault(); - } - }, -}); - -Haste.Views.EditorView = Backbone.View.extend({ - el: 'textarea', - - initialize: function() { - this.codeMirror = CodeMirror.fromTextArea(this.el, { - mode: 'null', - lineNumbers: true, - theme: 'solarized-dark' - }); - - this.actionsView = new Haste.Views.ActionsView({ parent: this }); - }, - - render: function() { - this.codeMirror.setOption('mode', this.model.get('mode') || 'null'); - this.codeMirror.setValue(this.model.get('data') || ''); - - return this; - }, - - new: function() { - this.model = new Haste.Models.Document(); - - this.model.on('change', this.render, this); - this.model.on('change', this.toggleLock, this); - this.model.on('change', this.actionsView.toggleActions, this.actionsView); - - this.model.trigger('change'); - }, - - load: function(key, extension) { - this.new(); - - var mode = Haste.extensionMap[extension]; - this.model.set({ key: key, mode: mode }, { silent: true }); - - this.model.fetch(); - }, - - save: function() { - var data = this.codeMirror.getValue(); - - if (!data) { return; } - - this.model.save('data', data, { - success: function(model, response) { - Backbone.history.navigate(model.id); +// Set the key up for certain things to be enabled +haste.prototype.configureKey = function(enable) { + var $this, i = 0; + $('#box2 .function').each(function() { + $this = $(this); + for (i = 0; i < enable.length; i++) { + if ($this.hasClass(enable[i])) { + $this.addClass('enabled'); + return true; } - }); - }, + } + $this.removeClass('enabled'); + }); +}; - toggleLock: function() { - this.codeMirror.setOption('readOnly', !this.model.isNew()); - this.actionsView.toggleActions(); +// 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', cs: 'cs', 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' +}; + +// 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 (var 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) { + var h = ''; + for (var 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 + var parts = key.split('.', 2); + // Ask for what we want + var _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) { + var currentData = this.doc.data; + this.newDocument(); + this.$textarea.val(currentData); + } +}; + +// Lock the current document +haste.prototype.lockDocument = function() { + var _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); + var 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() { + var _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.ctrlKey && evt.keyCode == 84; + }, + shortcutDescription: 'control + t', + action: function() { + window.open('https://twitter.com/share?url=' + encodeURI(window.location.href)); + } + } + ]; + for (var i = 0; i < this.buttons.length; i++) { + this.configureButton(this.buttons[i]); + } +}; + +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(evt) { + $('#box3 .label').text(options.label); + $('#box3 .shortcut').text(options.shortcutDescription || ''); + $('#box3').show(); + $(this).append($('#pointer').remove().show()); + }); + // Hide the label + options.$where.mouseleave(function(evt) { + $('#box3').hide(); + $('#pointer').hide(); + }); +}; + +// Configure keyboard shortcuts for the textarea +haste.prototype.configureShortcuts = function() { + var _this = this; + $(document.body).keydown(function(evt) { + var button; + for (var i = 0 ; i < _this.buttons.length; i++) { + button = _this.buttons[i]; + if (button.shortcut && button.shortcut(evt)) { + evt.preventDefault(); + button.action(); + return; + } + } + }); +}; + +///// Tab behavior in the textarea - 2 spaces per tab $(function() { - Haste.init(); + + $('textarea').keydown(function(evt) { + if (evt.keyCode === 9) { + evt.preventDefault(); + var myValue = ' '; + // http://stackoverflow.com/questions/946534/insert-text-into-textarea-with-jquery + // For browsers like Internet Explorer + if (document.selection) { + this.focus(); + sel = document.selection.createRange(); + sel.text = myValue; + this.focus(); + } + // Mozilla and Webkit + else if (this.selectionStart || this.selectionStart == '0') { + var startPos = this.selectionStart; + var endPos = this.selectionEnd; + var 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(); + } + } + }); + }); diff --git a/static/application.min.js b/static/application.min.js index c361c48..5c5ce83 100644 --- a/static/application.min.js +++ b/static/application.min.js @@ -1 +1 @@ -window.Haste={Models:{},Views:{},Routers:{},extensionMap:{clj:"clojure",coffee:"coffeescript",css:"css",diff:"diff",go:"go",hs:"haskell",html:"htmlmixed",js:"javascript",lua:"lua",md:"markdown",markdown:"markdown",sql:"mysql",pl:"perl",php:"php",py:"python",r:"r",rb:"ruby",scm:"scheme",xml:"xml",yml:"yaml"},init:function(){new Haste.Routers.Document,Backbone.history.start({pushState:!0})}},Haste.Models.Document=Backbone.Model.extend({idAttribute:"key",urlRoot:"/documents"}),Haste.Routers.Document=Backbone.Router.extend({routes:{":id.:extension":"show",":id":"show","":"new"},initialize:function(){this.editor=new Haste.Views.EditorView},show:function(a,b){this.editor.load(a,b)},"new":function(){this.editor.new()}}),Haste.Views.ActionsView=Backbone.View.extend({el:"header",events:{"click .new":"new","click .save":"save","click .edit":"edit","click .raw":"raw","click .twitter":"raw"},initialize:function(){this.parent=this.options.parent},toggleActions:function(){var a="disabled";this.parent.model.isNew()?($(".save",this.el).removeClass(a),$(".edit, .raw, .twitter",this.el).addClass(a)):($(".save",this.el).addClass(a),$(".edit, .raw, .twitter",this.el).removeClass(a)),this.setLink(".raw","raw/"+this.parent.model.id),this.setLink(".twitter","https://twitter.com/share?url="+encodeURI(window.location.href))},setLink:function(a,b){this.parent.model.isNew()&&(b="#"),$(a,this.el).attr("href",b)},"new":function(a){a.preventDefault(),this.parent.new(),Backbone.history.navigate("")},save:function(a){a.preventDefault();if(!this.parent.model.isNew())return;this.parent.save()},edit:function(a){a.preventDefault();if(this.parent.model.isNew())return;this.parent.model.set("key",null),Backbone.history.navigate("/")},raw:function(a){this.model.isNew()&&a.preventDefault()}}),Haste.Views.EditorView=Backbone.View.extend({el:"textarea",initialize:function(){this.codeMirror=CodeMirror.fromTextArea(this.el,{mode:"null",lineNumbers:!0,theme:"solarized-dark"}),this.actionsView=new Haste.Views.ActionsView({parent:this})},render:function(){return this.codeMirror.setOption("mode",this.model.get("mode")||"null"),this.codeMirror.setValue(this.model.get("data")||""),this},"new":function(){this.model=new Haste.Models.Document,this.model.on("change",this.render,this),this.model.on("change",this.toggleLock,this),this.model.on("change",this.actionsView.toggleActions,this.actionsView),this.model.trigger("change")},load:function(a,b){this.new();var c=Haste.extensionMap[b];this.model.set({key:a,mode:c},{silent:!0}),this.model.fetch()},save:function(){var a=this.codeMirror.getValue();if(!a)return;this.model.save("data",a,{success:function(a,b){Backbone.history.navigate(a.id)}})},toggleLock:function(){this.codeMirror.setOption("readOnly",!this.model.isNew()),this.actionsView.toggleActions()}}),$(function(){Haste.init()}) \ No newline at end of file +var haste_document=function(){this.locked=!1};haste_document.prototype.htmlEscape=function(a){return a.replace(/&/g,"&").replace(/>/g,">").replace(/'+a+"");$("#messages").prepend(c),setTimeout(function(){c.slideUp("fast",function(){$(this).remove()})},3e3)},haste.prototype.lightKey=function(){this.configureKey(["new","save"])},haste.prototype.fullKey=function(){this.configureKey(["new","duplicate","twitter","raw"])},haste.prototype.configureKey=function(a){var b,c=0;$("#box2 .function").each(function(){b=$(this);for(c=0;c";$("#linenos").html(b)},haste.prototype.removeLineNumbers=function(){$("#linenos").html(">")},haste.prototype.loadDocument=function(a){var b=a.split(".",2),c=this;c.doc=new haste_document,c.doc.load(b[0],function(a){a?(c.$code.html(a.value),c.setTitle(a.key),c.fullKey(),c.$textarea.val("").hide(),c.$box.show().focus(),c.addLineNumbers(a.lineCount)):c.newDocument()},this.lookupTypeByExtension(b[1]))},haste.prototype.duplicateDocument=function(){if(this.doc.locked){var a=this.doc.data;this.newDocument(),this.$textarea.val(a)}},haste.prototype.lockDocument=function(){var a=this;this.doc.save(this.$textarea.val(),function(b,c){if(b)a.showMessage(b.message,"error");else if(c){a.$code.html(c.value),a.setTitle(c.key);var d="/"+c.key;c.language&&(d+="."+a.lookupExtensionByType(c.language)),window.history.pushState(null,a.appName+"-"+c.key,d),a.fullKey(),a.$textarea.val("").hide(),a.$box.show().focus(),a.addLineNumbers(c.lineCount)}})},haste.prototype.configureButtons=function(){var a=this;this.buttons=[{$where:$("#box2 .save"),label:"Save",shortcutDescription:"control + s",shortcut:function(a){return a.ctrlKey&&a.keyCode===83},action:function(){a.$textarea.val().replace(/^\s+|\s+$/g,"")!==""&&a.lockDocument()}},{$where:$("#box2 .new"),label:"New",shortcut:function(a){return a.ctrlKey&&a.keyCode===78},shortcutDescription:"control + n",action:function(){a.newDocument(!a.doc.key)}},{$where:$("#box2 .duplicate"),label:"Duplicate & Edit",shortcut:function(b){return a.doc.locked&&b.ctrlKey&&b.keyCode===68},shortcutDescription:"control + d",action:function(){a.duplicateDocument()}},{$where:$("#box2 .raw"),label:"Just Text",shortcut:function(a){return a.ctrlKey&&a.shiftKey&&a.keyCode===82},shortcutDescription:"control + shift + r",action:function(){window.location.href="/raw/"+a.doc.key}},{$where:$("#box2 .twitter"),label:"Twitter",shortcut:function(b){return a.options.twitter&&a.doc.locked&&b.ctrlKey&&b.keyCode==84},shortcutDescription:"control + t",action:function(){window.open("https://twitter.com/share?url="+encodeURI(window.location.href))}}];for(var b=0;b=b))this.iframe=h('