/*****************************************************************************
 * Gossamer Forum - Advanced web community
 *
 *   Website  : http://gossamer-threads.com/
 *   Support  : http://gossamer-threads.com/scripts/support/
 *   Revision : $Id: gforum.js,v 1.89 2008/11/28 23:47:50 brewt Exp $
 *
 * Copyright (c) 2008 Gossamer Threads Inc.  All Rights Reserved.
 * Redistribution in part or in whole strictly prohibited. Please
 * see LICENSE file for full details.
 *****************************************************************************/

/**
 * GForum is an object to create a separate name space for GForum javascript
 * functions.
 *
 * Note: all parameters are set in include_common_head.html unless
 * otherwise noted.
 * @param staticUrl url of static build directory, from GForum/Config/Data
 * @param url Base GForum url
 * @param helpText help text for the current element on the page.
 * @param social_media list of social media sites (not display names!)
 * @param user_id the id of the user we want to deal with
 * @param timezone_zones hash containing regions as keys with cities as values
 * @param timezone_default the default timezone to display
 * @param timezone_last_selected
 * @param timezone_error_message message to display if ajax fails
 * @param timezone_loading_message messate to display while loading time
 */
function GForum() {
    this.staticUrl = '';
    this.imageUrl = '';
    this.gforum_url = '';
    this.helpText = '';
    // The following two vars get set in 'user_view.html'.
    this.social_media = [];
    this.user_id = '';
    // The following 'timezone' vars are set in 'include_profile_display.html'.
    this.timezone_zones = [];
    this.timezone_default = '';
    this.timezone_last_selected = [];
    this.timezone_error_message = '';
    this.timezone_loading_message = '';

    this.password_strength_lang = [
        'Weak',
        'Good',
        'Strong'
    ];
    this.password_strength_levels = [
        0,
        40,
        70
    ];
}

$(document).ready(function() {
    // For the tooltip icon, we want to display a popup box of text
    // further explaining whatever needs explaining. The help text needs
    // to be assigned to the title attribute of the tooltip image.
    $('.tooltip').hover(
        function() {
            var text = $(this).attr('title');
            $(this).attr('title', '');
            var id = $(this).attr('id');
            var newId = id+'_tooltip';

            // Create an info element after the tooltip icon with the help text
            $('body').prepend('<div class="info" id="'+newId+'">'+text+'</div>');
            GForum.showInfo(id, newId);
        },
        function() {
            // Remove the tooltip text when we stop hovering over the icon
            var id = '#'+$(this).attr('id')+'_tooltip';
            var text = $(id).html();
            $(id).hide().remove();
            $(this).attr('title', text);
        }
    );

    /*************************************************************************
     * This binding affects the info/info-parent setup.
     * With the info/info-parent setup, clicking on the info-parent causes
     * the info div to become visible.  This binding forces the info div to
     * become invisible again if neither that div, nor it's info-parent
     * has been clicked.
     *************************************************************************/
    $(document).bind("click", function(e) {
        var target;
        if (!e) { e = window.event; }
        if (e.target) {
            target = e.target;
        } else if (e.srcElement) {
            target = e.srcElement;
        }
        if (target.nodeType == 3) { // defeat Safari bug
            target = target.parentNode;
        }

        /* hide all popups unless we've clicked on that div or it's parent */
        if (target.className != 'info' && target.className != 'info-parent') {
            $('.info').each(function() {
                $(this).hide();
            });
        }
    });

    /*************************************************************************
     * When we click on an item with the 'favourite' class, we submit an ajax
     * request to the server, then we change the child elements in these ways:
     *
     * - update the favourites image icon
     * - update the number of users who've favourited this item
     *
     * Because the ajax request is really simple, we just assume it will
     * work and update the children regardless of the ajax response
     *************************************************************************/
    $(".favourite").click(function() {
        var regex = /(\d+)$/;
        var id = regex.exec($(this).attr('id'))[1];

        // Get the action to perform
        var action = '';
        var add = 0;
        if ($(this).hasClass('favourite_add')) {
            action = 'add';
            add = 1;
        }
        else {
            action += 'remove';
            add = -1;
        }

        // Toggle the action class
        $(this).toggleClass('favourite_remove');
        $(this).toggleClass('favourite_add');

        // Save the favourite to the database
        var url = GForum.url_get({ 'do': 'json_' + action + '_favourite', 'post_id': id });
        $.post(url.url, url.params);

        // Increase the favourited counter
        var favourite_count_link = $('#favourite_' + id + '_link');
        var favourite_count = parseInt(favourite_count_link.html().substr(1, favourite_count_link.html().length -1), 10) + add;
        favourite_count_link.html('(' + favourite_count + ')');

        if (favourite_count === 0) {
            favourite_count_link.addClass('hide');
        }
        else {
            favourite_count_link.removeClass('hide');
        }

        // Replace the favourite image
        var image = $(this).find('img');
        var img_url = image.attr('src');
        var img_title;
        if (action == 'add') {
            img_url = img_url.replace('post-favoff', 'post-favon');
            img_title = 'Remove Favourite';
        }
        else {
            img_url = img_url.replace('post-favon', 'post-favoff');
            img_title = 'Add Favourite';
        }
        image.attr('src', img_url);
        image.attr('title', img_title);

        return false;
    });
});

GForum.prototype.init_help_text = function(text) {
    GForum.helpText = text;

    // We can set a 'help-text' element that contains a grayed-out
    // string to indicate what default value should be entered.
    // When we click on it, we remove the 'gray' class and any
    // help text.
    $('.help-text').bind("focus", function() {
        if ($(this).val() == GForum.helpText) {
            $(this).val('');
            $(this).removeClass('gray');
        }
    });

    // If we haven't entered any text for our 'help-text' input,
    // then when it loses focus, we reset the help string and the
    // grayed-out style.
    $('.help-text').bind("blur", function() {
        if ($(this).val() === '') {
            $(this).val(GForum.helpText);
            $(this).addClass('gray');
        }
    });

    // If we haven't entered any text for our 'help-text' input,
    // then when we hit 'escape' (ie., keycode 0), we reset the
    // help string and the grayed-out style.
    $('.help-text').bind("keypress", function(e) {
        if (e.which === 0 && $(this).val() === '') {
            $(this).val('');
            $(this).addClass('gray');
            $(this).val(GForum.helpText);
            $(this).blur();
        }
    });
};

GForum.prototype.init_username_autocomplete = function () {
    $('#autocomplete').autocomplete(
        GForum.url_get({ 'do': 'json_user_list' }).url, {
            lineSeparator: ',',
            matchSubset: 0,
            loadingClass: 'input text-area select ac_loading',
            inputClass: 'profile-settings text',
            onItemSelect: function() {$('#autocomplete').focus();}
        }
    );
};

/**
 * Initialize autocomplete for tags. Ie., when modifying a post and
 * specifying tags for that post content, the tag input is handled
 * via this function.
 */
GForum.prototype.init_tags = function() {
    $('#autocomplete').autocomplete(
        GForum.url_get({ 'do': 'json_tags' }).url, {
            lineSeparator: ',',
            onItemSelect: function() {
                $('#autocomplete').focus();
                GForum.add_tags($('#autocomplete'));
            },
            loadingClass: 'input text-area select ac_loading',
            inputClass: 'profile-settings'
        }
    );

    // If the user has entered tags, we can assume
    // they want them added to the post.
    $('#post_write_form').submit(function() {
        GForum.add_tags($('#autocomplete'));
    });

    // Add entered tags when the 'Add' button is clicked, but also 
    // make sure to cancel the form submission
    $('#autocomplete-submit').click(function() {
        GForum.add_tags($('#autocomplete'));
        return false;
    });

    // If we hit the enter key when typing in the tag text input
    // area, then we want to add all tags in the text input to
    // the active tag list.
    $('#autocomplete').keypress(function(e) {
        if (e.which == 13) {
            GForum.add_tags($(this));
        }
    });

    // Now if we click on the image next to the tag
    // in the 'selected tags' div, that tag will be
    // removed from the active selection.
    $('#autocomplete-selected-items .remove-tag').click(function() {
        GForum.remove_tag($(this));
        return false;
    });
};

/**
 * Initialize the timezone jquery setup.
 * Set default select values and show the current time.
 */
GForum.prototype.init_timezone_select = function(zone_id, init_zone) {
    $('#timezone_region').val(zone_id);
    GForum.show_zone(zone_id, init_zone);

    $('#timezone_region').change(function() {
        GForum.show_zone($(this).val());
    });

    $('#user_timezone').change(function() {
        GForum.timezone_last_selected[$('#timezone_region').val()] = $(this).val();
        GForum.show_current_time($(this).val());
    });
};

/**
 * When a city/area has been selected, we need to perform an ajax request
 * to get the actual date for that area.
 */
GForum.prototype.show_current_time = function(timezone) {
    // First clear the div and set the temporary loading message.
    $('#show_current_time').empty();
    $('#show_current_time').append(GForum.timezone_loading_message);

    // Now do the ajax call.
    $.getJSON(GForum.url_get({ 'do': 'json_current_time' }).url, { 'timezone': timezone }, function(response) {
        $('#show_current_time').empty();
        if (!response.status) {
            $('#show_current_time').append(GForum.timezone_error_message);
        }
        else {
            $('#show_current_time').append(response.data);
        }
    });
};

/**
 * When selecting which timezone the user is in, this function handles when
 * a specific region is selected.  When the region is selected, we want to
 * get all cities/areas for that region and populate the 'city/area' select
 * with those values.
 */
GForum.prototype.show_zone = function(region, init_zone) {
    // For grammatical correctness, we append 's' to 'America' when creating the list.
    region = region == 'Americas' ? 'America' : region;

    // Remove the node since it's data has grown stale.
    $('#user_timezone').empty();

    // Cache the timezones locally.
    var zones = GForum.timezone_zones[region];

    // Append each timezone to the select object.
    for (var zone in zones) {
        if (typeof zone == 'string') {
            // Set the default value and text for this select option.
            var value = region + '/' + zones[zone];
            var text = zones[zone].replace(/\//g, ' - ').replace(/_/g, ' ');
            var selected = '';

            if (zones[zone] == 'local') {
                value = 'local';
                text = GForum.timezone_default;
            }

            // Set this zone as selected if it's been selected.
            if (init_zone && value == init_zone) {
                selected = ' selected';
                GForum.timezone_last_selected = $('#user_timezone').children().size();
            }
            else if (!init_zone && GForum.timezone_last_selected[region] && GForum.timezone_last_selected[region] == $('#user_timezone').children().size()) {
                selected = ' selected';
            }

            // Append a new select option to thise select element.
            $('#user_timezone').append('<option value="'+value+'"'+selected+'>'+text+'</option>');
        }
    }

    // Update the current time being displayed.
    if (!init_zone) {
        GForum.show_current_time($('#user_timezone').val());
    }
};

/**
 * For hidden posts we load two versions of the given post.  The default view
 * is for the 'hidden' content, which basically just lets you 'click here' to
 * show the actual post content.  The link has a class of 'show_post', and
 * this function handles hiding one and showing the other.
 */
GForum.prototype.init_hidden_posts = function() {
    $('.show_post').click(function() {
        if ($(this).attr('id').match(/_(\d+)$/)) {
            var id = RegExp.$1;
            $('#p' + id + '_hide').slideUp();
            $('#p' + id).slideDown();
        }
        return false;
    });
};

/**
 * For a given form, if there are a set of inputs A and B such
 * that some inputs from B must have the same value as some of those
 * in group A, then this function follows that mapping to assign
 * appropriate values.
 *
 * @input form_id the id of the form we are submitting.
 * @input opts of the form: { mirrored_input_id: id_of_value_to_mirror }
 *      where we can have as many mirrors and original values as needed.
 */
GForum.prototype.init_mirror_inputs = function(form_id, opts) {
    $('#'+form_id).submit(function() {
        for (var name in opts) {
            if (typeof name == 'string') {
                $('#'+name).val($('#'+opts[name]).val());
            }
        }
    });
};

/**
 * When we use GForum.helpText to show default text in any input, this
 * function is used to clear that default input on form submit.
 * @param id id of the form containing the default help text.
 */
GForum.prototype.clear_help_on_submit = function(id) {
    $('#'+id).submit(function() {
        var text = $('#autocomplete').val();
        if (text == GForum.helpText) {
            $('#autocomplete').val('');
        }
    });
};

/**
 * For use on the profile-view page.
 * We do an ajax request for all the available media types.  When we have
 * a result, then we call this insert function to insert it into the DOM.
 * @param media_type: currently 'flickr', 'youtube', 'delicious' or 'twitter'.
 * @param item: the JSON object resulting from the AJAX query.
 */
GForum.prototype.social_media_insert = function(media_type, item) {
    if (media_type == "flickr") {
        $('#' + media_type + '_content').append('<div class="flickr"><a href="' + item.link + '"><img src="' + item["media:thumbnail"].url + '"/></a></div>');
    }
    else if (media_type == 'youtube') {
        $('#' + media_type + '_content').append('<div class="youtube"><object width="150" height="150"><param name="movie" value="' + item.enclosure.url + '&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="' + item.enclosure.url + '&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="150" height="150"></embed></object></div>');
    }
    else if (media_type == 'delicious') {
        $('#' + media_type + '_content').append('<div><a href="' + item.link + '">' + item.title + '</a></div>');
    }
    else if (media_type == 'twitter') {
        date = item.pubDate.split(' ');
        $('#' + media_type + '_content').append('<div><span>' + date[2] + ' ' + date[1] + ', ' + date[4] + '</span<span>' + item.title + '</span></div>');
    }
};

/**
 * Loads the social media data
 * @param int user_id User_id to load the media for
 */
GForum.prototype.load_social_media = function(user_id) {
    // Do the AJAX request
    $.getJSON(GForum.url_get({ 'do': 'json_social_media_feed' }).url, { 'user_id': user_id }, function(json) {
        var data = json.data;

        $.each(json.data, function(media_name, media) {
            // Remove the spinner icon
            $('#' + media_name + '_spinner').remove();

            if (media.success == '0') {
                $('#' + media_name + '_error').html(media.message);
                $('#' + media_name + '_error').show();
            }
            else {
                var items;
                if (media.data.feed.channel.item.constructor == Array) {
                    // We have multiple items
                    items = media.data.feed.channel.item;
                }
                else {
                    items[0] = media.data.feed.channel.item;
                }

                for (i = 0; i < 5; i++) {
                    if (typeof items[i] == 'undefined') { break; }
                    GForum.social_media_insert(media_name, items[i]);
                }
            }

        });
    });
};

/**
 * Add tags from me to the element identified by '#autocomplete-selected-items'
 * @param object me: text input containing string of tags to be entered
 */
GForum.prototype.add_tags = function (me) {
    var tags = me.val().toLowerCase().replace(/^\s+/, '').replace(/\s+$/, '').split(/\s*,\s*/);
    var $ac_tags = $('#autocomplete-tags');

    // get all existing tags so we don't add duplicates
    var unique_tags = {};
    var current_tags = $ac_tags.val().split(/\s*,\s*/);
    for (var i = 0; i < current_tags.length; i++) {
        unique_tags[current_tags[i]] = 1;
    }

    for (var i = 0; i < tags.length; i++) {
        var tag = tags[i];
        if (!tag.length || unique_tags[tag]) {
            continue;
        }

        unique_tags[tag] = 1;

        // add the tag to the list of 'selected tags'
        var $button = $('<div class="button-remove remove-tag" title="' + this.html_escape(tag) + '">' + this.html_escape(tag) + '</div>');
        $button.click(function () {
            GForum.remove_tag($(this));
            return false;
        });
        $('#autocomplete-selected-items').append($button);

        // add the tag to the hidden input
        $ac_tags.val(($ac_tags.val() ? $ac_tags.val() + ',' : '') + tag);
    }

    // remove all tags from the input box so we know they've been 'added'
    me.val('');

    if ($ac_tags.val()) {
        $('#autocomplete-selected').slideDown();
    }
    else {
        $('#autocomplete-selected').slideUp();
    }
};

/**
 * Remove the clicked-upon tag from the active selection.
 * @param object me: the tag element that has been clicked
 */
GForum.prototype.remove_tag = function (me) {
    // remove the DOM element from the selected items element
    var tag = me.attr('title');
    me.remove();

    var tags = $('#autocomplete-tags').val().split(/\s*,\s*/);
    for (var i = 0; i < tags.length; i++) {
        if (tags[i] == tag) {
            tags.splice(i, 1);
            i--;
        }
    }

    $('#autocomplete-tags').val(tags.join(','));

    if (tags.length) {
        $('#autocomplete-selected').slideDown();
    }
    else {
        $('#autocomplete-selected').slideUp();
    }
};

/**
 * Show the child popup that is tied to the given parentId
 * @param string parentId: Id of the parent object
 * @param string thisId: Id of the element to show as a child
 * @param int justifyRight: 1-> justify right, otherwise justify left
 */
GForum.prototype.showInfo = function (parentId, thisId, justifyRight) {
    var offset = $('#'+parentId).offset();
    if (justifyRight) {
        $('#'+thisId).css('left', offset.left - $('#'+thisId).width());
    }
    else {
        $('#'+thisId).css('left', offset.left);
    }
    $('#'+thisId).css('top', offset.top + $('#'+parentId).height());
    $('#'+thisId).toggle();
};

/**
 * Populates the post reports div
 * @param int post_id Post id of post to process
 * @param string action Ajax action to call
 *
 * @return false Returns false to avoid link submission
 */
GForum.prototype.postReportBoxGet = function (post_id, action) {
    // Initialize the data store
    if (typeof $('#post_report_' + post_id).data('open') == 'undefined') {
        $('#post_report_' + post_id).data('open', 0);
        $('#post_report_' + post_id).data('last_action', action);
    }

    if (!$('#post_report_' + post_id).data('open') || $('#post_report_' + post_id).data('last_action') != action) {
        // Load the content of the post reports div
        $.getJSON(GForum.url_get({ 'do': action }).url, { 'post_id': post_id }, function(json) {
            $('#post_report_' + post_id).html(json.data.html);
        });

        $('#post_report_' + post_id).data('open', 1);
    } else {
        $('#post_report_' + post_id).data('open', 0);
    }

    // Open or close the post reports div
    if ($('#post_report_' + post_id).data('open') || $('#post_report_' + post_id).data('last_action') == action) {

        if ($('#post_report_' + post_id).data('open')) {
            $('#post_report_' + post_id).show();
        } else {
            $('#post_report_' + post_id).hide();
        }
    }

    $('#post_report_' + post_id).data('last_action', action);

    return false;
};

/**
 * Preopens the post reports div in case a moderator selects a post report
 * from the top navigation bar
 */
GForum.prototype.openPostReport = function() {
    // Get the anchor from the url
    var anchor = document.location.hash;

    if (anchor.substr(1, 12) == 'post_report_') {
        // Get the post id from the anchor
        var post_id = anchor.substr(13);

        // Fetch the content and open the post reports div
        GForum.postReportBoxGet(post_id, 'json_post_reports_box_get');
    }
};

/**
 * Initializes the post report elements in the post reports div
 * @param int post_id Post id to submit report for
 * @param int max_message_length Maximal length for a post report
 */
GForum.prototype.initPostReportBox = function(post_id, max_message_length) {
    $('#report_submit_' + post_id).click(function() { return GForum.submitPostReport(post_id); });
    $('#report_message_' + post_id).keyup(function() { GForum.restrictPostReportLength(post_id, max_message_length); });
};

/**
 * Initializes the post reports elements in the post reports div
 * @param int post_id Post id to submit report for
 */
GForum.prototype.initPostReportsBox = function(post_id) {
    // Assign the click functions for the moderation links
    $('#post_reports_lock_' + post_id).click(function() { return GForum.moderatePostReports(post_id, 'lock'); });
    $('#post_reports_mark_processed_' + post_id).click(function() { return GForum.moderatePostReports(post_id, 'mark_processed'); });

    // Opens the PM dialog and initializes the PM receipient checkboxes
    $('#post_reports_send_pm_' + post_id).click(function() {
        $('#post_reports_pm_message_wrapper_' + post_id).slideToggle();
        $('.post_reports_pm_user').toggleClass('hide');
        $('.post_reports_sub_heading').toggleClass('hide');

        return false;
    });

    // Checks/Unchecks all the PM receipients
    $('#post_reports_pm_all_' + post_id).click(function() {
        $('.post_reports_pm_user_checkbox_' + post_id).attr('checked', $(this).attr('checked') ? 'checked' : '');
    });

    // Checks/Unchecks all the PM receipients with the same name
    $('.post_reports_pm_user_checkbox_' + post_id).click(function() {
        var name = $(this).attr('name');

        // Check/Uncheck unique checkboxes
        var checked = 'checked';
        if (!$(this).attr('checked')) {
            checked = '';
        }
        $("input[name='" + name + "']").attr('checked', checked);

        // Check/Uncheck the Send PM to all reporters box
        var all_checked = 'checked';
        $('.post_reports_pm_user_checkbox_' + post_id).each(function() {
            if (!$(this).attr('checked')) {
                all_checked = '';
            }
        });
        $('#post_reports_pm_all_' + post_id).attr('checked', all_checked);
    });


    // Submits a private message to the users
    $('#post_reports_submit_pm_' + post_id).click(function() {
        GForum.postReportPrivateMessageSend(post_id);
    });
};

/**
 * AJAX request to submit a post report
 * @param int post_id Post id to submit report for
 *
 * @return false Returns false to avoid form submission
 */
GForum.prototype.submitPostReport = function(post_id) {
    var data = {
        'do': 'json_post_report_save',
        'post_id': post_id,
        'message': $('#report_message_' + post_id).val()
    };

    var url = GForum.url_get(data);
    $.post(url.base_url, url.params, function(json) {
        if (json.success == 1) {
            $('#report_message_' + post_id).val('');
            $('#report_error_' + post_id).text('');
            $('#post_report_' + post_id).slideToggle();
            $('#post_report_' + post_id).data('open', 0);
        } else {
            $('#report_error_' + post_id).show();
            $('#report_error_' + post_id).text(json.message);
        }
    },
    'json');

    return false;
};

/**
 * Locks a post report or marks it as processed
 * @param int post_id Post id to process reports for
 * @param string action Action to perform (lock/mark_processed)
 *
 * @return false Returns false to avoid link submission
 */
GForum.prototype.moderatePostReports = function(post_id, action) {
    var data = {
        'do': 'json_post_reports_moderate',
        'post_id': post_id,
        'action': action
    };

    var url = GForum.url_get(data);
    $.post(url.base_url, url.params, function(json) {
        // Remove the spacer and the Reports link
        if (action == 'mark_processed') {
            $('#post_report_' + post_id).slideToggle();
            $('#post_reports_link_' + post_id).remove();
            $('#post_reports_count_' + post_id).remove();
        } else if (action == 'lock') {
            $('#post_reports_lock_' + post_id).replaceWith(json.data.lock_timeout);
        }
    },
    'json');

    return false;
};

/**
 * Restricts the length of the post reports message and shows remaining characters counter
 * @param int post_id Post id of post to process
 * @param int max_length Maximal length of the post reports message
 *
 * @return false Returns false to avoid link submission
 */
GForum.prototype.restrictPostReportLength = function(post_id, max_length) {
    var report_message_remaining = max_length - $('#report_message_' + post_id).val().length;

    if (report_message_remaining <= 0) {
        report_message_remaining = 0;
        var scroll_top = $('#report_message_' + post_id).scrollTop();
        $('#report_message_' + post_id).val($('#report_message_' + post_id).val().substring(0, max_length));
        $('#report_message_' + post_id).scrollTop(scroll_top);
    }

    $('#report_message_remaining_chars_' + post_id).text(report_message_remaining);

};

/**
 * Sends a private message to the post reporters and the poster using an AJAX request
 * @param int post_id Post id of post to process
 */
GForum.prototype.postReportPrivateMessageSend = function(post_id) {
    var msg_input = $("textarea[name='post_reports_pm_message_" + post_id + "']");
    var msg_body = msg_input.val();

    // Create a unique indexed array containng all the users who get the message
    var tmp_pm_users = [];
    $('.post_reports_pm_user_checkbox_' + post_id).each(function() {
        if ($(this).attr('checked')) {
            tmp_pm_users[$(this).val()] = $(this).val();
        }
    });

    if($('#post_reports_pm_poster').attr('checked')) {
        tmp_pm_users[$('#post_reports_pm_poster').val()] = $('#post_reports_pm_poster').val();
    }

    // Remove the indexes from the array
    var pm_users = [];
    for (pm_user in tmp_pm_users) {
        if(typeof pm_user == 'string') {
            pm_users.push(pm_user);
        }
    }

    var data = {
        'do': 'json_post_reports_message_send',
        'post_id': post_id,
        'pm_users': pm_users,
        'msg_body': msg_body
    };

    var url = GForum.url_get(data);
    $.post(url.base_url, url.params, function(json) {
        if (json.success) {
            msg_input.val('');
            $('#post_reports_pm_message_wrapper_' + post_id).slideToggle();
            $('#reports_errors_' + post_id).text('');
            $('.post_reports_pm_user').toggle();
        } else {
            $('#reports_errors_' + post_id).text(json.message);
            $('#reports_errors_' + post_id).show();
        }
    },
    "json");
};

/**
 * Perform image maximum height/width resizing
 * @param object img The image object
 * @param int x (optional) Maximum image display width
 * @param int y (optional) Maximum image display height
 */
GForum.prototype.resizeImage = function(img, x, y) {
    x = x ? x : this.image_max_width;
    y = y ? y : this.image_max_height;
    var width = 0;
    var height = 0;
    if (x && x < img.width) {
        width = x;
    }
    if (y && y < img.height) {
        height = y;
    }
    if (width && height) {
        if ((img.width > width && img.height <= height) || (img.width > width && img.height > height && width < height)) {
            height = 0;
        }
        else {
            width = 0;
        }
    }
    if (width > 0) {
        img.width = width;
    }
    if (height > 0) {
        img.height = height;
    }
    if (width > 0 || height > 0) {
        var $link = $('<a href="' + img.src + '" title="Click to view full sized image" target="_blank" />');
        var $img = $(img);
        $img.replaceWith($link);
        $link.append($img);
    }
};

/**
 * Initializes the user profile google map
 * @param hash data Contains the user location information and the default zoom level
 *
 * @return bool Status of localizing the position on the map
 */
GForum.prototype.init_google_map = function(data) {
    // Initialize the map and add the controls
    map = new GMap2($('#map_canvas').get(0));
    map.addControl(new GSmallMapControl());
    map.addControl(new GMenuMapTypeControl());

    var point = '';
    if (data.longitude !== '' && data.latitude !== '') {
        // We use the longitude and latitude information to position the map
        point = new GLatLng(data.latitude, data.longitude);
        GForum.position_google_map(point, data.zoom_level);
        return true;
    } else if (data.location !== '') {
        // We use the users location to position the map
        geocoder = new GClientGeocoder();
        return geocoder.getLatLng(data.location, function(point) {
            if (point === null) {
                // Google couldn't find the location remove the map
                $('#map_canvas').remove();
                return false;
            }

            GForum.position_google_map(point, data.zoom_level);
            return true;
        });
    } else {
        // This shouldn't happen remove the map
        $('#map_canvas').remove();
        return false;
    }
};

/**
 * Centers the map and sets the marker
 * @param object point Point to center and where to set the maker
 * @param int zoom_level Zoom level of the map
 */
GForum.prototype.position_google_map = function(point, zoom_level) {
    map.setCenter(point, zoom_level);
    var marker = new GMarker(point);
    map.addOverlay(marker);
};

/**
 * Code to implement a check all checkbox.  Also supports the usage of shift
 * clicking to (un)select a range of checkboxes.
 * @param string check_boxes The name attribute of all the checkboxes
 * @param string check_all A css selector of the check all checkbox (eg. #check_all)
 */
GForum.prototype.init_check_all = function (check_boxes, check_all) {
    var $check_all = $(check_all);
    var $check_boxes = $('input[name=' + check_boxes + ']:checkbox');
    if (!$check_all.length || !$check_boxes.length) {
        return;
    }

    $check_all.click(function () {
        $check_boxes.each(function (i, cb) {
            cb.checked = $check_all.attr('checked');
        });
    });
    $check_all.removeClass('hide');

    var all_checked = true;
    // The last (un)checked checkbox
    var $last_checked;
    $check_boxes.each(function (i, cb) {
        var $cb = $(cb);
        $cb.click(function (e) {
            // Holding shift when clicking on a checkbox (un)selects everything from the previous box to the current one
            var shift = e && e.shiftKey && $last_checked && $last_checked.val() != $cb.val();
            // Used when 'shift' is true; 1 = first checkbox found, 2 = second checkbox found
            var cb_found = 0;
            // Whether or not the check all box should be (un)checked
            var ac = true;
            $check_boxes.each(function (j, cb) {
                if (shift) {
                    if (cb.value == $cb.val() || cb.value == $last_checked.val()) {
                        ++cb_found;
                    }
                    if (cb_found == 1) {
                        cb.checked = $last_checked.attr('checked');
                    }
                }

                if (!cb.checked) {
                    ac = false;
                }
            });
            $check_all.attr('checked', ac);
            $last_checked = $cb;
        });

        if (!$cb.attr('checked')) {
            all_checked = false;
        }
    });
    $check_all.attr('checked', all_checked);
};

/**
 * Turns links that call functions which are POST request only into a form
 * submission.  It turns all links that contain the attribute rel="post-only"
 * into post requests.
 */
GForum.prototype.init_post_only = function () {
    $("a[rel*='post-only']").click(function (e) {
        var href = $(this).attr('href');

        var q = href.indexOf('?');
        var action = q != -1 ? href.substr(0, q) : href;
        var query = q != -1 ? href.substr(q + 1) : '';
        var form = '<form action="' + GForum.html_escape(action) + '" method="post">';

        var args = query.split(/[;&]/);
        for (var i = 0; i < args.length; i++) {
            if (args[i].match(/([^=]+)=(.*)/)) {
                var key = unescape(RegExp.$1);
                var val = unescape(RegExp.$2);
                form += '<input type="hidden" name="' + GForum.html_escape(key) + '" value="' + GForum.html_escape(val) + '" />';
            }
        }
        form += '</form>';

        var $form = $(form);
        $(document.body).append($form);
        $form.submit();

        return false;
    });
};

/**
 * Initialize the password strength meter.
 * @param string pass_input css selector to the password input
 * @param string strength_text css selector to the text that will show the password strength
 */
GForum.prototype.init_password_strength = function (pass_input, strength_text) {
    var $pass_input = $(pass_input);
    var $strength_text = $(strength_text);
    if (!$pass_input || !$pass_input.length || !$strength_text || !$strength_text.length) {
        return;
    }

    $pass_input.keyup(function () {
        var strength = 'None';
        var pass = $pass_input.val();
        if (pass.length) {
            strength = GForum.password_strength(pass);
        }
        $strength_text.text(strength).attr('class', 'pass-strength-' + strength.replace(/\s/, '-').toLowerCase());
    });
};

/**
 * A simple password strength tester.
 * @param string password the password to check
 */
GForum.prototype.password_strength = function (password) {
    var N = 0;
    if (password.match(/\d/)) {
        N += 10;
    }
    if (password.match(/[a-z]/)) {
        N += 26;
    }
    if (password.match(/[A-Z]/)) {
        N += 26;
    }
    if (password.match(/[^a-zA-Z0-9]/)) {
        N += 32;
    }

// http://en.wikipedia.org/wiki/Password_strength
    var entropy = password.length * Math.log(N) / Math.log(2);

    var levels = this.password_strength_levels;
    for (var i = levels.length - 1; i >= 0; i--) {
        if (entropy >= levels[i]) {
            return this.password_strength_lang[i];
        }
    }
    return this.password_strength_lang[0];
}

/**
 * Initialize the avatar selector.
 * @param hash options The options for the avatar selector:
 *     carousel: css selector of the carousel div
 *     upload_input: css selector of the upload input (optional)
 *     selected_input: css selector of the hidden selected avatar input
 *     jCarouselLite options:
 *         btnPrev: css selector to the previous button
 *         btnNext: css selector to the next button
 *         scroll: the number of avatars to scroll (this should be less or equal to the visible option)
 *         visible: the number of avatars to show
 *         start: the avatar to initially show
 *         (you may also pass in any other jCarouselLite options)
 */
GForum.prototype.init_avatar_selector = function (options) {
    $(options.upload_input).change(function () {
        if ($(this).val()) {
// The user_icon input needs to be reset to an empty string for uploads to work
            $(options.selected_input).val('');
            $(options.carousel + ' img').removeClass('avatar_selected');
        }
    });

    $(options.carousel + ' img').click(function () {
        var $img = $(this);
        var src = $img.attr('src');

        $(options.carousel + ' img').removeClass('avatar_selected');
// When jCarousel is in circular mode, it makes a copy of the items, so you
// can't just add the class to the current item
        $(options.carousel + " img[src='" + src + "']").addClass('avatar_selected');

        var img = src.substr(GForum.imageUrl.length + 1);
        if (img == 'avatars/no-avatar.gif') {
            img = '';
        }
        $(options.selected_input).val(img);
    });

    $(options.carousel).jCarouselLite(options);
};

GForum.prototype.html_escape = function (text) {
    return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
};

GForum.prototype.html_unescape = function (text) {
    return text.replace(/&quot;/gi, '"').replace(/&gt;/gi, '>').replace(/&lt;/gi, '<').replace(/&amp;/gi, '&');
};

/**
 * Parses GForum.url, breaks it into its components, merges params arg into query string and returns a hash containing:
 * url - Complete url with merged query string (GET requests)
 * base_url Base url without query string (POST requests)
 * params Merged query string components (POST requests)
 *
 * @param params Parameters to merge into the query string
 *
 * @return Hash containing the url, base_url and params
 */
GForum.prototype.url_get = function(params) {
    var url = GForum.gforum_url;

    // Parse the url
    var regex = /^(.*?)(?:\?(.*?))?(?:#(.*?))?$/;
    var url_parts = regex.exec(url);
    var base_url = url_parts[1];
    var query_string = url_parts[2];

    // Parse the query string and add the parameters to params
    if(typeof query_string == 'string') {
        $.each(query_string.split(';'), function () {
            var pieces = this.split('=');
            params[pieces[0]] = pieces[1];
        });
    }

    // Create the new query_string
    var query_string_pieces = [];
    if(typeof params == 'object') {
        $.each(params, function (key, value) {
            query_string_pieces.push(key + '=' + value);
        });
    }
    query_string =  query_string_pieces.join(';');

    url = base_url;
    if (query_string) {
        url += '?' + query_string;
    }

    return { 'url': url, 'base_url': base_url, 'params': params };
};

var GForum = new GForum();
