/**
 * File:      lightbox.js
 * Title:     Advanced LightBox class.
 * Author:    Manuel Holtgrewe
 *
 * Based on the work of Chris Campbell (http://particletree.com) which is 
 * itself based on http://www.huddletogether.com/projects/lightbox/.
 *
 * We did some API and internal changes (that you won't see anyway when you 
 * use the "lbOn" CSS class to trigger light boxes) and made the class follow 
 * the Singleton pattern.
 *
 * Additionally, we changed the requests to use GET everywhere - links should
 * not send POSTs to the server since POST is reserved for state changing things.
 *
 * Dependencies:
 *
 *  - The Prototype library must have been loaded.
 *  - admin.js must have been loaded before this script is loaded. Otherwise,
 *    the BrowserDetection class will not be available
 */

/**
 * The LightBox allows you to display HTML content in a modal manner. When the
 * effect is triggered then the web site will be dimmed down (semitransparent
 * <div>) and a new <div> will be created that displays some content.
 *
 * You can trigger this effect in two ways:
 *
 * 1.) Set the class of your link to be "lbOn". If this is the case then 
 *     clicking on this link will open the resulting page in the light box.
 *
 *     You can close the light box by adding something like the following to its
 *     content:
 *
 *       <a href="#" class="lbAction" rel="deactivate">Close Lightbox.</a>
 *
 *     You link to other "light boxed" content with something like this:
 *
 *       <a href="confirm.html" class="lbAction" rel="insert">Go to Another Lightbox</a>
 *
 * 2.) Use it explicitely in your JavaScript. You should retrieve the Singleton
 *     instance by calling "LightBox.getInstance()" and you can then show a URL
 *     in the light box using "showLightBox(url)" and hide the light box again
 *     with "hideLightBox()". For example:
 *
 *       LightBox.getInstance().showLightBox('/');
 *       LightBox.getInstance().hideLightBox();
 *
 *
 * You can create new behaviours for links with the "lbAction" class the 
 * following way:
 *
 *  - Add a method to the LightBox' prototype class. Let's assume the name
 *    of this method is METHOD.
 *  - METHOD should expect one parameter: The anchor element that was clicked
 *    on.
 *  - Now, you can use this action with the following HTML code:
 *    
 *      <a href="foo.html" class="lbAction" rel="METHOD">Call Action</a>
 */
//////////////////////////////////////////////////////////////////////////
// Instance
//
var LightBox = Class.create();
LightBox.prototype = {

  yPos : 0,
  xPos : 0,
  
  active : false,
  
  /**
   * Empty constructor.
   */
  initialize: function() {},

  /**
   * Register the given anchor to display its content in the light box.
   *
   * @param ctrl
   *          The anchor element that should work as a light box trigger. The
   *          anchor must have the "href" attribute.
   */
  register: function(ctrl) {
    // Create event listener lambda that simply calls "showLightBox" on this.
    // Register this listener for click events on the element and prevent it
    // from linking anywhere by overwriting its onclick method.
    var eventListener = function() { this.showLightBox(ctrl.href); }
    Event.observe(ctrl, 'click', eventListener.bindAsEventListener(this), false);
    ctrl.onclick = function(){return false;};
  },
  
  /**
   * Displays the light box and queries the content from the given URL.
   *
   * @param url
   *          The URL of the content to display in the light box.
   */
  showLightBox: function(url) {	
		if (BrowserDetector.getInstance().browser == 'Internet Explorer 6'){
			this.getScroll();
			this.prepareIE();
			this.setScroll(0,0);
			this.hideSelects('hidden');
		}
    
    this.displayURL(url);
  },
  
  /**
   * MSIE fix method: IE requires height to 100% and overflow hidden or else
   * you can scroll down past the lightbox.
   */
  prepareIE: function(height, overflow) {

		var xScroll, yScroll;

		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = document.body.scrollWidth;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight){ 
			xScroll = document.body.scrollWidth;
			yScroll = document.body.scrollHeight;
		} else { 
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}

		var windowWidth, windowHeight;
		if (self.innerHeight) {	
			windowWidth = self.innerWidth;
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { 
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { 
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}	

		if(yScroll < windowHeight){
			pageHeight = windowHeight;
		} else { 
			pageHeight = yScroll;
		}

		if(xScroll < windowWidth){	
			pageWidth = windowWidth;
		} else {
			pageWidth = xScroll;
		}

		$('body').style.height = pageHeight;
		$('overlay').style.height = pageHeight;
		$('overlay').style.width = pageWidth;
  },
  
  /**
   * MSIE fix method: In IE, select elements hover on top of the lightbox
   */
  hideSelects: function(visibility) {
    selects = document.getElementsByTagName('select');
    for(i = 0; i < selects.length; i++) {
      selects[i].style.visibility = visibility;
    }
  },
  
  /**
   * Taken from lightbox implementation found at 
   * http://www.huddletogether.com/projects/lightbox/
   */
  getScroll: function() {
    if (self.pageYOffset) {
      this.yPos = self.pageYOffset;
    } else if (document.documentElement && document.documentElement.scrollTop){
      this.yPos = document.documentElement.scrollTop; 
    } else if (document.body) {
      this.yPos = document.body.scrollTop;
    }
  },
  
  /**
   * Taken from lightbox implementation found at 
   * http://www.huddletogether.com/projects/lightbox/
   */
  setScroll: function(x, y) {
    window.scrollTo(x, y); 
  },

  /**
   * Displays the light box with the contents at the given URL.
   *
   * @param url
   *          The URL to fetch the contents for the light box from.
   */
  displayURL: function(url) {
    // Make sure the light box is hidden before we display it again.
    if (this.active == true) this.hideLightBox();

    // Show the light box and load the contents.
    this.active = true;
    
    $('overlay').style.display = 'block';
    $('lightbox').style.display = 'block';
    
    this.loadInfo(url);
  },
  
  /**
   * Display the result of a custom AJAX request.
   *
   * This method expects a function to be passed in as the parameter. This
   * function must perform the AJAX result. The passed in function will be
   * passed the AJAX result handler as the only parameter.
   *
   * This result handler will display the request result in the light box.
   *
   * Example:
   *
   * <pre>
   * var requestSender = function(handler) {
   *    var request = new Ajax.Request(
   *      '/admin/foo/bar',
   *      { method: 'post', parameters: "delete=true", onComplete: handler }
   *    );
   * }
   * </pre>
   *
   * @param f
   *          The function that starts the AJAX request.
   */
  displayAjaxResult: function(f) {
    // Make sure the light box is hidden before we display it again.
    if (this.active == true) this.hideLightBox();

    // Show the light box and load the contents.
    this.active = true;
    
    $('overlay').style.display = 'block';
    $('lightbox').style.display = 'block';

    f(this.processInfo.bindAsEventListener(this));
  },
  
  /**
   * Hides the light box again.
   */
  hideLightBox: function() {
    $('overlay').style.display = 'none';
    $('lightbox').style.display = 'none';
    
    this.active = false;
  },
  
  /**
   * Begin AJAX request to the given URL and display the contents at that URL.
   *
   * @param url
   *          The URL to query the light box contents from.
   */
  loadInfo: function(url) {
    var myAjax = new Ajax.Request(
        url,
        { method: 'get', parameters: "", onComplete: this.processInfo.bindAsEventListener(this) }
    );
  },
  
  /**
   * Event handler for the successful AJAX response.
   *
   * @param response
   *          The XmlHttpRequest after response.
   */
  processInfo: function(response){
    info = "<div id='lbContent'>" + response.responseText + "</div>";
    new Insertion.Before($('lbLoadMessage'), info)
    $('lightbox').className = "done";  
    this.actions(); 
  },
  
  /**
   * Search through new links within the lightbox, and attach click event.
   *
   * The rel attribute of the anchor element is the name of the method
   * that will be called when the link is clicked.
   */
  actions: function(){
    lbActions = document.getElementsByClassName('lbAction');

    for(i = 0; i < lbActions.length; i++) {
      Event.observe(lbActions[i], 'click', this[lbActions[i].rel].bindAsEventListener(this), false);
      lbActions[i].onclick = function(){return false;};
    }

  },
  
  /**
   * Link to another light box from within the light box.
   *
   * Example of creating your own functionality once lightbox is initalized.
   */
  insert: function(e){
     link = Event.element(e).parentNode;
     Element.remove($('lbContent'));
   
     var myAjax = new Ajax.Request(
        link.href,
        {method: 'get', parameters: "", onComplete: this.processInfo.bindAsEventListener(this)}
     );
   
  },

  /**
   * Close the given light box again.
   * Example of creating your own functionality once lightbox is initiated
   */
  deactivate: function(){
    Element.remove($('lbContent'));
    
    if (BrowserDetector.getInstance().browser == "Internet Explorer"){
      this.setScroll(0,this.yPos);
      this.prepareIE("auto", "auto");
      this.hideSelects("visible");
    }
    
    this.hideLightBox();
  },

  /**
   * Handler for the document's "onload" event. Make all links with the "lbOn"
   * class trigger a light box.
   */
  onBodyLoad : function() {
    this.addLightboxMarkup();

    lightBox = LightBox.getInstance();
    elements = document.getElementsByClassName('lbOn');
    for (key in elements) {
      valid = lightBox.register(elements[key]);
    }
  },

  /**
   * Add in markup necessary to make this work. Basically two divs:
   * Overlay holds the shadow Lightbox is the centered square that the content is
   * put into.
   */
  addLightboxMarkup : function() {
  }
}

//////////////////////////////////////////////////////////////////////////
// Class Methods
//

/**
 * The LightBox class' method to return the Singleton instance.
 *
 * @return Returns the LightBox Singleton instance.
 */
LightBox.getInstance = function() {
  if (this.instance == null)
    this.instance = new LightBox();
    
  return this.instance;
}

//////////////////////////////////////////////////////////////////////////
// Event Handling
//

/**
 * Register an event listener so we can make all <a>'s with the "lbOn" class
 * trigger the lightbox effect. Register some more required handlers.
 */
Event.observe(window, 'load', LightBox.getInstance().onBodyLoad.bind(LightBox.getInstance()), false);
Event.observe(window, 'unload', Event.unloadCache, false);
