(function($){
  $.fn.overlay = function(options){
    var overlayContext = this;
    
    // Settings to configure overlay.
    this.options = jQuery.extend({
      colour: "#000000",
      opacity: 0.7,
      zIndex: 1,
      container: document.body,
      _onClick: function(){}
    });
    
    this.initialize = function(options){
      this.options.container = $(this.options.container);
      
      this.container = $(document.createElement("div"));
      this.container.attr("id", "OverlayContainer");
      this.container.css({
        background: "#000000",
        position: "absolute",
        left: "0px",
        top: "0px",
        width: "100%",
        opacity: 0.7,
        zIndex: this.options.zIndex
      });

      this.options.container.append(this.container);
      
      this.overlay = $(document.createElement("div"));
      this.overlay.attr("id", "Overlay");
      this.overlay.css({
        position: "absolute",
        left: "0px",
        top: "0px",
        width: "100%",
        height: "100%",
        zIndex: 2,
        backgroundColor: this.options.colour
      });
      
      this.container.append(this.overlay);  
      this.container.bind("click", { overlayContext: overlayContext }, function(event){
        event.data.overlayContext.hide();
      });
      
      this.container.css("display", "none");
      this.setPosition();
      
      $(window).bind("resize", { overlayContext: overlayContext}, function(event){
        event.data.overlayContext.setPosition();
      });
    };
    
    this.setPosition = function(){
      if(this.options.container.is("body")){
        var h = $(document).height() + "px";
        this.container.css({
          top: "0px",
          height: h
        });
      } else {
        var myCoords = this.options.container.position();
        this.container.css({
          top: myCoords.top + "px",
          height: this.options.container.height() + "px",
          left: myCoords.left + "px",
          width: this.options.container.width() + "px"
        });
      }
    };
    
    this.setOnClick = function(func, context){
      this.container.bind("click", function(){
        func.call(context);
      });
    }
    
    this.show = function(){
      this.container.css("opacity", 0);
      this.container.css("display", "block");
      this.container.fadeTo(600, this.options.opacity);
    };
    
    this.hide = function(){
      this.container.fadeOut(600);
    };
    
    this.initialize();
    return this;
  },
  
  $.fn.multibox = function(options){
    // Catching the jQuery object with all elements matched.
    var jQueryMatchedObj = this;
    
    // Settings to configure multibox.
    this.options = jQuery.extend({
      initialWidth: 250,
      initialHeight: 250,
      aspectRatio: 1.33,
      container: document.body,
      overlay: false,
      useOverlay: true,
      contentColor: "#ffffff",
      showNumbers: true,
      showControls: true,
      // showThumbnails: false,
      // autoPlay: false,
      // waitDuration: 2000,
      descClassName: false,
      descMinWidth: 400,
      descMaxWidth: 600,
      movieWidth: 320,
      movieHeight: 240,
      offset: {x:0, y:0},
      fixedTop: false,
      path: "files/",
      _onOpen: function(){},
      _onClose: function(){},
      openFromLink: true
      // relativeToWindow: true
    });
    
    this.initialize = function(jQueryMatchedObj, options){
      var multiboxContext = this;
      
      this.openClosePos = {};
      this.timer = 0;
      this.contentToLoad = {};
      this.index = 0;
      this.opened = false;
      this.contentObj = {};
      this.containerDefaults = {};
      this.createArray = [];
      
      if(this.options.useOverlay){
        this.overlay = new $().overlay({
          container: this.options.container
        });
      }
      
      if(this.options.useOverlay){
        this.overlay.setOnClick(this.close, multiboxContext);
      }
      
      $(jQueryMatchedObj).bind("click", { multiboxContext: multiboxContext }, function(event){
        event.data.multiboxContext.overlay.show();
      });
      
      if($(jQueryMatchedObj).hasClass("nomultibox")){
        $(jQueryMatchedObj).unbind("click");
      }
      
      this.content = $(jQueryMatchedObj);
      if(this.options.descClassName){
        this.descriptions = $("." + this.options.descClassName);
        this.descriptions.each(function(el){
          $(el).css("display", "none");
        });
      }
      
      this.container = $(document.createElement("div"));
      this.container.attr("class", "MultiBoxContainer");
      this.options.container.append(this.container);
      
      this.box = $(document.createElement("div"));
      this.box.addClass("MultiBoxContent");
      this.container.append(this.box);
      
      this.closeButton = $(document.createElement("div"));
      this.closeButton.addClass("MultiBoxClose");
      this.container.append(this.closeButton);
      this.closeButton.bind("click", { multiboxContext: multiboxContext }, function(event){
        event.data.multiboxContext.close();
      });
      
      this.controlsContainer = $(document.createElement("div"));
      this.controlsContainer.addClass("MultiBoxControlsContainer");
      this.container.append(this.controlsContainer);
      
      this.controls = $(document.createElement("div"));
      this.controls.addClass("MultiBoxControls");
      this.controlsContainer.append(this.controls);
      
      this.previousButton = $(document.createElement("div"));
      this.previousButton.addClass("MultiBoxPrevious");
      this.controls.append(this.previousButton);
      this.previousButton.bind("click", { multiboxContext: multiboxContext }, function(event){
        event.data.multiboxContext.previous();
      });
      
      this.nextButton = $(document.createElement("div"));
      this.nextButton.addClass("MultiBoxNext");
      this.controls.append(this.nextButton);
      this.nextButton.bind("click", { multiboxContext: multiboxContext }, function(event){
        event.data.multiboxContext.next();
      });
      
      this.title = $(document.createElement("div"));
      this.title.addClass("MultiBoxTitle");
      this.controls.append(this.title);
      this.titleMargin = this.title.css("marginLeft");
      
      this.number = $(document.createElement("div"));
      this.number.addClass("MultiBoxNumber");
      this.controls.append(this.number);
      
      this.description = $(document.createElement("div"));
      this.description.addClass("MultiBoxDescription");
      this.controls.append(this.description);
      
      if(this.content.length == 1){
        this.title.css("marginLeft", "0px");
        this.description.css("marginLeft", "0px");
        this.previousButton.css("display", "none");
        this.nextButton.css("display", "none");
        this.number.css("display", "none");
      }
      
      this.controls.append($(document.createElement("div")).css("clear", "both"));
      
      var contentNew = new Array();
      this.content.each(function(i, el){
        // If we don't want multibox on this.
        if($(el).hasClass("nomultibox")){
          return;
        }
        
        // We don't want null elements created by Flash.
        if($(el).html().length == 0){
          return;
        }
        
        contentNew.push(el);
      });
      
      this.content = $(contentNew);
      this.content.each(function(i, el){
        el.index = i;
        $(el).bind("click", { multiboxContext: multiboxContext }, function(event){
          event.preventDefault();
          event.data.multiboxContext.open(this);
        });
        
        if(($(el).attr("href")).indexOf("#") > -1){
          el.content = $(el).attr("href").substr(($(el).attr("href").indexOf("#")) + 1);
          if(el.content){
            $(el.content).css("display", "none");
          }
        }
      });
      
      this.reset();
    }
    
    this.create = function(obj){
      // STUB: Implemented later.
    }
    
    this.open = function(el){
      var openContext = this;
      this.index = jQuery.inArray(el, this.content);
      this.openId = $(el).attr("id");
      
      if(!this.opened){
        this.opened = true;
        
        if(this.options.useOverlay){
          this.overlay.show();
        }
        
        this.container.css(this.getOpenClosePos(el));
        this.container.css({
          "opacity": 0,
          "display": "block"
        });
        
        if(this.options.fixedTop){
          var top = this.options.fixedTop;
        } else {
          var top = (($(window).height() / 2) - (this.options.initialHeight / 2)) + this.options.offset.y + $(window).scrollTop();
        }
        
        this.container.animate({
          "width": this.options.initialWidth,
          "height": this.options.initialHeight,
          "top": top,
          "left": ($(window).width() / 2) - (this.options.initialWidth / 2) + this.options.offset.x,
          "opacity": 1
        }, 400);
        
        this.load(this.index);
        
      } else {
        if(this.options.showControls){
          this.hideControls();
        }
        
        this.getOpenClosePos(this.content[this.index]);
        this.timer = $.timer(500, function(){
          openContext.hideContent();
        });
        
        this.timer = $.timer(1100, function(){
          openContext.load(openContext.index);
        });
      }
    }
    
    this.load = function(index){
      var loadContext = this;
      this.box.addClass("MultiBoxLoading");
      this.getContent(index);
      
      if(this.type == "image"){
        var xH = this.contentObj.xH;
        this.contentObj.elem = $(document.createElement("img"));
        this.contentObj.elem.attr("src", this.content[index].href);
        this.contentObj.elem.bind("load", { loadContext: loadContext }, function(event){
          event.data.loadContext.resize();
        });
        this.contentObj.src = this.content[index].href;
        this.contentObj.xH = xH;
        
      } else {
        this.contentObj.src = this.content[index].href;
        this.resize();
      }
    }
    
    this.resize = function(){
      var resizeContext = this;
      switch(this.type){
        case "image":
          if(this.tempSRC != this.contentObj.src){
            // Check if the size exceeds screen.
            this.aspectRatio = this.contentObj.elem.attr("width") / this.contentObj.elem.attr("height");
            if(this.contentObj.elem.attr("width") > $(window).width()){
              this.contentObj.width = $(window).width() - 200;
              this.contentObj.height = this.contentObj.width / this.aspectRatio;
            } else {
              this.contentObj.width = this.contentObj.elem.attr("width");
              this.contentObj.height = this.contentObj.elem.attr("height");
            }
            
            if(this.contentObj.height > $(window).height()){
              this.contentObj.height = $(window).height() - 100;
              this.contentObj.width = this.contentObj.height * this.aspectRatio;
            }

            this.tempSRC = this.contentObj.src;
          }
          
          break;
          
        case "flashMp3":
          this.contentObj.height = 24;
          this.contentObj.width = 320;
          break;
        
        case "flashVideo":
          this.contentObj.height = 240;
          this.contentObj.width = 320;
          break;
        
        default:
          break;
      }
      
      if(this.options.fixedTop){
        var top = this.options.fixedTop;
      } else {
        var top = ($(window).height() / 2) - ((this.contentObj.height + this.contentObj.xH) / 2) + $(window).scrollTop() + this.options.offset.y - 20;
      }
      
      var left = ($(window).width() / 2) - (this.contentObj.width / 2) + this.options.offset.x;
      if(top < 0){
        top = 0;
      }
      
      if(left < 0){
        left = 0;
      }
      
      this.container.animate({
        "width": this.contentObj.width,
        "height": this.contentObj.height + this.contentObj.xH,
        "top": top,
        "left": left,
        "opacity": 1
      }, 800);
      
      this.timer = $.timer(500, function(){
        resizeContext.showContent();
      });
    }
    
    this.showContent = function(){
      var showContentContext = this;
      this.tempSRC = "";
      this.box.removeClass("MultiBoxLoading");
      this.removeContent();
      
      this.contentContainer = $(document.createElement("div"));
      this.contentContainer.attr("id", "MultiBoxContentContainer");
      this.contentContainer.css({
        "opacity": 0,
        "width": this.contentObj.width,
        "height": this.contentObj.height,
        "background": "#000000"
      });
      this.box.append(this.contentContainer);
      
      switch(this.type){
        case "image":
          this.contentObj.elem.css("height", this.contentObj.height);
          this.contentObj.elem.css("width", this.contentObj.width);
          this.contentContainer.append(this.contentObj.elem);
          break;
        
        case "htmlelement":
          this.contentContainer.append(this.elementContent.clone().css("display", "block"));
          break;
        
        case "ajax":
          $("#MultiBoxContentContainer").load(this.contentObj.url);
          break;
        
        default:
          var obj = this.contentContainer.append(this.createEmbedObject());
          if(this.str != ""){
            $("#MultiBoxMediaObject").empty();
            $("#MultiBoxMediaObject").append(this.str);
          }
          break;
      }
      
      this.contentContainer.fadeTo(500, 1);
      
      this.title.empty();
      this.title.append(this.contentToLoad.title);
      if(this.content.length > 1){
        this.number.empty();
        this.number.append(this.contentToLoad.number + " of " + this.content.length);
      } else {
        this.number.append("");
      }
      
      if(this.options.descClassName){
        if($(this.description).children(":first-child")){
          $(this.description).children(":first-child").remove();
        }
        
        if(this.contentToLoad.desc){
          this.contentToLoad.desc.append(this.description);
          this.contentToLoad.desc.css("display", "block");
        }
      }
      
      if(this.options.showControls){
        if(this.contentToLoad.title != "&nbsp;" || this.content.length > 1){
          this.timer = $.timer(800, function(){
            showContentContext.showControls();
          });
        }
      }
    }
    
    this.removeContent = function(){
      if($("#MultiBoxMediaObject")){
        $("#MultiBoxMediaObject").empty();
        $("#MultiBoxMediaObject").remove();
      }
      
      if($("#MultiBoxContentContainer")){
        $("#MultiBoxContentContainer").empty();
        $("#MultiBoxContentContainer").remove();
      }
    }
    
    this.getContent = function(index){
      var getContentContext = this;
      this.setContentType(this.content[index]);
      var desc = false;
      if(this.options.descClassName){
        this.descriptions.each(function(i, el){
          if($(el).hasClass(getContentContext.openId)){
            desc = $(el).clone();
          }
        });
      }
      
      this.contentToLoad = {
        title: this.content[index].title || "&nbsp;",
        desc: desc,
        number: index + 1
      };
    }
    
    this.setContentType = function(link){
      var str = link.href.substr(link.href.lastIndexOf(".") + 1).toLowerCase();
      var contentOptions = {};
      
      if(link.rel.length > 0){
        var optArr = link.rel.split(",");
        $(optArr).each(function(i, el){
          var ta = el.split(":");
          contentOptions[ta[0]] = ta[1];
        });
      }
      
      if(contentOptions.type != undefined){
        str = contentOptions.type;
      }
      
      this.contentObj = {};
      this.contentObj.url = link.href;
      this.contentObj.src = link.href;
      this.contentObj.xH = 0;
      
      if(contentOptions.width){
        this.contentObj.width = contentOptions.width;
      } else {
        this.contentObj.width = this.options.movieWidth;
      }
      
      if(contentOptions.height){
        this.contentObj.height = contentOptions.height;
      } else {
        this.contentObj.height = this.options.movieHeight;
      }
      
      if(contentOptions.panel){
        this.panelPosition = contentOptions.panel;
      } else {
        this.panelPosition = this.options.panel;
      }
      
      switch(str){
        case "jpg":
        case "image":
        case "gif":
        case "png":
          this.type = "image";
          break;
        
        case "swf":
          this.type = "flash";
          break;
        
        case "youtube":
          this.type = "youtube";
          break;
        
        case "flv":
        case "mp4":
          this.type = "flashVideo";
          this.contentObj.xH = 70;
          break;
        
        case "mov":
          this.type = "quicktime";
          break;
        
        case "wmv":
          this.type = "windowsMedia";
          break;
        
        case "rv":
        case "rm":
        case "rmvb":
          this.type = "real";
          break;
        
        case "mp3":
          this.type = "flashMp3";
          this.contentObj.width = 320;
          this.contentObj.height = 70;
          break;
        
        case "element":
          this.type = "htmlelement";
          this.elementContent = link.content;
          this.elementContent.css({
            "display": "block",
            "opacity": 0
          });
          
          if(this.elementContent.css("width") != "auto"){
            this.contentObj.width = this.elementContent.css("width");
          }
          
          this.contentObj.height = this.elementContent.height();
          this.elementContent.css({
            "display": "none",
            "opacity": 1
          });
          
          break;
        
        default:
          
          this.type = "htmlelement";
          if(contentOptions.ajax){
            this.type = "ajax";
          }
          
          break;
      }
    }
    
    this.showControls = function(){
      var showControlsContext = this;
      this.clicked = false;
      
      if(this.container.css("height") != "auto"){
        this.containerDefaults.height = parseInt(this.container.css("height"));
        this.containerDefaults.backgroundColor = this.options.contentColor;
      }
      
      this.container.css("height", "auto");
      
      if(this.content.length > 1){
        this.previousButton.css("visibility", "visible");
        this.nextButton.css("visibility", "visible");
        this.title.css("marginLeft", this.titleMargin);
        
        if(this.contentToLoad.number == 1){
          this.previousButton.addClass("MultiBoxPreviousDisabled");
        } else {
          this.previousButton.removeClass("MultiBoxPreviousDisabled");
        }
        
        if(this.contentToLoad.number == this.content.length){
          this.nextButton.addClass("MultiBoxNextDisabled");
        } else {
          this.nextButton.removeClass("MultiBoxNextDisabled");
        }
        
      } else {
        this.previousButton.css("visibility", "hidden");
        this.nextButton.css("visibility", "hidden");
        this.title.css("marginLeft", 0);
      }
        
      this.controlsContainer.animate({
        "height": this.controls.height()
      }, 300); 
      
      // Unbind any previous keypress events.
      $(window).unbind("keypress.multibox");
      this.timer = $.timer(400, function(){
        $(window).bind("keypress.multibox", { showControlsContext: showControlsContext }, function(event){
          if(event.keyCode == 37) event.data.showControlsContext.previous();
        });

        $(window).bind("keypress.multibox", { showControlsContext: showControlsContext }, function(event){
          if(event.keyCode == 39) event.data.showControlsContext.next();
        });

        $(window).bind("keypress.multibox", { showControlsContext: showControlsContext }, function(event){
          if(event.keyCode == 27) event.data.showControlsContext.close();
        });
      });
      
      if(this.options.useOverlay){
        this.overlay.setPosition();
      }
    }
    
    this.hideControls = function(){
      var hideControlsContext = this;
      
      this.controlsContainer.animate({
        "height": "0px"
      }, 300, function(){
        hideControlsContext.container.css(hideControlsContext.containerDefaults);
      });
    }
    
    this.getOpenClosePos = function(el){
      if(this.options.openFromLink){
        if($(el).children(":first-child")){
          var w = $(el).children(":first-child").width();
          if(w < 0){
            w = 0;
          }
          
          var h = $(el).children(":first-child").height();
          if(h < 0){
            h = 0;
          }
                    
          this.openClosePos = {
            width: w,
            height: h,
            top: ($(el).children(":first-child").offset()).top,
            left: ($(el).children(":first-child").offset()).left
          };
        
        } else {
          var w = $(el).width();
          if(w < 0){
            w = 0;
          }
          
          var h = $(el).height();
          if(h < 0){
            h = 0;
          }
          
          this.openClosePos = {
            width: w,
            height: h,
            top: ($(el).offset()).y,
            left: ($(el).offset()).x
          };
        }
        
      } else {
        if(this.options.fixedTop){
          var top = this.options.fixedTop;
        } else {
          var top = ($(window).height() / 2) - (this.options.initialHeight / 2) + this.options.offset.y;
        }
        
        this.openClosePos = {
          width: this.options.initialWidth,
          height: this.options.initialHeight,
          top: top,
          left: ($(window).width() / 2) - (this.options.initialWidth / 2) + this.options.offset.x
        };
      }
      
      return this.openClosePos;
    }
    
    this.hideContent = function(){
      var hideContentContext = this;
      this.box.addClass("MultiBoxLoading");
      if(this.contentContainer){ // This object might not exist anymore.
        this.contentContainer.animate({
          "opacity": 0
        }, 500);
      }
      
      this.timer = $.timer(500, function(){
        hideContentContext.removeContent();
      });
    }
    
    this.next = function(){
      var nextContext = this;
      if(this.index < this.content.length - 1){
        this.index++;
        this.openId = $(this.content[this.index]).attr("id");
        if(this.options.showControls){
          this.hideControls();
        }
        this.getOpenClosePos(this.content[this.index]);
        this.timer = $.timer(500, function(){
          nextContext.hideContent();
        });
        this.timer = $.timer(1000, function(){
          nextContext.load(nextContext.index);
        });
      }
    }
    
    this.previous = function(){
      var previousContext = this;
      if(this.index > 0){
        this.index--;
        this.openId = $(this.content[this.index]).attr("id");
        if(this.options.showControls){
          this.hideControls();
        }
        
        this.getOpenClosePos(this.content[this.index]);
        this.timer = $.timer(500, function(){
          previousContext.hideContent();
        });
        this.timer = $.timer(1000, function(){
          previousContext.load(previousContext.index);
        });
      }
    }
    
    this.close = function(){
      // Prevent popup if image was not completely loaded.
      if(this.type == "image"){
        this.contentObj.elem.unbind("load");
      }
      
      var closeContext = this;
      if(this.options.useOverlay){
        this.overlay.hide();
      }
      
      if(this.options.showControls){
        this.hideControls();
      }
      
      this.hideContent();
      this.timer = $.timer(500, function(){
        closeContext.zoomOut();
      });
    }
    
    this.zoomOut = function(){
      var zoomOutContext = this;
      if(this.contentContainer){ // This object might not exist anymore.
        this.contentContainer.animate({
          "width": this.openClosePos.width,
          "height": this.openClosePos.height,
          "top": this.openClosePos.top,
          "left": this.openClosePos.left,
          "opacity": 0
        }, 400);
      }
      
      this.timer = $.timer(500, function(){
        zoomOutContext.reset();
      });
    }
    
    this.reset = function(){
      this.container.css({
        "opacity": 0,
        "display": "none"
      });
      
      this.controlsContainer.css("height", "0px");
      this.removeContent();
      this.previousButton.removeClass("MultiBoxButtonDisabled");
      this.nextButton.removeClass("MultiBoxButtonDisabled");
      this.opened = false;
    }
    
    this.createEmbedObject = function(){
      switch(this.type){
        case "flash":
          var url = this.contentObj.url;
          
          var obj = $(document.createElement("div"));
          obj.attr("id", "MultiBoxMediaObject");

          this.str = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" '
          this.str += 'width="'+this.contentObj.width+'" ';
          this.str += 'height="'+this.contentObj.height+'" ';
          this.str += 'title="MultiBoxMedia">';
            this.str += '<param name="movie" value="'+url+'" />'
            this.str += '<param name="quality" value="high" />';
            this.str += '<embed src="'+url+'" ';
            this.str += 'quality="high" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" ';
            this.str += 'width="'+this.contentObj.width+'" ';
            this.str += 'height="'+this.contentObj.height+'"></embed>';
          this.str += '</object>';
          break;
        
        case "flashMp3":
        case "flashVideo":
          var url = this.contentObj.url;
 
          var obj = $(document.createElement("div"));
          obj.attr("id", "MultiBoxMediaObject");
          
          var so = new SWFObject("/blog/wp-content/themes/athenafaculty_v4/jwplayer.swf", "ply", this.contentObj.width, this.contentObj.height, "9", "#ffffff");
          so.addParam("allowfullscreen", "true");
          so.addParam("allowscriptaccess", "always");
          so.addParam("wmode", "opaque");
          so.addVariable("file", url);
          this.str = so.getSWFHTML();
          break;
          
        default:
          break;
      }
      
      return obj;
      // STUB: Not yet implemented.
    }
    
    this.initialize(jQueryMatchedObj, options);
    return this;
  }
})(jQuery);