/**
    Copyright © 2004-2006 Jeff Watkins <http://newburyportion.com/>
    
    Except where otherwise noted, this software is licensed under the Creative
    Commons Attribution License. To view a copy of this license, visit
    http://creativecommons.org/licenses/by/2.5/ or send a letter to
    Creative Commons, 543 Howard Street, 5th Floor, San Francisco,
    California, 94105, USA.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-
    INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
 **/

/*jsl:import scriptaculous/prototype.js*/
/*jsl:import helpers.js*/

//  Change this to be the image used when displaying the wait message when
//  loading content.
var kLightboxWaitImage="/includes/lightbox/images/loading.gif";


function LightBox()
{
    this.__init= false;
    /*  the init function should be called after the document has finished
        loading, otherwise, I might have trouble inserting the necessary
        content to make the lightbox work.
     */
    Event.observe( window, "load", this.init.bindAsEventListener(this) );
}
LightBox.prototype.init= function()
{
    if (this.__init)
        return;

    this.__init= true;

    this.box= document.createElement( "DIV" );
    this.box.id= "lightbox";
    
    this.wait= document.createElement( "DIV" );
    this.wait.id= "lightboxWait";
    
    var img= document.createElement( "IMG" );
    img.src= kLightboxWaitImage;
    this.wait.appendChild( img );
    
    this.message= document.createElement( "SPAN" );
    this.message.id= "lightboxWaitMessage";
    this.wait.appendChild( this.message );
    this.wait.style.display= "none";

    //  Create the shadow overlay
    this.overlay= document.createElement( "DIV" );
    this.overlay.id= "overlay";
    this.overlay.style.display= "none";
    
    //  Insert it all into the document
    var container= document.body;
    container.insertBefore( this.wait, null );
    container.insertBefore( this.box, null );
    container.insertBefore( this.overlay, null );

    //  Update all Anchors with rel=lightbox
    var anchors= document.getElementsByTagName( "A" );
    var a;
    var i;
    
    for (i=0; i<anchors.length; ++i)
    {
        a= anchors[i];
        if (!a.href || a.rel!=="lightbox")
            continue;
        //  Safari can't cancel click for anchor when using addEvent
        a.onclick= this.anchorClicked.bindAsEventListener(this);
    }

    //  Perform form management
    this.internalManageForm();

		this.box.style.display = "none";
}
LightBox.prototype.clear= function()
{
    this.box.innerHTML= "";
}
LightBox.prototype.show=function( what )
{
    what= what || this.box;
    if (what==this.box)
        Effect.Appear( this.box );
    else
        what.style.display="block";
}
LightBox.prototype.hide=function( what )
{
    what= what || this.box;
    what.style.display="none";
}
LightBox.prototype.active= function()
{
    return this.box.style.display==="block";
}
LightBox.prototype.setMayClose= function( closable )
{
    this.mayClose= closable;
    if (!this.closeButton)
        return;
    if (this.mayClose)
        this.show( this.closeButton );
    else
        this.hide( this.closeButton );
    if (this.overlay && this.mayClose)
        this.overlay.onclick= this.close.bindAsEventListener(this);
}
LightBox.prototype.close=function()
{
    this.box.style.display="none";
    this.overlay.style.display="none";
    this.wait.style.display="none";
    return false;
}
LightBox.prototype.setupWait= function( waitMessage, waitTimeout )
{
    this.waitMessage= waitMessage;
    this.waitTimer= window.setTimeout( this.waiting.bind(this),
                                       waitTimeout || 500 );
}
LightBox.prototype.hideWait= function()
{
    this.wait.style.display="none";
    if (this.waitTimer)
    {
        window.clearTimeout( this.waitTimer );
        this.waitTimer= 0;
    }
}
LightBox.prototype.showError= function( errorMessage )
{
    var error= $("lightboxError");
    var msg= $("lightboxErrorMessage");
    
    if (!error || !msg)
    {
        if (errorMessage.push)
            errorMessage= errorMessage.join( "\n" );
        alert( "One or more errors occurred:\n" + errorMessage );
        return;
    }
    
    if (errorMessage.push)
        errorMessage= errorMessage.join( "<br/>" );
    error.style.display="block";
    msg.innerHTML= errorMessage;
}
LightBox.prototype.hideError= function()
{
    var error= $("lightboxError");
    if (!error)
        return;
    error.style.display="none";
}
LightBox.prototype.internalManageForm= function()
{
    this.form= $(this.form);
    if (!this.form)
        return;

    //  TODO: Should I chain to the original onsubmit?
    //  Using onsubmit rather than an event listener because Safari can't
    //  cancel form submission using listeners.
    this.form.onsubmit= this.submitForm.bindAsEventListener(this);
}
LightBox.prototype.manageForm=function( form, submitCallback, beforeSubmitCallback )
{
    this.form= form;
    this.submitCallback= submitCallback;
    this.beforeSubmitCallback= beforeSubmitCallback;

    if (this.__init)
        this.internalManageForm();
}
LightBox.prototype.anchorClicked=function( event )
{
    var e= Event.element(event);
    this.load( e.href, e.waitMessage, e.waitTimeout );
    Event.stop( event );
    return false;
}
LightBox.prototype.submitForm=function( event )
{
    if (!this.form)
        return true;

    /*  If a callback was registered for the before submit event, call it. And
        if the callback returns false (not something that is equivalent to false
        but exactly false) cancel the form submission.
     */
    if (this.beforeSubmitCallback &&
        false===this.beforeSubmitCallback.call( null, this.form ))
    {
        Event.stop( event );
        return false;
    }

    var url= this.form.action;
    var body= Form.serialize( this.form );
    var method= this.form.method;
    
    if ('GET'===method.toUpperCase())
        url+= "?" + body;
    var r= new Ajax.Request( url,
                             { method: method, postBody: body,
                               onSuccess: this.submitCompleted.bind(this),
                               onFailure: this.submitFailed.bind(this) } );

    //  cancel regular form submission
    Event.stop( event );
    return false;
}
LightBox.prototype.submitCompleted=function( request )
{
    this.hideWait();

    //  Call the application if it cares
    if (this.submitCallback)
    {
        this.submitCallback.apply( null, arguments );
        return;
    }
    
    //  If no callback, just hide the lightbox
    this.hide( this.box );
}
LightBox.prototype.submitFailed=function( request )
{
    this.hideWait();
    var message= request.responseText || "Submission failed&hellip;";
    this.showError( message );
    this.show( this.box );
    center( this.box );
}
LightBox.prototype.load=function( url, waitMessage, waitTimeout )
{
    this.clear();
    this.show( this.overlay );
    this.setupWait( waitMessage || "Please wait&hellip;", waitTimeout || 500 );

    var r= new Ajax.Request( url, { onComplete: this.loadCompleted.bind(this),
                                    onFailure: this.loadFailed.bind(this) } );
}
LightBox.prototype.waiting= function()
{
    this.show( this.overlay );
    this.show( this.wait );
    center( this.wait );
    this.hide( this.box );
    this.message.innerHTML= this.waitMessage;
}
LightBox.prototype.loadCompleted= function( request )
{
    var kBodyBeginRegex= /<body\s+id="lightboxContent"\s*>/;
    var kBodyEndRegex= /<\/body>/;
    
    this.hideWait();

    /*  check to determine whether the response uses the lightbox master
        template, otherwise, use innerHTML to display the fragment
     */
    var chunks= request.responseText.split( kBodyBeginRegex );
    var content;
    
    if (1===chunks.length)
        content= chunks[0];
    else
        content= chunks[1].split( kBodyEndRegex )[0];

    //  @BUG: This doesn't work in MSIE...
    loadScriptsInFragment( request.responseText );

    //  Handle inline scripts
    var scripts= request.responseText.extractScripts();
    content += '<scr'+'ipt>'+scripts.join('\n')+'</scr'+'ipt>';
    Element.update( this.box, content );

    /*  Create a close button -- dynamically created here because updating the
        element will remove this button and there seems to be a problem with
        creating a sub-element within the main box for the content...
     */
    this.closeButton= document.createElement( "A" );
    this.closeButton.onclick= this.close.bindAsEventListener(this);
    this.closeButton.title="Close";
    this.closeButton.id="lightboxClose";
    this.closeButton.href="#";
    this.closeButton.style.display= (this.mayClose?"block":"none");
    this.box.insertBefore( this.closeButton, null );
    if (this.overlay && this.mayClose)
        this.overlay.onclick= this.close.bindAsEventListener(this);
    
    /*  Add a keypress hook. I look for the ESC key and dismiss the lightbox if
        setMayClose has been called to enable the close button.
     */
    Event.observe( window, "keypress", this.handleKeyPress.bindAsEventListener(this) );
    
    this.show( this.box );
    center( this.box );
}
LightBox.prototype.loadFailed= function( error )
{
    //  Treat a auth-required failure as a success to display the login form.
    if (403===error.req.status)
    {
        this.loadCompleted( error.req );
        return;
    }
    this.hideWait();
    alert( "load failed: " + request.status );
}
LightBox.prototype.handleKeyPress= function( event )
{
    if (Event.KEY_ESC==event.keyCode && this.mayClose)
    {
        this.close();
        Event.stop( event );
        return false;
    }
}

var lightbox= new LightBox();
