/*
Copyright 2008-2011 by PE Eliseev Stanislav
The program is distributed under the terms of the GNU General Public License version 3.

This file is part of ForSiter http://forsiter.com

    ForSiter is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation version 3 of the License.

    ForSiter is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ForSiter.  If not, see <http://www.gnu.org/licenses/>.
 */

(function ($)
{
    $.fn.fsScroll = function (settings)
    {
        this.settings = $.extend ({
            verticalGutter : 4,
            mouseWheelSpeed : 30,
            verticalMarginOut : 24,
            showArrows : false,
            arrowRepeatFreq : 100,
            arrowButtonSpeed : 50
        }, settings);
        
        var cmp = this;
        
        this.initFsScroll = function ($element)
        {
            if ($element.children ('.fsScrollContainer').get (0)) {
                cmp.refreshFsScroll ($element);
            } else {
                cmp.makeFsScroll ($element);
            }
        };
        
        this.makeFsScroll = function ($element)
        {
            $element.css ({
                overflow : 'hidden',
                padding : 0
            });
            
            var paneWidth = $element.width ();
            var paneHeight = $element.height ();
            if ($element.get (0).scrollHeight <= paneHeight) {
                return;
            }
            
            var content = cmp.removeTinyMCE ();
            cmp.$pane = $ ('<div class="fsScrollPane" ></div>');
            cmp.$pane.append ($element.children ());
            
            cmp.$container = $ ('<div class="fsScrollContainer"></div>');
            cmp.$container.css ({
                'width' : paneWidth + 'px',
                'height' : paneHeight + 'px'
            });
            cmp.$container.append (cmp.$pane);
            
            cmp.initMousewheel ();
            
            cmp.$verticalDrag = $ ('<div class="fsScrollDrag" ></div>');
            cmp.$verticalTrack = $ ('<div class="fsScrollVerticalBar"><div class="fsScrollTrack"></div></div>');
            
            cmp.$verticalTrack.find ('.fsScrollTrack').append (cmp.$verticalDrag);
            
            cmp.$verticalTrack.bind ('mousedown.fsscroll', cmp.clickOnTrack);
            
            if (cmp.settings.showArrows) {
                var $arrowDown = $ ('<a class="fsScrollArrow fsScrollArrowDown"></a>');
                var $arrowUp = $ ('<a class="fsScrollArrow fsScrollArrowUp fsScrollDisabled"></a>');
                cmp.$verticalTrack.prepend ($arrowUp);
                cmp.$verticalTrack.append ($arrowDown);
                $arrowDown.bind ('mousedown.fsscroll', cmp.arrowDownClick).bind ('click.fsscroll', function ()
                {
                    return false;
                });
                $arrowUp.bind ('mousedown.fsscroll', cmp.arrowUpClick).bind ('click.fsscroll', function ()
                {
                    return false;
                });
            }
            
            cmp.$container.append (cmp.$verticalTrack);
            
            $element.html (cmp.$container);
            if (cmp.settings.verticalMarginOut) {
                $element.css ({
                    'margin-right' : -cmp.settings.verticalMarginOut
                });
            }
            
            cmp.$pane.css ({
                'top' : '0px',
                'width' : cmp.getPaneWidth ()
            });
            
            cmp.$verticalDrag.bind ('mousedown.fsscroll', cmp.dragMouseDown);
            
            cmp.initTouch ();
            
            cmp.addTinyMCE (content);
        };
        
        this.initTouch = function ()
        {
            var touchStartY, moved, moving = false;
            var cmp2 = cmp;
            
            cmp.$container.unbind ('touchstart.fsscroll touchmove.fsscroll touchend.fsscroll click.fsscroll-touchclick');
            cmp.$container.bind ('touchstart.fsscroll', function (e)
            {
                var touch = e.originalEvent.touches[ 0 ];
                touchStartY = touch.pageY;
                moved = false;
                moving = true;
            });
            
            cmp.$container.bind ('touchmove.fsscroll', function (ev)
            {
                if ( !moving) {
                    return;
                }
                
                var touchPos = ev.originalEvent.touches[ 0 ].pageY;
                cmp2.scrollBy (touchStartY - touchPos);
                
                var moved2 = Math.abs (touchStartY - touchPos) > 5;
                if (moved2) {
                    touchStartY = touchPos;
                }
                
                moved = moved || moved2;
                
                return false;
            });
            
            cmp.$container.bind ('touchend.fsscroll', function (e)
            {
                moving = false;
            });
            
            cmp.$container.bind ('click.fsscroll-touchclick', function (e)
            {
                if (moved) {
                    moved = false;
                    return false;
                }
            });
        };
        
        this.arrowDownClick = function ()
        {
            var cmp2 = cmp;
            var scrollTimeout = null;
            doScroll = function ()
            {
                cmp2.scrollBy ( -cmp2.settings.arrowButtonSpeed);
                scrollTimeout = setTimeout (doScroll, cmp2.settings.arrowRepeatFreq);
            };
            
            $ (this).bind ('mouseup.fsscroll', function ()
            {
                clearTimeout (scrollTimeout);
                scrollTimeout = null;
            }).bind ('mouseout.fsscroll', function ()
            {
                clearTimeout (scrollTimeout);
                scrollTimeout = null;
            });
            
            doScroll ();
            return false;
        };
        
        this.arrowUpClick = function ()
        {
            var cmp2 = cmp;
            var scrollTimeout = null;
            doScroll = function ()
            {
                cmp2.scrollBy (cmp2.settings.arrowButtonSpeed);
                scrollTimeout = setTimeout (doScroll, cmp2.settings.arrowRepeatFreq);
            };
            
            $ (this).bind ('mouseup.fsscroll', function ()
            {
                clearTimeout (scrollTimeout);
                scrollTimeout = null;
            }).bind ('mouseout.fsscroll', function ()
            {
                clearTimeout (scrollTimeout);
                scrollTimeout = null;
            });
            
            doScroll ();
            return false;
        };
        
        this.getPaneWidth = function ()
        {
            return cmp.$container.width () - cmp.settings.verticalGutter - cmp.$verticalTrack.width ()
        };
        
        this.addTinyMCE = function (content)
        {
            var found = false;
            $ ('textarea.tinymce').each (function ()
            {
                tinyMCE.execCommand ('mceAddControl', true, $ (this).attr ('id'));
                found = true;
            });
            if (found && tinyMCE.activeEditor) {
                tinyMCE.activeEditor.setContent (content);
            }
        };
        
        this.removeTinyMCE = function ()
        {
            var content = '';
            if (tinyMCE.activeEditor) {
                content = tinyMCE.activeEditor.getContent ();
                $ ('textarea.tinymce').each (function ()
                {
                    tinyMCE.execCommand ('mceRemoveControl', false, $ (this).attr ('id'));
                });
            }
            return content;
        };
        
        this.removeScroll = function ($element)
        {
// var content = cmp.removeTinyMCE();
            
            var childsHtml = $element.find ('.fsScrollPane').html ();
            $element.html (childsHtml);
            $element.css ({
                'margin-right' : 0
            });
            
// cmp.addTinyMCE(content);
            
            return true;
        };
        
        this.clickOnTrack = function (e)
        {
            
            var startY = e.pageY - $ (this).offset ().top;
            var position = startY - cmp.$verticalDrag.height () / 2;
            cmp.positionDragY (position);
        };
        
        this.dragMouseDown = function (e)
        {
            $ ('html').bind ('dragstart.fsscroll selectstart.fsscroll', function ()
            {
                return false;
            });
            
            var startY = e.pageY - cmp.$verticalDrag.position ().top;
            
            $ ('html').bind ('mousemove.fsscroll', function (e)
            {
                cmp.positionDragY (e.pageY - startY);
            }).bind ('mouseup.fsscroll mouseleave.fsscroll', cmp.cancelDrag);
            return false;
        };
        
        this.cancelDrag = function ()
        {
            $ ('html').unbind ('dragstart.fsscroll selectstart.fsscroll mousemove.fsscroll mouseup.fsscroll mouseleave.fsscroll');
        };
        
        this.initMousewheel = function ()
        {
            var mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.fsscroll' : 'mousewheel.fsscroll';
            cmp.$container.unbind (mwEvent).bind (mwEvent, function (event, delta, deltaX, deltaY)
            {
                cmp.scrollBy (deltaY * cmp.settings.mouseWheelSpeed);
            });
        };
        
        this.positionDragY = function (position)
        {
            var begin = false;
            var end = false;
            if (position < 0) {
                position = 0;
                begin = true;
            } else if (position >= cmp.$verticalTrack.height () - cmp.$verticalDrag.height ()) {
                position = cmp.$verticalTrack.height () - cmp.$verticalDrag.height ();
                end = true;
            }
            cmp.$verticalDrag.css ('top', position + 'px');
            
            var k = (cmp.$verticalTrack.height () - cmp.$verticalDrag.height ()) / position;
            
            var paneHeight = cmp.$pane.height ();
            var containerHeight = cmp.$container.height ();
            var top = (containerHeight - paneHeight) / k;
            
            cmp.$pane.css ('top', top + 'px');
            
            if (cmp.settings.showArrows) {
                var $arrowDown = cmp.$verticalTrack.find ('.fsScrollArrowDown');
                var $arrowUp = cmp.$verticalTrack.find ('.fsScrollArrowUp');
                
                if (begin) {
                    $arrowDown.removeClass ('fsScrollDisabled');
                    $arrowUp.addClass ('fsScrollDisabled');
                } else if (end) {
                    $arrowUp.removeClass ('fsScrollDisabled');
                    $arrowDown.addClass ('fsScrollDisabled');
                } else {
                    $arrowUp.removeClass ('fsScrollDisabled');
                    $arrowDown.removeClass ('fsScrollDisabled');
                }
            }
        };
        
        this.scrollDown = function ()
        {
            var containerHeight = cmp.$container.height ();
            var paneHeight = cmp.$pane.height ();
            var top = containerHeight - paneHeight;
            cmp.scrollBy (top);
        };
        
        this.scrollBy = function (verticalPx)
        {
            var begin = false;
            var end = false;
            
            var currentTop = parseInt (cmp.$pane.position ().top, 10);
            var top = currentTop + verticalPx;
            top = top > 0 ? 0 : top;
            var paneHeight = cmp.$pane.height ();
            var containerHeight = cmp.$container.height ();
            if (paneHeight + top < containerHeight) {
                top = containerHeight - paneHeight;
                end = true;
            } else if (0 == top) {
                begin = true;
            }
            
            cmp.$pane.css ('top', top + 'px');
            
            var k = (paneHeight - containerHeight) / top;
            
            var trackHeight = cmp.$verticalTrack.height ();
            var dragTop = (trackHeight - cmp.$verticalDrag.height ()) / k * -1;
            cmp.$verticalDrag.css ('top', dragTop + 'px');
            
            if (cmp.settings.showArrows) {
                var $arrowDown = cmp.$verticalTrack.find ('.fsScrollArrowDown');
                var $arrowUp = cmp.$verticalTrack.find ('.fsScrollArrowUp');
                
                if (begin) {
                    $arrowDown.removeClass ('fsScrollDisabled');
                    $arrowUp.addClass ('fsScrollDisabled');
                } else if (end) {
                    $arrowUp.removeClass ('fsScrollDisabled');
                    $arrowDown.addClass ('fsScrollDisabled');
                } else {
                    $arrowUp.removeClass ('fsScrollDisabled');
                    $arrowDown.removeClass ('fsScrollDisabled');
                }
            }
        };
        
        this.refreshFsScroll = function ($element)
        {
            var paneWidth = $element.width ();
            var paneHeight = $element.height ();
            
            cmp.$container = $element.children ('.fsScrollContainer');
            cmp.$pane = cmp.$container.children ('.fsScrollPane');
            
            if ($element.height () >= cmp.$pane.height ()) {
                return cmp.removeScroll ($element);
            }
            
            cmp.$verticalTrack = cmp.$container.children ('.fsScrollVerticalBar');
            cmp.$verticalDrag = cmp.$verticalTrack.find ('.fsScrollDrag');
            
            cmp.$container.css ({
                'width' : paneWidth + 'px',
                'height' : paneHeight + 'px'
            });
            
            cmp.$pane.css ({
                'width' : cmp.getPaneWidth ()
            });
            
            if (cmp.settings.showArrows) {
                var scrollTrackHeight = cmp.$verticalTrack.height () - cmp.$verticalTrack.find ('.fsScrollArrowUp').height () - cmp.$verticalTrack.find ('.fsScrollArrowDown').height ();
                cmp.$verticalTrack.find ('.fsScrollTrack').height (scrollTrackHeight);
            }
        };
        
        return this.each (function ()
        {
            var $element = $ (this);
            cmp.initFsScroll ($element);
        });
    };
    
}) (jQuery);
