var TabObject = Class.$extend({
    items: {},
    itemselector: '',
    buttonscontainer: {},
    __init__: function(buttonscontainer, items) {
        this.items = items;
        
        this.buttonscontainer = buttonscontainer;
    },
    swap: function(tab, item) {
        $(this.buttonscontainer+' a.active').removeClass('active');
        
        if(!$(item+':visible').length) {
            $(tab).addClass('active');
        }
        this.show(item);
        resizeWindow();
        add_tooltips();
        return false;
    },
    show: function(item) {
        var current_is_visible = false;
        if($(item+':visible').length) {
            current_is_visible = true;
        }
        var callback = function(){};
        var sel = '';
        for(var t in this.items) {
            if(sel) {
                sel += ', ';
            }
            sel += this.items[t].item+'.subitem';
            if(this.items[t].item == '#'+$(item).attr('id') && this.items[t].callback) {
                callback = this.items[t].callback;
            }
        }
        $(sel).hide();
        if(!current_is_visible) {
            $(item).show(0, callback);
        }
        return false;
    },
    __include__ : [Tabs]
});

var Tags = {
    init: function() {
        $('a.taglink').click(function() {
            Tags.toggle(this);
            var active_tags = Tags.getActive();
            var filter = {};
            if(map_filter) {
                parse_str(map_filter, filter);
            }
            else {
                filter.filter = {};
            }
            filter.filter.tags = active_tags;
            Map.load_data(http_build_query(filter), true);
            return false;
        });
    },
    toggle: function(link) {
        $(link).toggleClass('active');
    },
    getActive: function() {
        var active_elements = $('a.taglink.active');
        var active_tags = [];
        for(var i = 0; i<active_elements.length; i++) {
            active_tags.push($(active_elements[i]).attr('title'));
        }
        return active_tags;
    }
};

/**
 * 
 *
 * @package default
 * @author Jonas Van Schoote
 **/
var Tabs = {
    toggle: function(button, item) {
        if($(item+':visible').length) {
            $(button).removeClass('active');
            $(item).hide();
        }
        else {
            $(item).show();
            $(button).addClass('active');
        }
        resizeWindow();
        return false;
    }
};

Pages = {};

var Pagination = Class.$extend({
    current_page: 1,
    total_pages: 1,
    filter: {},
    element: '',
    url: '',
    btn_prefix: '',
    callback: '',
    cycle: false,
    items_per_page: 20,
    full: false,
    partial: false,
    __init__: function(total_pages, items_per_page, filter, elem, url, btn_prefix, callback) {
        
        this.total_pages = total_pages;
        this.filter = filter;
        this.element = elem;
        this.url = url;
        this.btn_prefix = btn_prefix;
        this.callback = callback;
        this.items_per_page = items_per_page;
    },
    open: function(pagenum) {
        if(pagenum != this.current_page) {
            var ser = {};
            ser.filter = this.filter;
            ser.page = pagenum;
            ser.items_per_page = this.items_per_page;
            if(this.full) {
                ser.full = true;
            }
            if(this.partial) {
                ser.partial = this.partial;
            }
            var t = this;
            
            jQuery.getJSON(this.url, ser, function(data){
                if(data.status == 'ok') {
                    t.current_page = pagenum;
                    $(t.btn_prefix+' input').val(pagenum);
                    
                    $(t.btn_prefix+' .previous').removeClass('inactive');
                    $(t.btn_prefix+' .first').removeClass('inactive');
                    $(t.btn_prefix+' .next').removeClass('inactive');
                    $(t.btn_prefix+' .last').removeClass('inactive');
                    
                    if(pagenum == t.total_pages) {
                        $(t.btn_prefix+' .next').addClass('inactive');
                        $(t.btn_prefix+' .last').addClass('inactive');
                    }
                    
                    if(pagenum == 1) {
                        $(t.btn_prefix+' .previous').addClass('inactive');
                        $(t.btn_prefix+' .first').addClass('inactive');
                    }
                    
                    $(t.element).html(data.items);
                    resizeWindow();
                    if(t.callback) {
                        t.callback();
                    }
                }
                else {
                    alert('the other elements couldn\'t be loaded. Please try again.');
                }
            });
        }
        return false;
    },
    next: function() {
        if(this.current_page >= this.total_pages) {
            if(this.cycle) {
                this.open(1);
            }
        }
        else {
            this.open(this.current_page*1+1);
        }
        return false;
    },
    previous: function() {
        if(this.current_page <= 1) {
            if(this.cycle) {
                this.open(this.total_pages);
            }
        }
        else {
            this.open(this.current_page*1-1);
        }
        return false;
    }
});

var Account = {
    register: {
        form: 1,
        public_key: '',
        send_form: function() {
            //
            var ser = $('form#register_form').serializeArray();
            if (Account.register.form == 1) {
                var url = "/account/register_1.json";
            }
            else {
                var url = "/account/register.json";
            }
            jQuery.getJSON(url, ser, function(data){
                //
                if(data.status == 'ok') {
                    // add the countries
                    if (Account.register.form == 1) {
                        for (var name in data.countries) {
                            $('#register_country').append('<option value="'+name+'">'+data.countries[name]+'</option>');
                        }
                        //
                        Account.register.form = 2;
                        Popup.show('#signup-popup_2');
                        // hide the first part, remove the captcha
                        $('#recaptcha_response_field').attr('id', 'recaptcha_response_field_closed');
                    }
                    else if (Account.register.form == 2) {
                        Account.register.form = 3;
                        if(data.redirect) {
                            $('#signup_done').submit();
                        }
                    }
                }
                else if(data.status == 'blocked') {
                    $('#signup-popup, #signup-popup_2').remove();
                    $('#signup-blocked').attr('id', 'signup-popup').show();
                }
                else {
                    if(Account.register.form == 1){
                        Account.register.reloadcaptcha();
                    }
                    $("input[name='"+name+"']").parent().children('span').hide();
                    $("input").removeClass('error');
                    var bde = false;
                    var bdm = '';
                    console.log(data);
                    var bdc = 0;
                    for (var name in data.errors) {
                        if(name == 'register_general') {
                            $('#errors_general_'+Account.register.form).text(data.errors[name]).show();
                        }
                        else if(name == 'register_birthday_Day' || name == 'register_birthday_Month' || name == 'register_birthday_Year') {
                            bde = true;
                            bdm += '<br />'+data.errors[name];
                            bdc++;
                        }
                        else {
                            $("input[name='"+name+"']").addClass('error');
                            $("input[name='"+name+"']").parent().children('span').text(data.errors[name]).show();
                        }
                    }
                    if(bde) {
                        if(bdc == 3) {
                            bdm = 'Please enter a correct birthday';
                        }
                        $('#errors_birthday_'+Account.register.form).html(bdm).show();
                    }
                }
            });
        },
        close: function() {
            $('.popup').hide();
            $('#popup-background').hide();
        },
        reset: function() {
            $("#register_form input").removeClass('error').parent().children('span').hide();
            $("#register_form span.error-message").hide();
            $('#signup-popup input').val('');
            if($('#recaptcha_response_field_closed').length) {
                $('#recaptcha_response_field_closed').attr('id', 'recaptcha_response_field');
            }
            Account.register.form == 1;
        },
        getcaptcha: function() {
            Account.register.reset();
            $("#recaptcha").html('');
            Recaptcha.create(Account.register.public_key, "recaptcha", {
               theme: "custom",
               callback: Recaptcha.focus_response_field,
               custom_theme_widget: 'recaptcha_widget'
            });
        },
        reloadcaptcha: function() {
            Recaptcha.reload();
        }
    }
};

var Auth = {
    forgot: function(form) {
        var ser = $(form).serializeArray();
        var url = "/auth/forgotpasswordusername.json";
        $('span.error-message').hide();
        jQuery.getJSON(url, ser, function(data){
            //
            if(data.status == 'ok') {
                // add the countries
                $('#forgot-popup-done h2').text(data.message);
                //
                Popup.show('#forgot-popup-done');
            }
            else {
                $("input + span").hide();
                $("input").removeClass('error');
                for (var name in data.errors) {
                    if(name == 'general') {
                        $('#errors_general').text(data.errors[name]).show();
                    }
                    else {
                        $("input[name='"+name+"']").addClass('error');
                        
                        $("input[name='"+name+"'] + span").text(data.errors[name]).show();
                    }
                }
            }
        });
    }
};

var RatingOld = {
    like: function(button, item_id) {
        this.send(button, "/rate/like/"+item_id+".json");
    },
    dislike: function(button, item_id) {
        this.send(button, "/rate/dislike/"+item_id+".json");
    },
    send: function(button, url) {
        jQuery.getJSON(url, function(data){
            //
            if(data.status == 'ok') {
                // update the number of dislikes
                var number = $(button).text()*1+1;
                $(button).text(number);
            }
            else {
                alert(data.message);
            }
        });
    }
};

var Rating = Class.$extend({
    __init__: function(id, buttonLike, buttonDislike) {
        this.item_id = id;
        var that = this;
        jQuery(buttonLike).click(function() {
            that.send('like', buttonLike);
            return false;
        });
        jQuery(buttonDislike).click(function() {
            that.send('dislike', buttonDislike);
            return false;
        });
    },
    send: function(type, button) {
        var that = this;
        var url = "/rate/"+type+"/"+this.item_id+".json";
        jQuery.getJSON(url, function(data){
            //
            if(data.status == 'ok') {
                // update the number of dislikes
                
                var number = $(button).text()*1+1;
                $(button).text(number);
            }
            else {
                alert(data.message);
            }
        });
    }
});

var sendJoinPopup = function(url) {
    $.get(url, {
            text: $('#join-request-text').val(),
            action: 'join-popup'
        },
        function(response) {
            $('#general-popup').html(response);
        }
    );
    return false;
};

var sendMessagePopup = function(url) {
    $.get(url, {
            title: $('#message_title').val(),
            text: $('#message_text').val(),
            action: 'send-message'
        },
        function(response) {
            $('#general-popup').html(response);
        }
    );
    return false;
};

var sendFriendPopup = function(url) {
    $.get(url, {
            action: 'add_friend'
        },
        function(response) {
            $('#general-popup').html(response);
        }
    );
    
    return false;
};

var Popup = {
    friend_popup: function(id, screenname) {
        Popup.load('/popup/friend/'+id);
        return false;
    },
    message_popup: function(id, screenname) {
        Popup.load('/popup/message/'+id);
        return false;
    },
    join_popup: function(type, id) {
        Popup.load('/popup/join/'+type.toLowerCase()+'/'+id);
        return false;
    },
    warning_popup: function(message, title, left, right, close_url) {
        var url = '/popup/warning?';
        url += 'message='+urlencode(message);
        url += '&title='+urlencode(title);
        if(left.label) {
            url += '&bllabel='+urlencode(left.label);
            if(left.url) {
                url += '&blurl='+urlencode(left.url);
            }
            if(left.close) {
                url += '&blclose=true';
            }
        }
        if(right.label) {
            url += '&brlabel='+urlencode(right.label);
            if(right.url) {
                url += '&brurl='+urlencode(right.url);
            }
            if(right.close) {
                url += '&brclose=true';
            }
        }
        if(close_url) {
            $('#popup-background').click(function(){
                window.location = close_url;
            });
        }
        
        Popup.load(url);
        return false;
    },
    upload_popup: function(id) {
        $('#general-popup').load('/map/video/'+id+'?fullview=true', function() {
            Popup.show('#general-popup');
            $('.player-'+id).jwplayer({
                controlbar: 'over',
                flashplayer:'/javascripts/jwplayer/player.swf',
                skin:'/javascripts/jwplayer/five-1/five.xml'
            });
        });
    },
    load: function(url) {
        $('#general-popup').load(url, function() {
            
            Popup.show('#general-popup');
        });
        
        return false;
    },
    show: function(name, message) {
        // message is a propossed way of adding messages to the popup
        
        // copied from hide() because using the full hide function would cause problems with embeded videos using iframes
        $(Popup.current).hide();
        Popup.current = null;
        $('#popup-background').hide();
        
        $(name).css({
            'position': 'fixed',
            'left': '50%',
            'top': '150px'
        });
        $(name).show();
        
        Popup.current = name;
        
        $(window).scroll(Popup.update);
        $(window).resize(Popup.update);
        
        resizeWindow();
        $('#popup-background').show().click(function(){
            Popup.hide();
        });
        
        Popup.update();
        return false;
    },
    hide: function(all) {
        if(all) {
            $('div.popup').hide();
        }
        else {
            $(Popup.current).hide();
        }
        Popup.current = null;
        
        $('#popup-background').hide();
        
        // clears out the content, to make sure the video stops playing
        $('.content-element.video-element').html('');
        try {
            player.stop();
        } catch(e) {}
        
        return false;
    },
    update: function() {
        //var offset = $(window).height()/2-410/2-$('#header').height()+$(document).scrollTop();
        
        var p_height = $(Popup.current).height();
        if(p_height == 0) {
            p_height = $(Popup.current+' div').height();
        }
        
        var w_height = $(window).height();
        var offset = 150;
        if(w_height-p_height < 300) {
            if(w_height-p_height < 0) {
                offset = -$(document).scrollTop();
            }
            else {
                offset = (w_height-p_height)/2;
            }
        }
        
        $(Popup.current).css({
            'top':offset+"px"
        });
    }
};

var Search = {
    tags: function(terms) {
        Search.send('tags', terms);
    },
    words: function(terms) {
        Search.send('words', terms);
    },
    map: function(terms) {
        Search.send('map', terms);
    },
    send: function(type, terms) {
        var url = '/search/'+type+'/'+urlencode(terms);
        window.location = url;
    }
};

/*
    Novice jQuery script for select boxes by Marco Jardim
*/

function closeSelect() {
    $('.select ul').hide();
}

var SelectReplacer = Class.$extend({
	select_id: '',
	list_id: '',
	timeoutId: '0',
	__init__: function(sl,ul) {
		this.select_id = sl;
		this.list_id = ul;
		var t = this;
		
		//Shows the List hidden, otherwise hides it
		$(this.select_id+' span').click(function() {
		    if($(t.list_id).css('display') == 'block') { 
		        t.hideMenu();
		    } else {
		        t.showMenu();
            }
		});
		
		//Hides List on timeout
		$(this.select_id).mouseout(function(){
    	    t.timeoutId = setTimeout(function() {
    	        t.hideMenu();
    	    }, 1000);
	    });
        
        //Clears counter on return
	    $(this.select_id).mouseover(function(){
	        window.clearTimeout(t.timeoutId);
	    });
	    
	    //Sets the real select option as selected
	    $(t.list_id + ' li').bind('click',function(){
	        var opt_id = '#option-'+$(this).attr('id').replace('item-','');
	        $(opt_id).attr('selected','selected');
	        $(t.select_id + ' span').text( $(this).text() );
			t.hideMenu();
	    });
	    
	    //Adjusts the size of the fake selects to match and fit inside the box
	    //the 20 pixels is the right padding of the select_id
	    if( $(this.select_id).width() < ($(this.list_id).width()+20) ) {
	        $(this.select_id).width($(this.list_id).width()+20);
	        $(this.list_id).width($(this.list_id).width()+20);
	    }
	},
	showMenu: function() {
		$(this.list_id).show();
	},
	hideMenu: function() {
		$(this.list_id).hide();	
	}
});

function domasonry() {
    if($('.tags #tagcloud-items').length) {
        // resizeable = false, because it is buggy.
        $('.tags #tagcloud-items').masonry({ columnWidth: 10, resizeable: false });
    }
}

function add_tooltips() {
    var tips = 1;
    jQuery('.user-tooltip').each(function() {
        var link = $(this).attr('href')+'.tip';
        $(this).tooltip({
            position: 'top right',
            offset: [0,3],
            predelay: 100,
            effect: 'fade',
            tip: '#user_tooltip',
            onBeforeShow: function(event, coordinates) {
                $('#user_tooltip').html('');
                var trigger = this.getTrigger();
                var conf = this.getConf();
                jQuery.get(link, function(data) {
                    $('#user_tooltip').html(data);
                    $('#user_tooltip').css('top', (trigger.offset().top-$('#user_tooltip').outerHeight()+conf.offset[0])+'px');
                    $('#user_tooltip').css('left', (trigger.offset().left+conf.offset[1])+'px');
                });
            }
        });
    });
    
    jQuery('.help-tooltip-inverted').each(function() {
        // /help/mapsearch
        var link = '/help/'+$(this).attr('title')+'.tip';
        $(this).removeAttr('title').tooltip({
            position: 'bottom left',
            offset: [2,2],
            tip: '#help_tooltip_inverted',
            predelay: 100,
            effect: 'fade',
            onBeforeShow: function(event, coordinates) {
                $('#help_tooltip_inverted').html('');
                var that = this;
                jQuery.get(link, function(data) {
                    $('#help_tooltip_inverted').html(data);
                });
            }
        });
    });
    
    jQuery('.help-tooltip').each(function() {
        // /help/mapsearch
        var link = '/help/'+$(this).attr('title')+'.tip';
        $(this).removeAttr('title').tooltip({
            position: 'top left',
            offset: [-2,2],
            tip: '#help_tooltip',
            predelay: 100,
            effect: 'fade',
            onBeforeShow: function(event, c) {
                $('#help_tooltip').html('');
                var trigger = this.getTrigger();
                var conf = this.getConf();
                jQuery.get(link, function(data) {
                    $('#help_tooltip').html(data);
                    //var pos = $(this).position();
                    $('#help_tooltip').css('top', (trigger.offset().top-$('#help_tooltip').outerHeight()+conf.offset[0])+'px');
                });
            }
        });
    });
}

function check_permissions() {
    $('.not-allowed').click(function() {
        // Find the id of the element
        var name = $(this).attr('name');
        var id = name.replace('channel-', '');
        if(id == name) {
            var type = 'Group';
        }
        else {
            var type ='Channel';
        }
        id = id.replace('group-', '');

    	if($('.hello').length == 0) {
    	    var referrer = $(this).attr('href');
    	    
    	    if(jQuery('#referrer').length) {
    	        jQuery('#referrer').val(referrer);
    	    }
    	    else {
    	        var refstring = '<input id="referrer" type="hidden" name="referrer" value="'+referrer+'">';
    	        jQuery('#login-form input[name=action]').after(refstring);
    	    }
    		Popup.show('#login-popup');
    	}
    	else {
    	    Popup.join_popup(type, id);
    	}
    	
    	return false;
    });
    add_tooltips();
}




function resizeWindow() {
    var contentHeight = jQuery('#content > div').height() || 0;
    
    var documentHeight = contentHeight + jQuery('#header').height() + jQuery('#menus').height();
    var windowHeight = jQuery(window).height();
    
    if(windowHeight >= documentHeight) {
        if(jQuery('#content > div').length) {
            var height = (windowHeight - documentHeight) + jQuery('#content > div').height();
            jQuery('#content').height(height);
            jQuery('#popup-background').height(height + jQuery('#menus').height());
        }
        else {
        // used on the home page only
            jQuery('#popup-background').height(jQuery(document).height());
        }
    }
    else if(jQuery('#content').height() != contentHeight) {
        jQuery('#content').height(contentHeight);
    }
    
    try {
        if(Map) {
            Map.checkResize();
        }
    }
    catch(e) {
        
    }
    
    return false;
}

jQuery(window).resize(function() {
    resizeWindow();
}).scroll(function() {
    resizeWindow();
});

jQuery(function() {
    Tags.init();
    check_permissions();
    add_tooltips();
    domasonry();
    resizeWindow();
    jQuery(".emptyonclick").emptyonclick();
});

// other jQuery plugins

/*
* jQuery emptyonclick plugin
*
* Created by Andreas Creten (andreas@madewithlove.be) on 2008-06-06.
* Copyright (c) 2008 madewithlove. All rights reserved.
*
* Version: 1.2
*
* Changelog :
* Version 1.2 (17 Jun 2008)
*  - Empty the fields onsubmit when they are not changed
*
* Version 1.1 (11 Jun 2008)
*  - Fixed a bug when working with an empty field (no default value)
*
* Version 1.0 (06 Jun 2008):
*  - Original version
*/
jQuery.fn.emptyonclick = function(options) {
    return this.each(function(options){
        $.emptyonclick(this, options);
    });
};

jQuery.emptyonclick = function(element, options) {
    var defaultValue = jQuery(element).val();
    
    // Bind event handlers to the element
    jQuery(element)
    // On Focus: Store the default value if it's not set, empty the field
    .bind("focus", function(e) {
        if(defaultValue == jQuery(this).val())
            jQuery(this).val('');

    })
    // On Blur: if the field is empty, reset the default value
    .bind("blur", function(e) {
        if(!jQuery(this).val()) {
            jQuery(this).val(defaultValue);
        }
    });

    // Search for the form which has the element
    jQuery(element).parents('form')
    // If the form gets resetted, set the default value back
    .bind('reset', function(e) {
        jQuery(element).val(defaultValue);
        jQuery(element).removeClass(options.changeClass);
    }) 
    // If the form gets submitted empty, remove the default values
    .bind('submit', function(e) {
        if(jQuery(element).val() == defaultValue)
            jQuery(element).val('');
    });
    
    jQuery(element)
    .bind('do-submit', function(e) {
        if(jQuery(element).val() == defaultValue)
            jQuery(element).val('');
    });
};


// HELPERS PHPJS

function parse_str (str, array){
    // http://kevin.vanzonneveld.net
    // +   original by: Cagri Ekin
    // +   improved by: Michael White (http://getsprink.com)
    // +    tweaked by: Jack
    // +   bugfixed by: Onno Marsman
    // +   reimplemented by: stag019
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: stag019
    // -    depends on: urldecode
    // %        note 1: When no argument is specified, will put variables in global scope.
    // *     example 1: var arr = {};
    // *     example 1: parse_str('first=foo&second=bar', arr);
    // *     results 1: arr == { first: 'foo', second: 'bar' }
    // *     example 2: var arr = {};
    // *     example 2: parse_str('str_a=Jack+and+Jill+didn%27t+see+the+well.', arr);
    // *     results 2: arr == { str_a: "Jack and Jill didn't see the well." }

    var glue1 = '=', glue2 = '&', array2 = String(str).split(glue2),
    i, j, chr, tmp, key, value, bracket, keys, evalStr, that = this,
    fixStr = function (str) {
        return that.urldecode(str).replace(/([\\"'])/g, '\\$1').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
    };

    if (!array) {
        array = this.window;
    }

    for (i = 0; i < array2.length; i++) {
        tmp = array2[i].split(glue1);
        if (tmp.length < 2) {
            tmp = [tmp, ''];
        }
        key   = fixStr(tmp[0]);
        value = fixStr(tmp[1]);
        while (key.charAt(0) === ' ') {
            key = key.substr(1);
        }
        if (key.indexOf('\0') !== -1) {
            key = key.substr(0, key.indexOf('\0'));
        }
        if (key && key.charAt(0) !== '[') {
            keys    = [];
            bracket = 0;
            for (j = 0; j < key.length; j++) {
                if (key.charAt(j) === '[' && !bracket) {
                    bracket = j + 1;
                }
                else if (key.charAt(j) === ']') {
                    if (bracket) {
                        if (!keys.length) {
                            keys.push(key.substr(0, bracket - 1));
                        }
                        keys.push(key.substr(bracket, j - bracket));
                        bracket = 0;
                        if (key.charAt(j + 1) !== '[') {
                            break;
                        }
                    }
                }
            }
            if (!keys.length) {
                keys = [key];
            }
            for (j = 0; j < keys[0].length; j++) {
                chr = keys[0].charAt(j);
                if (chr === ' ' || chr === '.' || chr === '[') {
                    keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
                }
                if (chr === '[') {
                    break;
                }
            }
            evalStr = 'array';
            for (j = 0; j < keys.length; j++) {
                key = keys[j];
                if ((key !== '' && key !== ' ') || j === 0) {
                    key = "'" + key + "'";
                }
                else {
                    key = eval(evalStr + '.push([]);') - 1;
                }
                evalStr += '[' + key + ']';
                if (j !== keys.length - 1 && eval('typeof ' + evalStr) === 'undefined') {
                    eval(evalStr + ' = [];');
                }
            }
            evalStr += " = '" + value + "';\n";
            eval(evalStr);
        }
    }
}

function urldecode (str) {
    // http://kevin.vanzonneveld.net
    // +   original by: Philip Peterson
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: AJ
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: travc
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Lars Fischer
    // +      input by: Ratheous
    // +   improved by: Orlando
    // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
    // +      bugfixed by: Rob
    // %        note 1: info on what encoding functions to use from: http://xkr.us/articles/javascript/encode-compare/
    // %        note 2: Please be aware that this function expects to decode from UTF-8 encoded strings, as found on
    // %        note 2: pages served as UTF-8
    // *     example 1: urldecode('Kevin+van+Zonneveld%21');
    // *     returns 1: 'Kevin van Zonneveld!'
    // *     example 2: urldecode('http%3A%2F%2Fkevin.vanzonneveld.net%2F');
    // *     returns 2: 'http://kevin.vanzonneveld.net/'
    // *     example 3: urldecode('http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a');
    // *     returns 3: 'http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a'
    
    return decodeURIComponent(str.replace(/\+/g, '%20'));
}

function http_build_query (formdata, numeric_prefix, arg_separator) {
    // http://kevin.vanzonneveld.net
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Legaev Andrey
    // +   improved by: Michael White (http://getsprink.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +    revised by: stag019
    // -    depends on: urlencode
    // *     example 1: http_build_query({foo: 'bar', php: 'hypertext processor', baz: 'boom', cow: 'milk'}, '', '&amp;');
    // *     returns 1: 'foo=bar&amp;php=hypertext+processor&amp;baz=boom&amp;cow=milk'
    // *     example 2: http_build_query({'php': 'hypertext processor', 0: 'foo', 1: 'bar', 2: 'baz', 3: 'boom', 'cow': 'milk'}, 'myvar_');
    // *     returns 2: 'php=hypertext+processor&myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&cow=milk'

    var value, key, tmp = [];

    var _http_build_query_helper = function (key, val, arg_separator) {
        var k, tmp = [];
        if (val === true) {
            val = "1";
        } else if (val === false) {
            val = "0";
        }
        if (val !== null && typeof(val) === "object") {
            for (k in val) {
                if (val[k] !== null) {
                    tmp.push(_http_build_query_helper(key + "[" + k + "]", val[k], arg_separator));
                }
            }
            return tmp.join(arg_separator);
        } else if (typeof(val) !== "function") {
            return this.urlencode(key) + "=" + this.urlencode(val);
        } else {
            throw new Error('There was an error processing for http_build_query().');
        }
    };

    if (!arg_separator) {
        arg_separator = "&";
    }
    for (key in formdata) {
        value = formdata[key];
        if (numeric_prefix && !isNaN(key)) {
            key = String(numeric_prefix) + key;
        }
        tmp.push(_http_build_query_helper(key, value, arg_separator));
    }

    return tmp.join(arg_separator);
}

function urlencode (str) {
    // http://kevin.vanzonneveld.net
    // +   original by: Philip Peterson
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: AJ
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: travc
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Lars Fischer
    // +      input by: Ratheous
    // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Joris
    // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
    // %          note 1: This reflects PHP 5.3/6.0+ behavior
    // %        note 2: Please be aware that this function expects to encode into UTF-8 encoded strings, as found on
    // %        note 2: pages served as UTF-8
    // *     example 1: urlencode('Kevin van Zonneveld!');
    // *     returns 1: 'Kevin+van+Zonneveld%21'
    // *     example 2: urlencode('http://kevin.vanzonneveld.net/');
    // *     returns 2: 'http%3A%2F%2Fkevin.vanzonneveld.net%2F'
    // *     example 3: urlencode('http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a');
    // *     returns 3: 'http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a'

    str = (str+'').toString();
    
    // Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
    // PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
    return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').
                                                                    replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
