/*
* Custom jQuery plugin: fs.modal()
* Mod by Josh Rencher for FolioSnap (10/22/2008)
* =====================================================
* Based On:
* jQuery blockUI plugin * Version 2.09 (09/16/2008)
* @requires jQuery v1.2.3 or later
*/

(function($) {


// Global methods for opening/closing modal window
$.modalOpen = function(opts) { install(opts); };
$.modalClose = function(opts) { remove(opts); };


// Default behavior and style (to disable CSS and use external stylesheet, call "$.modalOpen.defaults.css = {};")
$.modalOpen.defaults = {

  // bordered content wrapper
  wrapper: "<table class='fsmodal-wrapper'>" +
           "<tr><td class='top-left'></td><td class='top'></td><td class='top-right'></td></tr>" +
           "<tr class='fsmodal-titlerow'><td class='left'></td><td class='fsmodal-title'></td><td class='right'></td></tr>" +
           "<tr><td class='left'></td><td class='fsmodal-inner' align='center'></td><td class='right'></td></tr>" +
           "<tr><td class='bottom-left'></td><td class='bottom'></td><td class='bottom-right'></td></tr>" +
           "</table>",

  title: null,  // default to null

  message: null,  // default to null

  padding: 0,  // inside display layer, default to 0

  css: {
    padding: 0,  // outside display layer, should always be 0
    margin: 0,  // outside display layer, should always be 0
    fontFamily: "Trebuchet MS,Arial,Helvetica,sans-serif",
    fontSize: "14px",
    color: "#000000", 
    cursor: "default"
  },

  cssOverlay: {
    backgroundColor: "#000000", 
    opacity: "0.6",
    cursor: "default"
  },
  
  // fadeOut time in milliseconds (set to 0 to disable fadeout on modalClose)
  fadeOut: 400,

  // callback method invoked when unblocking has completed; the callback is
  // passed the element that has been unblocked (which is the window object
  // for page blocks) and the options that were passed to the unblock call:
  // onModalClose(element, options)
  onModalClose: null

};


// Set defaults
var pageBlock = null;
var pageBlockEls = [];


// ########## BEG: modalOpen ###################################################
function install(opts) {

  // Merge default options with passed options
  opts = $.extend({}, $.modalOpen.defaults, opts || {});
  opts.css = $.extend({}, $.modalOpen.defaults.css, opts.css || {});
  opts.cssOverlay = $.extend({}, $.modalOpen.defaults.cssOverlay, opts.cssOverlay || {});
  var msg = opts.message[0];  // expects jQuery object (array), so grab zero-index

  // Remove any existing blocks
  if(pageBlock) remove({fadeOut:0});
    
  // If an existing element is being used as the modal content, capture
  // its current place and display style in the DOM so we can restore it.
  if(msg.parentNode || msg.jquery) {
    var node = msg;
    var data = {};
    $(window).data("modalOpen.history", data);
    data.element = node;
    data.parent = node.parentNode;
    data.display = node.style.display;
    data.position = node.style.position;
    data.parent.removeChild(node);
  }
  
  var z = 1000;  // base Z-index for modal layers

  // Three layers are used for modal (regardless of platform for simplicity's sake)
  // LAYER ONE: iframe layer used to supress bleed through of underlaying content in IE
  // LAYER TWO: overlay layer to dim page and prevent interaction outside of modal
  // LAYER THREE: display layer to show modal contents
    
  var lyr1 = ($.browser.msie) ? $('<iframe class="fsmodal" style="z-index:'+ z++ +';border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="javascript:false;"></iframe>') : $('<div class="fsmodal" style="display:none"></div>');
  var lyr2 = $('<div class="fsmodal" style="z-index:'+ z++ +';cursor:wait;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
  var lyr3 = $('<div class="fsmodal fsmodalview" style="z-index:'+z+';position:fixed"></div>');

  // Apply style to display layer
  if(msg) lyr3.css(opts.css);

  // Apply style to overlay layer (except Firefox on Linux, due to opacity issues)
  if(!($.browser.mozilla && /Linux/.test(navigator.platform))) lyr2.css(opts.cssOverlay);
  lyr2.css("position","fixed");
    
  // Apply transparency to iframe layer (IE only)
  if($.browser.msie) lyr1.css("opacity","0");

  // Add all layers to page (<body></body>)
  $([lyr1[0],lyr2[0],lyr3[0]]).appendTo("body");
    

  // ********** BEG: IE FIXES **************************************************
  // IE7 must use absolute positioning in quirks mode. Must also account for activex issues when scrolling.
  var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent);
  var expr = $.browser.msie && (!$.boxModel || $("object,embed").length > 0);
  if(ie6 || expr) {
    // 1) Give body 100% height
    if($.boxModel) $("html,body").css("height","100%");  // why only in non-quirks mode (when $.boxModel true)?
    // 2) Simulate fixed position
    $.each([lyr1,lyr2,lyr3], function(i,o) {
      var thisstyle = o[0].style;
      thisstyle.position = "absolute";
      if(i < 2) {
        thisstyle.setExpression("height","Math.max(document.body.scrollHeight, document.body.offsetHeight) - ($.boxModel ? 0 : 4) + \"px\"");
        thisstyle.setExpression("width","$.boxModel && document.documentElement.clientWidth || document.body.clientWidth + \"px\"");
      }
    });
  }
  // ********** END: IE FIXES **************************************************


  // Add wrapper to display layer, add modal contents to wrapper
  var modalwrapper = $(opts.wrapper);
  lyr3.append(modalwrapper);
  if(!opts.title) $(".fsmodal-titlerow").hide();
  else $(".fsmodal-title").append(opts.title);
  if(!opts.padding) $(".fsmodal-inner").append(msg.innerHTML);
  else $(".fsmodal-inner").css("padding",opts.padding).append(msg.innerHTML);


  // Center horizontally
  var winWidth = $(window).width();
  var mWidth = $(".fsmodalview").width() + 20;  // twice left/right padding and border (adjust as needed)
  var mLeft = Math.ceil((winWidth - mWidth) / 2);
  $(".fsmodalview").css("left",mLeft+"px");

  // Center vertically
  var winHeight = $(window).height();
  var mHeight = $(".fsmodalview").height() + 30;  // twice top/bottom padding and border (adjust as needed)
  var mTop = Math.ceil((winHeight - mHeight) / 2);
  // ADD RULE TO RE-SET mTOP BASED ON HEIGHTS!
  $(".fsmodalview").css("top",mTop+"px");

  // Show display layer
  lyr3.show();


  // Bind key and mouse events
  bind(1,opts);
  pageBlock = lyr3[0];
  pageBlockEls = $(":input:enabled:visible",pageBlock);
  setTimeout(focus, 20);


};
// ########## END: ENABLE BLOCK ################################################


// ########## BEG: REMOVE BLOCK ################################################
function remove(opts) {

  var data = $(window).data("modalOpen.history");
  opts = $.extend({}, $.modalOpen.defaults, opts || {});
  bind(0,opts); // unbind events
  var els = $("body").children().filter(".fsmodal");
  
  //pageBlock = pageBlockEls = null;
  pageBlock = null;
  pageBlockEls = null;

  if(opts.fadeOut) {
    els.fadeOut(opts.fadeOut);
    setTimeout(function() { reset(els,data,opts); }, opts.fadeOut);
  }
  else reset(els,data,opts);

};
// ########## END: REMOVE BLOCK ################################################


// RESET: Move blocking element back into the DOM where it started
function reset(els,data,opts) {
  els.each(function(i,o) {
    // remove via DOM calls so we don't lose event handlers
    if(this.parentNode) this.parentNode.removeChild(this);
  });
  if(data && data.element) {
    data.element.style.display = data.display;
    data.element.style.position = data.position;
    data.parent.appendChild(data.element);
    $(data.element).removeData("modalOpen.history");
  }
  if(typeof opts.onModalClose == "function") opts.onModalClose(opts);
};


// BIND: Bind (b=1) or unbind (b=0) the event handler
function bind(b,opts) {
  // don't bother unbinding if there is nothing to unbind
  if(!b && !pageBlock) return;
  // bind anchors and inputs for mouse and key events
  var events = "mousedown mouseup keydown keypress click";
  b ? $(document).bind(events,opts,handler) : $(document).unbind(events,handler);
};


// HANDLER: Suppress keyboard/mouse events when blocking
function handler(e) {
  // allow tab navigation (conditionally)
  if(e.keyCode && e.keyCode == 9) {
    if(pageBlock) {
      var els = pageBlockEls;
      var fwd = !e.shiftKey && e.target == els[els.length-1];
      var back = e.shiftKey && e.target == els[0];
      if(fwd || back) {
        setTimeout(function() { focus(back) }, 10);
        return false;
      }
    }
  }
  // allow events within the message content
  if($(e.target).parents("div.fsmodalview").length > 0) return true;        
  // allow events for content that is not being blocked
  return $(e.target).parents().children().filter("div.fsmodal").length == 0;
};


// FOCUS: Auto-set focus to first input element (if any)
function focus(back) {
  if(!pageBlockEls) return;
  var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
  if(e) e.focus();
};


})(jQuery);
