/* POST SCROLLING */

/* Handle the window being scrolled */
function windowScroll() {
    if(ignore_scroll_event) {
        // Scroll event was caused by script; this will be a single event, so ignore it and pass through
        ignore_scroll_event = false;
    }
    else {
        // We need to push the timeout back if it's set
        if(user_scroll_handled) {
            clearTimeout(user_scroll_handled);
            delete user_scroll_handled;
        }
        in_auto_scroll_zone = false; // Don't try to scroll while user is scrolling
        user_scroll_handled = setTimeout(updateAutoScroll, USER_SCROLL_DELAY);
    }
}

/* Scroll the window to the bottom IF we're in the auto scroll zone */
function scrollIfNeeded() {
    if(in_auto_scroll_zone) scrollToBottom();
}

/* Updates in_auto_scroll_zone to reflect the new state (after the user has scrolled the window) */
function updateAutoScroll() {
    bottomLine = $('contentFrame').scrollTop + $('contentFrame').clientHeight;
    in_auto_scroll_zone = ($('contentFrame').scrollHeight - bottomLine < MAX_SCROLL_DIST);
}

/* Scroll to the bottom of the message list */
function scrollToBottom() {
    ignore_scroll_event = true;
    if(Prototype.Browser.IE) $('contentFrame').scrollTop = 1; // required to fix IE bug
    $('contentFrame').scrollTop = $('contentFrame').scrollHeight;
    in_auto_scroll_zone = true;
}

/* Scroll to top of list */
function scrollToTop() {
    $('contentFrame').scrollTop = 1;
    $('contentFrame').scrollTop = 0;
}

/* DOCUMENT TITLE */

/* Update document title to reflect new message count */
function updateTitle() {
    document.title = SZ_DEFAULT_TITLE;
    if(new_message_count > 0) {
        document.title += " (" + new_message_count + ")";
    }
}

/* Called when the user moves the mouse or presses a key - unsets new message count */
function clearNewPostCount() {
    new_message_count = 0;
    updateTitle();
}

/* INPUT/FORM HANDLING */

/* Handles a message box key press */
function chatKeyPressed(field, event) {
    switch(event.keyCode) {
        case Event.KEY_RETURN:
            if (event.shiftKey) {
                return;
            } else if (event.ctrlKey || event.metaKey) {
                return;
            } else {
                sendNewPost();
            }
            Event.stop(event);
    }
}

/* Submits search form on enter, otherwise passes key */
function searchKeyPressed(field, event) {
    switch(event.keyCode) {
        case Event.KEY_RETURN:
            requestSearch();
            Event.stop(event);
    }
}

/* Updates current form focus */
function updateFocus() {
    if(user_info) setTimeout(function(){$('messageBox').focus()}, 1);
}

/* Highlight a form field when focused */
function formFocus(obj) {
    obj.style.backgroundColor = '#ffffbb';
    obj.style.borderColor     = '#ffffbb';
    obj.style.borderStyle     = 'inset';
}

/* Undo the highlight */
function formBlur(obj) {
    obj.style.backgroundColor = '#f3f3f3';
    obj.style.borderColor     = '#eee';
    obj.style.borderStyle     = 'outset';
}

/* PERIPHERAL UI */

/* Decodes query string to display a good or bad message */
function handleQueryStringParams() {
    // Only handle these parameters once per page load
    if(!handled_query_string) { 
        handled_query_string = true;
        var qs = window.location.search.toQueryParams();
        if(qs.gm) {
            displayGoodMessage(qs.gm.replace(/\+/g, ' '));
        }
        else if(qs.bm) {
            displayBadMessage(qs.bm.replace(/\+/g, ' '));
        }
        if(qs.hidebar) {
            hideSidebar()
        }
        if(qs.noimages) {
            no_images = 1;
        }
    }
}

/* Displays a good message */
function displayGoodMessage(msg) {
    $('pnlBadMessage').hide();
    $('pnlGoodMessage').innerHTML = msg;
    $('pnlGoodMessage').show(); // used to be Effect.Appear, but this caused infinite recursion bugs
    setTimeout(function(){ $('pnlGoodMessage').hide();}, GOOD_MSG_STAY * 1000);
}

/* Displays a bad message */
function displayBadMessage(msg) {
    $('pnlGoodMessage').hide();
    $('pnlBadMessage').innerHTML = msg;
    $('pnlBadMessage').show(); // used to be Effect.Shake, but scriptaculous is as buggy as a bag of hammers
    setTimeout(function(){$('pnlBadMessage').hide();}, BAD_MSG_STAY * 1000);
}

/* Hides the bad message, if present */
function clearBadMessage() {
    $('pnlBadMessage').hide();
}

/* Makes a new user tab visible */
function userSetTab(name) {
    slideShow('pnl' + name);
    panels = ['Login', 'Logout', 'PleaseWait'].without(name);
    panels.each(function(p) { slideHide('pnl' + p); } );
}

/* Toggles a panel's BODY visibility */
function togglePanel(id) {
    $(id).toggle();
    cookie_jar.put('panel_' + id, $(id).visible());
    updatePanelArrow(id);
}

/* Hides/shows panel bodies to match the user's preferences */
function loadPanelSettings() {
    ['searchFormBody', 'uploadBody', 'userList', 'helpBoxBody'].each(function(id) {
        visible = cookie_jar.get('panel_' + id);
        if(!visible) visible = (id == 'searchFormBody' || id == 'helpBoxBody') ? 'false' : 'true';
        if(visible == 'false') $(id).hide();
        else $(id).show();
        updatePanelArrow(id);
    });
}

/* Update arrow to reflect new panel visibility */
function updatePanelArrow(id) {
    graphic = $(id).visible() ? 'up' : 'down';
    $('arrow_' + id).src = graphic + '-arrow-icon.gif';
}

/* Help panel functions. */
help_links = $H({ bbcode: 'BBCode', bbcode_more: '(more)', commands: 'Commands', smileys: 'Smileys' });

/* Initialises help boxes (e.g. smiley list) and renders links */
function updateHelpText() {
    // Render the help content
    $('help_bbcode').innerHTML = bbcodeHelp();
    $('help_smileys').innerHTML = smileysHelp();
    
    // Render empty links
    var links = new Array();
    help_links.keys().each( function(k) {
        links.push('<span id="helplink_' + k + '">&nbsp;</span>');
    });
    $('helpLinks').innerHTML = links.join(' | ');
    var saved_tab = cookie_jar.get('helptab');
    if(saved_tab) showHelp(saved_tab);
    else showHelp('bbcode'); // show the first page
}

/* Shows a particular help box */
function showHelp(id) {
    help_links.each( function(link) {
        if(link.key == id) {
            $('help_' + link.key).show();
            $('helplink_' + link.key).innerHTML = link.value;
            cookie_jar.put('helptab', link.key);
        }
        else {
            $('help_' + link.key).hide();
            $('helplink_' + link.key).innerHTML = '<a  href="javascript:void(null)" onClick="showHelp(\'' + link.key + '\');">' + link.value + '</a>';
        }
    });
}

/* Returns a table of smileys */
function smileysHelp() {
    var columns = 5;
    var done = {};
    var html = '<table width="100%">';
    var index = 0;
    smileys.each(function(smiley) {
        // Only show the first glyph that corresponds to a given smiley
        if(!done[smiley.value]) {
            done[smiley.value] = 1;
            if(index % columns == 0) html += '<tr>';
            html += '<td><a href="javascript:void(null)" class="smiley" onClick="$(\'messageBox\').value += \'' + smiley.key + '\';" /><img src="smileys/' + smiley.value + '.gif" width="15" height="15" alt="' + smiley.key + '" title="' + smiley.key + '" /></a></td>';
            if(index + 1 % columns == 0) html += '</tr>';
            index++;
        }
        // Close the <tr> if not done already
        if(index % columns == 0) html += '</tr>';
    });
    html += '</table>';
    return html;
}

/* Returns a table of BBcode */
function bbcodeHelp() {
    var html = '<table width="100%"><tr><th>Code</th><th>Result</th></tr>';
    // Regular, boring tags
    [
        '[b]Bold[/b]', '[i]Italic[/i]', '[u]Underline[/u]',
        '[s]Strike[/s]', '[color=red] Yeah! [/color]', '[huge]Huge[/huge]',
        '[big]Big[/big]', '[small]Small[/small]', '[tiny]Tiny[/tiny]',
        '[sp]Hermione dies[/sp]', '[song]Numa numa![/song]',
        '[url=www.google.com] Google [/url]'
    ].each(function(code) {
        html += '<tr><td>' + code + '</td><td>' + BBParse(code) + '</td></tr>';
    });
    html += '</table>';
    return html;
}

/* Logs a user in. Sets user_info variable and hides login form. */
function userLogin(user) {
    updatePhpSessId();
    user_info = user;
    updateLogoutUserTag(user);
    userSetTab('Logout');
    $('uploadForm').show();
    $('topicEditIcon').show();
    updateFocus();
}

/* Logs a user out. Unsets user_info variable and shows login form. */
function userLogout() {
    updatePhpSessId();
    user_info = null;
    userSetTab('Login');
    $('uploadForm').hide();
    $('topicEditIcon').hide();
    updateFocus();
}

/* Updates the logout panel to reflect the logged-in user's name */
function updateLogoutUserTag(user) {
    new_name = user.username;
    $('logoutUsername').innerHTML   = new_name.escapeHTML();
    $('logoutUsername').style.color = getNameColour(new_name);
    $('logoutUsername').className   = getGradientClassName(new_name) + ' nonPostAuthor';
    gradientText.set($('logoutUsername')); // Update gradient
    
    // Also set sig link
    $('sigLink').href = "sigcache/" + user.user_id + ".png";
}

/* Shows a div if it is not already visible */
function slideShow(div) {
    if(!$(div).visible()) $(div).show();
}

/* Hides a div unless it is already invisible */
function slideHide(div) {
    if($(div).visible()) $(div).hide();
}

/* YOUTUBE/GOOGLE VIDEO */

/* Requests Youtube video information */
function requestYoutubeInfo(video_id) {
    setTimeout(function() { makeRequest('fetchYoutubeInfo', { id: video_id }, renderYoutubeInfo) }, 100);
}

/* Requests Google Video information */
function requestGoogleVideoInfo(video_id) {
    setTimeout(function() { makeRequest('fetchGoogleVideoInfo', { id: video_id }, renderGoogleVideoInfo) }, 100);
}

/* Renders fetched Youtube information inside the user's post */
function renderYoutubeInfo(data) {
    renderVideoInfo('youtube', data);
}

/* Renders fetched Google Video information inside the user's post. */
/* Also fills in the video thumbnail, having fetched the auto-generated URL */
function renderGoogleVideoInfo(data) {
    renderVideoInfo('googlevideo', data);
    if($('googlevideo_thumb_' + data.id)) {
        var html = '<a href="javascript:newGoogleVideo(\'' + data.id.escapeHTML() + '\')">';
        html += '<img src="' + data.thumbnail + '" width="100" height="75" alt="Google Video thumbnail"';
        html += ' onLoad="scrollIfNeeded()" /></a>';
        $('googlevideo_thumb_' + data.id).innerHTML = html;
    }
}

/* Actually performs the rendering, which is (in theory) the same for all video types */
function renderVideoInfo(idtag, data, call_count) {
    var html = '';
    if(!call_count) call_count = 0;
    if(!data || data == 'fail') {
        html = 'Unable to fetch video information.';
    }
    else if(data.error) {
        html = data.error.escapeHTML();
    }
    else {
        html = '<p class="videoName"><span id="' + idtag + '_videoname_' + data.id.escapeHTML() + '">' + data.name.truncate(200) + '</span>';
        html += ' ' + data.rating + '<br /><span class="videoDescription>';
        html += data.description + '</span></p>';
    }
    
    // Sometimes the div doesn't exist yet, so wait a bit and retry
    if($(idtag + '_info_' + data.id)) {
        $(idtag + '_info_' + data.id).innerHTML = html;
    }
    else if(call_count < 5) {
        setTimeout(function() {
            renderVideoInfo(idtag, data, call_count + 1);
        }, 100);
    }
}

/* Draggable Youtube videos */
function Draggable(drag_element, drag_handle)
{
  var xDelta = 0, yDelta = 0;
  var xStart = 0, yStart = 0;
  var hl = drag_handle;
  var el = drag_element;
  var oldOnMouseUp = null;
  var oldOnMouseMove = null;
  if(!hl) hl = el;

  // remove the events
  function enddrag()
  {
    document.onmouseup = oldOnMouseUp;
    document.onmousemove = oldOnMouseMove;
  }

  // fire each time it's dragged
  function drag(e)
  {
    e = e || window.event;
    xDelta = xStart - parseInt(e.clientX);
    yDelta = yStart - parseInt(e.clientY);
    xStart = parseInt(e.clientX);
    yStart = parseInt(e.clientY);
    el.style.top = (parseInt(el.style.top) - yDelta) + 'px';
    el.style.left = (parseInt(el.style.left) - xDelta) + 'px';
  }

  // initiate the drag
  function md(e)
  {
    e = e || window.event;
    xStart = parseInt(e.clientX);
    yStart = parseInt(e.clientY);
    oldOnMouseUp = document.onmouseup;
    document.onmouseup = enddrag;
    oldOnMouseMove = document.onmousemove;
    document.onmousemove = drag;
    return false;
  }

  // tie it into the element
  hl.onmousedown = md;
}

/* Create a new Youtube video */
function newYoutubeVideo(id) {
    var title = 'Youtube Video';
    if($('youtube_videoname_' + id)) {
        // We're re-truncating, so make sure we don't accidentally cut an &amp; in half or something
        title = $('youtube_videoname_' + id).innerHTML.unescapeHTML().truncate(40).escapeHTML();
    }
    newPopup(
        "youtube_" + id,
        title,
        '<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/' + id + '"></param><param name="bgcolor" value="#eeeeee"></param><embed src="http://www.youtube.com/v/' + id + '" type="application/x-shockwave-flash" bgcolor="#eeeeee" width="425" height="350"></embed></object>'
    );
}

/* Creates a new Google Video video */
function newGoogleVideo(id) {
    var title = 'Google Video';
    if($('googlevideo_videoname_' + id)) {
        title = $('googlevideo_videoname_' + id).innerHTML.unescapeHTML().truncate(40).escapeHTML();
    }
    newPopup(
        "googlevideo_" + id,
        title,
        '<embed style="width:425px; height:350px;" type="application/x-shockwave-flash" src="http://video.google.com/googleplayer.swf?docId=' + id + '" bgcolor="#eeeeee"></embed>'
    );
}

var popup_new_x, popup_new_y;
var popups_open = 0;
var popups_opened = 0;

/* Create a draggable, closable pop-up DIV that floats over the message window */
function newPopup(id, heading, content) {
    // Create new windows such that two don't overlap. Once we've created 10 without deleting
    // one, or we've deleted all of them, we go back to the starting position.
    if(popups_open == 0 || popups_opened > 9) {
        popup_new_x = 42;
        popup_new_y = 270;
    }
    else if(popups_opened > 1) {
        popup_new_x += 20;
        popup_new_y += 20;
    }
    var html = '<div id="popup_' + id + '" class="fixedbox" style="top: ' + popup_new_x + 'px; left: ' + popup_new_y + 'px">';
    html += '<div id="dragbar_' + id + '" class="fixedboxHeading">' + heading + '</div>';
    html += '<div class="fixedboxCloseButton"><a class="fixedboxCloseLink" href="javascript:closePopup(\'' + id + '\')" title="Close window">';
    html += '<img align="middle" title="Close window" alt="Close window" src="close.gif"/></a></div>';
    html += '<div class="fixedboxContent">' + content + '</div></div>';
    $('contentFrame').innerHTML += html; // todo: try other locations
    new Draggable($('popup_' + id), $('dragbar_' + id));
    popups_open++;
    popups_opened++;
    // Hide 'open here' link, if applicable
    if($('openlink_' + id)) $('openlink_' + id).hide();
}

/* Close a pop-up window */
function closePopup(id) {
    $('contentFrame').removeChild($('popup_' + id));
    popups_open--;
    popups_opened = 0;
    // Restore 'open here' link, if applicable
    if($('openlink_' + id)) $('openlink_' + id).show();
}

/* SEARCH PANEL */
function createCalendars() {
    // History
    Calendar.setup({
        flat:           "historyCalendar",
        flatCallback:   requestHistory
    });
    // Search start date
    Calendar.setup({
        inputField     :    "searchStartDate", 
        ifFormat       :    "%d/%m/%Y",
        weekNumbers    :    false,
        button         :    "setStartDateButton",
        step           :    1
    });
    // Search end date
    Calendar.setup({
        inputField     :    "searchEndDate",
        ifFormat       :    "%d/%m/%Y",
        weekNumbers    :    false,
        button         :    "setEndDateButton",
        step           :    1
    });
}

function openSearchOptions() {
    $('searchOptionsOpen').show();
    $('searchOptionsClosed').hide();
}

function closeSearchOptions() {
    $('searchOptionsOpen').hide();
    $('searchOptionsClosed').show();
}

/* TOPIC EDITING/RENDERING */

/* Updates the topic */
function updateTopic(topic) {
    $('topic').update(formatTopic(topic));
    raw_topic = topic;
}

/* Opens up the topic editing form */
function editTopic() {
    if(!user_info) return; // Can't edit if not logged in
    if(!raw_topic) return; // Can't edit if we aren't loaded yet
    if(ignore_topic_link_click) {
        ignore_topic_link_click = 0;
        return;
    }
    $('topicEditBox').value = raw_topic;
    $('topic').hide();
    $('topicLoading').hide();
    $('topicEdit').show();
    $('topicEditBox').focus();
}

/* Cancels the topic edit */
function cancelTopicEdit() {
    $('topicEdit').hide();
    $('topic').show();
    $('topicEditBox').value = raw_topic;
    $('messageBox').focus();    
}

/* Send a change topic message */
function attemptTopicEdit() {
    if(!user_info || $F('topicEditBox').replace(/\s/, '').length == 0) {
        // Do nothing
        return;
    }
    else {
        $('topicEdit').hide();
        $('topicLoading').show();
        makeRequest('setNewTopic', {
            topic: $F('topicEditBox')
        }, receiveTopicEditConfirmation);
        updateTopic($F('topicEditBox'));
    }
}

/* Callback for topic editing, whether successful or not */
function receiveTopicEditConfirmation(data) {
    if(data == 'fail' || (data && data.error)) {
        addSystemErrorPost("The topic could not be changed. Please try again later.");
    }
    $('topicLoading').hide();
    $('topic').show();
    resetHeartbeat();
}

/* Handles a topic edit key press, allowing Enter as a shortcut for the submit button */
function topicEditKeyPressed(field, event) {
    switch(event.keyCode) {
        case Event.KEY_RETURN:
            attemptTopicEdit();
            Event.stop(event);
            break;
        case Event.KEY_ESC:
            cancelTopicEdit();
            Event.stop(event);
            break;
    }
}

/* Javascript to interface with SWFUpload.js and its flash counterpart. Handles creation and callbacks. */
var current_file;

/* Create the SWF Uploader object with appropriate settings */
function createSwfUploader() {
	swfu = new SWFUpload({
		upload_target_url: "server.php",
        flash_url: "SWFUpload.swf",
        debug: false,
        
		file_size_limit: "10240",	// 10 MB
        file_types: generateAllowedFileTypesList(),
        file_types_description: "Allowed file types",
        begin_upload_on_queue: true,
        use_server_data_event: false, // allows server callback once uploaded
        validate_files: false,
        
        file_queued_handler: uploadStart,
        file_progress_handler: uploadProgress,
        file_complete_handler: uploadComplete,
        file_cancelled_handler: uploadCancelled,
        error_handler: uploadError, 
        
        ui_container_id: 'swfUploadDiv',
        degraded_container_id: 'nonSwfUploadDiv'
	});
    updatePhpSessId();
}

/* Generate allowed file types from the listed extensions in format.js. Kind of a hack, yes. */
function generateAllowedFileTypesList() {
    return ext_icon.values().flatten().map(function(x){return'*.'+x;}).join(';');
}

/* Called when a new upload begins */
function uploadStart(file) {
    current_file = file.name;
    var html = file.name.escapeHTML() + ' <span style="font-size: 7pt;">(' + humanFileSize(file.size) + ')</span><br />';
    html += '<div class="progressBarBorder"><div class="progressBar" id="uploadProgress" style="width: 0px;">';
    html += '</div></div><a id="swfUploadCancelLink" class="swfUploadCancelLink" href="javascript:swfu.cancelUpload(\'';
    html += file.id + '\'); this.blur();">';
    html += 'Cancel upload</a>';
    $('swfUploadProgressDiv').innerHTML = html;
    $('swfUploadProgressDiv').show();
}

/* Called periodically to update upload progress */
function uploadProgress(file, bytes_done) {
    if(!current_file || current_file != file.name) {
        uploadStart(file);
    }
    $('uploadProgress').style.width = Math.min(Math.max(0, Math.floor((bytes_done/file.size)*100) - 1), 99) + '%';
}

/* Called when an upload is complete */
function uploadComplete(file, server_data) {
    current_file = null;
    $('swfUploadProgressDiv').hide();
    displayGoodMessage('Upload of file ' + file.name.escapeHTML() + ' complete.');
}

/* Called when the user cancels an upload in progress */
function uploadCancelled(file) {
    current_file = null;
    $('swfUploadProgressDiv').hide();
    displayBadMessage('Upload of file ' + file.name.escapeHTML() + ' cancelled.');
}

/* Called in the event of an upload error */
function uploadError(err, file, msg) {
    current_file = null;
    $('swfUploadProgressDiv').hide();
    displayBadMessage('Error uploading file ' + file.name.escapeHTML() + ': ' + msg.escapeHTML());
}

/* Update the SWFU object with the current PHPSESSID */
function updatePhpSessId() {
    if(!swfu) return;
    var ca = $A(document.cookie.split(';'));
    var id;
    ca.each(function(item) {
        var fv = item.split('=');
        if(fv[0].match(/^\s*PHPSESSID\s*$/i) && fv[1]) {
            id = fv[1];
        }
    });
    if(id) {
        swfu.setPostParams({"PHPSESSID": id});
        var params = swfu.getSetting('post_params');
        if(params['PHPSESSID'] != id) {
            setTimeout('updatePhpSessId()', 100); // retry until the Flash object has been created
        }
    }
}

/* SIDEBAR WIDTH */

function setSidebarWidth(pix) {
    ['topic', 'searchHeader', 'historyHeader', 'post', 'content'].each(function(id) {
       elem = $(id + 'Frame');
       elem.style.left = pix + "px";
    });
    $('sidebarFrame').style.width = pix + "px";
}

function showSidebar() {
    setSidebarWidth(250);
    $('sidebarToggleIcon').innerHTML = '<img src="arrow_left.gif" onClick="hideSidebar();" alt="Click to hide sidebar" title="Click to hide sidebar" align="absmiddle" />';
}

function hideSidebar() {
    setSidebarWidth(0);
    $('sidebarToggleIcon').innerHTML = '<img src="arrow_right.gif" onClick="showSidebar();" alt="Click to show sidebar" title="Click to show sidebar" align="absmiddle" />';
    if($('sidebarFrame').style.width > 0) {
        setTimeout(hideSidebar, 10) // retry until it's hidden
    }
}