/*
   SiteComponents version:
   6.6.4.1, tag SC_6_6_4_1, created Tue Oct 27 21:28:23 +0100 2009
   $Name:  $
   
   Disclaimer
   
   While we make every effort to ensure that this code is fit for its intended
   purpose, we make no guarantees as to its functionality. CoreTrek AS will
   accept no responsibility for the loss of data or any other damage or
   financial loss caused by use of this code.
   
   Copyright
   
   This programming code is copyright of CoreTrek AS. Permission to run this
   code is given to approved users of CoreTrek's publishing system CorePublish.
   
   This source code may not be copied, modified or otherwise repurposed for use
   by a third party without the written permission of CoreTrek AS.
   
   Contact webmaster@coretrek.com for information.
  
*/

/*

    ============================================================================
    IMPORTANT! This javascript is dependent on Prototype.
    ============================================================================

    SiteComponents Lightbox

    Displays a given URL or HTML snippet in a modal layer. All anchor tags that
    is set with the css-class "lightbox" will be opened in a lightbox. Just
    include this js-file, or the compressed lib.js, to enable.

    * Important SiteComponents requirements: *
    The requirements needs to be of the same version as this JavaScript file!

      - Kjapp lightbox style definitions
      - File html/images/lightbox/ff-overlay-img.png
      - File html/history-frame.php
      - Also remember to set the html/body height to 100% in your CSS

    If you want to programmatically open a lightbox, this can be done using
    the lightbox objects show or showHtml methods like this:

    lightbox.show('http://coretrek.no/cms/corepublish/webpublisering/');
    (If the URL is an image, the image will be opened in an image tag inside
    the lightbox)
    
    lightbox.showHtml("<h1>I'm in a lightbox!</h1>");
    
    The lightbox will fire the following custom events:
    
    - lightbox:contentDimensionChanged
      Fired when the content dimensions of a visible lightbox is changed
      
    - lightbox:contentUpdated
      Fired when the content of the lightbox is updated
      
    - lightbox:doShowFinished
      Fired aften the lightbox id displayed on screen

*/


// ----------------------------------------------------------------------------


var Lightbox = Class.create({

    // =====  PUBLIC METHODS  =================================================

    /**
     * Use this method to display a given HTML snippet inside a lightbox.
     * If you have a regular HTML (prototype) element, use it's innerHTML
     * property to get the HTML to display.
     */
    showHtml: function(html) {
        this.prepareToShow();
        this.content.update(html);
        this.lightbox.fire('lightbox:contentUpdated');
        this.doShow();
    },

    /**
     * Use this if the content to be displayed inside the lightbox is avaliable
     * at an URL. All content from the URL will be fetched using Ajax and
     * inserted into the lightbox.
     *
     * Please note that because of Ajax limitations, the URL must be on the
     * same host as the rest of the site.
     *
     * Images will be opened using an image tag, inside the lightbox.
     *
     * A.lightbox links is rewritten to call this function with the href
     * property as contentUrl.
     */
    show: function(contentUrl) {
        this.prepareToShow();
        lightbox.getAndShowContentFromUrl(contentUrl);
    },

    /**
     * Use this to hide an open lightbox.
     */
    hide: function() {
        this.historyPolling.stop();
        
        // Hide the lightbox
        this.lightbox.hide();
        this.content.hide();
        this.overlay.hide();
        this.spinner.hide();
        
        // Reset the original overflow definition on html
        this.htmlElement.setStyle({ overflow: this.htmlOverflowStyle });
        
        // Stop the content size sampler and remove the resize listeners
        this.contentDimensionChangedRunner.stop();
        Event.stopObserving(window, 'resize', this.windowResizeListenerObj);
        
        this.content.stopObserving('lightbox:contentDimensionChanged', this.windowResizeListenerObj);
        this.lightbox.stopObserving('lightbox:contentUpdated');
        
        // Reenable the invisibe persistent objects
        this.showPersistentObjects(true);
        
        // Clear the lightbox content (" ")
        this.content.update();
        
        if(window.location.hash == "#modal") {
            history.go(-1);
        }
        
        // Reset the lightbox container width to the original size
        this.container.setStyle({ width: this.originalContainerWidth + 'px'});
    },

    /**
     * This function initializes all a.lightbox links and add the
     * lightbox.show() method as the click action.
     */
    initializeLightboxAnchors: function() {
        $$('a.lightbox').each(function (element) {
            element.observe('click', function (event) {
                event.stop();

                var element = Event.findElement(event, 'a');
                if(element != document) {
                    lightbox.activeElement = element;
                    lightbox.show(element.getAttribute('href'));
                }
            });
        });
    },
    
    /**
     * Used to check if the lightbox is visible.
     */
    isVisible: function() {
       return this.lightbox.visible;
    },
    
    /**
     * Constructor: initializes and inserts the lightbox into the document.
     */
    initialize: function() {
        this.history = new Object();
        
        this.htmlElement = $$('html').first();
        var objBody = $$('body').first();
        
        // Add history frame for Internet Explorer
        if(Prototype.Browser.IE) {
            var objHistoryFrame = document.createElement('iframe');
            
            objHistoryFrame.setAttribute('id', 'lightbox-history-frame');
            objHistoryFrame.src = "/history-frame.php";
            
            objHistoryFrame.style.display = 'block';
            objHistoryFrame.style.visibility = 'hidden';
            objHistoryFrame.style.position = 'absolute';
            objHistoryFrame.style.top = '0';
            objHistoryFrame.style.left = '0';
            objHistoryFrame.style.width = '1px';
            objHistoryFrame.style.height = '1px';
            
            objBody.appendChild(objHistoryFrame);
            this.historyFrame = objHistoryFrame;
        }

        objBody.insert({ bottom: '<div id="lightbox"></div><div id="lightbox-spinner"></div>' });
        
        var lightboxElement = $('lightbox');
        lightboxElement.setStyle({ display: 'none' });
        lightboxElement.update('<div id="lightbox-overlay"></div><div id="lightbox-container"></div>');
        
        var container = $('lightbox-container');
        
        container.update('<div id="lightbox-close-container"><a href="#" id="lightbox-close"><span>Close</span></a></div>');
        container.insert({ bottom: '<div id="lightbox-start"><div class="left"></div><div class="right"></div></div><div id="lightbox-content-left"></div><div id="lightbox-content-right"></div><div id="lightbox-outer-content"><div id="lightbox-content"></div></div><div id="lightbox-end"><div class="left"></div><div class="right"></div></div>'})
        
        this.content = $('lightbox-content');
        this.lightbox = $('lightbox');
        this.container = $('lightbox-container');
        this.overlay = $('lightbox-overlay');
        this.spinner = $('lightbox-spinner');
        
        this.spinner.setStyle({
            position: 'absolute',
            zIndex: 1000,
            display: 'none'
        });
        
        this.containerTop = Element.getDimensionFromStyle(this.container, 'top');
        
        // Set opacity on overlay layer. Because of a bug in Firefox, causing
        // fixed positioned layers (behind a flash) to render the flash
        // invisible. This has to be done with a semitransparent png on this
        // browser. Make sure that the png has the same design as the css
        // styled overlay layer has on the other browsers.
        if(Prototype.Browser.Gecko) {
            this.overlay.setStyle({
                background: 'url(/images/lightbox/ff-overlay-img.png)'
            });
        } else {
            this.overlay.setStyle({
                opacity: 0.5
            });
        }
        
        // Add observer to close lightbox if escape key is pressed
        document.observe('keyup', function(event) {
            if(event.keyCode == Event.KEY_ESC) {
                this.clickToCloseListener(event);
            }
        }.bind(this));
        
        // Add observers to close lightbox if overlay or close button is clicked
        $('lightbox-close').observe('click', this.clickToCloseListener.bindAsEventListener(this));
        this.overlay.observe('click', this.clickToCloseListener.bindAsEventListener(this));
        this.lightbox.observe('lightbox:contentReady', this.contentReadyListener.bindAsEventListener(this));
        this.lightbox.observe('lightbox:cpWriteMediaObjectRun', this.resizeContentToMediaObjectSizeListener.bindAsEventListener(this));
        
        // IE6 & 7 has a buggy z-index bug causing nested z-indexed elements to
        // behave a bit frustrating. We need to fix using JavaScript. Detect IE
        // versions below IE8 (in strict mode) and fix z-index
        // Ref: http://richa.avasthi.name/blogs/tepumpkin/2008/01/11/ie7-lessons-learned/
        if(Prototype.Browser.IE && (typeof document.documentMode == 'undefined' || document.documentMode == 7)) {
            var zi = 1000;
            this.container.select('div').each(function(element) {
                element.style.zIndex = zi--;
            });
        }
    },
    
    
    // =====  PRIVATE METHODS =================================================
    
    /**
     * Gets the accumulated horizontal padding between the content and outer
     * edge of the container. When resizing to content size we set the container
     * width to be this padding + the width of the content.
     * 
     * @protected
     */
    getContainerHorizontalPadding: function() {
        if(typeof this.containerPadding == 'undefined') {
            var current = this.content;
            var padding = 0;
            while(current != null && current != this.container) {
                padding += Element.getDimensionFromStyle(current, 'padding-left');
                padding += Element.getDimensionFromStyle(current, 'padding-right');
                padding += Element.getDimensionFromStyle(current, 'margin-left');
                padding += Element.getDimensionFromStyle(current, 'margin-right');
                current = current.getOffsetParent();
            }
            
            this.containerPadding = padding;
        }
        
        return this.containerPadding;
    },
    
    /**
     * Function add the HTML and listeners needed to handle closing the
     * lightbox on back navigation.
     * 
     * @protected
     */
    prepareHistory: function() {
        if(Prototype.Browser.IE) {
            this.historyFrame.src = "/history-frame.php?modal";
        } else {
            window.location.hash = "#modal";
        }

        this.historyPolling = new PeriodicalExecuter(function(pe) {
            var close = false;
            if(Prototype.Browser.IE) {
                var frameTitle = this.historyFrame.contentWindow.document.title;
                if(frameTitle.indexOf("?modal") < 0) {
                    close = true;
                }
            } else {
                var hash = window.location.hash;
                if(window.location.hash != "#modal") {
                    close = true;
                }
            }
            
            if(close) {
                pe.stop();
                if(this.isVisible()) {
                    this.hide();
                }
            }
        }.bind(this), .5);
    },
    
    /**
     * In IE6/IE7 and Opera some items will always show on top of the lightbox.
     * This function used to hide these elements.
     * 
     * @protected
     */
    showPersistentObjects: function(show) {
        $$('select', 'embed', 'object').each(function(element) {
            if(show) {
                element.style.visibility = 'visible';
            } else {
                element.style.visibility = 'hidden';
            }
        });
    },
    
    /**
     * Prepare to show the lightbox. This will initialize the lightbox and start
     * the spinner.
     *
     * @protected
     */ 
    prepareToShow: function() {
        if(!this.prepared) {
            this.prepareHistory();
            
            this.content.hide();
            this.showPersistentObjects(false);
            
            this.overlay.show();
            this.container.hide();
            this.lightbox.show();
            
            // Disable scrollbars for main window
            this.scolloffset = document.viewport.getScrollOffsets();
            this.htmlOverflowStyle = this.htmlElement.getStyle('overflow');
            this.htmlElement.setStyle({ overflow: 'hidden' });
            
            // Reposition the lightbox and overlay. Show spinner
            this.repositionLightboxAndOverlay();
            this.repositionSpinner();
            this.spinner.show();
            
            this.prepared = true;
        }
    },
    
    /**
     * Function will reposition the lightbox and overlay correctly inside
     * the visible viewport. The function do not do anything with the lightbox
     * itself. This is done when the lightbox content is ready to be displayed.
     *
     * @protected
     */
    repositionLightboxAndOverlay: function() {
        var viewportOffsets = document.viewport.getScrollOffsets();
        var viewportDim = document.viewport.getDimensions();
        var containerDim = this.container.getDimensions();
        
        // Get the lightbox width and height. The lightbox will always fill 
        // the document viewpor
        var lightboxHeight = [containerDim['height'] + this.containerTop, viewportDim['height']].max();
        var lightboxWidth = [containerDim['width'], viewportDim['width']].max();
        
        // Position the lightbox inside the viewport
        this.lightbox.setStyle({
            top: viewportOffsets['top'] + 'px',
            left: viewportOffsets['left'] + 'px',
            width: viewportDim['width'] + 'px',
            height: viewportDim['height'] + 'px'
        });
        
        // Resize overlay to fill the visible viewport
        this.overlay.setStyle({
            left: '0px',
            top: '0px',
            height: lightboxHeight + 'px',
            width: lightboxWidth + 'px'
        });
    },
    
    /**
     * Internal method to actually set the lightbox to be visible. This method
     * needs the lightbox content to be set before it is called.
     * 
     * @protected
     */
    doShow: function() {
        if(!this.preparedForShow) {
            this.prepareToShow();
        }
    
        // Reset the preparedToShow parameter as early as possible. Just in
        // case another show is initialized
        this.prepared = false;
    
        this.content.show();
        
        this.container.setStyle({
            visibility: 'hidden'
        });
    
        this.container.show();
        
        if(this.originalContainerWidth == null) {
            this.originalContainerWidth = this.container.getWidth();
        }
    
        // We need to reposition the lightbox and overlay if the window or
        // lightbox content dimensions is changed. Lets add a few observers for
        // this.
        this.windowResizeListenerObj = this.windowResizeListener.bindAsEventListener(this);
        Event.observe(window, 'resize', this.windowResizeListenerObj);
        this.content.observe('lightbox:contentDimensionChanged', this.windowResizeListenerObj);
    
        // There is no predefined event for resized element content. This
        // happens in the lightbox when e.g. there are images that gets
        // displayed after the lightbox is displayed (because of loading time).
        // We add a PeriodicalExecuter that samples the content dimensions, and
        // fire a custom event if the size is changed.
        this.lastContentDimensions = this.content.getDimensions();
        this.contentDimensionChangedRunner = new PeriodicalExecuter(function(pe) {
            var contentDim = this.content.getDimensions();
            if(contentDim['height'] != this.lastContentDimensions['height'] ||
               contentDim['width'] != this.lastContentDimensions['width']) {
                this.content.fire('lightbox:contentDimensionChanged');
            }
            this.lastContentDimensions = this.content.getDimensions();
        }.bind(this), .5);
        
        // Lets adjust the lightbox content width if the lightbox contains an
        // element with a css-defined width, or the first element is an image
        var contentWidth = -1;
        var firstDiv = this.content.down('div');
        
        if(typeof firstDiv != 'undefined' && firstDiv.identify() != 'lightbox_flash_container' && firstDiv.getWidth() > 0) {
            var contentWidth = firstDiv.getWidth();
        } else {
            var firstElement = this.content.down();
            if(firstElement != null && typeof firstElement != 'undefined') {
                
                if(firstElement.tagName == 'IMG') {
                    contentWidth = firstElement.getWidth();
                    // If image has not finished loading, it's width will be 0.
                    // Add an observer to resize content when image has loaded 
                    if(contentWidth == 0) {
                        firstElement.observe('load', this.resizeContentToImageSizeListener.bindAsEventListener(this));
                    }
                } else if(firstElement.identify() == 'lightbox_flash_container' && firstElement.down() != null) {
                    var width = firstElement.down().getDimensionFromStyle('width');
                    if(width > 0) {
                        this.lightbox.fire('lightbox:contentReady');
                    }
                }
            }
        }
        
        // If the width of the content could be detected, we add the container
        // padding to it and reset the container width. If not we leave it at
        // the width defined in css.
        if(contentWidth > 0) {
            this.container.setStyle({
                width: contentWidth + this.getContainerHorizontalPadding() + 'px'
            });
            this.lightbox.fire('lightbox:contentReady');
        }
        
        this.lightbox.fire('lightbox:doShowFinished');
    },
    
    /**
     * Fetch content from a given url, and display this in the lightbox. This is
     * only intended for internal use (by this class). Use the method 
     * lightbox.show(contentUrl) instead.
     * 
     * @protected
     */
    getAndShowContentFromUrl: function(contentUrl) {
        if(contentUrl == null) {
            alert("No URL provided");
            return;
        }
        
        // If the URL is for a jpg, jpeg, png og gif file we generate an img tag
        // and display the image in the lightbox
        if(contentUrl.match(/\.(jpg|jpeg|png|gif)$/)) {
            // Lets preload the image while the loading state is running. This
            // will not work if the browser has disabled cache
            var image = new Image();
            
            // Show the image when the image is preloaded and ready
            Event.observe(image, 'load', function(event) {
                this.showHtml('<img src="' + contentUrl + '" alt=""/>');
            }.bindAsEventListener(this));
            
            image.src = contentUrl;
        }
        // If the URL is for a flash file (swf) or flash movie (flv) we
        // automatically embed the flash in the lightbox
        else if(contentUrl.match(/\.(swf|flv)/)) {
            // src, sessionName, width, height, mimeType, loop, autoplay, controller, objId, flashVars) {
            var html = '<div id="lightbox_flash_container"></div>';
            
            // There is no good automatic way to detect flv/swf dimensions
            // using JavaScript. The author have to have provided the 
            // dimensions to use, using the link name attribute. The
            // dimensions needs to be set in a widthXXXheightYYY string in
            // the name attribute of the link. If this is not set, we default
            // to a width of 500px and a height of 500px
            var defaultWidth = 500;
            var defaultHeight = 300;
            if(typeof this.activeElement != 'undefined') {
                var name = this.activeElement.readAttribute('name');
                if(name != null && name.match(/width\d+height\d/)) {
                    var width = name.match(/width(\d+)/).last();
                    var height = name.match(/height(\d+)/).last();
                }
            }
            
            var extension = contentUrl.match(/.(swf|flv)$/).last();
            var mimeType = '';
            switch(extension) {
                case 'swf':
                    mimeType = 'application/x-shockwave-flash';
                    
                    var flashvars = { };
                    break;
                case 'flv':
                    mimeType = 'video/flv';
                    
                    // We want the controls to float over the video
                    var flashVars = { floatingcontrols: 'true' }
                    break
            }
            
            // Run the cpWriteMediaObject method when the flash container is
            // inserted and available
           this.lightbox.observe('lightbox:contentUpdated', function(event) {
               // If width is provided in link name we simply insert the obj
               if(typeof width != 'undefined' && typeof height != 'undefined') {
                   this.insertMediaObject(contentUrl, width, height, mimeType, flashVars);
               }
               // If dimensions is unavailable we try getting file dimensions
               // from the mmarchive.fileinfo service. If this fails we use
               // the default dimensions
               else {
                   var params = {
                       service: 'mmarchive.fileinfo',
                       fileUrl: contentUrl
                   };
                   
                   new Ajax.Request('/xmlhttprequest.php', {
                       parameters: params,
                       onSuccess: function(res) {
                           var info = res.responseText.evalJSON();
                           this.insertMediaObject(contentUrl, info.width, info.height, mimeType, flashVars);
                       }.bind(this),
                       onFailure: function() {
                           this.insertMediaObject(contentUrl, defaultWidth, defaultHeight, mimeType, flashVars);
                       }.bind(this)
                   });
               }
           }.bindAsEventListener(this));

           this.showHtml(html);
        }
        
        // Other content is read and inserted into the lightbox
        else {
            new Ajax.Request(contentUrl, {
                method: 'get',
                
                onSuccess: function(transport) {
                    var content = transport.responseText;
                    
                    // Try extracting the real content. This uses a rather
                    // complex regexp matching. The reason for this is the
                    // differences in DOM XML parsing in different browsers.
                    // And when using XML parsing, some elements (e.g. textarea)
                    // is rewritten to it's short open/close type (e.g
                    // <textarea ... />. This do not work in XHTML.
                    // The regexp assumes that the regular SiteComponents
                    // placeholde-content(-no-right) is used!
                    
                    if(content.match(/<body\b/)) {
                        var match = content.match(/<div id="placeholder-content">([\s\S]*?)<div id="placeholder-/);
                        if(match == null) {
                            var match = content.match(/<div id="placeholder-content-no-right">([\s\S]*?)<div id="placeholder-/);
                        }
                        
                        if(match != null) {
                            var newContent = match.first();
                            var match = newContent.match(/([\s\S]*)<\/div>/);
                            
                            content = match.first();
                            content = content.replace('placeholder-content', 'lightbox-placeholder-content');
                        }
                    }
                    
                    this.content.update(content);
                    this.lightbox.fire('lightbox:contentUpdated');
                    this.doShow();
                }.bind(this),
                
                onFailure: function(request) {
                    this.hide();
                    alert("Could not connect to URL");
                }.bind(this)
            });
        }
    },
    
    /**
     *
     * @protected
     */
    insertMediaObject: function(url, width, height, mimeType, flashVars) {
        cpWriteMediaObject(url, 'null', width, height, mimeType, 0, 1, 'null', 'lightbox_flash_container', flashVars, true);
        this.lightbox.fire('lightbox:cpWriteMediaObjectRun'); 
    },
    
    /**
     * Repositions the spinner at the center of the visible viewport.
     * 
     * @protected
     */
    repositionSpinner: function() {
        var offsets = document.viewport.getScrollOffsets();
        var dimensions = document.viewport.getDimensions();
        
        this.spinner.setStyle({
            top: offsets.top + 'px',
            left: offsets.left + 'px',
            width: dimensions.width + 'px',
            height: dimensions.height + 'px'
        });
    },
    
    /**
     * Internal function used to set the width of the content
     *
     * @protected
     */
    setContentWidth: function(width) {
        this.container.setStyle({
            width: width + this.getContainerHorizontalPadding() + 'px'
        });
    },
    
    // =====  EVENT LISTENERS =================================================
    
    contentReadyListener: function(event) {
        this.container.setStyle({
            visibility: 'visible'
        });
        
        this.spinner.hide();
        this.repositionLightboxAndOverlay();
    },
    
    /**
     * Handle the window resizing.
     * 
     * @protected
     */
    windowResizeListener: function(event) {
        this.repositionLightboxAndOverlay();
    },
    
    /**
     * This listener will resize the lightbox content to an image. If the
     * lightbox contains a single image, this listener is triggered when the
     * image is finished loading.
     * 
     * @protected
     */
    resizeContentToImageSizeListener: function(event) {
        this.setContentWidth(event.element().getHiddenElementDimensions().width);
        this.lightbox.fire('lightbox:contentReady');
    },
    
    resizeContentToMediaObjectSizeListener: function(event) {
        var el = $('lightbox_flash_container').down();
        if(el != null) {
            this.setContentWidth(el.getDimensionFromStyle('width'));
            this.lightbox.fire('lightbox:contentReady');
        }
    },
    
    /**
     * If a close command should be issued to the lightbox in case of a mouse
     * click (e.g. click on overlay, click on close button). This is the
     * listener that should be attached to the elements. The listener will
     * cancel the event to stop event bubbling.
     *
     * @protected
     */
    clickToCloseListener: function(event) {
        event.stop();
        this.hide();
    }

});


// Initialize the lightbox on dom:loaded
var lightbox;
document.observe('dom:loaded', function(event) {
    lightbox = new Lightbox();
    lightbox.initializeLightboxAnchors();
});

