Commit 13bb094f authored by John Crepezzi's avatar John Crepezzi
Browse files

Revert "Refactor frontend"

This reverts commit 1950cc8d.
parent b43a55ff
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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 });
+119 −152
Original line number Diff line number Diff line
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 */

header h1 {
  background: #00222b;
  padding: 5px 22px;
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 a {
  background: transparent url('logo.png') no-repeat top center;
  display: block;
  overflow: hidden;
  text-indent: -9999px;
  width: 126px;
  height: 42px;
}
/* the line numbers */

header h1 a:hover {
  background-position: bottom center;
#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 ul {
  background: #08323c;
  font-size: 0;
  list-style: none;
  /*overflow: hidden;*/
  text-align: center;
}
/* code box when locked */

header ul li {
  display: inline-block;
  position: relative;
#box {
	padding: 0px;
	margin: 0px;
	width: 100%;
	border: 0px;
	outline: none;
	font-size: 13px;
}

header ul li .pointer {
  background: transparent url('hover-dropdown-tip.png') no-repeat;
  display: inline-block;
  text-align: center;
  width: 10px;
  height: 5px;
#box code {
	padding: 0px;
	background: transparent !important; /* don't hide hastebox */
}

header ul li a {
  background: transparent url('function-icons.png');
  display: block;
  overflow: hidden;
  text-indent: -9999px;
  width: 32px;
  height: 37px;
}
/* key */

header ul li a.disabled {
  cursor: default;
#key {
	position: fixed;
	top: 0px;
	right: 0px;
	z-index: +1000; /* watch out */
}

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;
#box1 {
	padding: 5px;
	text-align: center;
	background: #00222b;
}

.CodeMirror {
  line-height: 1em;
  height: 100%;
#box2 {
	background: #08323c;
	font-size: 0px;
	padding: 0px 5px;
}

.CodeMirror-scroll {
  height: 100%;
  overflow: auto;
  position: relative;
#box1 a.logo, #box1 a.logo:visited {
	display: inline-block;
	background: url(logo.png);
	width: 126px;
	height: 42px;
}

.CodeMirror-gutter {
  height: 100%;
  min-width: 2em;
  position: absolute;
  top: 0;
  left: 0;
#box1 a.logo:hover {
	background-position: 0 bottom;
}

.CodeMirror-gutter-text {
  text-align: right;
  padding: 0.4em 0.2em 0.4em 0.4em;
  white-space: pre;
#box2 .function {
	background: url(function-icons.png);
	width: 32px;
	height: 37px;
	display: inline-block;
	position: relative;
}

.CodeMirror-lines {
  padding: 0.4em;
#box2 .link embed {
	vertical-align: bottom; /* fix for zeroClipboard style */
}

.CodeMirror textarea {
  outline: 0;
#box2 .function.enabled:hover {
	cursor: hand;
	cursor: pointer;
}

.CodeMirror pre.CodeMirror-cursor {
#pointer {
	display: block;
	height: 5px;
	width: 10px;
	background: url(hover-dropdown-tip.png);
	bottom: 0px;
	position: absolute;
  visibility: hidden;
  z-index: 10;
	margin: auto;
	left: 0px;
	right: 0px;
}

.CodeMirror-focused pre.CodeMirror-cursor {
  visibility: visible;
#box3, #messages li {
	background: #173e48;
	font-family: Helvetica, sans-serif;
	font-size: 12px;
	line-height: 14px;
	padding: 10px 15px;
}

span.cm-header, span.cm-strong {
#box3 .label, #messages li {
	color: #fff;
	font-weight: bold;
}

span.cm-em {
  font-style: italic;
#box3 .shortcut {
	color: #c4dce3;
	font-weight: normal;
}

span.cm-emstrong {
  font-style: italic; font-weight: bold;
}
#box2 .function.save { background-position: -5px top; }
#box2 .function.enabled.save { background-position: -5px center; }
#box2 .function.enabled.save:hover { background-position: -5px bottom; }

span.cm-link {
  text-decoration: underline;
}
#box2 .function.new { background-position: -42px top; }
#box2 .function.enabled.new { background-position: -42px center; }
#box2 .function.enabled.new:hover { background-position: -42px bottom; }

/* Solarized (dark) theme */
#box2 .function.duplicate { background-position: -79px top; }
#box2 .function.enabled.duplicate { background-position: -79px center; }
#box2 .function.enabled.duplicate:hover { background-position: -79px bottom; }

.cm-s-solarized-dark {
  background: #002b36;
  color: #839496;
}
#box2 .function.raw { background-position: -116px top; }
#box2 .function.enabled.raw { background-position: -116px center; }
#box2 .function.enabled.raw:hover { background-position: -116px bottom; }

.cm-s-solarized-dark div.CodeMirror-selected {
  background: #586e75;
}
#box2 .function.twitter { background-position: -153px top; }
#box2 .function.enabled.twitter { background-position: -153px center; }
#box2 .function.enabled.twitter:hover { background-position: -153px bottom; }

.cm-s-solarized-dark .CodeMirror-gutter {
  background: #073642;
#messages {
	position:fixed;
	top:0px;
	right:138px;
	margin:0;
	padding:0;
	width:400px;
}

.cm-s-solarized-dark .CodeMirror-gutter-text {
  color: #586e75;
#messages li {
	background:rgba(23,62,72,0.8);
	margin:0 auto;
	list-style:none;
}

.cm-s-solarized-dark .CodeMirror-cursor {
  border-left: 1px solid #839496;
#messages li.error {
	background:rgba(102,8,0,0.8);
}

.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; }
+366 −141
Original line number Diff line number Diff line
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'
  },
///// represents a single document

  init: function() {
    new Haste.Routers.Document();
    Backbone.history.start({ pushState: true });
  }
var haste_document = function() {
  this.locked = false;
};

Haste.Models.Document = Backbone.Model.extend({
  idAttribute: 'key',
  urlRoot: '/documents'
});

Haste.Routers.Document = Backbone.Router.extend({
  routes: {
    ':id.:extension': 'show',
    ':id': 'show',
    '': 'new'
  },
// Escapes HTML tag characters
haste_document.prototype.htmlEscape = function(s) {
  return s
    .replace(/&/g, '&')
    .replace(/>/g, '>')
    .replace(/</g, '&lt;')
    .replace(/"/g, '&quot;');
};

  initialize: function() {
    this.editor = new Haste.Views.EditorView();
// Get this document from the server and lock it here
haste_document.prototype.load = function(key, callback, lang) {
  var _this = this;
  $.ajax('/documents/' + key, {
    type: 'get',
    dataType: 'json',
    success: function(res) {
      _this.locked = true;
      _this.key = key;
      _this.data = res.data;
      try {
        var high;
        if (lang === 'txt') {
          high = { value: _this.htmlEscape(res.data) };
        }
        else if (lang) {
          high = hljs.highlight(lang, res.data);
        }
        else {
          high = hljs.highlightAuto(res.data);
        }
      } catch(err) {
        // failed highlight, fall back on auto
        high = hljs.highlightAuto(res.data);
      }
      callback({
        value: high.value,
        key: key,
        language: high.language || lang,
        lineCount: res.data.split("\n").length
      });
    },
    error: function(err) {
      callback(false);
    }
  });
};

  show: function(id, extension) {
    this.editor.load(id, extension);
// Save this document to the server and lock it here
haste_document.prototype.save = function(data, callback) {
  if (this.locked) {
    return false;
  }
  this.data = data;
  var _this = this;
  $.ajax('/documents', {
    type: 'post',
    data: data,
    dataType: 'json',
    contentType: 'application/json; charset=utf-8',
    success: function(res) {
      _this.locked = true;
      _this.key = res.key;
      var high = hljs.highlightAuto(data);
      callback(null, {
        value: high.value,
        key: res.key,
        language: high.language,
        lineCount: data.split("\n").length
      });
    },

  new: function() {
    this.editor.new();
    error: function(res) {
      try {
        callback($.parseJSON(res.responseText));
      }
      catch (e) {
        callback({message: 'Something went wrong!'});
      }
    }
  });
};

Haste.Views.ActionsView = Backbone.View.extend({
  el: 'header',

  events: {
    'click .new': 'new',
    'click .save': 'save',
    'click .edit': 'edit',
    'click .raw': 'raw',
    'click .twitter': 'raw'
  },
///// represents the paste application

var haste = function(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();
  }
};

  initialize: function() {
    this.parent = this.options.parent;
  },
// Set the page title - include the appName
haste.prototype.setTitle = function(ext) {
  var title = ext ? this.appName + ' - ' + ext : this.appName;
  document.title = title;
};

  toggleActions: function() {
    var klass = 'disabled';
// Show a message box
haste.prototype.showMessage = function(msg, cls) {
  var msgBox = $('<li class="'+(cls || 'info')+'">'+msg+'</li>');
  $('#messages').prepend(msgBox);
  setTimeout(function() {
    msgBox.slideUp('fast', function() { $(this).remove(); });
  }, 3000);
};

    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);
    }
// Show the light key
haste.prototype.lightKey = function() {
  this.configureKey(['new', 'save']);
};

    this.setLink('.raw', 'raw/' + this.parent.model.id);
    this.setLink('.twitter', 'https://twitter.com/share?url=' + encodeURI(window.location.href));
  },
// Show the full key
haste.prototype.fullKey = function() {
  this.configureKey(['new', 'duplicate', 'twitter', 'raw']);
};

  setLink: function(el, href) {
    if (this.parent.model.isNew()) {
      href = '#';
// 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');
  });
};

    $(el, this.el).attr('href', href);
  },

  new: function(event) {
    event.preventDefault();
    this.parent.new();
    Backbone.history.navigate('');
  },

  save: function(event) {
    event.preventDefault();
// 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();
};

    if (!this.parent.model.isNew()) { return; }
// 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'
};

    this.parent.save();
  },
// 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;
};

  edit: function(event) {
    event.preventDefault();
// 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;
};

    if (this.parent.model.isNew()) { return; }
// 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() + '<br/>';
  }
  $('#linenos').html(h);
};

    this.parent.model.set('key', null);
    Backbone.history.navigate('/');
  },
// Remove the line numbers
haste.prototype.removeLineNumbers = function() {
  $('#linenos').html('&gt;');
};

  raw: function(event) {
    if (this.model.isNew()) {
      event.preventDefault();
// 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]));
};

Haste.Views.EditorView = Backbone.View.extend({
  el: 'textarea',
// 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);
  }
};

  initialize: function() {
    this.codeMirror = CodeMirror.fromTextArea(this.el, {
      mode: 'null',
      lineNumbers: true,
      theme: 'solarized-dark'
// 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);
    }
  });
};

    this.actionsView = new Haste.Views.ActionsView({ parent: this });
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);
      },

  render: function() {
    this.codeMirror.setOption('mode', this.model.get('mode') || 'null');
    this.codeMirror.setValue(this.model.get('data') || '');

    return this;
      action: function() {
        if (_this.$textarea.val().replace(/^\s+|\s+$/g, '') !== '') {
          _this.lockDocument();
        }
      }
    },

  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');
    {
      $where: $('#box2 .new'),
      label: 'New',
      shortcut: function(evt) {
        return evt.ctrlKey && evt.keyCode === 78  
      },

  load: function(key, extension) {
    this.new();

    var mode = Haste.extensionMap[extension];
    this.model.set({ key: key, mode: mode }, { silent: true });

    this.model.fetch();
      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]);
  }
};

  save: function() {
    var data = this.codeMirror.getValue();

    if (!data) { return; }

    this.model.save('data', data, {
      success: function(model, response) {
        Backbone.history.navigate(model.id);
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();
  });
};

  toggleLock: function() {
    this.codeMirror.setOption('readOnly', !this.model.isNew());
    this.actionsView.toggleActions();
// 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();
      }
    }
  });

});
+1 −1

File changed.

Preview size limit exceeded, changes collapsed.

static/backbone.min.js

deleted100644 → 0
+0 −37

File deleted.

Preview size limit exceeded, changes collapsed.

Loading