/**
 * Author: Maksim Horbachevsky, fantactuka.net
 * Date: 10.04.2009
 * Time: 10:59:36
 * Created by IntelliJ IDEA.
 */

(function($){

    $.fn.getIndex = function(c) {
        var e = this.get(0);
        for(var i = 0, l = c.length; i < l; i++){
            if(c[i] == e) return i;
        }
        return -1;
    };

    /**
     * @description enables element, removes 'disabled' css class
     */
    $.fn.enable = function() {
        return $(this).each(function() {
            $(this).attr('disabled', false).removeClass('disabled');
        });
    };

    /**
     * @description disables element, adds 'disabled' css class
     * @param clear <Boolean> clears value of element if true
     */
    $.fn.disable = function(clear) {
        return $(this).each(function() {
            $(this).attr('disabled', true).addClass('disabled');
            if(clear) $(this).val('');
        });
    };

    /**
     * @description set maxlength for field (useful for textareas)
     * @param max <Integer> maxlength
     * @param cb <Function> calback function that fires when length is greater that max
     */
    $.fn.maxlength = function(max, cb) {
        var limit = function() {
            var $this = $(this), val = $this.val();
            if(val.length > max) {
                $this.val(val.slice(0, max));
                cb && cb();
            }
        };
        return $(this).keyup(limit).blur(limit);
    };

    /**
     * @description gets data from elements class name.
     *              eg. <div class="cdata-id-5" id="test"></div>: $('test').cdata('id') => '5'
     * @param name <String> data item name
     * @return <String> data or null if was not foun
     */
    //TODO: refactor: ugly code
    $.fn.cdata = function(name) {
        if($(this).length) {
            var classes = $(this).attr('class').split(' '),
                    cdatas = [],
                    re = new RegExp('^cdata-' + name + '-', 'i');
            for(var i = 0, l = classes.length; i < l; i ++) {
                if(re.test(classes[i])) {
                    cdatas.push(classes[i].replace(re, ''));
                }
            }
            return cdatas.length ? (cdatas.length > 1 ? cdatas : cdatas[0]) : null;
        } else {
            return null;
        }
    };

    /**
     * @description
     * @param color1
     * @param color2
     */
    //TODO: save old color in element's data
    $.fn.highlight = function(color1, color2, bg) {
        return $(this).stop(true, true)
                .animate(bg ? { backgroundColor: color1 } : { color: color1 })
                .animate(bg ? { backgroundColor: color2 } : { color: color2 });
    };

    $.extend({

        /**
         * @description return current time
         */
        time: Date.now || function(){
	        return + new Date;
        },

        /**
         * @description localization
         * @param key <String> - hash key for string
         * @return <String> localized string
         */
        //TODO: move localized strings to separated file that can be requested according selected language 
        localize: function(key) {
            return {
                saveError: 'Error occured during saving information. Please reload the page and try again.',
                weightText: '<strong>{lbs}</strong> LBS <strong>{kg}</strong> KG',
                heightText: '<strong>{inc}</strong> FT <strong>{cm}</strong> CM',
                noMatches: 'No matches found',
                wrongExtension: 'Incorrect file extension. Please choose jpg, gif or png files',
                tooManyMatchingValues: 'PLEASE ENTER ZIP/POSTAL CODE',
                specifyCountry: 'Set country',
                specifyState: 'Select state/province',
                specifyZip: 'Select zip',
                specifyCity: 'Select city',
                specifyHeadline: 'Fill in headline field',
                assertiveLaidBackResults: ' Laid-Back: <strong>{laidback}</strong> Assertive: <strong>{assertive}</strong>',
                completeProfile: 'Please complete the Quick Stats and Basic Stats sections of your profile to continue.',
                cancelButton: 'Cancel',
                photoPrimaricyError: 'Please select a non-adult picture to be the Primary Picture. If you feel this picture is not adult in nature, please email <a href="mailto:support@findfred.com">support@findfred.com</a>. Thank You.',
                interests: ['Friends', 'Chat', 'Just browsing', 'Play it by ear', 'Fun']
            }[key];
        },

        /**
         * @description localization with passed data
         * @param key <String> - hash key for string
         * @param object <Object> - data to be passed to localized string
         * @return <String> localized string with passed data
         */
        localizeWithData: function(key, object) {
            return $.substitute($.localize(key), object);
        },

        /**
         * @description pass data from object to string, eg: $.substitute("Name: {name}", {name: 'Janny'}) -> "Name: Janny"
         * @param string <String> - string to be filled with data
         * @param object <Object> - data object to be passed to string
         * @return <String> string with passed data
         */
        substitute: function(string, object) {
            return string.replace(/\\?\{([^{}]+)\}/g, function(match, name){
                if (match.charAt(0) == '\\') return match.slice(1);
                return (object[name] != undefined) ? object[name] : '';
            });
        },

        /**
         * @description log all arguments to console (fireBug)
         */
        log: function() {
            window.console && window.console.log(arguments);
        },

        /**
         * @description preload images, eg $.preload('sample.jpg', 'dot.gif')
         */
        preload: function() {
            for(var i = 0, l = arguments.length; i < l; i ++) {
                $('<img>').attr(arguments[i]);
            }
        },

        /**
         * @description rize up popup
         * @param message <String, Array> - message to be shown in popup
         * @param title <String> - popup's title
         * @param adds <Object> - addititonal settings
         */
        errorModal: function(message, title, adds) {
            $('<div></div>').html(
                $.isArray(message) ?
                    $('<ul class="errorsList"></ul>').html( $.map(message, function(i) { return ('<li>' + i + '</li>'); }).join('') ) :
                    message
            ).dialog($.extend({
                title: title || 'Warning',
                closeOnEscape: true,
                draggable: true,
                resizable: false,
                bgiframe: true,
                minHeight: 40,
                modal: true
            }, adds || {}));
        },

        /**
         * @description set category elements to valid/invalid state
         * @param category <String> - category name
         * @param valid <Boolean> - category validation state
         */
        checkCategory: function(category, valid) {
            var item = $('#prlc li.cdata-ct-' + category);
            if(valid) {
                item.addClass('ok').removeClass('oops');
            } else {
                item.addClass('oops').removeClass('ok');
            }
        },

        /**
         * @description bind function.
         * jQuery is the only of popular frameworks that does not allow fast binding... Why people like it? 
         * @param fn <Function> - method to bind
         * @param to <Object> - object to be binded
         * @return <Function> - binded method
         */
        bind: function(fn, to) {
            return fn && function() {
                return fn.apply(to, arguments);
            };
        },

        /**
         * @description pass function.
         * jQuery is the only of popular frameworks that does not allow fast binding... Why people like it?
         * @param fn <Function> - method to pass arguments
         * @param args <Array> - arguments
         * @return <Function> - binded method
         */
        pass: function(fn, args, bind) {
            return fn && function() {
                return fn.apply(bind, args);
            };
        },

        /**
         * @description lambda function
         * @param a
         * @return a
         */
        lambda: function(a) {
            return a;
        },

        /**
         * @description dummy function
         */
        empty: function() {},

        /**
         * @description checks if all elements in array pass validation
         * @param a <Array> - array to check
         * @param fn <Function> - function to validate
         * @return <Boolean> true if all elements pass validation
         */
        every: function(a, fn) {
            return $.grep(a, fn).length == a.length;
        },

        /**
         * @description checks if any element in array pass validation
         * @param a <Array> - array to check
         * @param fn <Function> - function to validate
         * @return <Boolean> true if at least one element pass validation
         */
        any: function(a, fn) {
            return $.grep(a, fn).length > 0;
        },

        /**
         * @description checks if array contains element
         * @param el <Object> - element to check
         * @param ar <Array> - array to check
         * @return <Boolean> true if element is in array
         */
        contains: function(el, ar) {
            return $.inArray(el, ar) > -1;
        },

        /**
         * @description add elements to array, does not allow duplication
         * eg. include([1, 2, 4], 2, 5) => [1, 2, 4, 5] 
         */
        include: function() {
            var to = arguments[0];
            for(var i = 1, l = arguments.length; i < l; i ++) {
                if(!$.contains(arguments[i], to)) to.push(arguments[i]);
            }
            return to;
        },

        /**
         * @description check if number is within min and max
         * @param n <Integer> - num to check
         * @param min <Integer> - min limit
         * @param max <Integer> - max limit
         * @return <Boolean> true if number within min and max
         */
        within: function(n, min, max) {
            return n > min && n < max;
        },

        /**
         * @description limitate number
         * @param value <Integer> - number to limitate
         * @param min <Integer> - min limit
         * @param max <Integer> - max limit
         */
        limit: function(value, min, max) {
            return Math.min(Math.max(min, value), max);
        },

        /**
         * @description parse to int
         * @param a
         */
        toInt: function(a) {
            return a && parseInt(a, 10);
        },

        /**
         * @description aborts request
         * @param xhr <XmlHttpRequest> - request to abort
         *
         *  use $.request.abort
         */
        abort: function(xhr) {
            if(xhr) {
                xhr.abort();
                xhr.onreadystatechange = $.empty;
            }
            return xhr;
        },

        /**
         * @description global settings,
         * should be userd with $.settings(pluginName, [additionalSettings]). See below.
         * // TODO: move to separate file?
         */
        _settings: {
            ageLimit: [18, 100],
            heightLimit: [39, 99],
            weightLimit: [100, 300],
            scrollPane: {
                maxHeight: 500,
                scrollbarMargin: 0,
                showArrows: true,
                dragMinHeight: 30,
                dragMaxHeight: 30,
                scrollbarWidth: 13,
                arrowSize: 12
            }
        },
        /**
         * @description settings for plugins
         * @param name <String> - plugin name
         * @param add <Object> - additional settings (to add or overwrite default)
         * @return <Object> that contains settings
         */
        settings: function(name, add) {
            return $.extend($._settings[name], add || {});
        }

    });


    /**
     * @description jQuery ajax method wrapper to extend functionality
     * options <Object> - standart jQuery params + 'abort'
     *      - abort <String> - 'ignore' - doesn't send new request if one is already started // TODO: ToBeDone (not needed at the moment)
     *                       - 'cancel' - stops previous request and runs new one (default)
     *      - rest <Boolean> - if true - params in get requests will be separated with slashes: name/Sally/age/21
     *      - nocache <Boolean> - add random param to prevent request caching
     */
    $.request = (function() {

        // Static staff here.

        return function(options) {
            this.xhr = null;
            this.running = false;
            this.options = $.extend({ abort: 'cancel', rest: true, nocache: false }, options);
            return this;
        };
    })();

    $.request.prototype.send = function(o) {
        var options = $.extend({}, this.options, o);
        if(options.abort == 'cancel') {
            this.cancel();
        }
        if(options.rest && options.type == 'get') {
            var query = [options.url];
            for(var i in options.data) {
                query.push(i, encodeURIComponent(options.data[i]));
            }
            options.url = query.join('/');
            options.data = null;
        }
        if(options.nocache) {
            if(options.rest) {
                options.url = [options.url, 'cache', Math.random()].join('/');
            } else {
                options.url += ((/\?/).test(options.url) ? '&' : '?') + 'cache=' + Math.random();
            }
        }
        this.xhr = $.ajax(options);
        this.running = true;
        return this;
    };

    $.request.prototype.cancel = function() {
        $.request.abort(this.xhr);
        return this;
    };

    $.request.json = function(options) {
        return new $.request($.extend(options, { type: 'post', dataType: 'json' }));
    };

    $.request.get = function(url, options) {
        return new $.request($.extend(options, { type: 'get', url: url }));
    };

    $.request.post = function(data, options) {
        return new $.request($.extend(options, { type: 'post', data: data }));
    };

    $.request.abort = function(rq) {
        if(rq) {
            rq.abort();
            rq.onreadystatechange = $.empty;
        }
        return rq;
    };


})(jQuery);
