revos / rownumber

// ==UserScript==
// @name        rownumber
// @namespace   wmmailru
// @description rownumber
// @version     0.01
// @author      revos
// @include     http://wmmail.ru/index.php*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @run-at      document-end
// @grant       none
// @license     MIT
// ==/UserScript==

var css = document.createElement("style");
css.type = "text/css";
css.innerHTML = ".linedwrap{border:1px solid silver;padding:3px;width:none!important;}.linedtextarea{padding:0;margin:0;}.linedtextarea textarea,.linedwrap .codelines .lineno{font-size:12pt;font-family:monospace;line-height:1.14!important;}.linedtextarea textarea{padding-right:.3em;padding-top:.3em;border:0;}.linedwrap .lines{margin-top:0;width:50px;float:left;overflow:hidden;border-right:1px solid silver;margin-right:10px;}.linedwrap .codelines{padding-top:5px;}.linedwrap .codelines .lineno{color:#AAA;padding-right:.5em;padding-top:0;text-align:right;white-space:nowrap;}.linedwrap .codelines .lineselect{color:red;}input:active,input:focus,input:hover,textarea:active,textarea:focus,textarea:hover{outline:0!important;}";
document.head.appendChild(css);

(function ($) {

  $.fn.linedtextarea = function (options) {

    // Get the Options
    var opts = $.extend({}, $.fn.linedtextarea.defaults, options);

    /*
     * Helper function to make sure the line numbers are always
     * kept up to the current system
     */
    var fillOutLines = function (codeLines, h, lineNo) {
      while ((codeLines.height() - h) <= 0) {
        if (lineNo == opts.selectedLine)
          codeLines.append("<div class='lineno lineselect'>" + lineNo + "</div>");
        else
          codeLines.append("<div class='lineno'>" + lineNo + "</div>");

        lineNo++;
      }
      return lineNo;
    };

    /*
     * Iterate through each of the elements are to be applied to
     */
    return this.each(function () {
      var lineNo = 1;
      var textarea = $(this);

      /* Turn off the wrapping of as we don't want to screw up the line numbers */
      textarea.attr("wrap", "off");
      textarea.css({
        resize: 'vertical'
      })
      var originalTextAreaWidth = textarea.outerWidth();

      /* Wrap the text area in the elements we need */
      textarea.wrap("<div class='linedtextarea'></div>");
      var linedTextAreaDiv = textarea.parent().wrap("<div class='linedwrap' style='width:" + originalTextAreaWidth + "px'></div>");
      var linedWrapDiv = linedTextAreaDiv.parent();

      linedWrapDiv.prepend("<div class='lines' style='width:25px'></div>");

      var linesDiv = linedWrapDiv.find(".lines");
      linesDiv.height(textarea.height() + 6);

      /* Draw the number bar; filling it out where necessary */
      linesDiv.append("<div class='codelines'></div>");
      var codeLinesDiv = linesDiv.find(".codelines");
      lineNo = fillOutLines(codeLinesDiv, linesDiv.height(), 1);

      /* Move the textarea to the selected line */
      if (opts.selectedLine != -1 && !isNaN(opts.selectedLine)) {
        var fontSize = parseInt(textarea.height() / (lineNo - 2));
        var position = parseInt(fontSize * opts.selectedLine) - (textarea.height() / 2);
        textarea[0].scrollTop = position;
      }

      /* Set the width */
      var sidebarWidth = linesDiv.outerWidth();
      var paddingHorizontal = parseInt(linedWrapDiv.css("border-left-width")) + parseInt(linedWrapDiv.css("border-right-width")) + parseInt(linedWrapDiv.css("padding-left")) + parseInt(linedWrapDiv.css("padding-right"));
      var linedWrapDivNewWidth = originalTextAreaWidth - paddingHorizontal;
      var textareaNewWidth = originalTextAreaWidth - sidebarWidth - paddingHorizontal - 20;

      textarea.width(textareaNewWidth);
      linedWrapDiv.width(linedWrapDivNewWidth);

      /* React to the scroll event */
      textarea.scroll(function (tn) {
        var domTextArea = $(this)[0];
        var scrollTop = domTextArea.scrollTop;
        var clientHeight = domTextArea.clientHeight;
        codeLinesDiv.css({
          'margin-top': (-1 * scrollTop) + "px"
        });
        lineNo = fillOutLines(codeLinesDiv, scrollTop + clientHeight, lineNo);
      });

      textarea = $(this)[0];

      new MutationObserver(resize).observe(textarea, {
        attributes: true,
        attributeFilter: ["style"]
      });

      function resize(tn) {
        linesDiv.height(textarea.clientHeight + 4);
        lineNo = fillOutLines(codeLinesDiv, linesDiv.height(), lineNo);
      }

    });
  };

  // default options
  $.fn.linedtextarea.defaults = {
    selectedLine: -1,
    selectedClass: 'lineselect'
  };
})(jQuery);

$("#zdtext").linedtextarea();
var ta = document.getElementById("zdtext");
var calculateHeight = function () {
  var
    style = (window.getComputedStyle) ?
    window.getComputedStyle(ta) : ta.currentStyle,

    // This will get the line-height only if it is set in the css,
    // otherwise it's "normal"
    taLineHeight = parseInt(style.lineHeight, 10),
    // Get the scroll height of the textarea
    taHeight = calculateContentHeight(ta, taLineHeight),
    // calculate the number of lines
    numberOfLines = Math.ceil(taHeight / taLineHeight) - 1;
  var lines = document.getElementsByClassName("lineno");
  for (i = 0; i < lines.length; i++) {
    if (i == (numberOfLines - 1)) {
      lines[i].style["color"] = "red";
    }
    else if (i < (numberOfLines - 1)) {
      lines[i].style["color"] = "#AAAAAA";
    }
    else {
      lines[i].style["color"] = "white";
    }
  }
};
var calculateContentHeight = function (ta, scanAmount) {
  var origHeight = ta.style.height,
    height = ta.offsetHeight,
    scrollHeight = ta.scrollHeight,
    overflow = ta.style.overflow;
  /// only bother if the ta is bigger than content
  if (height >= scrollHeight) {
    /// check that our browser supports changing dimension
    /// calculations mid-way through a function call...
    ta.style.height = (height + scanAmount) + 'px';
    /// because the scrollbar can cause calculation problems
    ta.style.overflow = 'hidden';
    /// by checking that scrollHeight has updated
    if (scrollHeight < ta.scrollHeight) {
      /// now try and scan the ta's height downwards
      /// until scrollHeight becomes larger than height
      while (ta.offsetHeight >= ta.scrollHeight) {
        ta.style.height = (height -= scanAmount) + 'px';
      }
      /// be more specific to get the exact height
      while (ta.offsetHeight < ta.scrollHeight) {
        ta.style.height = (height++) + 'px';
      }
      /// reset the ta back to it's original height
      ta.style.height = origHeight;
      /// put the overflow back
      ta.style.overflow = overflow;
      return height;
    }
  }
  else {
    return scrollHeight;
  }
}
calculateHeight();
if (ta.addEventListener) {
  ta.addEventListener("mouseup", calculateHeight, false);
  ta.addEventListener("keyup", calculateHeight, false);
}
else if (ta.attachEvent) { // IE
  ta.attachEvent("onmouseup", calculateHeight);
  ta.attachEvent("onkeyup", calculateHeight);
}