
//////////////////////
// MDialog
//////////////////////

function createDeadBackgroundDiv(owner, className, zIndexHint)
{
    var modalDiv = createCoverDiv(className, zIndexHint);

    // Make sure no events get passed the modal div.  This is what deadens the background. 
    var boundStopEvent = owner.stopEvent.bindAsEventListener(owner);
    modalDiv.onclick = boundStopEvent;
    modalDiv.onmouseover = boundStopEvent;
    modalDiv.onmouseout = boundStopEvent;
    modalDiv.onmousedown = boundStopEvent;
    modalDiv.onmouseup = boundStopEvent;
    modalDiv.onkeydown = boundStopEvent;
    modalDiv.onkeyup = boundStopEvent;
    modalDiv.onkeypress = boundStopEvent;

    modalDiv.style.zIndex = 0;
    document.body.appendChild(modalDiv);
    modalDiv.style.zIndex = zIndexHint;   
    return modalDiv;
}

function MDialog(dialogContents, zIndexHint, baseElement)
{
    // 99999's are to ensure the dialogs and background get above partner ads and other elements

    // Create a big transparent div to force modality of the dialog
    this.modalDiv = createDeadBackgroundDiv(this, 'modalDialog', 2 * zIndexHint + 99999);

    // Create the div for the actual dialog.
    var allowFixedPositioning = false;
    var useIE6Hack = isIE6() && baseElement == undefined && allowFixedPositioning;    
    var dialogDiv = document.createElement('div');
    Element.update(dialogDiv, dialogContents);
    findAndEvalJavascript(dialogContents);
    dialogDiv.style.zIndex = 2 * zIndexHint + 999999 + 1;
    if (useIE6Hack) {
        dialogDiv.id = "fixedPosDivIE6";
        document.body.appendChild(dialogDiv);
        if (!elementFitsInsideWindow(dialogDiv)) {
            // if the dialogDiv cannot diaply on the screen, allow it to scroll.
            document.body.removeChild(dialogDiv);
            dialogDiv.id = "normalDiv";
            dialogDiv.style.position = "absolute";
            document.body.appendChild(dialogDiv);
        }
    }
    else {
        dialogDiv.style.position = "absolute";
        dialogDiv.style.top = '-1000px';
        dialogDiv.style.left = '-1000px';
        // We must first put it into the dom to get layout performed so we can then center it.
        // This does not appear to result in any noticable flicker.
        document.body.appendChild(dialogDiv);
    
        var dialogHeight = dialogDiv.clientHeight;
        var dialogWidth = dialogDiv.clientWidth;
        var newLeft;
        var newTop;
        var elementFitsInWindow = elementFitsInsideWindow(dialogDiv);
        if (baseElement == undefined) {
            newLeft = Math.floor((windowInnerWidth() - dialogWidth) / 2);
            newTop = Math.floor((windowInnerHeight() - dialogHeight) * .40);
            if (elementFitsInWindow && allowFixedPositioning) {
                dialogDiv.style.position = "fixed";
            }
            else {
                newLeft += tx_windowScrollLeft();
                newTop += tx_windowScrollTop();
            }
        } else {
            newLeft = absoluteRight(baseElement);
            newTop = absoluteTop(baseElement);
        }
        
        // Fixup bottom first, then fixup top, so that at worst the top part is visible
        if (!elementFitsInWindow || baseElement != undefined) {
            var overhang = -availableSpaceBottom(dialogDiv, newTop);
            if (overhang >= 0) {
                newTop -= overhang + 10;
            }
            overhang = -availableSpaceTop(dialogDiv, newTop);
            if (overhang >= 0) {
                newTop += overhang + 10;
            }
        }
    
        dialogDiv.style.left = newLeft + 'px';
        newTop = Math.max(newTop, 5);
        dialogDiv.style.top = newTop + 'px';
    }

    this.dialogDiv = dialogDiv;    
    
    document.dialogContext.observers.notify("load", this);
    
    // Need to cache this exact function so we can stopObserving when the dialog closes.
    this.handleResizeObserver = this.handleResize.bindAsEventListener(this);
    Event.observe(window, 'resize', this.handleResizeObserver, false);
    this.handleKeyDownObserver = this.handleKeyDown.bindAsEventListener(this);
    Event.observe(document.documentElement, 'keydown', this.handleKeyDownObserver, false);
}

function elementFitsInsideWindow(element)
{
    var docElement = document.documentElement;
    var doesFit = element.clientHeight < docElement.clientHeight && element.clientWidth < docElement.clientWidth;
    return doesFit;
}

MDialog.prototype.close = function()
{
    //trace("mdialog close");
    
    if (this.dialogDiv.parentNode != null)
        this.dialogDiv.parentNode.removeChild(this.dialogDiv);
    if (this.modalDiv.parentNode != null)
        this.modalDiv.parentNode.removeChild(this.modalDiv);
    
    Event.stopObserving(window, 'resize', this.handleResizeObserver, false);
    Event.stopObserving(document.documentElement, 'keydown', this.handleKeyDownObserver, false);
}

MDialog.prototype.handleResize = function(event)
{
    // This makes sure the modal div fills the browser as it is resized
    // We first set the modalDiv to 1px so that it shrinks and we can determine
    // what size the window wants to be (this handles the case of shrinking the windwo
    // after expanding it)
    this.modalDiv.style.width = '1px';
    this.modalDiv.style.height = '1px';

    // On FF we can immediately see the effects of the relayout from the above, but on IE we
    // have to have a small delay, so we wait 20ms to actually resize.
    setTimeout(function() {
        this.modalDiv.style.width = coverDivWidth();
        this.modalDiv.style.height = coverDivHeight();
    }.bind(this), 20);
}

MDialog.prototype.handleKeyDown = function(event)
{
    if (event.keyCode == Event.KEY_ESC) {
        // close dialog, making sure context knows to pop this dialog, etc.
        //trace("escape key closer");
        document.dialogContext.close();
        Event.stop(event);
    }
}


MDialog.prototype.stopEvent = function(event)
{
    Event.stop(event);
}

// finish the work of signin, update profile, or reset password
MDialog.prototype.finishSecureDialog = function(errorCode)
{
    if (errorCode == 0) {
        goToPendingUrl(this, false);
    }
    else { 
        // Clear all password fields, set error value and re-submit form 
        // to return a validation error page. The re-submit is forced over 
        // http to trigger the standard ajax dialog action.
        var inputs = document.getElementsByTagName('input');
        for (var i = 0; i < inputs.length; i++) {
            if (inputs[i].type == 'password') {
                inputs[i].value = "";
            }
        }
        //var status_array = status.split('|');
        document.getElementById('errorStatus').value = errorCode;
        
        var formElement = document.getElementById(this.secureFormId);
        var index = formElement.action.indexOf('/action');
        if (index > -1) // reset to http
            formElement.action = formElement.action.substring(index);
        openDialogForForm(this.secureFormId); 
    }    
}

////////////////////
// MDialogContext
////////////////////

function MDialogContext()
{
    this.activeDialogs = [];
    // for notification on load, close event
    this.observers = new TxEventObserver();
    this.enableUITimeoutId = null;
    this.enableUI(true);
}

MDialogContext.prototype.open = function(dialogContents, baseElement)
{
    var dialog = new MDialog(dialogContents, this.activeDialogs.length + 20, baseElement);
    this.activeDialogs.push(dialog);
    this.enableUI(true);
    return dialog;
}

MDialogContext.prototype.close = function()
{
    //trace("dialogcontext close");
    this.observers.notify("close");
    var dialog = this.activeDialogs.pop();
    dialog.close();
    this.enableUI(true);
}

MDialogContext.prototype.getActiveDialog = function()
{
    var activeDialogsLength = this.activeDialogs.length;
    var dialog = activeDialogsLength > 0 ? this.activeDialogs[activeDialogsLength - 1] : null;
    return dialog;
}

/**
    When a dialog is being opened, this must be called (with isEnabled = false) to
    prevent further operations from going on.  It immediately puts up a dead
    background which prevents further mouse events from being processed.
    This dead background is removed when this is called with false or after
    5 seconds.  When the dialog is actually open, this is called with false
    to re-enable opening.  However, by that time a new dead background is
    provided by the dialog itself.
*/
MDialogContext.prototype.enableUI = function(isEnabled)
{
    this.isUIEnabled = isEnabled;
    if (isEnabled) {
        if (this.enableUITimeoutId != null) {
            window.clearInterval(this.enableUITimeoutId);
            this.enableUITimeoutId = null;
        }
        var deadBackground = this.deadBackground;
        if (deadBackground != null) {
            if (deadBackground.parentNode != null) {
                deadBackground.parentNode.removeChild(deadBackground);
            }
            this.deadBackground = null;
        }
    }
    else {
        this.deadBackground = createDeadBackgroundDiv(this, 'deadBackground', 100 + 99999);
        this.enableUITimeoutId = window.setTimeout("document.dialogContext.enableUI(true)", 10000);
    }
}

MDialogContext.prototype.stopEvent = function(event)
{
    Event.stop(event);
}

// Install one global MDialogContext for the document
document.dialogContext = new MDialogContext();

////////////////////////////////////////////////
// Convenience Functions for Users of this API
////////////////////////////////////////////////

// Invokes a dialog using contents received from the url
// via an ajax request.  Recommended usage:
// <a href="http://some-url-that-returns-dialog-content"
//    onclick="return openDialog(this, this.href);">Open</a>
//
// baseElement is optional, and is used for initial positioning.
// isPendingUrlForDialog==true means that url should be used in another
//    openDialog() call, to open a subsequent dialog
function openDialog(element, url, pendingOperationUrl, baseElement, isPendingUrlForDialog)
{
    if (!document.dialogContext.isUIEnabled) {
        return false;
    }
    // Bug #222: if the dialog being opened doesn't have any text fields on it
    // which take the focus the focus will stay on the element that was clicked
    // and subsequent "enter" key events will cause another dialog to be opened.
    // We prevent that by explcitly  blur'ing the element which was clicked.
    if (element != null) {
        element.blur();
    }
    document.dialogContext.enableUI(false);
    var handleAjaxResponse = function(xmlhttp) {
        //var startTime = new Date().getTime();
        var dialogContents = getResponseText(xmlhttp);
        if (dialogContents == "") {
            // empty content from server indicates an error, don't show dialog
            window.location.reload();
        }
        else if (dialogContents.startsWith("url=")) {
            window.location = dialogContents.substring(4);
        }
        else if (!handleAjaxExceptionPage(dialogContents)) {
            //dialogContents = tx_stripHeadTagFromAjaxResponse(dialogContents);
            var dialog = document.dialogContext.open(dialogContents, baseElement);
            dialog.pendingOperationUrl = pendingOperationUrl;
            dialog.isPendingUrlForDialog = isPendingUrlForDialog;
            //var totalTime = new Date().getTime() - startTime;
            //alert("Total client time: " + totalTime + " msec");
        }
    };
    
    var optionsDict = {
            method: 'get',
            onSuccess: handleAjaxResponse
    };
    var ajax = new Ajax.Request(url, optionsDict);
    return false;
}

/**
 * Submits the form and uses the response to open a new dialog.
 */
function openDialogForForm(formId, isSecure)
{
    var formElement = document.getElementById(formId);
    var url = formElement.action;
    if (isSecure) {
        return submitSecureDialog(formId);
    }

    if (!document.dialogContext.isUIEnabled) {
        return false;
    }
    document.dialogContext.enableUI(false);
    var handleAjaxResponse = function(xmlhttp) {
        var dialogContents = getResponseText(xmlhttp);
        if (handleAjaxExceptionPage(dialogContents)) {
            return;
        }
        var scriptMarker = "<!-- script: ";
        if (dialogContents.indexOf(scriptMarker) == 0) {
            var endIndex = dialogContents.indexOf(" -->");
            var scriptString = dialogContents.substring(scriptMarker.length, endIndex);
            closeDialog();
            eval(scriptString);
        }
        else if (dialogContents == "" || dialogContents == null) {
            goToPendingUrl(document.dialogContext.getActiveDialog(), false);
        }
        else if (dialogContents == "refresh") {
            goToPendingUrl(document.dialogContext.getActiveDialog(), true);
        }
        else {
            // carry forward the pendingOperationUrl and close the current dialog
            var activeDialog = document.dialogContext.getActiveDialog();
            var pendingOperationUrl = activeDialog.pendingOperationUrl;
            var isPendingUrlForDialog = activeDialog.isPendingUrlForDialog;
            closeDialog();
            var dialog = document.dialogContext.open(dialogContents);
            dialog.pendingOperationUrl = pendingOperationUrl;
            dialog.isPendingUrlForDialog = isPendingUrlForDialog;
        }
    };

    var formValuesString = Form.serialize(formElement);
    var optionsDict = {
        method: 'post',
        contentType:  'application/x-www-form-urlencoded',
        postBody: formValuesString,
        onSuccess: handleAjaxResponse
    };
    new Ajax.Request(url, optionsDict);
    return false;
}

// Closes the currently active dialog.  Recommended usage:
// <a href="." onclick="return closeDialog();">Close</a>
function closeDialog()
{
    document.dialogContext.close();
    return false;
}


// Closes the current dialog but follows the pending url
function okDialog()
{
    goToPendingUrl(document.dialogContext.getActiveDialog(), false);
    return false;
}

function goToPendingUrl(activeDialog, mustAtLeastRefresh)
{
    var pendingOperationUrl = activeDialog == null ? null : activeDialog.pendingOperationUrl;
    if (pendingOperationUrl != null) {
        activeDialog.pendingOperationUrl = null;
        // Empty string for pending operation url means: simply close the dialog -- do not refresh the page.
        if (pendingOperationUrl == "") {
            if (mustAtLeastRefresh) {
                window.location.reload();
            } else {
                closeDialog();
            }
        }
        else {
            if (activeDialog.isPendingUrlForDialog == true) {
                closeDialog();
                openDialog(null, pendingOperationUrl);
            }
            else {
                window.location = pendingOperationUrl;
            }
        }
    }
    else {
        // refresh current page to pick up any changes.
        window.location.reload();
    }
}

/**
 * Submits the form without closing dialog or refreshing page and waits for response before returning.
 */
function submitDialogQuietly(formId)
{
    var formElement = document.getElementById(formId);
    var url = formElement.action;
    var handleAjaxResponse = function(xmlhttp) {
        var dialogContents = getResponseText(xmlhttp);
    };

    var formValuesString = Form.serialize(formElement);
    var optionsDict = {
        method: 'post',
        asynchronous: false,
        contentType: 'application/x-www-form-urlencoded',
        postBody: formValuesString,
        onSuccess: handleAjaxResponse
    };
    new Ajax.Request(url, optionsDict);
    return false;
}

// SSL support: submits form directly to an iframe which sets 
// a cookie which the main window polls to extract status.
function submitSecureDialog(formId)
{
    document[formId].submit();
    
    var activeDialog = document.dialogContext.getActiveDialog();
    activeDialog.secureFormId = formId;
}

function addDialogLoadObserver(observerFunction)
{
    document.dialogContext.addLoadObserver(observerFunction);
}

