jQuery.fn.extend({
    textArea: function(args) {
        return this.each(function() {
            new jQuery.textArea(this, args);
        });
    }
});

jQuery.textArea = function(textObj, args) {
    if (!args) args = {};
    var line_height = args.lineHeight || 16;
    var wheel_speed = args.wheelSpeed || 18;

    var $text = $(textObj);
    $text.wrap('<div class="textwrapper"></div>').css('line-height', line_height + 'px').css('overflow', 'hidden');
    var $wrapper = $('.textwrapper');
    $wrapper.css('width', $text.width() + 22 + 'px');

    var textHeight = function(e, text) {
        var font = {
            family: 'monospace',
            size: 'medium',
            style: 'normal',
            variant: 'normal',
            weight: 'normal'
        };
        var $e = $(e);
        if (!text) {
            text = e.value.substr(
                0,
                (
                    (
                        'selectionStart' in e /*DOM 3*/
                        && function() {
                            return e.selectionEnd;
                        }
                    )
                    ||
                    (
                        document.selection
                        && function() {
                            $text.focus();
                            var range = document.selection.createRange();
                            if (range == null) return '';
                            var re = e.createTextRange();
                            var rc = re.duplicate();
                            re.moveToBookmark(range.getBookmark());
                            rc.setEndPoint('EndToStart', re);
                            
                            return rc.text.length;
                        }
                    )
                    ||
                        /* browser not supported */
                    function() {
                        return 0;
                    }
                ) ()
            );
        }

        var div = document.createElement('div');
        var $div = $(div);
        text = text.replace(/\n/g, "<br>");
        $div.attr('id', 'textareatmp').addClass('textarea').css({
            position: "absolute",
            margin: "0",
            width: ($e.width() || 500) + "px",
            height: 'auto',
            'line-height' : line_height + 'px',
/*            whiteSpace: 'pre', */
            fontFamily: font.family,
            fontSize: font.size,
            fontStyle: font.style,
            fontVariant: font.variant,
            fontWeight: font.weight
        }).html(text);
        $e.after($div);

        var offsetTop = $div.height();
        $('#textareatmp').remove();
        return offsetTop;
    };

/* Geting scroll height from the textarea object */
    var content_height = textHeight(textObj, $text.val()) || $text.scrollHeight;
    var text_height   = $text.height();
    var drag_height   = 0;
    var scroll_amount = 0;
    var pane_height   = $wrapper.height() - 24;
    var max_y         = text_height;
    var drag_position = 0;
    var track_scroll  = 0;
    var track_scroll_mouse_pos = 0;
    var scroll_interval;
    var current_offset;
    var wheel_multiplier;
    var hidden_height = 0;

    var $up; var $down; var $drag; var $track_pane
    var upClick = function() {
        var scroll_top = $text.scrollTop();
        if (scroll_top > 0 && scroll_top - line_height > 0) {
          $text.scrollTop(scroll_top - line_height);

          var ntop = parseInt($drag.css('top')) - scroll_amount;
          if (ntop < 0) $drag.css({'top': '0px'});
          else $drag.css({'top': ntop + 'px'});
        }
        else {
          $text.scrollTop(0);
          $drag.css('top', '0px');
        }
    };  

    var downClick = function() {
        $text.scrollTop($text.scrollTop() + line_height);
        var ntop = parseInt($drag.css('top')) + scroll_amount;        
        if (ntop > max_y) $drag.css({'top': max_y + 'px'});
        else $drag.css({'top': ntop + 'px'});
    };

    var getPos = function (event, c) {
        var p = c == 'X' ? 'Left' : 'Top';
        return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0;
    };

    var dragScroll = function() {
        if (track_scroll > 8 || track_scroll % 4 == 0) {
            positionDrag((drag_position - ((drag_position - track_scroll_mouse_pos) / 2)));
        }

        track_scroll++;
    };

    var initDrag = function() {
        current_offset      = $drag.offset(false);
        current_offset.top -= drag_position;
        wheel_multiplier = 2 * wheel_speed * max_y / content_height;
    };

    var positionDrag = function(destY) {
        destY = destY < 0 ? 0 : (destY > max_y ? max_y : destY);
        drag_position = destY;

        var scrolltop = destY || 0;
        if (scrolltop > 0) scrolltop = scrolltop * content_height / pane_height;
        $text.scrollTop(scrolltop);

        $drag.css({'top': destY + 'px' });
    };

    var mouseMove = function(event) {
        track_scroll_mouse_pos = getPos(event, 'Y') - current_offset.top;
    };

    var mouseUp = function() {
        clearInterval(scroll_interval);
        jQuery('html').css({cursor: 'default'}).unbind('mouseup', mouseUp).unbind('mousemove', mouseMove);
    };

    var mouseDown = function(event) {
        initDrag();
        jQuery('html').css({ cursor: 'pointer'}).bind('mouseup', mouseUp).bind('mousemove', mouseMove);
        scroll_interval = setInterval(dragScroll, 50);
    };

    var createScrollbar = function ($wrapper) {
        var scroll = document.createElement('div');
        var $scroll = $(scroll);
        $scroll.addClass('scrollbar').css('height', $wrapper.height() + 'px');

        var track_pane  = document.createElement('div');
        $track_pane = $(track_pane);

        var up = document.createElement('div');
        $up = $(up);
        $scroll.append($up.addClass('up-scrollbar'));

        var down = document.createElement('div');
        $down = $(down);
        $scroll.append($down.addClass('down-scrollbar'));

        var drag  = document.createElement('div');
        $drag = $(drag);
        $track_pane.append($drag.addClass('drag-scrollpane').css({ top: '0px' }));
        $scroll.append($track_pane.addClass('track-scrollpane').css('height', pane_height + 'px'));

        $wrapper.find('.textarea').after($scroll);
        return $scroll;
    };
  
/* Create scrollbar */
    var $scrollbar = createScrollbar($wrapper);
    var activeScrollbar = function() {
        drag_height    = content_height > text_height ? parseInt(text_height * 100 / content_height) : 0;
        max_y          = pane_height - drag_height;
        scroll_amount  = parseInt((line_height * 100) / (content_height - text_height));
        var old_height = $drag.height();
        $drag.css({ height: drag_height + 'px'});

        if (drag_height == 0) {
            $up.unbind('click', upClick);
            $down.unbind('click', downClick);
            $drag.hide().unbind('mousedown', mouseDown).unbind('mouseup', mouseUp);
        }
        else {
            $up.bind('click', upClick);
            $down.bind('click', downClick);
            if (old_height == 0) $drag.css({ top: '0px' });
            $drag.show().bind('mousedown', mouseDown).bind('mouseup', mouseUp);
        }
    };
    activeScrollbar();

    $text.bind('mousewheel', function(event, num) {
        initDrag();
        var d = drag_position;
        positionDrag(drag_position - num * wheel_multiplier);
        var dragOccured = d != drag_position;
        return !dragOccured;
    }).bind('keyup', function(event) {
        content_height = textHeight(this, $text.val());
        activeScrollbar();

        var offsetTop = textHeight(this);
        var scrollTop = $text.scrollTop();

        var dragTop = $drag.offset(false).top - 20;
        var accuateTop = offsetTop >= pane_height ? pane_height - (offsetTop % pane_height) : offsetTop;
       // $('p').html("DRAG HEIGHT: " + drag_height + ' CONTENT HEIGHT ' + content_height);
        
        if (event.keyCode == 86) {
            $text.scrollTop(content_height);
            $drag.css({ top: pane_height - drag_height + 'px' });
        }
        else if (event.keyCode == 38 && scrollTop > 0 && offsetTop - line_height < scrollTop) {
            upClick();
        }
        else if ((offsetTop - scrollTop >= pane_height && event.keyCode == 40) || offsetTop == content_height) {
            downClick();
        }
        else if (event.keyCode != 40 && offsetTop >= pane_height - 5 && (scrollTop == 0 || offsetTop - scrollTop >= pane_height - 5)) {
            downClick();
        }
    }).bind('mouseup', function() { $(this).keyup(); });
}

