/**
 * f7 Form jQuery Plugin
 *
 * based on Uni-Form jQuery Plugin with Validation
 *
 * @see http://sprawsm.com/uni-form/
 * @license MIT http://www.opensource.org/licenses/mit-license.php
 */
jQuery.fn.f7Form = function(extended_settings) {

    /**
     * Self reference for closures
     *
     * @var object
     */
    var self = this;

    /**
     * Object extending the defaults object
     *
     * @var object
     */
    var settings = jQuery.extend(
        jQuery.fn.f7Form.defaults,
        extended_settings
    );

    /**
     * Language abstration string
     *
     * to extend, use a script tag to include a file from the localization folder
     *
     * @var object
     */
    var i18n_strings = jQuery.fn.f7Form.language;

    /**
     * Supported validators
     *
     * @var Object
     */
    this.validators = {

        /**
         * Get the value for a validator that takes parameters
         *
         * @param string name
         * @param string all classes on element
         *
         * @return mixed || null
         */
        get_val : function(name, classes, default_value) {
            var value = default_value;
            classes = classes.split(' ');
            for(var i = 0; i < classes.length; i++) {
                if(classes[i] == name) {
                    if((classes[i + 1] != 'undefined') && ('val-' === classes[i + 1].substr(0,4))) {
                        value = parseInt(classes[i + 1].substr(4),10);
                        return value;
                    }
                }
            }
            return value;
        },

        /**
         * Value of field is not empty, whitespace will be counted as empty
         *
         * @param jQuery field
         * @param string caption
         */
        required : function(field, caption) {
        	if (! caption) {
        		caption = "This field";
        	}
            if(field.is(':radio')) {
                var name = field.attr('name');
                if($("input[name=" + name + "]:checked").length) {
                    return true;
                }
                return i18n('req_radio', caption);
            }
            if(field.is(':checkbox')) {
                var name = field.attr('name');
                if(field.is(":checked")) {
                    return true;
                }
                return i18n('req_checkbox', caption);
            }
            if(jQuery.trim(field.val()) == '') {
                return i18n('required', caption);
            }
            return true;
        },

        /**
         * Value is shorter than allowed
         *
         * @param jQuery field
         * @param sting caption
         */
        validateMinLength : function(field, caption) {
            var min_length = this.get_val('validateMinLength', field.attr('class'), 0);

            if((min_length > 0) && (field.val().length < min_length)) {
                return i18n('minlength', caption, min_length);
            }
            return true;
        },

        /**
         * Value is less than min
         *
         * @param jQuery field
         * @param sting caption
         */
        validateMin : function(field, caption) {
            var min_val = this.get_val('validateMin', field.attr('class'), 0);

            if((parseInt(field.val(),10) < min_val)) {
                return i18n('min', caption, min_val);
            }
            return true;
        },

        /**
         * Value is longer than allowed
         *
         * @param jQuery field
         * @param string caption
         */
        validateMaxLength : function(field, caption) {
            var max_length = this.get_val('validateMaxLength', field.attr('class'), 0);

            if((max_length > 0) && (field.val().length > max_length)) {
                return i18n('maxlength', caption, max_length);
            }
            return true;
        },

        /**
         * Value is greater than max
         *
         * @param jQuery field
         * @param sting caption
         */
        validateMax : function(field, caption) {
            var max_val = this.get_val('validateMax', field.attr('class'), 0);

            if((parseInt(field.val(),10) > max_val)) {
                return i18n('max', caption, max_val);
            }
            return true;
        },

        /**
         * Element has same value as that of the target Element
         *
         * This does not use the val-{name} format, and instead
         * is only the name of the element
         *
         * class="validateSameAs field_id"
         *
         * @param jQuery field
         * @param string caption
         */
        validateSameAs : function(field, caption) {
            var classes = field.attr('class').split(' ');
            var target_field_name = '';

            for(var i = 0; i < classes.length; i++) {
                if(classes[i] == 'validateSameAs') {
                    if(classes[i + 1] != 'undefined') {
                        target_field_name = classes[i + 1];
                        break;
                    }
                }
            }

            if(target_field_name) {
                var target_field = jQuery('input[name="' + target_field_name + '"]');
                if(target_field.length > 0) {
                    if(target_field.val() != field.val()) {
                        var target_field_caption = target_field.closest('div.'+settings.holder_class).find('label').text().replace('*','');
                        return i18n('same_as', caption, target_field_caption);
                    }
                }
            }

            return true;
        },


        /**
         * Whole numbers are allowed with spaces as seperators.
         *
         * @param jQuery field
         * @param string caption
         */
        validateInteger : function(field, caption) {
            if(field.val().replace(/ /, '').match(/^[\+-]?[0-9]+$/) || field.val() == '') {
                return true;
            }
            return i18n('integer', caption);
        },

        /**
         * Whole numbers are allowed with spaces or commas as seperators.
         *
         * @param jQuery field
         * @param string caption
         */
        validateIntegerCommas : function(field, caption) {
            if(field.val().replace(/(,| )/, '').match(/^[\+-]?[0-9]+$/) || field.val() == '') {
                return true;
            }
            return i18n('integer', caption);
        },


        /**
         * Valid hex color
         *
         * @param jQuery field
         * @param string caption
         */
        validateHexcolor : function(field, caption) {
            if(field.val().match(/^#?(([0-F]{3}){1,2})$/)) {
                return true;
            } else {
                return i18n('hexcolor', caption);
            }
        },

        /**
         * Valid system string (suitable for filenames)
         *
         * @param jQuery field
         * @param string caption
         */
        validateSystem : function(field, caption) {
            if(field.val().match(/[\\/:*?"<>|%#&[\]{}$()]/)) {
                return i18n('system', caption);
            } else {
                return true;
            }
        },



        /**
         * Valid email address
         *
         * @param jQuery field
         * @param string caption
         */
        validateEmail : function(field, caption) {
            if(field.val().match(/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,63})+$/)) {
                return true;
            } else {
                return i18n('email', caption);
            }
        },

        /**
         * Valid URL (http://,https://,ftp://)
         *
         * @param jQuery field
         * @param string caption
         */
        validateUrl : function(field, caption) {
//            if(field.val().match(/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i)) {
            if(field.val().match(/^((ht|f)tps?:\/\/)?([\w\-]+\.)+([\w]{2,5})(:\d+)?(\/[\w\-.?=%;]*)*\/?/i)) {
                return true;
            }
            return i18n('url', caption);
        },

        /**
         * Number is only valid value (integers and floats)
         *
         * @param jQuery field
         * @param string caption
         */
        validateDecimal : function(field, caption) {
            if(field.val().match(/^[\+-]?[0-9 ]+(\.[0-9 ]+)?$/) || field.val() == '') {
                return true;
            }
            return i18n('decimal', caption);
        },

        /**
         * Suitable for US currency, or anything in the -1,123.54 format
         *
         * @param jQuery field
         * @param string caption
         */
        validateCurrency : function(field, caption) {
            if(field.val().replace(/(,| )/, '').match(/^(\+|(-))?([0-9]{0,10})(\.([0-9]{2}))?$/) || field.val() == '') {
                return true;
            }
            return i18n('currency', caption);
        },

	    /**
         * Suitable for US postal format
         *
         * @param jQuery field
         * @param string caption
         */
        validatePostalUS : function(field, caption) {
            if(field.val().match(/^[0-9]{5}(-[0-9]{4})?$/) || field.val() == '') {
                return true;
            }
            return i18n('postalUS', caption);
        },
	    /**
         * Suitable for CA postal format
         *
         * @param jQuery field
         * @param string caption
         */
        validatePostalCA : function(field, caption) {
            if(field.val().match(/^[ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d$/) || field.val() == '') {
                return true;
            }
            return i18n('postalCA', caption);
        },
	    /**
         * Suitable for UK postal format
         *
         * @param jQuery field
         * @param string caption
         */
        validatePostalUK : function(field, caption) {
            if(field.val().match(/^[A-Z]{1,2}\d[A-Z\d]? \d[ABD-HJLNP-UW-Z]{2}$/) || field.val() == '') {
                return true;
            }
            return i18n('postalUK', caption);
        },

        /**
         * US Tax ID, SSN or EIN
         *
         * @param jQuery field
         * @param string caption
         */
        validateTaxidUS : function(field, caption) {
            if(field.val().match(/^(([07][1-7]|1[0-6]|2[0-7]|[35][0-9]|[468][0-8]|9[0-589])-\d{7}|\d{3}-\d{2}-\d{4})$/) || field.val() == '') {
                return true;
            }
            return i18n('taxidUS', caption);
        },

        /**
         * Letters only
         *
         * @param jQuery field
         * @param string caption
         */
        validateAlpha : function(field, caption) {
            if(field.val().match(/^[a-zA-Z]+$/)) {
                return true;
            }
            return i18n('alpha', caption);
        },

        /**
         * Letters and numbers
         *
         * @param jQuery field
         * @param string caption
         */
        validateAlphaNum : function(field, caption) {
            if(field.val().match(/\W/)) {
                return i18n('alphanum', caption);
            }
            return true;
        },

        /**
         * Simple phrases
         *
         * @param jQuery field
         * @param string caption
         */
        validatePhrase : function(field, caption) {
            if((field.val() == '') || field.val().match(/^[\w\d\.\-_\(\)\*'# :,]+$/i)) {
                return true;
            }
            return i18n('phrase', caption);
        },

        /**
         * Phone number
         *
         * @param jQuery field
         * @param string caption
         */
        validatePhoneUS : function(field, caption) {
            phoneNumber = /^(1\D?)?\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})(x\d+)?$/;
            if(phoneNumber.test(field.val())) {
                return true;
            }
            return i18n('phoneUS', caption);
        },


        /**
         * Callback validator
         *
         * Lets you define your own validators. Usage:
         *
         * <input name="myinput" class="validateCallback my_callback" />
         *
         * This will result in f7Form searching for window.my_callback funciton and
         * executing it with field and caption arguments. Sample implementation:
         *
         * window.my_callback = function(field, caption) {
         *   if(field.val() == '34') {
         *     return true;
         *   } else {
         *     return caption + ' value should be "34"';
         *   }
         * }
         *
         * @param jQuery field
         * @param caption
         */
        validateCallback : function(field, caption) {
            var classes = field.attr('class').split(' ');
            var callback_function = '';

            for(var i = 0; i < classes.length; i++) {
                if(classes[i] == 'validateCallback') {
                    if(classes[i + 1] != 'undefined') {
                        callback_function = classes[i + 1];
                        break;
                    }
                }
            }

            if(window[callback_function] != 'undefined' && (typeof window[callback_function] == 'function')) {
                return window[callback_function](field, caption);
            }

            return i18n('callback', caption, callback_function);
        }

    };

    /**
     * Simple replacement for i18n + sprintf
     *
     * @param string language for for the local i18n object
     * @param mixed substitution vars
     *
     * @return internationalized string
     */
    var i18n = function(lang_key) {
        var lang_string = i18n_strings[lang_key];
        var bits = lang_string.split('%');
        var out = bits[0];
        var re = /^([ds])(.*)$/;
        for (var i=1; i<bits.length; i++) {
            p = re.exec(bits[i]);
            if (!p || arguments[i] == null) continue;
            if (p[1] == 'd') {
                out += parseInt(arguments[i], 10);
            } else if (p[1] == 's') {
                out += arguments[i];
            }
            out += p[2];
        }
        return out;
    };

    /**
     * Uni-Form form validation error
     *
     * @param string title of the form error
     * @param array  list of error messages to show
     *
     * @return false
     */
    var showFormError = function(form, title, messages) {
      if($('#errorMsg').length) {
        $('#errorMsg').remove();
      }
      $message =
          $('<div />')
              .attr('id','errorMsg')
              .html("<h3>" + title + "</h3>");
      if(messages.length) {
          $message.append($('<ol />'));
          for(m in messages) {
              $('ol', $message).append(
                  $('<li />').text(messages[m])
              );
          }
      }
      form.prepend($message);
      $('html, body').animate({
          scrollTop: form.offset().top
      }, 500);
      $('#errorMsg').slideDown();
      return false;
    };

    var showFormSuccess = function(form, title) {
      if($('#okMsg').length) {
        $('#okMsg').remove();
      }
      $message =
          $('<div />')
              .attr('id','okMsg')
              .html("<h3>" + title + "</h3>");
      form.prepend($message);
      $('html, body').animate({
          scrollTop: form.offset().top
      }, 500);
      $('#okMsg').slideDown();
      return false;
    };

    return this.each(function() {
        var form = jQuery(this);

        /**
         * Set the results of form validation on the form element
         *
         * @param object $input jQuery form element
         * @param bool   valid  true if the form value passed all validation
         * @param string text   validation error message to display
         *
         * @return null
         */
        var validate = function($input,valid,text) {
        oldtext = text;
            var $p = $input.closest('div.' + settings.holder_class)
                           .andSelf()
                           .toggleClass(settings.invalid_class, !valid)
                           .toggleClass(settings.error_class, !valid)
                           .toggleClass(settings.valid_class, valid)
                           .find('p.formHint');
            if (! valid && ! $p.data('info-text')) {
                $p.data('info-text', $p.html());
            }
            else if (valid) {
                text = $p.data('info-text');
            }

            if (text !== undefined) {
                $p.html(text);
    	    } else {
                $p.html("");
	    }
        };

        /**
         * Select form fields and attach the higlighter functionality
         *
         */
        form.find(settings.field_selector).each(function(){
            var $input = $(this),
                value = $input.val();

            $input.data('default-color',$input.css('color'));

            if (value === $input.data('default-value') || ! value) {
                $input.not('select').css("color", settings.default_value_color);
                $input.val($input.data('default-value'));
            }
        });

        /**
         * If we've set ask_on_leave we'll register a handler here
         *
         * We need to seriaze the form data, wait for a beforeunload,
         * then serialize and compare for changes
         *
         * If they changed things, and haven't submitted, we'll let them
         * know about it
         *
         */
        if(settings.ask_on_leave || form.hasClass('askOnLeave')) {
            var initial_values = form.serialize();
            $(window).bind("beforeunload", function(e) {
                if((initial_values != form.serialize())
                    && (settings.ask_on_leave || form.hasClass('askOnLeave'))
                ) {
                    return ($.isFunction(settings.on_leave_callback))
                        ? settings.on_leave_callback(form)
                        : confirm(i18n('on_leave'));
                }
            });
        }

        /**
         * Handle the submission of the form
         *
         * Tasks
         *  * Remove any default values from the form
         *  * If prevent_submit is set true, return false if
         *    there are outstanding errors in the form
         *
         * Todo
         *  * it would be novel to use prevent_submit to disable
         *    the submit button in the blur handler
         *
         * @return bool
         */
        form.submit(function(){
            // in the case of a previously failed submit, we'll remove our marker
            form.removeClass('failedSubmit');

            // remove the default values from the val() where they were being displayed
            form.find(settings.field_selector).each(function(){
                if($(this).val() === $(this).data('default-value')) { $(this).val(""); }
            });

            // traverse and revalidate making sure that we haven't missed any fields
            // perhaps if a field was filled in before f7Form was initialized
            // or if blur failed to fire correctly
            if(settings.prevent_submit || form.hasClass('preventSubmit')) {
              // use blur to run the validators on each field
              form.find(settings.field_selector).each(function(){
                $(this).blur();
              });

              if (form
                    .find('.' + settings.invalid_class)
                    .add('.' + settings.error_class).length
              ) {
                form.addClass('failedSubmit');
                return ($.isFunction(settings.prevent_submit_callback))
                    ? settings.prevent_submit_callback(form)
                    : showFormError(form, i18n('submit_msg'), [i18n('submit_help')]);
              }

              settings.ask_on_leave = false;
              form.removeClass('askOnLeave');
              return true;
            }

            // qUnit needs to run this function, and still prevent the submit
            if(form.parents('#qunit-fixture').length) {
              return false;
            }

            settings.ask_on_leave = false;
            form.removeClass('askOnLeave');
            return true;
        });

        /**
         * Set the form focus class
         *
         * Remove any classes other than the focus class and hide the default label text
         *
         */
        form.delegate(settings.field_selector, 'focus', function() {
            form.find('.' + settings.focused_class).removeClass(settings.focused_class);

            var $input = $(this);

            $input.parents().filter('.'+settings.holder_class+':first').addClass(settings.focused_class);

            if($input.val() === $input.data('default-value')){
                $input.val("");
            }

            $input.not('select').css('color',$input.data('default-color'));
        });

        /**
         * Validate a form field on the blur event
         *
         * Search the classnames on the element for the names of
         * validators, and run them as we find them
         *
         * If the validators fail, we trigger either 'success' or 'error' events
         *
         */
        form.delegate(settings.field_selector, 'blur', function() {
            var $input = $(this);
            if ($input.data('ignore-blur')) {
            	return;
            }

            var label  = $(this)
                .closest('div.' + settings.holder_class)
                .find('label').text().replace('*','');

            // remove focus from form element
            form.find('.' + settings.focused_class).removeClass(settings.focused_class);

            // (if empty or equal to default value) AND not required
            if(($input.val() === "" || $input.val() === $input.data('default-value'))
                && !$input.hasClass('required')
            ){
                $input.not('select').css("color",settings.default_value_color);
                $input.val($input.data('default-value'));
                return;
            }

            // run the validation and if they all pass, we mark the color and move on
            var has_validation = false;
            for(validator in self.validators) {
                if($input.hasClass(validator)){
                    has_validation = true;
                    var validation_result = self.validators[validator]($input, label);
                    if(typeof(validation_result) == 'string') {
                        $input.trigger('error', validation_result);
                        return;
                    }
                }
            }

            // if it had validation and we didn't return above,
            // then all validation passed
            if (has_validation) {
                $input.trigger('success');
            }

            // return the color to the default
            $input.css('color', $input.data('default-color'));
            return;
        });

        /**
         * Handle a validation error in the form element
         *
         * This will set the field to have the error marker
         * and update the warning text
         *
         * @param event  e
         * @param string validation message
         */
        form.delegate(settings.field_selector,'error',function(e,text) {
            validate($(this), false, text);
        });

        /**
         * Handle a succesful validation in the form element
         *
         * Remove any error messages and set the validation
         * marker to be success
         *
         * @param event  e
         * @param string unused
         */
        form.delegate(settings.field_selector,'success',function(e,text) {
            validate($(this), true);
        });
    });
};

/**
 * Internationalized language strings for validation messages
 */
jQuery.fn.f7Form.language = {
    required      : '%s is required',
    req_radio     : 'Please make a selection',
    req_checkbox  : 'You must select this checkbox to continue',
    minlength     : '%s should be at least %d characters long',
    min           : '%s should be greater than or equal to %d',
    maxlength     : '%s should not be longer than %d characters',
    max           : '%s should be less than or equal to %d',
    same_as       : '%s is expected to be same as %s',
    email         : '%s is not a valid email address',
    url           : '%s is not a valid URL',
    system        : '%s cannot contain special characters',
    number        : '%s needs to be a number',
    integer       : '%s needs to be a whole number',
    decimal       : '%s needs to be a decimal number',
    hexcolor      : '%s needs to be a hexadecimal color',
    currency      : '%s needs to be a currency number',
    postalUS      : '%s needs to be a valid US zip code',
    postalCA      : '%s needs to be a valid Canadian postal code',
    postalUK      : '%s needs to be a valid UK postal code',
    taxidUS       : '%s needs to be a valid US tax id',
    alpha         : '%s should contain only letters (without special characters or numbers)',
    alphanum      : '%s should contain only numbers and letters (without special characters)',
    phrase        : '%s should contain only alphabetic characters, numbers, spaces, and the following: . , - _ () * # :',
    phoneUS       : '%s should be a US phone number',
    date          : '%s should be a date (mm/dd/yyyy)',
    callback      : 'Failed to validate %s field. Validator function (%s) is not defined!',
    on_leave      : 'Are you sure you want to leave this page without saving this form?',
    submit_msg    : 'Sorry, this form needs corrections.',
    submit_help   : 'Please see the items marked below.',
    submit_success: 'Thank you, this form has been sent.'
};

/**
 * See the validation.md file for more information about these options
 */
jQuery.fn.f7Form.defaults = {
    prevent_submit          : false,
    prevent_submit_callback : false,
    ask_on_leave            : false,
    on_leave_callback       : false,
    valid_class             : 'valid',
    invalid_class           : 'invalid',
    error_class             : 'ui-state-error',
    focused_class           : 'ui-state-highlight',
    holder_class            : 'ctrlHolder',
    field_selector          : 'input, textarea, select',
    default_value_color     : "#AFAFAF"
};


/*##############################################################################
#    ____________________________________________________________________
#   /                                                                    \
#  |               ____  __      ___          _____  /     ___    ___     |
#  |     ____       /  \/  \  ' /   \      / /      /__   /   \  /   \    |
#  |    / _  \     /   /   / / /    /  ___/  \__   /     /____/ /    /    |
#  |   / |_  /    /   /   / / /    / /   /      \ /     /      /____/     |
#  |   \____/    /   /    \/_/    /  \__/  _____/ \__/  \___/ /           |
#  |                                                         /            |
#  |                                                                      |
#  |   Copyright (c) 2007                             MindStep SCOP SARL  |
#  |   Herve Masson                                                       |
#  |                                                                      |
#  |      www.mindstep.com                              www.mjslib.com    |
#  |   info-oss@mindstep.com                           mjslib@mjslib.com  |
#   \____________________________________________________________________/
#
#  Version: 1.0.0
#
#  (Svn version: $Id: jquery.printf.js 3434 2007-08-27 09:31:20Z herve $)
#
#----------[This product is distributed under a BSD license]-----------------
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#
#     1. Redistributions of source code must retain the above copyright
#        notice, this list of conditions and the following disclaimer.
#
#     2. Redistributions in binary form must reproduce the above copyright
#        notice, this list of conditions and the following disclaimer in
#        the documentation and/or other materials provided with the
#        distribution.
#
#  THIS SOFTWARE IS PROVIDED BY THE MINDSTEP CORP PROJECT ``AS IS'' AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MINDSTEP CORP OR CONTRIBUTORS
#  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
#  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
#  OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
#  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
#  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
#  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#  The views and conclusions contained in the software and documentation
#  are those of the authors and should not be interpreted as representing
#  official policies, either expressed or implied, of MindStep Corp.
#
################################################################################
#
#	This is a jQuery [jquery.com] plugin that implements printf' like functions
#	(Examples and documentation at: http://mjslib.com)
#
#	@author: Herve Masson
#	@version: 1.0.0 (8/27/2007)
#	@requires jQuery v1.1.2 or later
#
#	(Based on the legacy mjslib.org framework)
#
##############################################################################*/

(function($) {

	/*
	**	Just an equivalent of the corresponding libc function
	**
	**	var str=jQuery.sprintf("%010d %-10s",intvalue,strvalue);
	**
	*/

	$.sprintf=function(fmt)
	{
		return _sprintf_(fmt,arguments,1);
	}


	/*
	**	vsprintf takes an argument list instead of a list of arguments (duh!)
	**	(useful when forwarding parameters from one of your functions to a printf call)
	**
	**	str=jQuery.vsprintf(parameters[,offset]);
	**
	**		The 'offset' value, when present, instructs vprintf to start at the
	**		corresponding index in the parameter list instead, of 0
	**
	**	Example 1:
	**
	**		function myprintf(<printf like arguments>)
	**		{
	**			var str=jQuery.vsprintf(arguments);
	**			..
	**		}
	**		myprintf("illegal value : %s",somevalue);
	**
	**
	**	Example 2:
	**
	**		function logit(level,<the rest is printf like arguments>)
	**		{
	**			var str=jQuery.vsprintf(arguments,1);	// Skip prm #1
	**			..
	**		}
	**		logit("error","illegal value : %s",somevalue);
	**
	*/

	$.vsprintf=function(args,offset)
	{
		if(offset === undefined)
		{
			offset=0;
		}
		return _sprintf_(args[offset],args,offset+1);
	}


	/*
	**	logging using formatted messages
	**	================================
	**
	**	If you _hate_ debugging with alert() as much as I do, you might find the
	**	following routines valuable.
	**
	**	jQuery.alertf("The variable 'str' contains: '%s'",str);
	**		Show an alert message with a printf-like argument.
	**
	**	jQuery.logf("This is a log message, time is: %d",(new Date()).getTime());
	**		Log the message on the console with the info level
	**
	**	jQuery.errorf("The given value (%d) is erroneous",avalue);
	**		Log the message on the console with the error level
	**
	*/

	$.alertf=function()
	{
		return alert($.vsprintf(arguments));
	}

	$.vlogf=function(args)
	{
		if("console" in window)
		{
			console.info($.vsprintf(args));
		}
	}

	$.verrorf=function(args)
	{
		if("console" in window)
		{
			console.error($.vsprintf(args));
		}
	}

	$.errorf=function()
	{
		$.verrorf(arguments);
	}

	$.logf=function()
	{
		$.vlogf(arguments);
	}


	/*-------------------------------------------------------------------------------------------
	**
	**	Following code is private; don't use it directly !
	**
	**-----------------------------------------------------------------------------------------*/

	FREGEXP	= /^([^%]*)%([-+])?(0)?(\d+)?(\.(\d+))?([doxXcsf])(.*)$/;
	HDIGITS	= ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];

	function _empty(str)
	{
		if(str===undefined || str===null)
		{
			return true;
		}
		return (str == "") ? true : false;
	}

	function _int_(val)
	{
		return Math.floor(val);
	}

	function _printf_num_(val,base,pad,sign,width)
	{
		val=parseInt(val,10);
		if(isNaN(val))
		{
			return "NaN";
		}
		aval=(val<0)?-val:val;
		var ret="";

		if(aval==0)
		{
			ret="0";
		}
		else
		{
			while(aval>0)
			{
				ret=HDIGITS[aval%base]+ret;
				aval=_int_(aval/base);
			}
		}
		if(val<0)
		{
			ret="-"+ret;
		}
		if(sign=="-")
		{
			pad=" ";
		}
		return _printf_str_(ret,pad,sign,width,-1);
	}

	function _printf_float_(val,base,pad,sign,prec)
	{
		if(prec==undefined)
		{
			if(parseInt(val) != val)
			{
				// No decimal part and no precision -> use int formatting
				return ""+val;
			}
			prec=5;
		}

		var p10=Math.pow(10,prec);
		var ival=""+Math.round(val*p10);
		var ilen=ival.length-prec;
		if(ilen==0)
		{
			return "0."+ival.substr(ilen,prec);
		}
		return ival.substr(0,ilen)+"."+ival.substr(ilen,prec);
	}

	function _printf_str_(val,pad,sign,width,prec)
	{
		var npad;

		if(val === undefined)
		{
			return "(undefined)";
		}
		if(val === null)
		{
			return "(null)";
		}
		if((npad=width-val.length)>0)
		{
			if(sign=="-")
			{
				while(npad>0)
				{
					val+=pad;
					npad--;
				}
			}
			else
			{
				while(npad>0)
				{
					val=pad+val;
					npad--;
				}
			}
		}
		if(prec>0)
		{
			return val.substr(0,prec);
		}
		return val;
	}

	function _sprintf_(fmt,av,index)
	{
		var output="";
		var i,m,line,match;

		line=fmt.split("\n");
		for(i=0;i<line.length;i++)
		{
			if(i>0)
			{
				output+="\n";
			}
			fmt=line[i];
			while(match=FREGEXP.exec(fmt))
			{
				var sign="";
				var pad=" ";

				if(!_empty(match[1])) // the left part
				{
					// You can't add this blindly because mozilla set the value to <undefined> when
					// there is no match, and we don't want the "undefined" string be returned !
					output+=match[1];
				}
				if(!_empty(match[2])) // the sign (like in %-15s)
				{
					sign=match[2];
				}
				if(!_empty(match[3])) // the "0" char for padding (like in %03d)
				{
					pad="0";
				}

				var width=match[4];	// the with (32 in %032d)
				var prec=match[6];	// the precision (10 in %.10s)
				var type=match[7];	// the parameter type

				fmt=match[8];

				if(index>=av.length)
				{
					output += "[missing parameter for type '"+type+"']";
					continue;
				}

				var val=av[index++];

				switch(type)
				{
				case "d":
					output += _printf_num_(val,10,pad,sign,width);
					break;
				case "o":
					output += _printf_num_(val,8,pad,sign,width);
					break;
				case "x":
					output += _printf_num_(val,16,pad,sign,width);
					break;
				case "X":
					output += _printf_num_(val,16,pad,sign,width).toUpperCase();
					break;
				case "c":
					output += String.fromCharCode(parseInt(val,10));
					break;
				case "s":
					output += _printf_str_(val,pad,sign,width,prec);
					break;
				case "f":
					output += _printf_float_(val,pad,sign,width,prec);
					break;
				default:
					output += "[unknown format '"+type+"']";
					break;
				}
			}
			output+=fmt;
		}
		return output;
	}

})(jQuery);


/*!
 * a-tools 1.5.1
 *
 * Copyright (c) 2009 Andrey Kramarev(andrey.kramarev[at]ampparit.com), Ampparit Inc. (www.ampparit.com)
 * Licensed under the MIT license.
 * http://www.ampparit.fi/a-tools/license.txt
 *
 * Basic usage:

    <textarea></textarea>
    <input type="text" />

    // Get current selection
    var sel = $("textarea").getSelection()

    // Replace current selection
    $("input").replaceSelection("foo");

    // Count characters
    alert($("textarea").countCharacters());

    // Set max length without callback function
    $("textarea").setMaxLength(7);

    // Set max length with callback function which will be called when limit is exceeded
    $("textarea").setMaxLength(10, function() {
        alert("hello")
    });

    // Removing limit:
    $("textarea").setMaxLength(-1);

    // Insert text at current caret position
    $("#textarea").insertAtCaretPos("hello");

    // Set caret position (1 = beginning, -1 = end)
    $("#textArea").setCaretPos(10);

    // Set Selection
    $("#textArea").setSelection(10,15);

 */
 var caretPositionAmp=[]; function init(){if(navigator.appName=="Microsoft Internet Explorer"){obj=document.getElementsByTagName("TEXTAREA");var b,a=0;for(a=0;a<obj.length;a++){b=obj[a];caretPositionAmp[a]=b.value.length;b.onmouseup=function(){b=document.activeElement;for(var c=0;c<obj.length;c++)if(obj[c]==b)break;b.focus();var e=document.selection.createRange(),h=b.createTextRange(),d=h.duplicate();h.moveToBookmark(e.getBookmark());d.setEndPoint("EndToStart",h);caretPositionAmp[c]=d.text.length};b.onkeyup=function(){b=document.activeElement; for(var c=0;c<obj.length;c++)if(obj[c]==b)break;b.focus();var e=document.selection.createRange(),h=b.createTextRange(),d=h.duplicate();h.moveToBookmark(e.getBookmark());d.setEndPoint("EndToStart",h);caretPositionAmp[c]=d.text.length}}}}window.onload=init; jQuery.fn.extend({getSelection:function(){var b=this.jquery?this[0]:this,a,c,e,h=0;b.onmousedown=function(){document.selection&&typeof b.selectionStart!="number"?document.selection.empty():window.getSelection().removeAllRanges()};if(document.selection){var d=document.selection.createRange(),f=0,g=0,i=0;a=document.getElementsByTagName("TEXTAREA");for(c=0;c<a.length;c++)if(a[c]==b)break;if(b.value.match(/\n/g)!=null)h=b.value.match(/\n/g).length;if(d.text){e=d.text;if(typeof b.selectionStart=="number"){a= b.selectionStart;c=b.selectionEnd;if(a==c)return{start:a,end:c,text:d.text,length:c-a}}else{a=b.createTextRange();e=a.duplicate();firstRe=a.text;a.moveToBookmark(d.getBookmark());secondRe=a.text;e.setEndPoint("EndToStart",a);if(firstRe==secondRe&&firstRe!=d.text||e.text.length>firstRe.length)return{start:caretPositionAmp[c],end:caretPositionAmp[c],text:"",length:0};a=e.text.length;c=e.text.length+d.text.length}if(h>0)for(e=0;e<=h;e++){var k=b.value.indexOf("\n",g);if(k!=-1&&k<a){g=k+1;f++;i=f}else if(k!= -1&&k>=a&&k<=c)if(k==a+1){f--;i--;g=k+1}else{g=k+1;i++}else e=h}if(d.text.indexOf("\n",0)==1)i+=2;a-=f;c-=i;return{start:a,end:c,text:d.text,length:c-a}}b.focus();if(typeof b.selectionStart=="number")a=b.selectionStart;else{d=document.selection.createRange();a=b.createTextRange();e=a.duplicate();a.moveToBookmark(d.getBookmark());e.setEndPoint("EndToStart",a);a=e.text.length}if(h>0)for(e=0;e<=h;e++){k=b.value.indexOf("\n",g);if(k!=-1&&k<a){g=k+1;f++}else e=h}a-=f;if(a==0&&typeof b.selectionStart!= "number"){a=caretPositionAmp[c];c=caretPositionAmp[c]}return{start:a,end:a,text:d.text,length:0}}else if(typeof b.selectionStart=="number"){a=b.selectionStart;c=b.selectionEnd;e=b.value.substring(b.selectionStart,b.selectionEnd);return{start:a,end:c,text:e,length:c-a}}else return{start:undefined,end:undefined,text:undefined,length:undefined}},replaceSelection:function(b){var a=this.jquery?this[0]:this,c,e;e=0;var h,d,f=0,g=0,i=a.scrollTop==undefined?0:a.scrollTop;c=document.getElementsByTagName("TEXTAREA"); for(var k=0;k<c.length;k++)if(c[k]==a)break;if(document.selection&&typeof a.selectionStart!="number"){i=document.selection.createRange();if(typeof a.selectionStart!="number"){var j;d=a.createTextRange();h=d.duplicate();c=d.text;d.moveToBookmark(i.getBookmark());j=d.text;try{h.setEndPoint("EndToStart",d)}catch(m){return this}if(c==j&&c!=i.text||h.text.length>c.length)return this}if(i.text){part=i.text;if(a.value.match(/\n/g)!=null)f=a.value.match(/\n/g).length;c=h.text.length;if(f>0)for(j=0;j<=f;j++){var l= a.value.indexOf("\n",e);if(l!=-1&&l<c){e=l+1;g++}else j=f}i.text=b;caretPositionAmp[k]=h.text.length+b.length;d.move("character",caretPositionAmp[k]);document.selection.empty();a.blur()}return this}else if(typeof a.selectionStart=="number"&&a.selectionStart!=a.selectionEnd){c=a.selectionStart;e=a.selectionEnd;a.value=a.value.substr(0,c)+b+a.value.substr(e);e=c+b.length;a.setSelectionRange(e,e);a.scrollTop=i;return this}return this},setSelection:function(b,a){b=parseInt(b);a=parseInt(a);var c=this.jquery? this[0]:this;c.focus();if(typeof c.selectionStart!="number"){re=c.createTextRange();if(re.text.length<a)a=re.text.length+1}if(a<b)return this;if(document.selection){var e=0,h=0,d=0,f=0;if(typeof c.selectionStart!="number"){re.collapse(true);re.moveEnd("character",a);re.moveStart("character",b);re.select()}else{if(typeof c.selectionStart=="number"){if(c.value.match(/\n/g)!=null)e=c.value.match(/\n/g).length;if(e>0)for(var g=0;g<=e;g++){var i=c.value.indexOf("\n",d);if(i!=-1&&i<b){d=i+1;h++;f=h}else if(i!= -1&&i>=b&&i<=a)if(i==b+1){h--;f--;d=i+1}else{d=i+1;f++}else g=e}b+=h;a+=f;c.selectionStart=b;c.selectionEnd=a}c.focus()}return this}else if(c.selectionStart||c.selectionStart==0){c.focus();window.getSelection().removeAllRanges();c.selectionStart=b;c.selectionEnd=a;c.focus();return this}},insertAtCaretPos:function(b){var a=this.jquery?this[0]:this,c,e,h,d,f,g,i;c=e=0;var k=a.scrollTop==undefined?0:a.scrollTop;i=document.getElementsByTagName("TEXTAREA");for(var j=0;j<i.length;j++)if(i[j]==a)break;a.focus(); if(document.selection&&typeof a.selectionStart!="number"){if(a.value.match(/\n/g)!=null)c=a.value.match(/\n/g).length;i=parseInt(caretPositionAmp[j]);if(c>0)for(var m=0;m<=c;m++){var l=a.value.indexOf("\n",h);if(l!=-1&&l<=i){h=l+1;i-=1;e++}}}caretPositionAmp[j]=parseInt(caretPositionAmp[j]);a.onkeyup=function(){if(document.selection&&typeof a.selectionStart!="number"){a.focus();d=document.selection.createRange();f=a.createTextRange();g=f.duplicate();f.moveToBookmark(d.getBookmark());g.setEndPoint("EndToStart", f);caretPositionAmp[j]=g.text.length}};a.onmouseup=function(){if(document.selection&&typeof a.selectionStart!="number"){a.focus();d=document.selection.createRange();f=a.createTextRange();g=f.duplicate();f.moveToBookmark(d.getBookmark());g.setEndPoint("EndToStart",f);caretPositionAmp[j]=g.text.length}};if(document.selection&&typeof a.selectionStart!="number"){d=document.selection.createRange();if(d.text.length!=0)return this;f=a.createTextRange();textLength=f.text.length;g=f.duplicate();f.moveToBookmark(d.getBookmark()); g.setEndPoint("EndToStart",f);c=g.text.length;if(caretPositionAmp[j]>0&&c==0){e=caretPositionAmp[j]-e;f.move("character",e);f.select();d=document.selection.createRange();caretPositionAmp[j]+=b.length}else if(!(caretPositionAmp[j]>=0)&&textLength==0){d=document.selection.createRange();caretPositionAmp[j]=b.length+textLength}else if(!(caretPositionAmp[j]>=0)&&c==0){f.move("character",textLength);f.select();d=document.selection.createRange();caretPositionAmp[j]=b.length+textLength}else if(!(caretPositionAmp[j]>= 0)&&c>0){f.move("character",0);document.selection.empty();f.select();d=document.selection.createRange();caretPositionAmp[j]=c+b.length}else if(caretPositionAmp[j]>=0&&caretPositionAmp[j]==textLength){if(textLength!=0){f.move("character",textLength);f.select()}else f.move("character",0);d=document.selection.createRange();caretPositionAmp[j]=b.length+textLength}else{if(caretPositionAmp[j]>=0&&c!=0&&caretPositionAmp[j]>=c){e=caretPositionAmp[j]-c;f.move("character",e)}else caretPositionAmp[j]>=0&&c!= 0&&caretPositionAmp[j]<c&&f.move("character",0);document.selection.empty();f.select();d=document.selection.createRange();caretPositionAmp[j]+=b.length}d.text=b;a.focus();return this}else if(typeof a.selectionStart=="number"&&a.selectionStart==a.selectionEnd){h=a.selectionStart+b.length;c=a.selectionStart;e=a.selectionEnd;a.value=a.value.substr(0,c)+b+a.value.substr(e);a.setSelectionRange(h,h);a.scrollTop=k;return this}return this},setCaretPos:function(b){var a=this.jquery?this[0]:this,c,e=0,h=0,d; d=document.getElementsByTagName("TEXTAREA");for(var f=0;f<d.length;f++)if(d[f]==a)break;a.focus();if(parseInt(b)==0)return this;if(parseInt(b)>0){b=parseInt(b)-1;if(document.selection&&typeof a.selectionStart=="number"&&a.selectionStart==a.selectionEnd){if(a.value.match(/\n/g)!=null)e=a.value.match(/\n/g).length;if(e>0)for(var g=0;g<=e;g++){d=a.value.indexOf("\n",c);if(d!=-1&&d<=b){c=d+1;b=parseInt(b)+1}}}}else if(parseInt(b)<0){b=parseInt(b)+1;if(document.selection&&typeof a.selectionStart!="number"){b= a.value.length+parseInt(b);if(a.value.match(/\n/g)!=null)e=a.value.match(/\n/g).length;if(e>0){for(g=0;g<=e;g++){d=a.value.indexOf("\n",c);if(d!=-1&&d<=b){c=d+1;b=parseInt(b)-1;h+=1}}b=b+h-e}}else if(document.selection&&typeof a.selectionStart=="number"){b=a.value.length+parseInt(b);if(a.value.match(/\n/g)!=null)e=a.value.match(/\n/g).length;if(e>0){b=parseInt(b)-e;for(g=0;g<=e;g++){d=a.value.indexOf("\n",c);if(d!=-1&&d<=b){c=d+1;b=parseInt(b)+1;h+=1}}}}else b=a.value.length+parseInt(b)}else return this; if(document.selection&&typeof a.selectionStart!="number"){c=document.selection.createRange();if(c.text!=0)return this;a=a.createTextRange();a.collapse(true);a.moveEnd("character",b);a.moveStart("character",b);a.select();caretPositionAmp[f]=b;return this}else if(typeof a.selectionStart=="number"&&a.selectionStart==a.selectionEnd){a.setSelectionRange(b,b);return this}return this},countCharacters:function(){var b=this.jquery?this[0]:this;if(b.value.match(/\r/g)!=null)return b.value.length-b.value.match(/\r/g).length; return b.value.length},setMaxLength:function(b,a){this.each(function(){var c=this.jquery?this[0]:this,e=c.type,h,d;if(parseInt(b)<0)b=1E8;if(e=="text")c.maxLength=b;if(e=="textarea"||e=="text"){c.onkeypress=function(f){var g=c.value.match(/\r/g);d=b;if(g!=null)d=parseInt(d)+g.length;f=f||event;g=f.keyCode;h=document.selection?document.selection.createRange().text.length>0:c.selectionStart!=c.selectionEnd;if(c.value.length>=d&&(g>47||g==32||g==0||g==13)&&!f.ctrlKey&&!f.altKey&&!h){c.value=c.value.substring(0, d);typeof a=="function"&&a();return false}};c.onkeyup=function(){var f=c.value.match(/\r/g),g=0,i=0;d=b;if(f!=null){for(var k=0;k<=f.length;k++)if(c.value.indexOf("\n",i)<=parseInt(b)){g++;i=c.value.indexOf("\n",i)+1}d=parseInt(b)+g}if(c.value.length>d){c.value=c.value.substring(0,d);typeof a=="function"&&a();return this}}}else return this});return this}});
