/**============================================================================
 *  submit_disabler.js
 *
 * @projectDescription Сильно упрощенная версия аналогичного модуля,
 * применяемого в административной части сайта НОМОС-БАНКа. Делает кнопку
 * сабмита формы неактивной, если хоть одно поле с классом js_required
 * заполнено неверно.
 *-----------------------------------------------------------------------------
 * @version 1.0
 *
 * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
 * @copyright Станислав Муравьёв, Алексей Лури <webmaster@starr.ru>, 2009
 *-----------------------------------------------------------------------------
 * Необходим jQuery, подключать только после него.
 * В HTML-коде необхоидмо присвоить всем кнопкам, доступность которых зависит
 * от факта заполненности всех полей, класс disabled_submit, всем обязательным
 * полям - класс js_required. В CSS можно определить класс mistake, который
 * добавляется каждому незаполненному полю. Поля, имеющие класс password,
 * должны быть идентичны, в поле s1024 должно быть не менее 1024 символов,
 * в поле imagefile должна быть ссылка на файл с расширением JPG, GIF, JPEG, JPE
 * или PNG; в поле с классом email должен быть корректный адрес электронной
 * почты. В случае несоответствий добавляются классы mistake_password,
 * mistake_s1024, mistake_imagefile, mistake_email соответственно.
 * Также поля с классом email должны иметь РАЗНЫЕ значения, иначе ставится
 * класс mistake_duplicate_email.
 *=============================================================================
 */
 
 /**
  * Метод, запускаемый при загрузке страницы.
  *
  * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
  */
$(function(){
    /**
     * Набор неактивных кнопок
     *
     * @type {jQuery}
     */
    var jqSubmits = $(".disabled_submit");

    //Если таковые вообще есть, то делаем дальше, иначе нам делать нечего.
    if(jqSubmits.length > 0){
        /**
         * Набор обязательных полей
         *
         * @type {jQгery}
         */
        var jqRequiredFields = $(".js_required");

        /**
         * Поля для ввода паролей
         *
         * @type {jQuery}
         */
        var jqEmailFields = $(".email");

        /**
         * Поля для ввода адресов электронной почты
         *
         * @type {jQuery}
         */
        var jqPasswordFields = $(".password");

        /**
         * Поля длиной не менее 1024 символов
         *
         * @type {jQuery}
         */
        var jq1024SymbolsFields = $(".s1024");

        /**
         * Поля для ввода имени файла с картинкой
         *
         * @type {jQuery}
         */
        var jqImageFields = $(".imagefile");

        //Назначаем обработчики
        jqRequiredFields.click(checkForDisable).keyup(checkForDisable).blur(checkForDisable);

        //И один раз запускаем сами
        checkForDisable();
    }

    /**
     * Метод проверяет необходимость активации/деактивации кнопок и производит
     * ее. К незаполненным элементам добавляется класс mistake.
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function checkForDisable(){
        //Внимание! Написать условие, содержащее конкатенацию значений
        //без присвоения их переменным нельзя, так как  используется упрощенное
        //вычисление логических выражений и как только получим ложь, выполнение
        //остальных функций прекращается. А нам необходимо выполнить
        //все без исключения!

        /**
         * Есть ли незаполненные обязательные поля?
         *
         * @type {Boolean}
         */
        var bThereIsEmptyRequiredField = requiredFieldsContainEmptyOne();

        /**
         * Есть ли поля, которые должны содержать больше, чем 1024 символа,
         * в которых символов меньше?
         *
         * @type {Boolean}
         */
        var bThereIsIncorrect1024Field =
                                      neededFieldsContainsLessThan1024Symbols();

        /**
         * Есть ли поля ввода электронной почты, содержащие неверные значения?
         *
         * @type {Boolean}
         */
        var bThereIsIncorrectEmailField = emailFieldsContainIncorrectEmail();

        /**
         * Есть ли поля ввода паролей с разными значениями?
         *
         * @type {Boolean}
         */
        var bThereAreDifferentPasswordFields = passwordFieldsAreDifferent();

        /**
         * Есть ли поля ввода электронной почты с одинаковыми значениями?
         *
         * @type {Boolean}
         */
        var bThereAreIdenticalEmails = emailFieldsAreIdentical();

        /**
         * Есть ли поля для ввода имен графических файлов с неверными
         * расширениями?
         *
         * @type {Boolean}
         */
        var bThereIsIncorrectImageField = imageFieldsIsIncorrect();

        if(bThereAreDifferentPasswordFields ||
           bThereIsIncorrectEmailField ||
           bThereIsIncorrect1024Field ||
           bThereAreIdenticalEmails ||
           bThereIsIncorrectImageField ||
           bThereIsEmptyRequiredField){
            //Есть ошибочные поля, кнопки должны быть недоступны
            jqSubmits.attr("disabled", "disabled");
        }
        else{
            //Все верно, кнопки доступны
            jqSubmits.removeAttr("disabled");
        }
    }

    /**
     * Проверка факта заполненности обязательных полей. Для незаполненных
     * обязательных полей ставится класс mistake.
     *
     * @return {Boolean} Истина, если все обязательные поля заполнены, и ложь
     *                   в противном случае
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function requiredFieldsContainEmptyOne(){
        /**
         * Нужно ли деактивировать кнопки
         * 
         * @type {Boolean}
         */
        var bNeedToDisable = false;

        //Прогуляемся по полям, iNumber - порядковый номер элемента
        jqRequiredFields.each(function(/* int */ iNumber){
            /**
             * Текущий элемент
             *
             * @type {jQuery}
             */
            var jqCurrentElement = $(this);

            if(jqCurrentElement.val() == ""){
                //Есть пустой элемент - ему класс mistake и флаг деактивации
                jqCurrentElement.addClass("mistake");
                bNeedToDisable = true;
            }
            else{
                jqCurrentElement.removeClass("mistake");
            }
        });

        return bNeedToDisable;
    }

    /**
     * Проверка, что поля, которые должны содержать не менее 1024 символов,
     * содержат 1024 символов или более.
     *
     * @return {Boolean} Истина, если все поля, которые должны содержать
     *                   не менее 1024 символов, содержат не менее 1024 символов
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function neededFieldsContainsLessThan1024Symbols(){
        /**
         * Нужно ли деактивировать кнопки
         * 
         * @type {Boolean}
         */
        var bNeedToDisable = false;

        //Прогуляемся по полям, iNumber - порядковый номер элемента
        jq1024SymbolsFields.each(function(/* int */ iNumber){
            /**
             * Текущий элемент
             *
             * @type {jQuery}
             */
            var jqCurrentElement = $(this);

            /**
             * Значение этого элемента
             *
             * @type {String}
             */
            var sElementValue = jqCurrentElement.val();

            if(sElementValue.length < 1024){
                //Неверно - класс mistake и флаг деактивации
                jqCurrentElement.addClass("mistake_s1024");
                bNeedToDisable = true;
            }
            else{
                jqCurrentElement.removeClass("mistake_s1024");
            }
        });

        return bNeedToDisable;
    }

    /**
     * Проверка, что поля, которые должны содержать пароли, идентичны.
     *
     * @return {Boolean} Истина, если все поля, которые должны содержать пароли,
     *                   идентичны.
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function passwordFieldsAreDifferent(){
        /**
         * Нужно ли деактивировать кнопки
         * 
         * @type {Boolean}
         */
        var bNeedToDisable = false;

        if(jqPasswordFields.length > 1){
            //Имеет смысл, если полей больше 1
            /**
             * Значение пароля в первом поле
             *
             * @type {String}
             */
            var sCurrentPassword = jqPasswordFields.val();

            //Прогуляемся по полям, iNumber - порядковый номер элемента
            jqPasswordFields.each(function(/* int */ iNumber){
                /**
                 * Текущий элемент
                 *
                 * @type {jQuery}
                 */
                var jqCurrentElement = $(this);

                if(jqCurrentElement.val() != sCurrentPassword){
                    //Облом - несоответствие
                    bNeedToDisable = true;
                    //Дальше проверять не имеет смысла
                    return false;
                }
            });

            if(bNeedToDisable){
                jqPasswordFields.addClass("mistake_password");
            }
            else{
                jqPasswordFields.removeClass("mistake_password");
            }
        }
        return bNeedToDisable;
    }

    /**
     * Проверка, что поля, которые должны содержать адрес электронной почты,
     * содержат корректный адрес электронной почты
     *
     * @return {Boolean} Истина, если все поля, которые должны содержать
     *                   адрес электронной почты, содержат корректный адрес
     *                   электронной почты.
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function emailFieldsContainIncorrectEmail(){
        /**
         * Нужно ли деактивировать кнопки
         * 
         * @type {Boolean}
         */
        var bNeedToDisable = false;

        //Прогуляемся по полям, iNumber - порядковый номер элемента
        jqEmailFields.each(function(/* int */ iNumber){
            /**
             * Текущий элемент
             *
             * @type {jQuery}
             */
            var jqCurrentElement = $(this);

            if(isEmail(jqCurrentElement.val())){
                //Неверно - класс mistake и флаг деактивации
                jqCurrentElement.removeClass("mistake_email");
            }
            else{
                jqCurrentElement.addClass("mistake_email");
                bNeedToDisable = true;
            }
        });

        return bNeedToDisable;
    }

    /**
     * Проверка, что переданное значение является корректным адресом
     * электронной почты.
     *
     * @param {String} sString Проверяемое значение
     *
     * @return {Boolean} Истина, если переданное значение является корректным
     *                   адресом электронной почты
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function isEmail(sString){
        /**
         * Регулярное выражение для проверки адреса электронной почты
         * 
         * @type {RegExp}
         */
        var reEmail =
                 /^[a-zA-Z0-9\_\-\.]+\@[a-zA-Z0-9\-\.]{2,}\.[a-zA-Z0-9\-]{2,}$/;

        return reEmail.test(sString);
    }

    /**
     * Проверка, что поля, которые содержат адреса электронной почты, различны.
     *
     * @return {Boolean} Истина, если все поля, которые должны содержать адреса
     *                   электронной почты, различны.
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function emailFieldsAreIdentical(){
        /**
         * Нужно ли деактивировать кнопки
         * 
         * @type {Boolean}
         */
        var bNeedToDisable = false;

        if(jqEmailFields.length > 1){
            //Имеет смысл, если полей больше 1
            /**
             * Существующие значения адресов
             *
             * @type {Array}
             */
            var asEmails = new Array();

            //Прогуляемся по полям, iNumber - порядковый номер элемента
            jqEmailFields.each(function(/* int */ iNumber){
                /**
                 * Значение текущего элемента
                 *
                 * @type {String}
                 */
                var sCurrentValue = $(this).val();
                //Переведем в нижний регистр
                sCurrentValue = sCurrentValue.toLowerCase();
                if($.inArray(sCurrentValue, asEmails) < 0){
                    //Если нет такого элемента в нашем массиве, добавим его
                    asEmails.push(sCurrentValue);
                }
            });
            bNeedToDisable = (asEmails.length != jqEmailFields.length);

            if(bNeedToDisable){
                jqEmailFields.addClass("mistake_duplicate_email");
            }
            else{
                jqEmailFields.removeClass("mistake_duplicate_email");
            }
        }
        return bNeedToDisable;
    }

    /**
     * Проверка, что поля, в которых нужно указывать имя файла для загрузки,
     * содержат имя файла с расширением JPG, JPEG, JPE, GIF или PNG.
     *
     * @return {Boolean} Истина, если все поля,  в которых нужно указывать имя
     *                   файла для загрузки, содержат корректное имя файла.
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function imageFieldsIsIncorrect(){
        /**
         * Нужно ли деактивировать кнопки
         * 
         * @type {Boolean}
         */
        var bNeedToDisable = false;

        //Если есть хотя бы одно соответствующее поле (иначе бессмыссленно)
        if(jqImageFields.length > 0){
            /**
             * Список доступных расширений имен файлов
             *
             * @type {Array}
             */
            var asCorrectExtensions = getCorrectExtensions();

            //Прогуляемся по полям, iNumber - порядковый номер элемента
            jqImageFields.each(function(/* int */ iNumber){
                /**
                 * Текущий элемент
                 *
                 * @type {jQuery}
                 */
                var jqElement = $(this);
                //Сравниваем
                if($.inArray(getFileExtension(jqElement.val().toLowerCase()),
                             asCorrectExtensions) >= 0){
                    //Расширение корректно
                    jqElement.removeClass("mistake_imagefile");
                }
                else{
                    //Расширение некорректно
                    jqElement.addClass("mistake_imagefile");
                    bNeedToDisable = true;
                }
            });
        }
        return bNeedToDisable;
    }

    /**
     * Из переданного полного имени файла выделяет расширение (часть имени после
     * последней точки, включая точку).
     *
     * @param {String} sFileName Имя или полный путь к файлу."
     *
     * @return {String} Расширение файла (вместе с точкой)
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function getFileExtension(sFileName){
        /**
         * Результат работы метода
         *
         * @type {String}
         */
        var sResult = "INCORRECT_EXTENSION";

        /**
         * Позиция последней точки в переданном имени файла
         *
         * @type {int}
         */
        var iLastDotPosition = sFileName.lastIndexOf(".");

        if(iLastDotPosition > 0){
            //Есть расширение и имя файла (не менее 1 символа).
            sResult = sFileName.slice(iLastDotPosition);
        }
        return sResult;
    }

    /**
     * Возвращает массив допустимых расширений графических файлов.
     *
     * @return {Array} Массив строк, содержащий расширения (с точками) в нижнем
     *                 регистре
     *
     * @author Станислав Муравьёв <webmaster@starr.ru> 28.03.2009
     */
    function getCorrectExtensions(){
        return [".png", ".jpg", ".jpe", ".jpeg", ".gif"]
    }
});