2  * jqModal - Minimalist Modaling with jQuery
 
   4  * Copyright (c) 2007 Brice Burgess <bhb@iceburg.net>, http://www.iceburg.net
 
   5  * Licensed under the MIT License:
 
   6  * http://www.opensource.org/licenses/mit-license.php
 
   8  * $Version: 2007.??.?? +r12 beta
 
   9  * Requires: jQuery 1.1.3+
 
  13  * Initialize a set of elements as "modals". Modals typically are popup dialogs,
 
  14  * notices, modal windows, and image containers. An expando ("_jqm") containing
 
  15  * the UUID or "serial" of the modal is added to each element. This expando helps
 
  16  * reference the modal's settings in the jqModal Hash Object (jQuery.jqm.hash)
 
  18  * Accepts a parameter object with the following modal settings;
 
  20  * (Integer) zIndex - Desired z-Index of the modal. This setting does not override (has no effect on) preexisting z-Index styling (set via CSS or inline style).  
 
  21  * (Integer) overlay - [0-100] Translucency percentage (opacity) of the body covering overlay. Set to 0 for NO overlay, and up to 100 for a 100% opaque overlay.  
 
  22  * (String) overlayClass - This class is applied to the body covering overlay. Allows CSS control of the overlay look (tint, background image, etc.).
 
  23  * (String) closeClass - A close trigger is added to all elements matching this class within the modal.
 
  24  * (Mixed) trigger - An open trigger is added to all matching elements within the DOM. Trigger can be a selector String, a jQuery collection of elements, a DOM element, or a False boolean.
 
  25  * (Mixed) ajax - If not false; The URL (string) to load content from via an AJAX request.
 
  26  *                If ajax begins with a "@", the URL is extracted from the requested attribute of the triggering element.
 
  27  * (Mixed) target - If not false; The element within the modal to load the ajax response (content) into. Allows retention of modal design (e.g. framing and close elements are not overwritten by the AJAX response).
 
  28  *                  Target may be a selector string, jQuery collection of elements, or a DOM element -- but MUST exist within (as a child of) the modal.
 
  29  * (Boolean) modal - If true, user interactivity will be locked to the modal window until closed.
 
  30  * (Boolean) toTop - If true, modal will be posistioned as a first child of the BODY element when opened, and its DOM posistion restored when closed. This aids in overcoming z-Index stacking order/containment issues where overlay covers whole page *including* modal.
 
  31  * (Mixed) onShow - User defined callback function fired when modal opened.
 
  32  * (Mixed) onHide - User defined callback function fired when modal closed.
 
  33  * (Mixed) onLoad - User defined callback function fired when ajax content loads.
 
  36  * @param Map options User defined settings for the modal(s).
 
  38  * @cat Plugins/jqModal
 
  44 overlayClass: 'jqmOverlay',
 
  45 closeClass: 'jqmClose',
 
  56 // For each element (aka "modal") $.jqm() has been called on;
 
  57 //  IF the _jqm expando exists, return (do nothing)
 
  58 //  ELSE increment serials and add _jqm expando to element ("serialization")
 
  60 return this.each(function(){if(this._jqm)return;s++;this._jqm=s;
 
  62 // ... Add this element's serial to the jqModal Hash Object 
 
  63 //  Hash is globally accessible via jQuery.jqm.hash. It consists of;
 
  64 //   c: {obj} config/options
 
  65 //   a: {bool} active state (true: active/visible, false: inactive/hidden)
 
  66 //   w: {JQ DOM Element} The modal element (window/dialog/notice/etc. container)
 
  67 //   s: {int} The serial number of this modal (same as "H[s].w[0]._jqm")
 
  68 //   t: {DOM Element} The triggering element
 
  70 H[s]={c:$.extend(o,p),a:false,w:$(this).addClass('jqmID'+s),s:s};
 
  72 // ... Attach events to trigger showing of this modal
 
  73 o.trigger&&$(this).jqmAddTrigger(o.trigger);
 
  76 // Adds behavior to triggering elements via the hide-show (HS) function.
 
  78 $.fn.jqmAddClose=function(e){return HS(this,e,'jqmHide');};
 
  79 $.fn.jqmAddTrigger=function(e){return HS(this,e,'jqmShow');};
 
  81 // Hide/Show a modal -- first check if it is already shown or hidden via the toggle state (H[{modal serial}].a)
 
  82 $.fn.jqmShow=function(t){return this.each(function(){!H[this._jqm].a&&$.jqm.open(this._jqm,t)});};
 
  83 $.fn.jqmHide=function(t){return this.each(function(){H[this._jqm].a&&$.jqm.close(this._jqm,t)});};
 
  88 // Function is executed by $.jqmShow to show a modal
 
  89 // s: {INT} serial of modal
 
  90 // t: {DOM Element} the triggering element
 
  92 // set local shortcuts
 
  93 //  h: {obj} this Modal's "hash"
 
  94 //  c: {obj} (h.c) config/options
 
  95 //  cc: {STR} closing class ('.'+h.c.closeClass)
 
  96 //  z: {INT} z-Index of Modal. If the Modal (h.w) has the z-index style set it will use this value before defaulting to the one passed in the config (h.c.zIndex)
 
  97 //  o: The overlay object
 
  98 // mark this modal as active (h.a === true)
 
  99 // set the triggering object (h.t) and the modal's z-Index.
 
 100 open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=/^\d+$/.test(h.w.css('z-index'))&&h.w.css('z-index')||c.zIndex,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});h.t=t;h.a=true;h.w.css('z-index',z);
 
 102  // IF the modal argument was passed as true;
 
 103  //    Bind the Keep Focus Function if no other Modals are open (!A[0]),
 
 104  //    Add this modal to the opened modals stack (A) for nested modal support,
 
 105  //    and Mark overlay to show wait cursor when mouse hovers over it.
 
 106  if(c.modal) {!A[0]&&F('bind');A.push(s);o.css('cursor','wait');}
 
 108  // ELSE IF an overlay was requested (translucency set greater than 0);
 
 109  //    Attach a Close event to overlay to hide modal when overlay is clicked.
 
 110  else if(c.overlay > 0)h.w.jqmAddClose(o);
 
 112  // ELSE disable the overlay
 
 115  // Add the Overlay to BODY if not disabled.
 
 116  h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):false;
 
 119  //  Set the Overlay to 100% height/width, and fix-position it via JS workaround
 
 120  if(ie6&&$('html,body').css({height:'100%',width:'100%'})&&o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}
 
 122  // IF the modal's content is to be loaded via ajax;
 
 123  //  determine the target element {JQ} to recieve content (r),
 
 124  //  determine the URL {STR} to load content from (u)
 
 125  if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
 
 127   // Load the Content (and once loaded);
 
 128    // Fire the onLoad callback (if exists),
 
 129    // Attach closing events to elements inside the modal that match the closingClass,
 
 130    // and Execute the jqModal default Open Callback
 
 131   r.load(u,function(){c.onLoad&&c.onLoad.call(this,h);cc&&h.w.jqmAddClose($(cc,h.w));O(h);});}
 
 133  // ELSE the modal content is NOT to be loaded via ajax;
 
 134  //  Attach closing events to elements inside the modal that match the closingClass
 
 135  else cc&&h.w.jqmAddClose($(cc,h.w));
 
 137  // IF toTop was passed and an overlay exists;
 
 138  //  Remember the DOM posistion of the modal by inserting a tagged (matching serial) <SPAN> before the modal
 
 139  //  Move the Modal from its current position to a first child of the body tag (after the overlay)
 
 140  c.toTop&&h.o&&h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);   
 
 142  // Execute user defined onShow callback, or else show (make visible) the modal.
 
 143  // Execute the jqModal default Open Callback.
 
 144  // Return false to prevent trigger click from being followed.
 
 145  (c.onShow)?c.onShow(h):h.w.show();O(h);return false;
 
 149 // Function is executed by $.jqmHide to hide a modal
 
 150   // mark this modal as inactive (h.a === false)
 
 151 close:function(s){var h=H[s];h.a=false;
 
 152  // If modal, remove from modal stack.
 
 153    // If no modals in modal stack, unbind the Keep Focus Function
 
 154  if(h.c.modal){A.pop();!A[0]&&F('unbind');}
 
 156  // IF toTop was passed and an overlay exists;
 
 157  //  Move modal back to its previous ("remembered") position.
 
 158  h.c.toTop&&h.o&&$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
 
 160  // Execute user defined onHide callback, or else hide (make invisible) the modal and remove the overlay.
 
 161  if(h.c.onHide)h.c.onHide(h);else{h.w.hide()&&h.o&&h.o.remove()}return false;
 
 164 // set jqModal scope shortcuts;
 
 165 //  s: {INT} serials placeholder
 
 166 //  H: {HASH} shortcut to jqModal Hash Object
 
 167 //  A: {ARRAY} Array of active/visible modals
 
 168 //  ie6: {bool} True if client browser is Internet Explorer 6
 
 169 //  i: {JQ, DOM Element} iframe placeholder used to prevent active-x bleedthrough in IE6
 
 170 //    NOTE: It is important to include the iframe styling (iframe.jqm) in your CSS!
 
 172 var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
 
 174 //  O: The jqModal default Open Callback;
 
 175 //    IF ie6; Add the iframe to the overlay (if overlay exists) OR to the modal (if an iframe doesn't already exist from a previous opening)
 
 176 //    Execute the Modal Focus Function
 
 177 O=function(h){if(ie6)h.o&&h.o.html('<p style="width:100%;height:100%"/>').prepend(i)||(!$('iframe.jqm',h.w)[0]&&h.w.prepend(i)); f(h);},
 
 179 //  f: The Modal Focus Function;
 
 180 //    Attempt to focus the first visible input within the modal
 
 181 f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(e){}},
 
 183 //  F: The Keep Focus Function;
 
 184 //    Binds or Unbinds (t) the Focus Examination Function to keypresses and clicks
 
 185 F=function(t){$()[t]("keypress",x)[t]("keydown",x)[t]("mousedown",x);},
 
 187 //  x: The Focus Examination Function;
 
 188 //    Fetch the current modal's Hash as h (supports nested modals)
 
 189 //    Determine if the click/press falls within the modal. If not (r===true);
 
 190 //      call the Modal Focus Function and prevent click/press follow-through (return false [!true])
 
 191 //      ELSE if so (r===false); follow event (return true [!false])
 
 192 x=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);r&&f(h);return !r;},
 
 194 // hide-show function; assigns click events to trigger elements that 
 
 195 //   hide, show, or hide AND show modals.
 
 197 // Expandos (jqmShow and/or jqmHide) are added to all trigger elements. 
 
 198 // These Expandos hold an array of modal serials {INT} to show or hide.
 
 200 //  w: {DOM Element} The modal element (window/dialog/notice/etc. container)
 
 201 //  e: {DOM Elemet||jQ Selector String} The triggering element
 
 202 //  y: {String} Type (jqmHide||jqmShow)
 
 204 //  s: {array} the serial number of passed modals, calculated below;
 
 205 HS=function(w,e,y){var s=[];w.each(function(){s.push(this._jqm)});
 
 207 // for each triggering element attach the jqmHide or jqmShow expando (y)
 
 208 //  or else expand the expando with the current serial array
 
 209  $(e).each(function(){if(this[y])$.extend(this[y],s);
 
 211  // Assign a click event on the trigger element which examines the element's
 
 212  //  jqmHide/Show expandos and attempts to execute $.jqmHide/Show on matching modals
 
 213  else{this[y]=s;$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return false;});}});return w;};