/**
 * Sets limit to textarea
 *
 * @param counterLabel expression of counter label elements (simple caption for counter)
 * @param counter expression of counter elements (display remaining characters)
 * @param limit
 */
(
    function ($)
    {
        $.fn.setLimit = function(limit)
        {
            return this.each(
                    function ()
                    {
                        SetFieldLimit($(this), limit);
                    });
        }

        /**
         * Sets limit to field.
         *
         * @param field Element
         * @param limit Number - character limit
         */
        function SetFieldLimit(field, limit)
        {
            limit = limit || field.attr("maxlength") || 0;

            if (!limit)
            {
                return;
            }

            if (field.is("input"))
            {
                // already has limits
                field.attr("maxlength", limit);
                return;
            }

            if (!field.is("textarea"))
            {
                // shouldn't have limits
                return;
            }

            field.data("maxlength", limit);
            SetEventHandlers(field, limit);
        }

        /**
         * Sets event handlers to limit the text area.
         *
         * @param field Element
         * @param limit Number
         */
        function SetEventHandlers(field, limit)
        {
            if ($.browser.mozilla || $.browser.opera)
            {
                field.bind
                (
                    "input",
                    function ()
                    {
                        OnInput(field, limit);
                    }
                );
            }
            else
            {
                field.bind
                (
                    "keypress",
                    function (event)
                    {
                        return OnKeyPress(field, event, limit);
                    }
                );

                field.bind
                (
                    "paste",
                    function ()
                    {
                        return OnPaste(field, limit);
                    }
                );

                field.bind
                (
                    "drop",
                    function ()
                    {
                        return OnPaste(field, limit);
                    }
                );
            }
        }

        /**
         * On key press event handler.
         *
         *
         * @param field Element
         * @param event Event
         * @param limit Number
         * @return Boolean returns true if field limit is not reached, in other cases returns false
         */
        function OnKeyPress(field, event, limit)
        {
            var len = field.val().length;
            if (len < limit)
            {
                return true;
            }

            var keyCode = event.keyCode;
            switch(keyCode)
            {
                case 8:
                case 9:
                case 17:
                case 36:
                case 35:
                case 37:
                case 38:
                case 39:
                case 40:
                case 46:
                case 65:
                    return true;
                default:
                    return false;
            }
        }

        /**
         * On input event handler.
         *
         * @param field Element
         * @param limit Number
         */
        function OnInput(field, limit)
        {
            var len = field.val().length;
            if (len <= limit)
            {
                return;
            }

            field.val(field.val().substr(0, limit));
        }

        /**
         * On paste event handler.
         *
         * @param field Element
         * @param limit Number
         */
        function OnPaste(field, limit)
        {
            var len = field.val().length;
            var result = true;
            var clip = window.clipboardData.getData("Text");
            var clipLen = clip.length;
            var availLen = limit - len;

            if (availLen > 0)
            {
                if (availLen < clipLen)
                {
                    clip = clip.substr(0, availLen);
                    window.clipboardData.setData("Text", clip);
                }
                return true;
            }
            return false;
        }
    }
)(jQuery);

/**
 * Creates counter for field
 */
(
    function ($)
    {
        var idResizeWindowsTimeout = null;
        var counterContainer = null;
        var counterLabel = null;
        var counter = null;
        var isShow = true;

        $.fn.setCounter = function (label)
        {
            label = label || "";

            return this.each
                    (
                        function ()
                        {
                            var el = $(this);
                            CreateCounter(label);
                            SetEventHandlers(el, label, el.attr("maxlength") || el.data("maxlength"));
                        }
                    );
        }

        /**
         * Creates counter element and assign counter and counterContainer variables.
         * @param label String
         */
        function CreateCounter(label)
        {
            if (counter && counterContainer && counterLabel)
            {
                return;
            }

            counterLabel = $(document.createElement("div")).addClass("chars_counter_label").text(label);

            counter = $(document.createElement("div")).addClass("chars_count");

            var tempDiv = $(document.createElement("div"))
                        .addClass("chars_counter_container")
                        .append(counterLabel).append(counter);

            tempDiv = $(document.createElement("div"))
                        .css({
                                position:"absolute",
                                zIndex:10000
                            })
                        .append(tempDiv);
            $(document.body).append(tempDiv);
            tempDiv.hide();
            counterContainer = tempDiv;
        }

        /**
         * Shows counter for field
         *
         * @param field Element
         * @param label String
         */
        function ShowCounter(field, label)
        {
            var offset = field.offset();
            var fieldWidth = field.outerWidth();
            var fieldHeight = field.outerHeight();

            var containerWidth = counterContainer.outerWidth();

            var left = offset.left + (fieldWidth - containerWidth) + "px";
            var top = offset.top + fieldHeight + "px";

            counterContainer.css({left: left, top: top});
            counterContainer.show();
            counterLabel.text(label);
        }

        /**
         * Sets event handlers for counter.
         * @param field Element
         * @param label String
         */
        function SetEventHandlers(field, label, charsLimit)
        {
            if (!charsLimit || charsLimit <= 0)
            {
                return;
            }

            field.focus
            (
                function ()
                {
                    UpdateCounter(counter, field, charsLimit);
                    ShowCounter(field, label);
                }
            )

            field.blur
            (
                function ()
                {
                    counterContainer.hide();
                }
            )

            if ($.browser.mozilla || $.browser.opera)
            {
                field.bind
                (
                    "input",
                    function ()
                    {
                        UpdateCounter(counter, field, charsLimit);
                    }
                );
            }
            else
            {
                field.keyup
                (
                    function ()
                    {
                        UpdateCounter(counter, field, charsLimit);
                    }
                );
            }

            function onResizeWindow()
            {
                var left = field.offset().left + (field.outerWidth() - counterContainer.outerWidth()) + "px";
                counterContainer.css({left: left});
            }

            $(window).resize(
                function ()
                {
                    if (idResizeWindowsTimeout)
                    {
                        clearTimeout(idResizeWindowsTimeout);
                    }

                    idResizeWindowsTimeout = setTimeout(onResizeWindow, 100);

                }
            );
        }

        /**
         * Updates counter according to current field state
         *
         * @param counter Element
         * @param field Element
         * @param charsLimit Number - max length of field
         */
        function UpdateCounter(counter, field, charsLimit)
        {
            var diff = charsLimit - field.val().length;
            diff = diff < 0 ? 0 : diff;
            counter.text(diff);
        }
    }
)(jQuery);

