/*
 * jQuery Popup plugin v1.0
 * File Date: 1/13/2009
 *
 * Copyright (c) 2009 Jim Salyer
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 */

// apply the Opera height fix for JQuery
$.fn._height = $.fn.height;
$.fn.height = function()
{
  if (this[0] == window && window.opera && window.XMLHttpRequest)
    return window.innerHeight;
  else
    return $.fn._height.apply(this, arguments);
};

// create the global properties
$.extend({
  popup: {
    // grouping tracker
    groups: {
      dummy: []
    },
    // boolean flag to determine if we're closing a window or just switching to a new one
    paging: true,
    // get window properties from the query string
    serializeQueryString: function(url)
    {
      // split the URL to get the query string
      var params = {};
      url = url.split("?");
      
      if (url.length > 1)
      {
        // get the query string and split it into the different variables
        var query = url[1].replace(/&amp;/g, "&").split("&");
        $.each(query, function(i, n)
        {
          // look for popup specific variables
          if (/^popup_/i.test(n))
          {
            // get and apply the value of the popup specific variables
            var val = n.split("=");
            val[0] = val[0].replace(/popup_/i, "");
            if (val.length < 1) val[1] = false;
            if (/^true|false$/i.test(val[1])) val[1] = eval(val[1]);
            if (/^[0-9.]+$/.test(val[1])) val[1] = parseFloat(val[1]);
            params[val[0]] = val[1];
          }
        });
      }
      return params; // return a properties object for the script to use
    },
    // get the page's dimensions
    pageDimensions: function pageDimensions()
    {
      // get the document's scroll dimensions
      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;
      }
    
      // get the browser window's viewport dimensions
      var windowHeight, windowWidth;
      if (self.innerHeight)
      {	
        windowHeight = self.innerHeight;
        windowWidth = self.innerWidth;
      }
      else if (document.documentElement && document.documentElement.clientHeight)
      { 
        windowHeight = document.documentElement.clientHeight;
        windowWidth = document.documentElement.clientWidth;
      }
      else if (document.body)
      { 
        windowHeight = document.body.clientHeight;
        windowWidth = document.body.clientWidth;
      }
      
      /* get the scrollbar size */
      
      // create an inner element to receive the scrollbars
      var inner = $('<div></div>').css({
        height: 200,
        width: "100%"
      });
      
      // create an outer element to change to get the scrollbars
      var outer = $('<div></div>').css({
        visibility: "hidden",
        position: "absolute",
        left: 0,
        top: 0,
        height: 150,
        width: 150,
        overflow: "hidden"
      }).append(inner).appendTo("body");
      
      // get the width without and the width with the scrollbar by adjusting how the element deals with overflow
      var w1 = inner.width();
      outer.css("overflow", "scroll");
      var w2 = inner.width();
      if (w1 == w2) w2 = outer.width();
      
      // get rid of the elements and calculate the scrollbar width
      outer.remove();
      var scrollbarSize = w1 - w2;
      
      // get the page's dimensions based upon the larger values between the scroll and viewport dimensions
      var pageHeight, pageWidth;
      var scrollbarH = 0, scrollbarV = 0;
      
      if(yScroll < windowHeight)
        pageHeight = windowHeight;
      else
      {
        pageHeight = yScroll;
        if (yScroll > windowHeight) scrollbarV = scrollbarSize;
      }
    
      if(xScroll < windowWidth)	
    	  pageWidth = windowWidth;
      else
      {
        pageWidth = xScroll;
        if (xScroll > windowWidth) scrollbarH = scrollbarSize;
      }
      
      // return an object with both the viewport and page dimensions
      return {
        viewport: {
          height: windowHeight,
          width: windowWidth,
          scrollHeight: windowHeight - scrollbarH,
          scrollWidth: windowWidth - scrollbarV
        },
        page: {
          height: pageHeight,
          width: pageWidth,
          scrollHeight: pageHeight - scrollbarH,
          scrollWidth: pageWidth - scrollbarV
        }
      };
    }
  }
});

$.fn.extend({
  popup: function(locals)
  {
    // initialize the default properties and merge them with the given properties
    var globals = {
      group: "",
      ajax: false,
      iframe: false,
      cssPrefix: "popup",
      caption: true,
      pages: true,
      overlayOpacity: .5,
      x: "auto",
      y: "auto",
      height: 480,
      width: 640,
      showOverlay: function(overlay, loader, win){overlay.show();},
      hideOverlay: function(overlay, loader, win){overlay.hide();},
      showLoader: function(overlay, loader, win){loader.show();},
      hideLoader: function(overlay, loader, win){loader.hide().remove();},
      showWindow: function(overlay, loader, win){win.show();},
      hideWindow: function(overlay, loader, win){win.hide().remove();}
    };
    $.extend(globals, locals);
    
    // loop through each given element and return the selected elements for JQuery chaining
    return $(this).each(function()
    {
      // get the relevant attributes of the link element
      var el = $(this);
      var href = el.attr("href") || "";
      var title = el.attr("title") || "";
      var group = "dummy";
      
      // merge the properties with the ones from the query string
      var props = {};
      $.extend(props, globals, $.popup.serializeQueryString(href));
      
      // remove the properties from the URL (to avoid query string processing errors on destination pages)
      var search = href.split("?");
      if (search.length > 1)
      {
        // parse out each popup specific variable
        search[1] = search[1].replace(/popup_\w+(\=\w+)(&|&amp;)?/gi, "");
        if (search[1] == "")
          href = search[0];
        else
          href = search.join("?");
      }
      
      // if a group is given, process it
      if (props.group != "")
      {
        // create the given group (if it hasn't been created already) and add the given window to it
        group = props.group;
        if (!$.popup.groups[group]) $.popup.groups[group] = [];
        $.popup.groups[group].push(el);
      }
      
      // initialize the window type and get the link extension for window type evaluation
      var windowType = "invalid";
      var extension = href.split(".");
      extension = extension.length > 1 ? extension[extension.length - 1].toLowerCase() : "";
      var hash = href.split("#");
      hash = hash.length > 1 ? hash[hash.length - 1] : "";
      
      // set up the window type based upon properties and/or the link extension
      if (/bmp|gif|jpeg|jpg|png/.test(extension))
        windowType = "image";
      else if (extension == "swf")
        windowType = "flash";
      else if (props.ajax)
        windowType = "ajax";
      else if (props.iframe)
        windowType = "iframe";
      else if (hash != "")
        windowType = "html";
      
      // create the reusable elements (image cache and overlay)
      if ($("#" + props.cssPrefix + "_cache").length <= 0)
      {
        // create a hidden cache element to use as a buffer for images
        $('<div id="' + props.cssPrefix + '_cache"></div>').css({
          visibility: "hidden",
          position: "absolute",
          left: -10000,
          top: 0
        }).appendTo("body");
        
        // create the background overlay
        var overlay = $('<div id="popup_overlay">&nbsp;</div>').appendTo("body");
      }
      
      // override the given link's click event to use open the popup
      el.click(function()
      {
        // only continue if we're dealing with a valid window
        if (windowType != "invalid")
        {
          // initialize the window's elements
          $.popup.paging = false;
          var overlay = $("#popup_overlay");
          var loader = $('<div class="popup_loader"><span>Loading...</span></div>'.replace(/popup_/gi, props.cssPrefix + "_")).appendTo("body");
          var win = $('<div class="popup_window"><div class="popup_canvas"><table class="popup_header"><tr><td class="popup_title"></td><td class="popup_close"><a href="#" title="Close Window"><span>Close Window</span></a></td></tr></table><div class="popup_content"><div class="popup_sizer">&nbsp;</div></div><div class="popup_caption">&nbsp;</div><table class="popup_navigation"><tr><td class="popup_previous"><a href="#" title="Previous"><span>Previous</span></a></td><td class="popup_pages">&nbsp;</td><td class="popup_next"><a href="#" title="Next"><span>Next</span></td></tr></table></div></div>'.replace(/popup_/gi, props.cssPrefix + "_")).appendTo("body");
          var sizer = win.find("div." + props.cssPrefix + "_sizer");
          
          // set up a reusable function to hide the loader and show the window
          var show = function()
          {
            // get the window's size and the window's content area size
            var winSize = win.popupSize();
            var sizerSize = sizer.popupSize(win, true);
            
            // check for the window being too large horizontally
            var resizedX = false;
            if (props.x == "auto" && winSize.outerWidth > $(window).width())
            {
              var diff = sizerSize.width - winSize.outerWidth + $(window).width()
              sizer.width(diff);
              win.width(diff + spacing);
              resizedX = true;
            }
            else if (props.x + win.outerWidth() > $(window).width())
            {
              var diff = sizerSize.width - props.x - winSize.outerWidth + $(window).width()
              sizer.width(diff);
              win.width(diff + spacing);
              resizedX = true;
            }
            
            // check for the window being too large vertically
            var resizedY = false;
            if (props.y == "auto" && winSize.outerHeight > $(window).height())
            {
              sizer.height(sizerSize.height - winSize.outerHeight + $(window).height());
              resizedY = true;
            }
            else if (props.y + winSize.outerHeight > $(window).height())
            {
              sizer.height(sizerSize.height - props.y - winSize.outerHeight + $(window).height());
              resizedY = true;
            }
            
            // position the window accordingly
            if (resizedX || resizedY) winSize = win.popupSize();
            win.css({
              left: $(window).scrollLeft() + (props.x == "auto" ? ($(window).width() - winSize.outerWidth) / 2 : props.x),
              top: $(window).scrollTop() + (props.y == "auto" ? ($(window).height() - winSize.outerHeight) / 2 : props.y)
            });
            
            // run the overridable hideLoader and showWindow functions
            globals.hideLoader(overlay, loader, win);
            globals.showWindow(overlay, loader, win);
          };
          
          // set up a reusable function to hide and remove the window elements
          var hide = function()
          {
            // run the overridable hideWindow, hideLoader and hideOverlay functions
            globals.hideWindow(overlay, loader, win);
            globals.hideLoader(overlay, loader, win);
            if (!$.popup.paging) globals.hideOverlay(overlay, loader, win);
            $(document).unbind("keyup", keyHandler);
          };
          
          // set up key handling
          var keyHandler = function(e)
          {
            e = e || window.event;
            var keyCode = e.which || e.keyCode;
            
            switch (keyCode)
            {
              case 27: // close the window
                hide();
                break;
              case 37: // move to the previous window (if applicable)
                $("td." + props.cssPrefix + "_previous a:visible").click();
                break;
              case 39: // move to the next window (if applicable)
                $("td." + props.cssPrefix + "_next a:visible").click();
            }
          };
          $(document).bind("keyup", keyHandler);
          
          // hide the optional window sub elements and get the page dimensions
          win.find("div." + props.cssPrefix + "_caption, table." + props.cssPrefix + "_navigation, td." + props.cssPrefix + "_previous a, td." + props.cssPrefix + "_next a").hide();
          var dims = $.popup.pageDimensions();
          
          // position and show the overlay (with its overridable showOverlay function)
          overlay.css({
            left: 0,
            top: 0
          }).height(dims.page.scrollHeight).width(dims.page.scrollWidth).css("opacity", props.overlayOpacity);
          globals.showOverlay(overlay, loader, win);
          
          // position and show the loader
          var loaderSize = loader.popupSize();
          loader.css({
            left: $(window).scrollLeft() + ($(window).width() - loaderSize.outerWidth) / 2,
            top: $(window).scrollTop() + ($(window).height() - loaderSize.outerHeight) / 2
          });
          globals.showLoader(overlay, loader, win);
          
          // set up the close link's functionality
          win.find("td." + props.cssPrefix + "_close a").click(function()
          {
            hide();
            return false;
          });
          
          // set up the window's title and caption
          if (props.caption) win.find("div." + props.cssPrefix + "_caption").html(title).show();
          win.find("td." + props.cssPrefix + "_title").html(props.caption ? props.title : title);
          
          // get the window's size and the window's content area size
          var winSize = win.popupSize();
          var sizerSize = sizer.popupSize(win, true);
          
          // get the spacing between the window and its content area and apply it with the initial content
          var spacing = winSize.width - sizerSize.width;
          if (props.height != "auto") sizer.height(props.height);
          sizer.width(props.width);
          win.width(props.width + spacing);
          
          // if the window is in a group, process the group elements accordingly
          if ($.popup.groups[group].length > 1)
          {
            // get the index of the given window in its group
            var index = $.inArray(el, $.popup.groups[group]);
            win.find("table." + props.cssPrefix + "_navigation").show();
            
            // if we're past the first window in the group, allow for going back one
            if (index > 0)
            {
              // add the previous link's functionality and show it (if not disabled through the properties)
              win.find("td." + props.cssPrefix + "_previous a").click(function()
              {
                $.popup.paging = true;
                hide();
                $.popup.groups[group][index - 1].triggerHandler("click");
                return false;
              }).show();
            }
            
            // create the paging containers
            var pages = win.find("td." + props.cssPrefix + "_pages").empty();
            var pager =$('<table><tr>').appendTo(pages).find("tr");
            
            // loop through and create a direct link for each window 
            $.each($.popup.groups[group], function(i, n)
            {
              // create the link's container, put the link in it and add the elments to the pager
              var page = $('<td></td>').appendTo(pager);
              $('<a href="#" title="Item ' + (i + 1) + '"><span>' + (i + 1) + '</span></a>').click(function()
              {
                $.popup.paging = true;
                hide();
                $.popup.groups[group][i].triggerHandler("click");
                return false; 
              }).appendTo(page);
            });
            
            // add the status element to the paging area
            $('<div class="' + props.cssPrefix + '_status">Item ' + (index + 1) + ' of ' + $.popup.groups[group].length + '</div>').appendTo(pages);
                        
            // if we're not to the last window in the group, allow for going ahead one
            if (index < $.popup.groups[group].length - 1)
            {
              // add the next link's functionality and show it (if not disabled through the properties
              win.find("td." + props.cssPrefix + "_next a").click(function()
              {
                $.popup.paging = true;
                hide();
                $.popup.groups[group][index + 1].triggerHandler("click");
                return false;
              }).show();
            }
          }
          
          // empty the content area of its initial content
          sizer.empty();
          if (windowType == "image")
          {
            // show an image and size the window based upon the image's size
            var cache = $("#" + props.cssPrefix + "_cache").empty();
            $('<img class="' + props.cssPrefix + '_image" src="' + href + '">').load(function()
            {
              var img = $(this).clone().appendTo(sizer);
              var imgSize = img.popupSize(win, false);
              sizer.height(imgSize.outerHeight).width(imgSize.outerWidth);
              win.width(imgSize.outerWidth + spacing);
              show();
            }).error(function()
            {
              alert("The image could not be loaded.");
              hide();
            }).appendTo(cache);
          }
          else if (windowType == "flash")
          {
            $('<object data="' + href + '" type="application/x-shockwave-flash" height="' + props.height + '" width="' + props.width + '"><param name="movie" value="' + href + '"><param name="wmode" value="transparent"><p>The object could not be loaded.</p></object>').appendTo(sizer);
            show();
          }
          else if (windowType == "ajax")
          {
            // show external content through an Ajax request
            if (hash != "") href = href.replace(/#/, " #");
            sizer.load(href, function()
            {
              show();
            });
          }
          else if (windowType == "iframe")
          {
            // show external content through an IFrame
            var iframe = $('<iframe class="' + props.cssPrefix + '_iframe" src="' + href + '" frameborder="0" border="0" height="' + props.height + '" width="' + props.width + '"></iframe>').appendTo(sizer);
            var iframeSize = iframe.popupSize(win, false);
            sizer.height(iframeSize.outerHeight).width(iframeSize.outerWidth);
            win.width(iframeSize.outerWidth + spacing);
            show();
          }
          else if (windowType == "html")
          {
            // show HTML from the current page
            sizer.html($("#" + hash).html());
            show();
          }
          return false;
        }
      });
    });
  },
  // get the size of an element without having to make it visible to the viewer
  popupSize: function(p, setWidth)
  {
    // 1. get the element and the given properties
    // 2. if we need to use the element's parent, initialize that target
    var el = $(this);
    p = p || el;
    setWidth = setWidth || false;
    
    // 1. get the target element's initial left coordinate
    // 2. move the target element off screen
    var l = p.offset().left;
    p.css("left", -10000).show();
    
    // 1. set the width of the inner element
    // 2. get the inner element's size within the outer element
    if (setWidth) el.width("100%");
    var size = {
      height: el.height(),
      width: el.width(),
      outerHeight: el.outerHeight(),
      outerWidth: el.outerWidth()
    };
    
    // 1. hide the target element and move it back into its original position
    // 2. return the element size
    p.hide().css("left", l);
    return size;
  }
});
