text: // Confirm jQuery/Ui is present.  If not give a friendly message.
  //if (!jQuery) { throw "jQuery is required to be loaded before markedit can be used."; }
  //if (!jQuery.ui) { throw "jQuery UI is required to be loaded before markedit can be used."; }
  
  (function($) {
  
      //
      //  Language defaults (en-US)
      //
      var MarkEditLanguageDefaults = function() {
  
          return {
  
              'defaultButtons': {
                  'bold': {
                      'tip': 'Bold'
                  },
                  'italic': {
                      'tip': 'Italic'
                  },
                  'underline': {
                      'tip': 'Underline'
                  },
                  'strike': {
                      'tip': 'Strike'
                  },
                  'link': {
                      'tip': 'Insert Hyperlink'
                  },
                  'image': {
                      'tip': 'Insert Image'
                  },
                  'code': {
                      'tip': 'Code Sample'
                  },
                  'quote': {
                      'tip': 'Blockquote'
                  },
                  'numberlist': {
                      'tip': 'Numered List'
                  },
                  'bulletlist': {
                      'tip': 'Bullet List'
                  },
                  'line': {
                      'tip': 'Horizontal Line'
                  },
                  'heading': {
                      'tip': 'Heading'
                  },
                  'undo': {
                      'tip': 'Undo'
                  },
                  'redo': {
                      'tip': 'Redo'
                  },
                  'edit': {
                      'text': 'Compose',
                      'tip': 'View in Edit Mode'
                  },
                  'preview': {
                      'text': 'Preview',
                      'tip': 'View in Preview Mode'
                  }
              },
  
              'dialog': {
                  'insertLink': {
                      'title': 'Insert Link',
                      'helpText': 'Enter the URL to be inserted.',
                      'insertButton': 'Insert',
                      'cancelButton': 'Cancel'
                  },
                  'insertImage': {
                      'title':'Insert Image',
                      'helpText': 'Enter the URL of the image to be inserted.',
                      'insertButton': 'Insert',
                      'cancelButton': 'Cancel'
                  }
              },
  
              'errors' : {
                  'markeditNotTextarea':'MarkEdit tag must be a <textarea>',
                  'cannotLocateTextarea':'<textarea> tag could not be located in order to fetch the markeditGetState.'
              },
  
              'markup' : {
                  'boldStart': '__',
                  'boldEnd': '__',
                  'italicStart': "''",
                  'italicEnd': "''",
                  'underlineStart': '++',
                  'underlineEnd': '++',
                  'strikeStart': '--',
                  'strikeEnd': '--',
                  'inlineCodeStart': '@@',
                  'inlineCodeEnd': '@@',
                  'imageLinkStart': '[',
                  'imageLinkEnd': ']',
                  'linkStart': '[',
                  'linkMiddle': ' ',
                  'linkEnd': ']',
                  'bulletPrefix': '* '
              },
  
              'markup2' : {
                  'boldStart': '**',
                  'boldEnd': '**',
                  'italicStart': "_",
                  'italicEnd': "_",
                  'underlineStart': '',
                  'underlineEnd': '',
                  'strikeStart': '',
                  'strikeEnd': '',
                  'inlineCodeStart': '`',
                  'inlineCodeEnd': '`',
                  'imageLinkStart': '![',
                  'imageLinkEnd': ']',
                  'linkStart': '[',
                  'linkMiddle': '][',
                  'linkEnd': ']',
                  'bulletPrefix': ' - '
              },
              'markups': {
                  'creole': {
                      'boldStart': '**',
                      'boldEnd': '**',
                      'italicStart': "//",
                      'italicEnd': "//",
                      'underlineStart': '++',
                      'underlineEnd': '++',
                      'strikeStart': '--',
                      'strikeEnd': '--',
                      'inlineCodeStart': '@@',
                      'inlineCodeEnd': '@@',
                      'imageLinkStart': '[',
                      'imageLinkEnd': ']',
                      'linkStart': '[',
                      'linkMiddle': ' ',
                      'linkEnd': ']',
                      'bulletPrefix': '* ',
                      'quotePrefix': '> ',
                      'makeLinkList': false
                  },
                  'dotclear': {
                      'boldStart': '__',
                      'boldEnd': '__',
                      'italicStart': "//",
                      'italicEnd': "//",
                      'underlineStart': "++",
                      'underlineEnd': "++",
                      'strikeStart': '--',
                      'strikeEnd': '--',
                      'inlineCodeStart': '@@',
                      'inlineCodeEnd': '@@',
                      'imageLinkStart': '((',
                      'imageLinkEnd': '))',
                      'linkStart': '[[',
                      'linkMiddle': ' ',
                      'linkEnd': ']]',
                      'bulletPrefix': '* ',
                      'quotePrefix': '> ',
                      'makeLinkList': false
                  },
                  'mediawiki': {
                      'boldStart': "'''",
                      'boldEnd': "'''",
                      'italicStart': "''",
                      'italicEnd': "''",
                      'underlineStart': '<u>',
                      'underlineEnd': '</u>',
                      'strikeStart': '<s>',
                      'strikeEnd': '</s>',
                      'inlineCodeStart': '@@',
                      'inlineCodeEnd': '@@',
                      'imageLinkStart': '[[Image:',
                      'imageLinkEnd': ']]',
                      'linkStart': '[[',
                      'linkMiddle': ' ',
                      'linkEnd': ']]',
                      'bulletPrefix': '* ',
                      'quotePrefix': '> ',
                      'makeLinkList': false
                  },
  
              }
          };
  
      };
  
  
      // Initialize Language object
      // This object can be loaded externally by another script to provide
      if (typeof(MarkEditLanguage) === 'undefined') {
          MarkEditLanguage = MarkEditLanguageDefaults();
      }
      else {
          MarkEditLanguage = $.extend({}, MarkEditLanguageDefaults(), MarkEditLanguage);
      }
  
      // Declare history object; Might use it later.
      var MarkEditHistory = null;
      var MarkEditIndex = 0;
  
      //
      //  $.markeditSetSyntax
      //
      $.fn.markeditSetSyntax = function(syntax) {
          syntax = syntax.toLowerCase();
          MarkEditLanguage.markup = MarkEditLanguage.markups[syntax];
      };
  
     /*
          Button Definition
          {
              'id' : String,              -- Used to identify the button
              'css' : String,             -- CSS class(es) to add to the button
              'tip' : String,             -- Mouse-over tool tip to be shown (html Title)
              'click' : function,         -- Function to be executed on Click event
              'mouseover' : function,     -- Function to be executed on Click event
              'mouseout' : function       -- Function to be executed on Click event
          }
      */
  
  
      //
      //  $.markeditToolbar
      //
      $.fn.markeditToolbar = function(config) {
          $(this).each(function() {
              var options = $.extend({}, $.fn.markeditToolbar.defaults, config);
              MarkEditToolbar.createToolbar(this, options);
          });
          return this;
      };
  
  
      //
      //  $.markeditToolbar.defaults
      //
      $.fn.markeditToolbar.defaults = {
          'backgroundMode': 'light',
          'layout': 'bold italic | link quote code image | numberlist bulletlist heading line',
          'buttons' : []           // Uses button definition (see above)
      };
  
  
      //
      //  $.markeditAddGroup
      //
      $.fn.markeditAddGroup = function(toggleGroup, cssClass) {
  
          var groups = [];
  
          $(this).each(function() {
              var css = 'toolbar-group';
  
              // Add css classes based on config
              if (typeof(cssClass) !== 'undefined'){
                  css += ' ' + cssClass;
              }
              if (toggleGroup) {
                  css += ' toggle-group';
              }
  
              // Create markup
              groups[groups.length] = $('<div class="' + css + '"></div>').appendTo(this);
          });
  
          if (groups.length > 1) {
              return groups;
          }
          else {
              return groups[0];
          }
      };
  
  
      //
      //  $.markeditAddButton
      //
      $.fn.markeditAddButton = function(button) {
  
          var tags = [];
  
          $(this).each(function() {
  
              var tag = $('<button type="button">&nbsp;</button>').appendTo(this);
  
              // Set attributes
              if (typeof(button.text) !== 'undefined') {
                  tag.text(button.text);
              } else {
                  tag.addClass('icon');
              }
  
              if (typeof(button.tip) !== 'undefined') {
                  tag.attr('title', button.tip);
              }
  
              tag.addClass('ui-state-default');
              tag.addClass('ui-corner-all');
              tag.addClass(button.id);
  
              if (typeof(button.toggle) !== 'undefined') {
                  if (button.toggle === true) {
                      tag.addClass('toggle');
                  }
              }
  
              if (typeof(button.css) !== 'undefined') {
                  tag.addClass(button.css);
              }
  
              // Bind events for new button
              // Click
              if (tag.hasClass('toggle')) {
                  // If toggle, call click only if inactive and update visual
                  tag.bind('click', function(){
                     if ($(this).hasClass('ui-state-active') === false) {
                          button.click();
  
                          // Drop active state from others
                          $(this).parent().children().each(function(){
                              $(this).removeClass('ui-state-active');
                          });
                          // Set active state on current button
                          $(this).addClass('ui-state-active');
                     }
                  });
              }
              else {
                  tag.bind('click', function(){
                     button.click();
                  });
              }
  
              // Mouse Over
              tag.bind('mouseover', function(){
                  tag.addClass("ui-state-hover");
                  if (typeof(button.mouseover) !== 'undefined') {
                      button.mouseover();
                  }
              });
  
              // Mouse Out
              tag.bind('mouseout', function(){
                  tag.removeClass("ui-state-hover");
                  if (typeof(button.mouseout) !== 'undefined') {
                      button.mouseout();
                  }
              });
  
              tags[tags.length] = tag;
  
          });
  
          if (tags.length > 1) {
              return tags;
          }
          else {
              return tags[0];
          }
  
      };
  
  
      //
      //  $.markedit  (THE BIG ENCHILLADA)
      //
      $.fn.markedit = function(config) {
  
          return $(this).each(function() {
  
              var options = $.extend({}, $.fn.markedit.defaults, config);
              options.toolbar = $.extend({}, $.fn.markeditToolbar.defaults, options.toolbar);
  
              if (this.tagName !== 'TEXTAREA') { throw MarkEditLanguage.errors.markeditNotTextarea; }
  
              // If no id, generate a unique one
              if (typeof($(this).attr('id')) === 'undefined') {
                  var uniqueId = 'markedit-' + String(++MarkEditIndex);
                  $(this).attr('id', uniqueId);
              }
  
              // Initailize History object if it's enabled
              if ( options.history ) {
                  // Initializing the History object will let the rest of MarkEdit know
                  if (MarkEditHistory === null) {
                      MarkEditHistory = {};
                  }
  
                  // Add an entry for this instance of MarkEdit
                  MarkEditHistory[$(this).attr('id')] = { 'undo': [], 'redo': [] };
  
                  // Set current state as first history item
                  MarkEdit.appendHistory($(this).markeditGetState(), $(this).attr('id'));
  
                  // Bind events to update history
                  var textarea = $(this);
                  $(this).keyup(function() {
                      MarkEdit.appendHistory($(textarea).markeditGetState(), $(textarea).attr('id'));
                  });
              }
  
              // Wrap our whole widget in a <div>
              $(this).wrap('<div class="markedit"></div>');
              var parent = $(this).parent();
  
              // Fire preload event
              options.preload(parent.get(0));
  
              if (options.syntax != null) {
                  $(this).markeditSetSyntax(options.syntax);
              }
              // Adjust toolbar based on options
              if (options.history && options.addHistoryButtons) {
                  options.toolbar.layout += ' | undo redo';
              }
              if (options.preview === 'toolbar') {
                  options.toolbar.layout += ' | (edit preview)';
              }
  
              // Create toolbar
              var toolbar = $(parent).markeditToolbar(options.toolbar, options.foregroundMode);
  
              // Create preview pane
              if (options.preview !== false) {
                  var previewPane = $('<div class="markedit-preview ui-widget-content"></div>');
  
                  // Set initial state for preview if enabled (now that it's created)
                  if (options.preview === 'toolbar') {
                      $(parent).append(previewPane);
  
                      var editButton = $(toolbar).children().find('button.edit');
                      editButton.addClass('ui-state-active');
                      editButton.parent().addClass('preview');
  
                      // Hide preview initially
                      $(previewPane).addClass('toggle-preview');
                      $(previewPane).attr('style', 'display:none');
  
                  }
                  else if (options.preview === 'bottom' || options.preview === 'below') {
  
                      $(parent).append(previewPane);
                      $(previewPane).addClass('bottom-preview');
                      $(this).markeditBindAutoPreview(previewPane);
  
                  }
                  else if (options.preview === 'top' || options.preview === 'above') {
  
                      $(parent).prepend(previewPane);
                      $(previewPane).addClass('top-preview');
                      $(this).markeditBindAutoPreview(previewPane);
  
                  }
                  else if (options.preview !== false) {
                      throw "Preview option '" + options.preview + "' is not recognized.";
                  }
              }
  
              // Fire postload event
              options.postload();
          });
      };
  
  
      //
      //  $.markedit.defaults
      //
      $.fn.markedit.defaults = {
  
          // features
          'preview': 'toolbar',       // Possible values:  toolbar, bottom || below, above || top, false
          'history': true,
  
          // markup
          'newlineToBr': true,
          'noPWrap': true,
  
          // functions
          'addHistoryButtons': true,
          'toolbar': $(this).markeditToolbar.defaults,
  
          // events
          'preload': function(){},
          'postload': function(){}
  
      };
  
  
      //
      //  $.markeditGetState
      //
      $.fn.markeditGetState = function() {
  
          //  GetState will return the following state object:
          //  state {
          //      'beforeSelect': String,
          //      'select': String,
          //      'afterSelect': String,
          //      'links': [
          //          { Number, String },
          //      ],
          //  }
  
          // Locate textarea
          var textarea = MarkEdit.getTextArea(this);
          textarea.focus();
  
          // Get IE selection (of course IE would take 5x the amount of code)
          var r;
          if (document.selection) {
              var range = document.selection.createRange();
              if (range !== null) {
  
                  // Get original text/selected text (as MS ranges) and parse
                  var textareaRange = textarea.createTextRange();
                  var beforeWithSelRange = textarea.createTextRange();
                  r = MarkEdit.getMarkdownLinks(textareaRange.text);
  
                  // Shift to area that's actually selected (using the document selection)
                  textareaRange.moveToBookmark(range.getBookmark());
                  beforeWithSelRange.setEndPoint('EndToEnd', textareaRange);
  
                  // Infer the pieces based on what IE gives us
                  var before = beforeWithSelRange.text;
                  before = before.substr(0, before.length - textareaRange.text.length);
                  var after = r.remainder.substr(before.length + textareaRange.text.length);
  
                  // Standardize line breaks
                  before = before.replace(/\r/g, '');
                  select = textareaRange.text.replace(/\r/g, '');
                  after = after.replace(/\r/g, '');
  
                  return {
                      'beforeSelect': before,
                      'select': select,
                      'afterSelect': after,
                      'links': r.links
                  };
              }
              else {
                  textarea.selectionStart = 0;
                  textarea.selectionEnd = 0;
              }
          }
  
          r = MarkEdit.getMarkdownLinks($(textarea).val());
          return {
              'beforeSelect': r.remainder.substr(0, textarea.selectionStart),
              'select': r.remainder.substr(textarea.selectionStart, (textarea.selectionEnd - textarea.selectionStart)),
              'afterSelect': r.remainder.substr(textarea.selectionEnd),
              'links': r.links
          };
  
      };
  
  
      //
      //  $.markeditSetState
      //
      $.fn.markeditSetState = function(state) {
  
          // Locate textarea
          var textarea = MarkEdit.getTextArea(this);
  
          MarkEdit.setState(textarea, state);
          MarkEdit.appendHistory(state, $(textarea).attr('id'));
  
      };
  
  
      //
      //  $.markeditUndo
      //
      $.fn.markeditUndo = function() {
  
          // Locate textarea
          var textarea = MarkEdit.getTextArea(this);
          var id = $(textarea).attr('id');
  
          if (MarkEditHistory !== null) {
              if (typeof(MarkEditHistory[id]) !== 'undefined') {
  
                  var undo = MarkEditHistory[id].undo;
                  var redo = MarkEditHistory[id].redo;
                  if (undo.length > 1) {
                      redo.push(undo[undo.length-1]);
                      undo.pop();
                      var state = undo[undo.length-1];
                      MarkEdit.setState(textarea, state);
                  }
  
              }
          }
      };
  
  
      //
      //  $.markeditRedo
      //
      $.fn.markeditRedo = function() {
  
          // Locate textarea
          var textarea = MarkEdit.getTextArea(this);
          var id = $(textarea).attr('id');
  
          if (MarkEditHistory !== null) {
              if (typeof(MarkEditHistory[id]) !== 'undefined') {
  
                  var undo = MarkEditHistory[id].undo;
                  var redo = MarkEditHistory[id].redo;
                  if (redo.length > 0) {
                      undo.push(redo[redo.length-1]);
                      redo.pop();
                      MarkEdit.setState(textarea, undo[undo.length-1]);
                  }
  
              }
          }
      };
  
  
      //
      //  $.markeditSetBold
      //
      $.fn.markeditSetBold = function(){
          var state = $(this).markeditGetState();
  
          state = MarkEdit.stateTrim(state);
          state.select = MarkEditLanguage.markup.boldStart + state.select + MarkEditLanguage.markup.boldEnd;
  
          $(this).markeditSetState(state);
          return this;
      };
  
  
      //
      //  $.markeditSetItalic
      //
      $.fn.markeditSetItalic = function(){
          var state = $(this).markeditGetState();
  
          state = MarkEdit.stateTrim(state);
          state.select = MarkEditLanguage.markup.italicStart + state.select + MarkEditLanguage.markup.italicEnd;
  
          $(this).markeditSetState(state);
          return this;
      };
  
      //
      //  $.markeditSetUnderline
      //
      $.fn.markeditSetUnderline = function(){
          var state = $(this).markeditGetState();
  
          state = MarkEdit.stateTrim(state);
          state.select = MarkEditLanguage.markup.underlineStart + state.select + MarkEditLanguage.markup.underlineEnd;
  
          $(this).markeditSetState(state);
          return this;
      };
  
  
      //
      //  $.markeditSetStrike
      //
      $.fn.markeditSetStrike = function(){
          var state = $(this).markeditGetState();
  
          state = MarkEdit.stateTrim(state);
          state.select = MarkEditLanguage.markup.strikeStart + state.select + MarkEditLanguage.markup.strikeEnd;
  
          $(this).markeditSetState(state);
          return this;
      };
  
      //
      //  $.markeditSetLinkOrImage
      //
      $.fn.markeditSetLinkOrImage = function(image, options, url, text, overwriteSelection) {
  
          var state = $(this).markeditGetState();
  
          // Prep arguments
          if (typeof(image) === 'undefined') { image = false; }
  
          // Make sure selection is clean and get selected Url
          // This also adds state.selectedUrl and state.selectedUrlIndex to the state
          state = MarkEdit.cleanSelection(state, MarkEdit.RegexLinkStart, MarkEdit.RegexLinkEnd);
  
          // Get the selected URL
          var afterMatch = MarkEdit.RegexLinkEnd.exec(state.afterSelect);
          var urlIndex = -1;
          var selUrl = '';
          if (afterMatch) {
              urlIndex = Number(afterMatch[2]) - 1;
              selUrl = state.links[urlIndex];
          }
  
          // If no URL open up the dialog and initialize callback
          if (typeof(url) === 'undefined') {
  
              var defaultValue = 'https://';
              var config = null;
              var handler = MarkEdit.basicPrompt;
  
              if (image) {
                  config = MarkEditLanguage.dialog.insertImage;
                  if (options.imageSelector != null) {
                      handler = options.imageSelector;
                  }
              } else {
                  config = MarkEditLanguage.dialog.insertLink;
                  if (options.linkSelector != null) {
                      handler = options.linkSelector;
                  }
              }
              if (selUrl.length > 0) { defaultValue = selUrl; }
  
              var parent_tag = this;
              handler(config, defaultValue, function(promptValue) {  // Ok Click:
                  // IE will loose the selection state unless we re-apply it
                  $(parent_tag).markeditSetState(state);
                  $(parent_tag).markeditSetLinkOrImage(image, options, promptValue, text, overwriteSelection);
              }, function(){  // Cancel Click:
                  // IE will loose the selection state unless we re-apply it
                  $(parent_tag).markeditSetState(state);
              });
  
          }
          else {
  
              // Set link text/image alt text if a selection is not already present
              if (text) {
                  if (state.select.length === 0 || overwriteSelection) {
                      state.select = text;
                  }
              }
  
              // If we already have a link/image and we're inserting another one
              // just change the URL instead of doubling up the markdown syntax
              if (urlIndex !== -1) {
                  state.links[urlIndex] = url;
              }
              else {
                  // Otherwise, insert a new link/image
                  state.links[state.links.length] = url;
  
                  if (image) {
                      state.select = MarkEditLanguage.markup.imageLinkStart + state.select;
                      state.select += url + "|"
                          + MarkEditLanguage.markup.imageLinkEnd;
                  }
                  else {
                      state.select = MarkEditLanguage.markup.linkStart + state.select;
                      state.select += MarkEditLanguage.markup.linkMiddle + url
                          + MarkEditLanguage.markup.linkEnd;
                  }
              }
  
              $(this).markeditSetState(state);
  
          }
  
          return this;
      };
  
  
      //
      //  $.markeditSetCode
      //
      $.fn.markeditSetCode = function(){
          var state = $(this).markeditGetState();
  
          // Clean the inline-code selection (if present)
          state = MarkEdit.cleanSelection(state, /`(.*)?$/, /^(.*)?`/);
  
          // Determine if we have an existing code
          var inline = (state.beforeSelect.search(/`$/) > -1);
          var indentBeforeSelect = (state.beforeSelect.search(/ {4}$/) > -1);
          var indentInSelect = (state.select.search(/^ {4}/) > -1);
          var selectLines = [];
  
          if (inline) {
              state.beforeSelect = state.beforeSelect.substr(0, state.beforeSelect.length -1);
              state.afterSelect = state.afterSelect.substr(1);
          }
          else if (indentBeforeSelect || indentInSelect) {
              selectLines = state.select.split('\n');
              state.select = '';
  
              // Remove indented code
              for (var i = 0; i < selectLines.length; i++ ) {
  
                  // Determine if the 4-space indent is selected or not
                  if (indentBeforeSelect && i === 0) {
                      state.beforeSelect = state.beforeSelect.substr(0, state.beforeSelect.length - 4);
                      state.select += selectLines[i];
                  }
                  else {
                      state.select += selectLines[i].substr(4);
                  }
  
                  // Add line returns since we trimmed them
                  if (i !== (selectLines.length - 1)) {
                      state.select += '\n';
                  }
              }
          }
          else {
              // No existing code markdown is present
              // Determine if we're at the beginning of a line or if we're inline
              if (state.beforeSelect.search(/\n$/) > -1) {
                  // Add indented code
                  selectLines = state.select.split('\n');
                  state.select = '';
  
                  for (var j = 0; j < selectLines.length; j++ ) {
                      state.select += '    ' + selectLines[j];
                      if (j !== (selectLines.length - 1)) {
                          state.select += '\n';
                      }
                  }
              }
              else {
                  // Set inline code
                  state.select = MarkEditLanguage.markup.inlineCodeStart + state.select
                   + MarkEditLanguage.markup.inlineCodeEnd;
              }
          }
  
          $(this).markeditSetState(state);
          return this;
      };
  
  
      //
      //  $.markeditSetQuote
      //
      $.fn.markeditSetQuote = function() {
          var state = $(this).markeditGetState();
  
          // Check if we have an existing list
          var altSelects = [];
          altSelects[0] = MarkEdit.RegexNumberPrefix;
          altSelects[1] = MarkEdit.RegexBulletPrefix;
  
          state = MarkEdit.setPrefix(state, MarkEdit.RegexQuotePrefix, altSelects, function(){
              return MarkEditLanguage.markup.quotePrefix;
          });
  
          $(this).markeditSetState(state);
      };
  
  
      //
      //  $.markeditSetList
      //
      $.fn.markeditSetList = function(type) {
          var state = $(this).markeditGetState();
  
          // Check if we have an existing list
          var altSelects = [];
          var pattern;
          if (type === 'number') {
              pattern = MarkEdit.RegexNumberPrefix;
              altSelects[0] = MarkEdit.RegexBulletPrefix;
          } else if (type === 'bullet') {
              pattern = MarkEdit.RegexBulletPrefix;
              altSelects[0] = MarkEdit.RegexNumberPrefix;
          }
          altSelects[1] = MarkEdit.RegexQuotePrefix;
  
          state = MarkEdit.setPrefix(state, pattern, altSelects, function(){
              if (type === 'number') {
                  var listNum = MarkEdit.getNextListNumber(state);
                  return ' ' + (++listNum) + '. ';
              } else if (type === 'bullet') {
                  return MarkEditLanguage.markup.bulletPrefix;
              }
              return '';
          });
  
          $(this).markeditSetState(state);
          return this;
      };
  
  
      //
      //  $.markeditInsertLine
      //
      $.fn.markeditInsertLine = function(type) {
          var state = $(this).markeditGetState();
          state = MarkEdit.stateTrim(state);
  
          state.beforeSelect = state.beforeSelect.rtrim() + '\n\n----------';
          state.select = '';
          state.afterSelect = '\n\n' + state.afterSelect.ltrim();
  
          $(this).markeditSetState(state);
          return this;
      };
  
  
      //
      //  $.markeditSetHeading
      //
      $.fn.markeditSetHeading = function(type) {
          var state = $(this).markeditGetState();
          state = MarkEdit.stateTrim(state);
  
          if (state.select.length === 0) {
              return this;
          }
  
          var headingMatch = (/(.*)\n(-+|=+)/).exec(state.select + state.afterSelect);
          if (headingMatch) {
  
              var heading = String(headingMatch[1]);
              var line = String(headingMatch[2]);
  
              // Remove existing code from state
              state.select = '';
              if ((/^(-|=)/).exec(state.afterSelect) > -1) {
                  state.afterSelect = state.afterSelect.rtrim();
                  state.afterSelect = state.afterSelect.substr(line.length + 1);
              }
  
              // Determine which char
              switch (line.substr(0,1)) {
                  case '-':
                      line = line.replace(/-/g, '=');
                      state.select = heading;
                      state.afterSelect = '\n' + line + state.afterSelect;
                      break;
  
                  case '=':
                      state.select = heading;
                      break;
              }
  
          }
          else {
  
              // No heading, add one
              state.beforeSelect = state.beforeSelect.rtrim() + '\n\n';
  
              state.select = state.select.trim();
              var headingLength = state.select.length;
              var underline = '';
              state.select += '\n';
  
              for (var i = 0; i < headingLength; i++ ) {
                  underline += '-';
              }
  
              state.afterSelect = underline + '\n\n' + state.afterSelect.ltrim();
  
          }
  
          $(this).markeditSetState(state);
          return this;
      };
  
  
      //
      //  $.markeditGetHtml
      //
      $.fn.markeditGetHtml = function() {
  
          // Load Showdown if it's not loaded
          if (typeof(MarkEditShowDown) === 'undefined') {
              if (typeof(Attacklab) === 'undefined') {
                  throw 'Showdown.js (Attacklab.showdown) must be loaded before MarkEdit.';
              }
              MarkEditShowDown = new Attacklab.showdown.converter();
          }
  
          // Render the preview
          var textarea = MarkEdit.getTextArea(this);
          var text = $(textarea).val();
          if (typeof(text) !== 'undefined') {
              var html =  MarkEditShowDown.makeHtml($(this).val());
              html = html.replace(/\r/g, '');
  
              // Convert newlines to <br/> inside a <p>
              var lineBreakInP = /(<p>(?:[\S\s](?!<\/p>))*)\n([\S\s]*?<\/p>)/g;
              var lineBreaksRemaining = lineBreakInP.exec(html);
  
              while (lineBreaksRemaining !== null) {
                  html = html.replace(lineBreakInP, '$1<br />$2');
                  lineBreaksRemaining = lineBreakInP.exec(html);
              }
  
              return html;
          }
          else {
              return '';
          }
  
      };
  
  
      //
      //  $.markeditTogglePreview
      //
      $.fn.markeditTogglePreview = function() {
  
          var textarea = MarkEdit.getTextArea(this);
          var toolbar = $(textarea).parent().children().filter('.markedit-toolbar').eq(0);
          var previewPane = $(textarea).parent().children().filter('div.markedit-preview').eq(0);
  
          if ($(textarea).attr('rel') === 'hidden') {
              $(textarea).attr('rel', 'visible');
              $(textarea).show();
              $(previewPane).hide();
              $(toolbar).markeditEnableToolbar();
          }
          else {
              $(textarea).attr('rel', 'hidden');
              $(textarea).hide();
              $(previewPane).show();
              toolbar.markeditDisableToolbar('.preview button');
  
              $(previewPane).html($(textarea).markeditGetHtml());
          }
          return this;
  
      };
  
  
      //
      //  $.markeditDisableToolbar
      //
      $.fn.markeditDisableToolbar = function(exclude){
  
          var buttons = $(this).children().find('button');
  
          if (typeof(exclude) !== 'undefined') {
              buttons = $(buttons).not(exclude);
          }
  
          $(buttons).each(function(){
              $(this).addClass('ui-state-disabled');
              $(this).attr('disabled', 'disabled');
          });
  
      };
  
  
      //
      //  $.markeditEnableToolbar
      //
      $.fn.markeditEnableToolbar = function(exclude){
  
          var buttons = $(this).children().find('button');
  
          if (typeof(exclude) !== 'undefined') {
              buttons = $(buttons).not(exclude);
          }
  
          $(buttons).each(function(){
              $(this).removeClass('ui-state-disabled');
              $(this).attr('disabled', '');
          });
  
      };
  
  
      //
      //  $.markeditBindAutoPreview
      //
      $.fn.markeditBindAutoPreview = function(element){
  
          // Convert out of jQuery & find Textarea
          if (element.get) { element = element.get(0); }
          var textarea = MarkEdit.getTextArea(this);
  
          // Enable events for auto-preview
          $(textarea).keyup(function(){
              $(element).html($(this).markeditGetHtml());
          });
          $(textarea).bind('updated', function() {
              $(element).html($(this).markeditGetHtml());
          });
  
          // Load initial value
          $(element).html($(this).markeditGetHtml());
      };
  
  
  
      // -----------------------------------------------------------------------
      //  MarkEdit Namespace
      // -----------------------------------------------------------------------
      var MarkEdit = function() {
  
          return {
  
              //
              //  Checks if the existing element is a <textarea>.  If not
              //  searches children for the textarea tag.
              //
              getTextArea: function(element) {
                  if (element.tagName === 'TEXTAREA') {
                      return element;
                  }
                  else if (element.get(0).tagName === 'TEXTAREA') {
                      return element.get(0);
                  }
                  else {
                      var textarea = $(element).children().filter('textarea').eq(0);
                      if (textarea === null) {
                          throw MarkEditLanguage.cannotLocateTextarea;
                      }
                      return textarea.get(0);
                  }
              },
  
  
              //
              //  Updates the state of the given MarkEdit
              //
              setState: function(textarea, state) {
  
                  // Trim state
                  state.afterSelect = state.afterSelect.rtrim();
  
                  // Update textarea
                  var text = state.beforeSelect + state.select + state.afterSelect + '\n\n';
  
                  // We want the Markdown list of links only for Markdown format.
                  if (MarkEditLanguage.markup.makeLinkList) {
                      text += MarkEdit.makeLinkList(state.links);
                  }
                  $(textarea).val(text);
  
                  var selStart = state.beforeSelect.length;
                  var selEnd = selStart + state.select.length;
  
                  // Make active
                  $(textarea).focus();
  
                  if (textarea.setSelectionRange) {
  
                      // Set selection for non-IE browsers
                      textarea.setSelectionRange(selStart, selEnd);
  
                  }
                  else {
  
                      // Set selection for IE
                      var range = textarea.createTextRange();
                      range.collapse(true);
                      range.moveEnd('character', selEnd);
                      range.moveStart('character', selStart);
                      range.select();
  
                  }
  
                  // Fire custom event
                  $(textarea).trigger('updated');
              },
  
  
              //
              //  Appends the state to the current history
              //
              appendHistory: function(state, id) {
                  if (MarkEditHistory !== null) {
                      if (typeof(MarkEditHistory[id]) !== 'undefined') {
                          MarkEditHistory[id].undo.push(state);
                          // History length: 50
                          if (MarkEditHistory[id].undo.length > 50) {
                              MarkEditHistory[id].undo.pop();
                          }
                      }
                  }
              },
  
  
              //
              //  Trims the state.select, but moves the white space
              //  to the beforeSelect/afterSelect
              //
              stateTrim: function(state, newlineOnly) {
  
                  var leadPattern;
                  var tailPattern;
  
                  if (newlineOnly) {
                      leadPattern = /^(?:\r?\n)+/;
                      tailPattern = /(?:\r?\n)+$/;
                  }
                  else {
                      leadPattern = /^\s+/;
                      tailPattern = /\s+$/;
                  }
  
                  var lead = leadPattern.exec(state.select);
                  var tail = tailPattern.exec(state.select);
  
                  if (lead) {
                      state.beforeSelect += lead;
                      state.select = state.select.substr(lead.length);
                  }
  
                  if (tail) {
                      state.afterSelect = tail + state.afterSelect;
                      state.select = state.select.substr(0, state.select.length - tail.length);
                  }
  
                  return state;
              },
  
  
              //
              //  Given an array, converts it into a Markdown link list
              //
              makeLinkList: function(links) {
                  var linkList = '';
  
                  $.each(links, function(i, val){
                      var link = '  [%id%]: %url%\n';
                      link = link.replace('%id%', i+1);
                      link = link.replace('%url%', val);
                      linkList += link;
                  });
  
                  return linkList;
              },
  
  
              //
              //  Given a body of Markdown, extract the links as an Array out from it
              //
              getMarkdownLinks: function(text) {
                  var def_pattern = / {2}\[\d+\]: .*/;
                  var link_lead_pattern = / {2}\[\d+\]: /;
  
                  var remainder = text;
                  var links = [];
  
                  var result = def_pattern.exec(text);
                  while (result) {
                      var lead = link_lead_pattern.exec(result);
                      var link = result.toString().replace(lead.toString(), '').trim();
                      links[links.length] = link;
                      remainder = remainder.replace(result, '');
                      result = def_pattern.exec(remainder);
                  }
  
                  return {
                      'remainder': remainder.rtrim(),
                      'links': links
                  };
              },
  
  
              //
              //  Show a basic dialog prompt and return a value
              //
              basicPrompt: function(config, defaultValue, okCallback, cancelCallback) {
                  // The callback function signature should look like this:
                  // function(promptValue);
  
                  var diag = $('<div></div>');
  
                  if (typeof(config.helpText) !== 'undefined') {
                      diag.append('<p>' + config.helpText + '</p>');
                  }
  
                  var diagInput = $('<input type="text"></input>');
  
                  if (typeof(defaultValue) !== 'undefined') {
                      diagInput.attr('value', defaultValue);
                  }
  
                  diag.append(diagInput);
  
                  $(diag).dialog({
                      bgiframe: true,
                      modal: true,
                      autoOpen: true,
                      width: 600,
                      dialogClass: 'markedit-dialog',
                      title: config.title,
                      resizable: false,
                      buttons: {
                          'Insert': function() {
                              $(this).dialog('close');
                              okCallback(diagInput.val());
                          },
                          'Cancel': function() {
                              $(this).dialog('close');
                              cancelCallback();
                          }
                      },
                      open: function() {
                          // Auto-select all text in input box
                          var input = diagInput.get(0);
                          var start = 0;
                          var end = $(input).val().length;
  
                          if (input.setSelectionRange) {
                              input.setSelectionRange(start, end);
                          }
                          else {
                              var range = input.createTextRange();
                              range.collapse(true);
                              range.moveEnd('character', end);
                              range.moveStart('character', start);
                              range.select();
                          }
                      }
                  });
  
                  // Replace the button text with the proper translation
                  // becuase jQuery can't apparently
                  var buttons = $(diag).parent().children().filter('.ui-dialog-buttonpane').children();
                  $(buttons).each(function(){
                      var buttonText = $(this).text();
  
                      if (buttonText === 'Insert') {
                          $(this).text(config.insertButton);
                      }
                      else if (buttonText === 'Cancel') {
                          $(this).text(config.cancelButton);
                      }
                  });
  
              },
  
              //
              //  Given a regex for the start and end, fix the user's selection if
              //  they're selecting part of a region (link, code, etc.) but really
              //  should be electing the whole thing.
              //
              cleanSelection: function(state, patternStart, patternEnd) {
  
                  // Clean selection lead
                  var beforeMatch = patternStart.exec(state.beforeSelect);
                  if (beforeMatch !== null) {
                      if (typeof(beforeMatch[1]) !== 'undefined') {
                          var selectionHead = String(beforeMatch[1]);
  
                          // Trim selection head from the end of beforeSelect
                          state.beforeSelect = state.beforeSelect.substr(0, state.beforeSelect.length - selectionHead.length);
  
                          // ...then add it to the real selection
                          state.select = selectionHead + state.select;
                      }
                  }
  
                  // Clean selection tail
                  var afterMatch = patternEnd.exec(state.afterSelect);
                  if (afterMatch !== null) {
                      if (typeof(afterMatch[1]) !== 'undefined') {
                          var selectionTail = String(afterMatch[1]);
  
                          // Trim the selection tail from the beginning of afterSelect
                          state.afterSelect = state.afterSelect.substr(selectionTail.length);
  
                          // ...then add it to the end of the real selection
                          state.select += selectionTail;
                      }
                  }
  
                  return state;
              },
  
  
              //
              //  Returns the proper prefix based on the type and current state
              //  Either a - (bullet) or the next number in a numbered sequence
              //
              getNextListNumber: function(state) {
                  // Find if there is a line above it in the selection
                  var match = (/ (\d{1,3}). (?:.*)?\n(?:.*)?$/).exec(state.select);
                  if (match) {
                      return Number(match[1]);
                  }
                  else {
                      // See if there is a line above the select
                      match = (/ (\d{1,3}). (?:.*)?\n(?:.*)?$/).exec(state.beforeSelect);
                      if (match) {
                          return Number(match[1]);
                      }
                  }
                  // Otherwise there must not be a numberic list already
                  return 0;
              },
  
  
              //
              //  Given a state will toggle the defined prefix
              //
              setPrefix: function(state, selectPattern, altSelectPatterns, getPrefixCallback) {
  
                  state = MarkEdit.stateTrim(state, true);
  
                  // Standardize selection to include prefix
                  var bsMatch = selectPattern.exec(state.beforeSelect);
  
                  // Check alt patterns as well
                  if (bsMatch === null) {
                      $.each(altSelectPatterns, function(i, pattern){
                          var match = pattern.exec(state.beforeSelect);
                          if (match) {
                              bsMatch = match;
                          }
                      });
                  }
  
                  if (bsMatch !== null) {
                      state.beforeSelect = state.beforeSelect.substr(0, state.beforeSelect.length - bsMatch[1].length);
                      state.select = bsMatch[1] + state.select;
                  }
  
                  // Standardize line breaks on state
                  state.beforeSelect = state.beforeSelect.rightNewlineTrim();
  
                  // Check if we have an actual selection or not
                  var lastPrefix = '';
                  if (state.select.length > 0) {
  
                      // Enumerate lines and check what we've got
                      var list = state.select.split('\n');
                      state.select = '';
  
                      for (var i = 0; i < list.length; i++) {
  
                          // Find current type (toggle off if found)
                          var text = '';
                          var typeMatch = selectPattern.exec(list[i]);
  
                          if (typeMatch !== null) {
                              // The line may contain the prefix but may be empty (ex: quote).
                              if (typeMatch[2] != null) {
                                  state.select += typeMatch[2].trim() + '\n';
                              }
                          }
                          else {
                              var prefix = getPrefixCallback(state);
  
                              // Find alternate type (switch prefix if found)
                              var altPatternFound = false;
                              for (var j = 0; j < altSelectPatterns.length; j++) {
  
                                  var altTypeMatch = altSelectPatterns[j].exec(list[i]);
  
                                  if (altTypeMatch !== null) {
                                      // Grab list item text
                                      if (typeof(altTypeMatch[2]) !== 'undefined' ) {
                                          text = altTypeMatch[2].trim();
                                      }
  
                                      // Adjust state accordingly
                                      state.select += prefix + text + '\n';
                                      lastPrefix = prefix;
                                      altPatternFound = true;
                                      break;
                                  }
  
                              }
  
                              // Current/Alt type not found, add new entry
                              if (!altPatternFound) {
                                  text = list[i].trim();
                                  if (text.length > 0) {
                                      state.select += prefix + list[i].trim() + '\n';
                                  }
                              }
                          }
                      }
  
                      // Fixup the new lines in before select
                      state.select = state.select.rightNewlineTrim();
                      if (state.select === lastPrefix) {
                          state.beforeSelect += '\n\n' + state.select;
                          state.select = '';
                      }
                      else {
                          state.beforeSelect += '\n\n';
                      }
  
                  }
                  else {
                      // Nothing was selected, so insert empty line
                      state.beforeSelect += '\n\n' + getPrefixCallback(state);
                  }
  
                  state.afterSelect = state.afterSelect.leftNewlineTrim();
                  state.afterSelect = '\n\n' + state.afterSelect;
  
                  return state;
              },
  
  
              //
              //  Given a string, shows a message box containing the value represented in hex.
              //  Useful for debugging when whitespace is invovled.
              //
              hexDebug: function(value, name) {
                  if (typeof(value) === 'string') {
                      var hex = '';
                      for (var i = 0; i < value.length; i++) {
                          hex += value.charCodeAt(i).toString(16) + ' ';
                      }
                      if (hex.length > 0) {
                          if (name) {
                              alert(name + ':\n' + hex);
                          }
                          else {
                              alert(hex);
                          }
                      }
                      else {
                          alert('Empty value.');
                      }
                  }
                  else
                  {
                      alert(value);
                  }
              },
  
  
              //
              //  Popup state in a readable way
              //
              alertState: function(state) {
                  alert(
                      'beforeSelect:\n[' + state.beforeSelect + ']\n\nselect:\n[' + state.select + ']\n\nafaterSelect:\n[' + state.afterSelect + ']'
                  );
              },
  
  
              //
              //  Regex Patterns for prefixes
              //
              RegexNumberPrefix: /( \d{1,3}. )(.*)?$/,
              RegexBulletPrefix: /( - )(.*)?$/,
              RegexQuotePrefix: /(> )(.*)?$/,
              RegexCodePrefix: /( {4})(.*)?$/,
              RegexLinkStart: /(?:!)?\[([^\]]+)?$/,
              RegexLinkEnd: /^([^\]]+)?\]\[(\d+)?\]/
          };
  
      }();
  
  
      // -----------------------------------------------------------------------
      //  MarkEditToolbar Namespace
      // -----------------------------------------------------------------------
      var MarkEditToolbar = function() {
  
          return {
  
              //
              //  Creates all of the toolbar markup and binds events
              //
              createToolbar: function(textarea_tag, options) {
  
                  // Prep toolbar wrapper
                  var toolbar = $('<div class="markedit-toolbar ui-widget-header"></div>').prependTo(textarea_tag);
                  if (options.backgroundMode === 'dark') {
                      toolbar.addClass('dark-bg');
                  }
                  else {
                      toolbar.addClass('light-bg');
                  }
  
                  // Sort configured buttons into groups
                  var groups = options.layout.split('|');
                  if (groups.length === 0) {
                      groups[0] = options.layout;
                  }
  
                  // Enumerate groups
                  for ( var g = 0; g < groups.length; g++ ) {
  
                      groups[g] = groups[g].trim();
  
                      var toggleGroup = false;
                      var match = (/^\((.*)\)$/).exec(groups[g]);
                      if (match) {
                          toggleGroup = true;
                          groups[g] = String(match[1]);
                      }
  
                      var group = toolbar.markeditAddGroup(toggleGroup);
  
                      // Turn buttons in this group into an Array
                      var button_list = groups[g].split(' ');
                      if (button_list.length === 0) {
                          button_list[0] = groups[g];
                      }
  
                      // Add buttons for each in group
                      for ( var b = 0; b < button_list.length; b++ ) {
                          var button = MarkEditToolbar.getButtonDefinition(button_list[b], textarea_tag, options);
                          if (toggleGroup) {
                              button.toggle = true;
                          }
  
                          if (button !== null) {
                              group.markeditAddButton(button);
                          }
                          else {
                              throw "Definition for button '"+ button_list[b] +"' was not found.  If this is a custom button, use the custom_buttons setting to configure it.";
                          }
                      }
  
                  }
  
              },
  
              //
              //  Get the definition of built-in or custom buttons
              //
              getButtonDefinition: function(button_id, textarea_tag, options) {
  
                  switch(button_id) {
  
                      case 'bold':
                          return {
                              'id': 'bold',
                              'tip': MarkEditLanguage.defaultButtons.bold.tip,
                              'click': function() { $(textarea_tag).markeditSetBold(); }
                          };
  
                      case 'italic':
                          return {
                              'id':' italic',
                              'tip': MarkEditLanguage.defaultButtons.italic.tip,
                              'click': function() { $(textarea_tag).markeditSetItalic(); }
                          };
  
                      case 'underline':
                          return {
                              'id':' underline',
                              'tip': MarkEditLanguage.defaultButtons.underline.tip,
                              'click': function() { $(textarea_tag).markeditSetUnderline(); }
                          };
  
                      case 'strike':
                          return {
                              'id':' strike',
                              'tip': MarkEditLanguage.defaultButtons.strike.tip,
                              'click': function() { $(textarea_tag).markeditSetStrike(); }
                          };
  
                      case 'link':
                          return {
                              'id':'link',
                              'tip': MarkEditLanguage.defaultButtons.link.tip,
                              'click': function() { $(textarea_tag).markeditSetLinkOrImage(false, options); }
                          };
  
                      case 'image':
                          return {
                              'id':'image',
                              'tip': MarkEditLanguage.defaultButtons.image.tip,
                              'click': function() { $(textarea_tag).markeditSetLinkOrImage(true, options); }
                          };
  
                      case 'code':
                          return {
                              'id':'code',
                              'tip': MarkEditLanguage.defaultButtons.code.tip,
                              'click': function() { $(textarea_tag).markeditSetCode(); }
                          };
  
                      case 'quote':
                          return {
                              'id':'quote',
                              'tip': MarkEditLanguage.defaultButtons.quote.tip,
                              'click': function() { $(textarea_tag).markeditSetQuote(); }
                          };
  
                      case 'numberlist':
                          return {
                              'id': 'numberlist',
                              'tip': MarkEditLanguage.defaultButtons.numberlist.tip,
                              'click': function() { $(textarea_tag).markeditSetList('number'); }
                          };
  
                      case 'bulletlist':
                          return {
                              'id': 'bulletlist',
                              'tip': MarkEditLanguage.defaultButtons.bulletlist.tip,
                              'click': function() { $(textarea_tag).markeditSetList('bullet'); }
                          };
  
                      case 'line':
                          return {
                              'id': 'line',
                              'tip': MarkEditLanguage.defaultButtons.line.tip,
                              'click': function() { $(textarea_tag).markeditInsertLine(); }
                          };
  
                      case 'heading':
                          return {
                              'id': 'heading',
                              'tip': MarkEditLanguage.defaultButtons.heading.tip,
                              'click': function() { $(textarea_tag).markeditSetHeading(); }
                          };
  
                      case 'undo':
                          return {
                              'id': 'undo',
                              'tip': MarkEditLanguage.defaultButtons.undo.tip,
                              'click': function() { $(textarea_tag).markeditUndo(); }
                          };
  
                      case 'redo':
                          return {
                              'id': 'redo',
                              'tip': MarkEditLanguage.defaultButtons.redo.tip,
                              'click': function() { $(textarea_tag).markeditRedo(); }
                          };
  
                      case 'edit':
                          return {
                              'id': 'edit',
                              'text': MarkEditLanguage.defaultButtons.edit.text,
                              'tip': MarkEditLanguage.defaultButtons.edit.tip,
                              'click': function() { $(textarea_tag).markeditTogglePreview(); }
                          };
  
                      case 'preview':
                          return {
                              'id': 'preview',
                              'text': MarkEditLanguage.defaultButtons.preview.text,
                              'tip': MarkEditLanguage.defaultButtons.preview.tip,
                              'click': function() { $(textarea_tag).markeditTogglePreview(); }
                          };
  
                      default:
                          return MarkEditToolbar.getCustomButtonDef(button_id, options);
                  }
  
              },
  
              //
              //  Get the definition of a custom button
              //
              getCustomButtonDef: function(id, options) {
  
                  var custom = options.buttons;
                  for ( var i = 0; i < custom.length; i++ ) {
                      var button = custom[i];
                      if (typeof(button.id) !== 'undefined') {
                          if (id === button.id) {
                              return button;
                          }
                      }
                  }
  
                  return null;
              }
  
          };
  
      }();
  
  
  })(jQuery);
  
  
  // A few extra helper methods
  
  String.prototype.trim = function() {
      return this.replace(/^\s+|\s+$/g,'');
  };
  
  String.prototype.ltrim = function() {
      return this.replace(/^\s+/g,'');
  };
  
  String.prototype.rtrim = function() {
      return this.replace(/\s+$/g,'');
  };
  
  String.prototype.leftNewlineTrim = function() {
      return this.replace(/^(\r|\n)+/g, '');
  }
  
  String.prototype.rightNewlineTrim = function() {
      return this.replace(/(\r|\n)+$/g, '');
  }