var browserName=navigator.appName;var browserVer=parseInt(navigator.appVersion);var version="";var msie4=(browserName=="Microsoft Internet Explorer"&&browserVer>=4);if((browserName=="Netscape"&&browserVer>=3)||msie4||browserName=="Konqueror"||browserName=="Opera"){version="n3";}else{version="n2";}
function blurLink(theObject){theObject.blur(); return false;}
function decryptCharcode(n,start,end,offset){n=n+offset;if(offset>0&&n>end){n=start+(n-end-1);}else if(offset<0&&n<start){n=end-(start-n-1);}
return String.fromCharCode(n);}
function decryptString(enc,offset){var dec="";var len=enc.length;for(var i=0;i<len;i++){var n=enc.charCodeAt(i);if(n>=0x2B&&n<=0x3A){dec+=decryptCharcode(n,0x2B,0x3A,offset);}else if(n>=0x40&&n<=0x5A){dec+=decryptCharcode(n,0x40,0x5A,offset);}else if(n>=0x61&&n<=0x7A){dec+=decryptCharcode(n,0x61,0x7A,offset);}else{dec+=enc.charAt(i);}}
return dec;}
function linkTo_UnCryptMailto(s){location.href=decryptString(s,-2);}
/**
 * A base library for Shadowbox used as a standalone (without another base
 * library/adapter combination).
 *
 * This file is part of Shadowbox.
 *
 * Shadowbox is an online media viewer application that supports all of the
 * web's most popular media publishing formats. Shadowbox is written entirely
 * in JavaScript and CSS and is highly customizable. Using Shadowbox, website
 * authors can showcase a wide assortment of media in all major browsers without
 * navigating users away from the linking page.
 *
 * Shadowbox is released under version 3.0 of the Creative Commons Attribution-
 * Noncommercial-Share Alike license. This means that it is absolutely free
 * for personal, noncommercial use provided that you 1) make attribution to the
 * author and 2) release any derivative work under the same or a similar
 * license.
 *
 * If you wish to use Shadowbox for commercial purposes, licensing information
 * can be found at http://mjijackson.com/shadowbox/.
 *
 * @authorMichael J. I. Jackson <mjijackson@gmail.com>
 * @copyright 2007-2008 Michael J. I. Jackson
 * @license http://creativecommons.org/licenses/by-nc-sa/3.0/
 * @version SVN: $Id: shadowbox-base.js 103 2008-06-27 06:19:21Z mjijackson $
 */

// create the Shadowbox object first
var Shadowbox = {};

Shadowbox.lib = function(){

// local style camelizing for speed
var styleCache = {};
var camelRe = /(-[a-z])/gi;
var camelFn = function(m, a){
return a.charAt(1).toUpperCase();
};
var toCamel = function(style){
var camel;
if(!(camel = styleCache[style])){
camel = styleCache[style] = style.replace(camelRe, camelFn);
}
return camel;
};

var view = document.defaultView;
var alphaRe = /alpha\([^\)]*\)/gi;

/**
 * Sets the opacity of the given element to the specified level.
 *
 * @param {HTMLElement} elThe element
 * @param {Number}opacity The opacity to use
 * @returnvoid
 * @private
 * @static
 */
var setOpacity = function(el, opacity){
var s = el.style;
if(window.ActiveXObject){ // IE
s.zoom = 1; // give "layout"
s.filter = (s.filter || '').replace(alphaRe, '') +
(opacity == 1 ? '' : ' alpha(opacity=' + (opacity * 100) + ')');
}else{
s.opacity = opacity;
}
};

return {

adapter: 'standalone',

/**
 * Gets the value of the style on the given element. This function
 * adapted from Ext.Element.getStyle().
 *
 * @param {HTMLElement} elThe DOM element
 * @param {String}style The name of the style (e.g. margin-top)
 * @return{mixed} The value of the given style
 * @public
 * @static
 */
getStyle: function(){
return view && view.getComputedStyle
? function(el, style){
var v, cs, camel;
if(style == 'float') style = 'cssFloat';
if(v = el.style[style]) return v;
if(cs = view.getComputedStyle(el, '')){
return cs[toCamel(style)];
}
return null;
}
: function(el, style){
var v, cs, camel;
if(style == 'opacity'){
if(typeof el.style.filter == 'string'){
var m = el.style.filter.match(/alpha\(opacity=(.+)\)/i);
if(m){
var fv = parseFloat(m[1]);
if(!isNaN(fv)) return (fv ? fv / 100 : 0);
}
}
return 1;
}else if(style == 'float'){
style = 'styleFloat';
}
var camel = toCamel(style);
if(v = el.style[camel]) return v;
if(cs = el.currentStyle) return cs[camel];
return null;
};
}(),

/**
 * Sets the style on the given element to the given value. May be an
 * object to specify multiple values. This function adapted from
 * Ext.Element.setStyle().
 *
 * @param {HTMLElement} elThe DOM element
 * @param {String/Object} style The name of the style to set if a
 *string, or an object of name =>
 *value pairs
 * @param {String}value The value to set the given style to
 * @returnvoid
 * @public
 * @static
 */
setStyle: function(el, style, value){
if(typeof style == 'string'){
var camel = toCamel(style);
if(camel == 'opacity'){
setOpacity(el, value);
}else{
el.style[camel] = value;
}
}else{
for(var s in style){
this.setStyle(el, s, style[s]);
}
}
},

/**
 * Gets a reference to the given element.
 *
 * @param {String/HTMLElement}elThe element to fetch
 * @return{HTMLElement} A reference to the element
 * @public
 * @static
 */
get: function(el){
return typeof el == 'string' ? document.getElementById(el) : el;
},

/**
 * Removes an element from the DOM.
 *
 * @param {HTMLElement} elThe element to remove
 * @returnvoid
 * @public
 * @static
 */
remove: function(el){
el.parentNode.removeChild(el);
},

/**
 * Gets the target of the given event. The event object passed will be
 * the same object that is passed to listeners registered with
 * addEvent().
 *
 * @param {mixed} e The event object
 * @return{HTMLElement} The event's target element
 * @public
 * @static
 */
getTarget: function(e){
var t = e.target ? e.target : e.srcElement;
return t.nodeType == 3 ? t.parentNode : t;
},

/**
 * Gets the page X/Y coordinates of the mouse event in an [x, y] array.
 * The page coordinates should be relative to the document, and not the
 * viewport. The event object provided here will be the same object that
 * is passed to listeners registered with addEvent().
 *
 * @param {mixed} e The event object
 * @return{Array} The page X/Y coordinates
 * @public
 * @static
 */
getPageXY: function(e){
var x = e.pageX || (e.clientX +
(document.documentElement.scrollLeft || document.body.scrollLeft));
var y = e.pageY || (e.clientY +
(document.documentElement.scrollTop || document.body.scrollTop));
return [x, y];
},

/**
 * Prevents the event's default behavior. The event object here will
 * be the same object that is passed to listeners registered with
 * addEvent().
 *
 * @param {mixed} e The event object
 * @returnvoid
 * @public
 * @static
 */
preventDefault: function(e){
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
},

/**
 * Gets the key code of the given event object (keydown). The event
 * object here will be the same object that is passed to listeners
 * registered with addEvent().
 *
 * @param {mixed} e The event object
 * @return{Number}The key code of the event
 * @public
 * @static
 */
keyCode: function(e){
return e.which ? e.which : e.keyCode;
},

/**
 * Adds an event listener to the given element. It is expected that this
 * function will be passed the event as its first argument.
 *
 * @param {HTMLElement} elThe DOM element to listen to
 * @param {String}nameThe name of the event to register
 *(i.e. 'click', 'scroll', etc.)
 * @param {Function}handler The event handler function
 * @returnvoid
 * @public
 * @static
 */
addEvent: function(el, name, handler){
if(el.addEventListener){
el.addEventListener(name, handler, false);
}else if(el.attachEvent){
el.attachEvent('on' + name, handler);
}
},

/**
 * Removes an event listener from the given element.
 *
 * @param {HTMLElement} elThe DOM element to stop listening to
 * @param {String}nameThe name of the event to stop
 *listening for (i.e. 'click')
 * @param {Function}handler The event handler function
 * @returnvoid
 * @public
 * @static
 */
removeEvent: function(el, name, handler){
if(el.removeEventListener){
el.removeEventListener(name, handler, false);
}else if(el.detachEvent){
el.detachEvent('on' + name, handler);
}
},

/**
 * Appends an HTML fragment to the given element.
 *
 * @param {HTMLElement} elThe element to append to
 * @param {String}htmlThe HTML fragment to use
 * @returnvoid
 * @public
 * @static
 */
append: function(el, html){
if(el.insertAdjacentHTML){
el.insertAdjacentHTML('BeforeEnd', html);
}else if(el.lastChild){
var range = el.ownerDocument.createRange();
range.setStartAfter(el.lastChild);
var frag = range.createContextualFragment(html);
el.appendChild(frag);
}else{
el.innerHTML = html;
}
}

};

}();
/**
 * The Shadowbox class.
 *
 * This file is part of Shadowbox.
 *
 * Shadowbox is an online media viewer application that supports all of the
 * web's most popular media publishing formats. Shadowbox is written entirely
 * in JavaScript and CSS and is highly customizable. Using Shadowbox, website
 * authors can showcase a wide assortment of media in all major browsers without
 * navigating users away from the linking page.
 *
 * Shadowbox is released under version 3.0 of the Creative Commons Attribution-
 * Noncommercial-Share Alike license. This means that it is absolutely free
 * for personal, noncommercial use provided that you 1) make attribution to the
 * author and 2) release any derivative work under the same or a similar
 * license.
 *
 * If you wish to use Shadowbox for commercial purposes, licensing information
 * can be found at http://mjijackson.com/shadowbox/.
 *
 * @authorMichael J. I. Jackson <mjijackson@gmail.com>
 * @copyright 2007-2008 Michael J. I. Jackson
 * @license http://creativecommons.org/licenses/by-nc-sa/3.0/
 * @version SVN: $Id: shadowbox.js 108 2008-07-11 04:19:01Z mjijackson $
 */

if(typeof Shadowbox == 'undefined'){
throw 'Unable to load Shadowbox, no base library adapter found';
}

/**
 * The Shadowbox class. Used to display different media on a web page using a
 * Lightbox-like effect.
 *
 * Useful resources:
 *
 * - http://www.alistapart.com/articles/byebyeembed
 * - http://www.w3.org/TR/html401/struct/objects.html
 * - http://www.dyn-web.com/dhtml/iframes/
 * - http://www.apple.com/quicktime/player/specs.html
 * - http://www.apple.com/quicktime/tutorials/embed2.html
 * - http://www.howtocreate.co.uk/wrongWithIE/?chapter=navigator.plugins
 * - http://msdn.microsoft.com/en-us/library/ms532969.aspx
 * - http://support.microsoft.com/kb/316992
 *
 * @class Shadowbox
 * @authorMichael J. I. Jackson <mjijackson@gmail.com>
 * @singleton
 */
(function(){

/**
 * The current version of Shadowbox.
 *
 * @var String
 * @private
 */
var version = '2.0';

/**
 * Contains the default options for Shadowbox.
 *
 * @var Object
 * @private
 */
var options = {

/**
 * Enable all animations besides fades.
 *
 * @var Boolean
 */
animate:true,

/**
 * Enable fade animations.
 *
 * @var Boolean
 */
animateFade:true,

/**
 * Specifies the sequence of the height and width animations. May be
 * 'wh' (width then height), 'hw' (height then width), or 'sync' (both
 * at the same time). Of course this will only work if animate is true.
 *
 * @var String
 */
animSequence: 'wh',

/**
 * The path to flvplayer.swf.
 *
 * @var String
 */
flvPlayer:'flvplayer.swf',

/**
 * Listen to the overlay for clicks. If the user clicks the overlay,
 * it will trigger Shadowbox.close().
 *
 * @var Boolean
 */
modal:false,

/**
 * The color to use for the modal overlay (in hex).
 *
 * @var String
 */
overlayColor: '#000',

/**
 * The opacity to use for the modal overlay.
 *
 * @var Number
 */
overlayOpacity: 0.8,

/**
 * The default background color to use for Flash movies (in hex).
 *
 * @var String
 */
flashBgColor: '#000000',

/**
 * Automatically play movies.
 *
 * @var Boolean
 */
autoplayMovies: true,

/**
 * Enable movie controllers on movie players.
 *
 * @var Boolean
 */
showMovieControls:true,

/**
 * A delay (in seconds) to use for slideshows. If set to anything other
 * than 0, this value determines an interval at which Shadowbox will
 * automatically proceed to the next piece in the gallery.
 *
 * @var Number
 */
slideshowDelay: 0,

/**
 * The duration of the resizing animations (in seconds).
 *
 * @var Number
 */
resizeDuration: 0.55,

/**
 * The duration of the fading animations (in seconds).
 *
 * @var Number
 */
fadeDuration: 0.35,

/**
 * Show the navigation controls.
 *
 * @var Boolean
 */
displayNav: true,

/**
 * Enable continuous galleries. When this is true, users will be able
 * to skip to the first gallery image from the last using next and vice
 * versa.
 *
 * @var Boolean
 */
continuous: false,

/**
 * Display the gallery counter.
 *
 * @var Boolean
 */
displayCounter: true,

/**
 * This option may be either 'default' or 'skip'. The default counter is
 * a simple '1 of 5' message. The skip counter displays a link for each
 * piece in the gallery that enables a user to skip directly to any
 * piece.
 *
 * @var String
 */
counterType:'default',

/**
 * Limits the number of counter links that will be displayed in a "skip"
 * style counter. If the actual number of gallery elements is greater
 * than this value, the counter will be restrained to the elements
 * immediately preceeding and following the current element.
 *
 * @var Number
 */
counterLimit: 10,

/**
 * The amount of padding to maintain around the viewport edge (in
 * pixels). This only applies when the image is very large and takes up
 * the entire viewport.
 *
 * @var Number
 */
viewportPadding:20,

/**
 * How to handle content that is too large to display in its entirety
 * (and is resizable). A value of 'resize' will resize the content while
 * preserving aspect ratio and display it at the smaller resolution. If
 * the content is an image, a value of 'drag' will display the image at
 * its original resolution but it will be draggable within Shadowbox. A
 * value of 'none' will display the content at its original resolution
 * but it may be cropped.
 *
 * @var String
 */
handleOversize: 'resize',

/**
 * An exception handling function that will be called whenever
 * Shadowbox should throw an exception. Will be passed the error
 * message as its first argument.
 *
 * @var Function
 */
handleException:null,

/**
 * The mode to use when handling unsupported media. May be either
 * 'remove' or 'link'. If it is 'remove', the unsupported gallery item
 * will merely be removed from the gallery. If it is the only item in
 * the gallery, the link will simply be followed. If it is 'link', a
 * link will be provided to the appropriate plugin page in place of the
 * gallery element.
 *
 * @var String
 */
handleUnsupported:'link',

/**
 * The initial height of Shadowbox (in pixels).
 *
 * @var Number
 */
initialHeight:160,

/**
 * The initial width of Shadowbox (in pixels).
 *
 * @var Number
 */
initialWidth: 320,

/**
 * Enable keyboard control.
 *
 * @var Boolean
 */
enableKeys: true,

/**
 * A hook function to be fired when Shadowbox opens. The single argument
 * will be the current gallery element.
 *
 * @var Function
 */
onOpen: null,

/**
 * A hook function to be fired when Shadowbox finishes loading its
 * content. The single argument will be the current gallery element on
 * display.
 *
 * @var Function
 */
onFinish: null,

/**
 * A hook function to be fired when Shadowbox changes from one gallery
 * element to the next. The single argument will be the current gallery
 * element that is about to be displayed.
 *
 * @var Function
 */
onChange: null,

/**
 * A hook function that will be fired when Shadowbox closes. The single
 * argument will be the gallery element most recently displayed.
 *
 * @var Function
 */
onClose:null,

/**
 * Skips calling Shadowbox.setup() in init(). This means that it must
 * be called later manually.
 *
 * @var Boolean
 */
skipSetup:false,

/**
 * An object containing names of plugins and links to their respective
 * download pages.
 *
 * @var Object
 */
errors: {

fla:{
name: 'Flash',
url:'http://www.adobe.com/products/flashplayer/'
},

qt: {
name: 'QuickTime',
url:'http://www.apple.com/quicktime/download/'
},

wmp:{
name: 'Windows Media Player',
url:'http://www.microsoft.com/windows/windowsmedia/'
},

f4m:{
name: 'Flip4Mac',
url:'http://www.flip4mac.com/wmv_download.htm'
}

},

/**
 * A map of players to the file extensions they support. Each member of
 * this object is the name of a player (with one exception), whose value
 * is an array of file extensions that player will "play". The one
 * exception to this rule is the "qtwmp" member, which contains extensions
 * that may be played using either QuickTime or Windows Media Player.
 *
 * - img: Image file extensions
 * - swf: Flash SWF file extensions
 * - flv: Flash video file extensions (will be played by JW FLV player)
 * - qt: Movie file extensions supported by QuickTime
 * - wmp: Movie file extensions supported by Windows Media Player
 * - qtwmp: Movie file extensions supported by both QuickTime and Windows Media Player
 * - iframe: File extensions that will be display in an iframe
 *
 * IMPORTANT: If this object is to be modified, it must be copied in its
 * entirety and tweaked because it is not merged recursively with the
 * default. Also, any modifications must be passed into Shadowbox.init
 * for speed reasons.
 *
 * @var Objectext
 */
ext: {
img:['png', 'jpg', 'jpeg', 'gif', 'bmp'],
swf:['swf'],
flv:['flv'],
qt: ['dv', 'mov', 'moov', 'movie', 'mp4'],
wmp:['asf', 'wm', 'wmv'],
qtwmp:['avi', 'mpg', 'mpeg'],
iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'pl', 'php',
'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml',
'txt', 'vbs']
}

};

// shorthand
var SB = Shadowbox;
var SL = SB.lib;

/**
 * Stores the default set of options in case a custom set of options is used
 * on a link-by-link basis so we can restore them later.
 *
 * @var Object
 * @private
 */
var default_options;

/**
 * An object containing some regular expressions we'll need later. Compiled
 * up front for speed.
 *
 * @var Object
 * @private
 */
var RE = {
domain: /:\/\/(.*?)[:\/]/, // domain prefix
inline: /#(.+)$/, // inline element id
rel:/^(light|shadow)box/i, // rel attribute format
gallery:/^(light|shadow)box\[(.*?)\]/i, // rel attribute format for gallery link
unsupported:/^unsupported-(\w+)/, // unsupported media type
param:/\s*([a-z_]*?)\s*=\s*(.+)\s*/, // rel string parameter
empty:/^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i // elements that don't have children
};

/**
 * A cache of options for links that have been set up for use with
 * Shadowbox.
 *
 * @var Array
 * @private
 */
var cache = [];

/**
 * An array containing the gallery objects currently being viewed. In the
 * case of non-gallery items, this will only hold one object.
 *
 * @var Array
 * @private
 */
var gallery;

/**
 * The array index of the current gallery that is currently being viewed.
 *
 * @var Number
 * @private
 */
var current;

/**
 * The current content object.
 *
 * @var Object
 * @private
 */
var content;

/**
 * The id to use for content objects.
 *
 * @var String
 * @private
 */
var content_id = 'shadowbox_content';

/**
 * Holds the current dimensions of Shadowbox as calculated by
 * setDimensions(). Contains the following properties:
 *
 * - height: The total height of #shadowbox
 * - width: The total width of #shadowbox
 * - inner_h: The height of #shadowbox_body
 * - inner_w: The width of #shadowbox_body
 * - top: The top to use for #shadowbox
 * - resize_h: The height to use for resizable content
 * - resize_w: The width to use for resizable content
 * - drag: True if dragging should be enabled (oversized image)
 *
 * @var Object
 * @private
 */
var dims;

/**
 * Keeps track of whether or not Shadowbox has been initialized. We never
 * want to initialize twice.
 *
 * @var Boolean
 * @private
 */
var initialized = false;

/**
 * Keeps track of whether or not Shadowbox is activated.
 *
 * @var Boolean
 * @private
 */
var activated = false;

/**
 * The timeout id for the slideshow transition function.
 *
 * @var Number
 * @private
 */
var slide_timer;

/**
 * Keeps track of the time at which the current slideshow frame was
 * displayed.
 *
 * @var Number
 * @private
 */
var slide_start;

/**
 * The delay on which the next slide will display.
 *
 * @var Number
 * @private
 */
var slide_delay = 0;

/**
 * These parameters for simple browser detection. Adapted from Ext.js.
 *
 * @var Object
 * @private
 */
var ua = navigator.userAgent.toLowerCase();
var client = {
isStrict: document.compatMode == 'CSS1Compat',
isOpera:ua.indexOf('opera') > -1,
isIE: ua.indexOf('msie') > -1,
isIE7:ua.indexOf('msie 7') > -1,
isSafari: /webkit|khtml/.test(ua),
isWindows:ua.indexOf('windows') != -1 || ua.indexOf('win32') != -1,
isMac:ua.indexOf('macintosh') != -1 || ua.indexOf('mac os x') != -1,
isLinux:ua.indexOf('linux') != -1
};
client.isBorderBox = client.isIE && !client.isStrict;
client.isSafari3 = client.isSafari && !!(document.evaluate);
client.isGecko = ua.indexOf('gecko') != -1 && !client.isSafari;

/**
 * You're not sill using IE6 are you?
 *
 * @var Boolean
 * @private
 */
var ltIE7 = client.isIE && !client.isIE7;

/**
 * Contains plugin support information. Each property of this object is a
 * boolean indicating whether that plugin is supported.
 *
 * - fla: Flash player
 * - qt: QuickTime player
 * - wmp: Windows Media player
 * - f4m: Flip4Mac plugin
 *
 * @var Object
 * @private
 */
var plugins;

// detect plugin support
if(navigator.plugins && navigator.plugins.length){
var detectPlugin = function(plugin_name){
var detected = false;
for (var i = 0, len = navigator.plugins.length; i < len; ++i){
if(navigator.plugins[i].name.indexOf(plugin_name) > -1){
detected = true;
break;
}
}
return detected;
};
var f4m = detectPlugin('Flip4Mac');
plugins = {
fla:detectPlugin('Shockwave Flash'),
qt: detectPlugin('QuickTime'),
wmp:!f4m && detectPlugin('Windows Media'), // if it's Flip4Mac, it's not really WMP
f4m:f4m
};
}else{
var detectPlugin = function(plugin_name){
var detected = false;
try{
var axo = new ActiveXObject(plugin_name);
if(axo) detected = true;
}catch(e){}
return detected;
};
plugins = {
fla:detectPlugin('ShockwaveFlash.ShockwaveFlash'),
qt: detectPlugin('QuickTime.QuickTime'),
wmp:detectPlugin('wmplayer.ocx'),
f4m:false
};
}

/**
 * Applies all properties of e to o.
 *
 * @param Objecto The original object
 * @param Objecte The extension object
 * @returnObjectThe original object with all properties
 *of the extension object applied
 * @private
 */
var apply = function(o, e){
for(var p in e) o[p] = e[p];
return o;
};

/**
 * Determines if the given object is an anchor/area element.
 *
 * @param mixed elThe object to check
 * @returnBoolean True if the object is a link element
 * @private
 */
var isLink = function(el){
return el && typeof el.tagName == 'string' && (el.tagName.toUpperCase() == 'A' || el.tagName.toUpperCase() == 'AREA');
};

/**
 * Gets the height of the viewport in pixels. Note: This function includes
 * scrollbars in Safari 3.
 *
 * @returnNumberThe height of the viewport
 * @public
 * @static
 */
SL.getViewportHeight = function(){
var h = window.innerHeight; // Safari
var mode = document.compatMode;
if((mode || client.isIE) && !client.isOpera){
h = client.isStrict ? document.documentElement.clientHeight : document.body.clientHeight;
}
return h;
};

/**
 * Gets the width of the viewport in pixels. Note: This function includes
 * scrollbars in Safari 3.
 *
 * @returnNumberThe width of the viewport
 * @public
 * @static
 */
SL.getViewportWidth = function(){
var w = window.innerWidth; // Safari
var mode = document.compatMode;
if(mode || client.isIE){
w = client.isStrict ? document.documentElement.clientWidth : document.body.clientWidth;
}
return w;
};

/**
 * Creates an HTML string from an object representing HTML elements. Based
 * on Ext.DomHelper's createHtml.
 *
 * @param Objectobj The HTML definition object
 * @returnStringAn HTML string
 * @public
 * @static
 */
SL.createHTML = function(obj){
var html = '<' + obj.tag;
for(var attr in obj){
if(attr == 'tag' || attr == 'html' || attr == 'children') continue;
if(attr == 'cls'){
html += ' class="' + obj['cls'] + '"';
}else{
html += ' ' + attr + '="' + obj[attr] + '"';
}
}
if(RE.empty.test(obj.tag)){
html += '/>';
}else{
html += '>';
var cn = obj.children;
if(cn){
for(var i = 0, len = cn.length; i < len; ++i){
html += this.createHTML(cn[i]);
}
}
if(obj.html) html += obj.html;
html += '</' + obj.tag + '>';
}
return html;
};

/**
 * Easing function used for animations. Based on a cubic polynomial.
 *
 * @param Numberx The state of the animation (% complete)
 * @returnNumberThe adjusted easing value
 * @private
 * @static
 */
var ease = function(x){
return 1 + Math.pow(x - 1, 3);
};

/**
 * Animates any numeric (not color) style of the given element from its
 * current state to the given value. Defaults to using pixel-based
 * measurements.
 *
 * @param HTMLElement elThe DOM element to animate
 * @param Stringp The property to animate (in camelCase)
 * @param mixed toThe value to animate to
 * @param Numberd The duration of the animation (in
 *seconds)
 * @param FunctioncbA callback function to call when the
 *animation completes
 * @returnvoid
 * @private
 * @static
 */
var animate = function(el, p, to, d, cb){
var from = parseFloat(SL.getStyle(el, p));
if(isNaN(from)) from = 0;

if(from == to){
if(typeof cb == 'function') cb();
return; // nothing to animate
}

var delta = to - from;
var op = p == 'opacity';
var unit = op ? '' : 'px'; // default unit is px
var fn = function(ease){
SL.setStyle(el, p, from + ease * delta + unit);
};

// cancel the animation here if set in the options
if(!options.animate && !op || op && !options.animateFade){
fn(1);
if(typeof cb == 'function') cb();
return;
}

d *= 1000; // convert to milliseconds
var begin = new Date().getTime();
var end = begin + d;

var timer = setInterval(function(){
var time = new Date().getTime();
if(time >= end){ // end of animation
clearInterval(timer);
fn(1);
if(typeof cb == 'function') cb();
}else{
fn(ease((time - begin) / d));
}
}, 10); // 10 ms interval is minimum on WebKit
};

/**
 * A utility function used by the fade functions to clear the opacity
 * style setting of the given element. Required in some cases for IE.
 *
 * @param HTMLElement elThe DOM element
 * @returnvoid
 * @private
 */
var clearOpacity = function(el){
var s = el.style;
if(client.isIE){
if(typeof s.filter == 'string' && (/alpha/i).test(s.filter)){
// careful not to overwrite other filters!
s.filter = s.filter.replace(/[\w\.]*alpha\(.*?\);?/i, '');
}
}else{
s.opacity = '';
s['-moz-opacity'] = '';
s['-khtml-opacity'] = '';
}
};

/**
 * Gets the computed height of the given element, including padding and
 * borders.
 *
 * @param HTMLElement elThe element
 * @returnNumberThe computed height of the element
 * @private
 */
var getComputedHeight = function(el){
var h = Math.max(el.offsetHeight, el.clientHeight);
if(!h){
h = parseInt(SL.getStyle(el, 'height'), 10) || 0;
if(!client.isBorderBox){
h += parseInt(SL.getStyle(el, 'padding-top'), 10)
+ parseInt(SL.getStyle(el, 'padding-bottom'), 10)
+ parseInt(SL.getStyle(el, 'border-top-width'), 10)
+ parseInt(SL.getStyle(el, 'border-bottom-width'), 10);
}
}
return h;
};

/**
 * Determines the player needed to display the file at the given URL. If
 * the file type is not supported, the return value will be 'unsupported'.
 * If the file type is not supported but the correct player can be
 * determined, the return value will be 'unsupported-*' where * will be the
 * player abbreviation (e.g. 'qt' = QuickTime).
 *
 * @param Stringurl The url of the file
 * @returnStringThe name of the player to use
 * @private
 */
var getPlayer = function(url){
var m = url.match(RE.domain);
var d = m && document.domain == m[1]; // same domain
if(url.indexOf('#') > -1 && d) return 'inline';
var q = url.indexOf('?');
if(q > -1) url = url.substring(0, q); // strip query string for player detection purposes
if(RE.img.test(url)) return 'img';
if(RE.swf.test(url)) return plugins.fla ? 'swf' : 'unsupported-swf';
if(RE.flv.test(url)) return plugins.fla ? 'flv' : 'unsupported-flv';
if(RE.qt.test(url)) return plugins.qt ? 'qt' : 'unsupported-qt';
if(RE.wmp.test(url)){
if(plugins.wmp) return 'wmp';
if(plugins.f4m) return 'qt';
if(client.isMac) return plugins.qt ? 'unsupported-f4m' : 'unsupported-qtf4m';
return 'unsupported-wmp';
}else if(RE.qtwmp.test(url)){
if(plugins.qt) return 'qt';
if(plugins.wmp) return 'wmp';
return client.isMac ? 'unsupported-qt' : 'unsupported-qtwmp';
}else if(!d || RE.iframe.test(url)){
return 'iframe';
}
return 'unsupported'; // same domain, not supported
};

/**
 * Handles all clicks on links that have been set up to work with Shadowbox
 * and cancels the default event behavior when appropriate.
 *
 * @param {Event} evThe click event object
 * @returnvoid
 * @private
 */
var handleClick = function(ev){
// get anchor/area element
var link;
if(isLink(this)){
link = this; // jQuery, Prototype, YUI
}else{
link = SL.getTarget(ev); // Ext, standalone
while(!isLink(link) && link.parentNode){
link = link.parentNode;
}
}

//SL.preventDefault(ev); // good for debugging

if(link){
SB.open(link);
if(gallery.length) SL.preventDefault(ev); // stop event
}
};

/**
 * Toggles the display of the nav control with the given id on and off.
 *
 * @param StringidThe id of the navigation control
 * @param Boolean onTrue to toggle on, false to toggle off
 * @returnvoid
 * @private
 */
var toggleNav = function(id, on){
var el = SL.get('shadowbox_nav_' + id);
if(el) el.style.display = on ? '' : 'none';
};

/**
 * Builds the content for the title and information bars.
 *
 * @param FunctioncbA callback function to execute after the
 *bars are built
 * @returnvoid
 * @private
 */
var buildBars = function(cb){
var obj = gallery[current];
var title_i = SL.get('shadowbox_title_inner');

// build the title
title_i.innerHTML = obj.title || '';

// build the nav
var nav = SL.get('shadowbox_nav');
if(nav){
var c, n, pl, pa, p;

// need to build the nav?
if(options.displayNav){
c = true;
// next & previous links
var len = gallery.length;
if(len > 1){
if(options.continuous){
n = p = true; // show both
}else{
n = (len - 1) > current; // not last in gallery, show next
p = current > 0; // not first in gallery, show previous
}
}
// in a slideshow?
if(options.slideshowDelay > 0 && hasNext()){
pa = slide_timer != 'paused';
pl = !pa;
}
}else{
c = n = pl = pa = p = false;
}

toggleNav('close', c);
toggleNav('next', n);
toggleNav('play', pl);
toggleNav('pause', pa);
toggleNav('previous', p);
}

// build the counter
var counter = SL.get('shadowbox_counter');
if(counter){
var co = '';

// need to build the counter?
if(options.displayCounter && gallery.length > 1){
if(options.counterType == 'skip'){
// limit the counter?
var i = 0, len = gallery.length, end = len;
var limit = parseInt(options.counterLimit);
if(limit < len){ // support large galleries
var h = Math.round(limit / 2);
i = current - h;
if(i < 0) i += len;
end = current + (limit - h);
if(end > len) end -= len;
}
while(i != end){
if(i == len) i = 0;
co += '<a onclick="Shadowbox.change(' + i + ');"';
if(i == current) co += ' class="shadowbox_counter_current"';
co += '>' + (++i) + '</a>';
}
}else{ // default
co = (current + 1) + ' ' + SB.LANG.of + ' ' + len;
}
}

counter.innerHTML = co;
}

cb();
};

/**
 * Hides the title and info bars.
 *
 * @param Boolean animTrue to animate the transition
 * @param FunctioncbA callback function to execute after the
 *animation completes
 * @returnvoid
 * @private
 */
var hideBars = function(anim, cb){
var obj = gallery[current];
var title = SL.get('shadowbox_title');
var info = SL.get('shadowbox_info');
var title_i = SL.get('shadowbox_title_inner');
var info_i = SL.get('shadowbox_info_inner');

// build bars after they are hidden
var fn = function(){
buildBars(cb);
};

var title_h = getComputedHeight(title);
var info_h = getComputedHeight(info) * -1;
if(anim){
// animate the transition
animate(title_i, 'margin-top', title_h, 0.35);
animate(info_i, 'margin-top', info_h, 0.35, fn);
}else{
SL.setStyle(title_i, 'margin-top', title_h + 'px');
SL.setStyle(info_i, 'margin-top', info_h + 'px');
fn();
}
};

/**
 * Shows the title and info bars.
 *
 * @param FunctioncbA callback function to execute after the
 *animation completes
 * @returnvoid
 * @private
 */
var showBars = function(cb){
var title_i = SL.get('shadowbox_title_inner');
var info_i = SL.get('shadowbox_info_inner');
var t = title_i.innerHTML != ''; // is there a title to display?

if(t) animate(title_i, 'margin-top', 0, 0.35);
animate(info_i, 'margin-top', 0, 0.35, cb);
};

/**
 * Loads the Shadowbox with the current piece.
 *
 * @returnvoid
 * @private
 */
var loadContent = function(){
var obj = gallery[current];
if(!obj) return; // invalid

var changing = false;
if(content){
content.remove(); // remove old content first
changing = true; // changing from some previous content
}

// determine player, inline is really just HTML
var p = obj.player == 'inline' ? 'html' : obj.player;

// make sure player is loaded
if(typeof SB[p] != 'function'){
SB.raise('Unknown player ' + obj.player);
}
content = new SB[p](content_id, obj); // instantiate new content object

listenKeys(false); // disable the keyboard temporarily
toggleLoading(true);

hideBars(changing, function(){ // if changing, animate the bars transition
if(!content) return;

// if opening, clear #shadowbox display
if(!changing){
 SL.get('shadowbox').style.display = '';
}

var fn = function(){
resizeContent(function(){
if(!content) return;


showBars(function(){
if(!content) return;

// append content just before hiding the loading layer
SL.get('shadowbox_body_inner').innerHTML = SL.createHTML(content.markup(dims));

toggleLoading(false, function(){
if(!content) return;

if(typeof content.onLoad == 'function'){
content.onLoad(); // call onLoad callback if present
}
if(options.onFinish && typeof options.onFinish == 'function'){
options.onFinish(gallery[current]); // fire onFinish handler
}
if(slide_timer != 'paused'){
SB.play(); // kick off next slide
}
listenKeys(true); // re-enable the keyboard
});
});
});
};

if(typeof content.ready != 'undefined'){ // does the object have a ready property?
var id = setInterval(function(){ // if so, wait for the object to be ready
if(content){
if(content.ready){
clearInterval(id); // clean up
id = null;
fn();
}
}else{ // content has been removed
clearInterval(id);
id = null;
}
}, 100);
}else{
fn();
}
});

// preload neighboring gallery images
if(gallery.length > 1){
var next = gallery[current + 1] || gallery[0];
if(next.player == 'img'){
var a = new Image();
a.src = next.content;
}
var prev = gallery[current - 1] || gallery[gallery.length - 1];
if(prev.player == 'img'){
var b = new Image();
b.src = prev.content;
}
}
};

/**
 * Calculates the dimensions for Shadowbox, taking into account the borders
 * and surrounding elements of the shadowbox_body. If the height/width
 * combination is too large for Shadowbox and handleOversize option is set
 * to 'resize', the resized dimensions will be returned (preserving the
 * original aspect ratio). Otherwise, the originally calculated dimensions
 * will be used. Stores all dimensions in the private dims variable.
 *
 * @param NumberheightThe content player height
 * @param Numberwidth The content player width
 * @param Boolean resizable True if the content is able to be
 *resized. Defaults to false.
 * @returnvoid
 * @private
 */
var setDimensions = function(height, width, resizable){
resizable = resizable || false;

var sb = SL.get('shadowbox_body');
var h = height = parseInt(height);
var w = width = parseInt(width);
var view_h = SL.getViewportHeight();
var view_w = SL.getViewportWidth();

// calculate the max width
var border_w = parseInt(SL.getStyle(sb, 'border-left-width'), 10)
+ parseInt(SL.getStyle(sb, 'border-right-width'), 10);
var extra_w = border_w + 2 * options.viewportPadding;
if(w + extra_w >= view_w){
w = view_w - extra_w;
}

// calculate the max height
var border_h = parseInt(SL.getStyle(sb, 'border-top-width'), 10)
+ parseInt(SL.getStyle(sb, 'border-bottom-width'), 10);
var bar_h = getComputedHeight(SL.get('shadowbox_title'))
+ getComputedHeight(SL.get('shadowbox_info'));
var extra_h = border_h + 2 * options.viewportPadding + bar_h;
if(h + extra_h >= view_h){
h = view_h - extra_h;
}

// handle oversized content
var drag = false;
var resize_h = height;
var resize_w = width;
var handle = options.handleOversize;
if(resizable && (handle == 'resize' || handle == 'drag')){
var change_h = (height - h) / height;
var change_w = (width - w) / width;
if(handle == 'resize'){
if(change_h > change_w){
w = Math.round((width / height) * h);
}else if(change_w > change_h){
h = Math.round((height / width) * w);
}
// adjust resized height or width accordingly
resize_w = w;
resize_h = h;
}else{
// drag on oversized images only
var link = gallery[current];
if(link) drag = link.player == 'img' && (change_h > 0 || change_w > 0);
}
}

// update dims
dims = {
height: h + border_h + bar_h,
width:w + border_w,
inner_h:h,
inner_w:w,
top:(view_h - (h + extra_h)) / 2 + options.viewportPadding,
resize_h: resize_h,
resize_w: resize_w,
drag: drag
};
};

/**
 * Resizes Shadowbox to the given height and width. If the callback
 * parameter is given, the transition will be animated and the callback
 * function will be called when the animation completes. Note: The private
 * content variable must be updated before calling this function.
 *
 * @param FunctioncbA callback function to execute after the
 *content has been resized
 * @returnvoid
 * @private
 */
var resizeContent = function(cb){
if(!content) return; // no content

// set new dimensions
setDimensions(content.height, content.width, content.resizable);

if(cb){
switch(options.animSequence){
case 'hw':
adjustHeight(dims.inner_h, dims.top, true, function(){
adjustWidth(dims.width, true, cb);
});
break;
case 'wh':
adjustWidth(dims.width, true, function(){
adjustHeight(dims.inner_h, dims.top, true, cb);
});
break;
case 'sync':
default:
adjustWidth(dims.width, true);
adjustHeight(dims.inner_h, dims.top, true, cb);
}
}else{ // window resize
adjustWidth(dims.width, false);
adjustHeight(dims.inner_h, dims.top, false);
var c = SL.get(content_id);
if(c){
// resize resizable content when in resize mode
if(content.resizable && options.handleOversize == 'resize'){
c.height = dims.resize_h;
c.width = dims.resize_w;
}
// fix draggable positioning if enlarging viewport
if(gallery[current].player == 'img' && options.handleOversize == 'drag'){
var top = parseInt(SL.getStyle(c, 'top'));
if(top + content.height < dims.inner_h){
SL.setStyle(c, 'top', dims.inner_h - content.height + 'px');
}
var left = parseInt(SL.getStyle(c, 'left'));
if(left + content.width < dims.inner_w){
SL.setStyle(c, 'left', dims.inner_w - content.width + 'px');
}
}
}
}
};

/**
 * Adjusts the height of #shadowbox_body and centers #shadowbox vertically
 * in the viewport.
 *
 * @param NumberheightThe height to use for #shadowbox_body
 * @param Numbertop The top to use for #shadowbox
 * @param Boolean animTrue to animate the transition
 * @param FunctioncbA callback to use when the animation
 *completes
 * @returnvoid
 * @private
 */
var adjustHeight = function(height, top, anim, cb){
height = parseInt(height);

// adjust the height
var sb = SL.get('shadowbox_body');
if(anim){
animate(sb, 'height', height, options.resizeDuration);
}else{
SL.setStyle(sb, 'height', height + 'px');
}

// adjust the top
var s = SL.get('shadowbox');
if(anim){
animate(s, 'top', top, options.resizeDuration, cb);
}else{
SL.setStyle(s, 'top', top + 'px');
if(typeof cb == 'function') cb();
}
};

/**
 * Adjusts the width of #shadowbox.
 *
 * @param Numberwidth The width to use for #shadowbox
 * @param Boolean animTrue to animate the transition
 * @param FunctioncbA callback to use when the animation
 *completes
 * @returnvoid
 * @private
 */
var adjustWidth = function(width, anim, cb){
width = parseInt(width);

// adjust the width
var s = SL.get('shadowbox');
if(anim){
animate(s, 'width', width, options.resizeDuration, cb);
}else{
SL.setStyle(s, 'width', width + 'px');
if(typeof cb == 'function') cb();
}
};

/**
 * Sets up a listener on the document for keystrokes.
 *
 * @param Boolean onTrue to enable the listener, false to turn
 *it off
 * @returnvoid
 * @private
 */
var listenKeys = function(on){
if(!options.enableKeys) return;
SL[(on ? 'add' : 'remove') + 'Event'](document, 'keydown', handleKey);
};

/**
 * A listener function that is fired when a key is pressed.
 *
 * @param mixed e The event object
 * @returnvoid
 * @private
 */
var handleKey = function(e){
var code = SL.keyCode(e);

// attempt to prevent default key action
SL.preventDefault(e);

if(code == 81 || code == 88 || code == 27){ // q, x, or esc
SB.close();
}else if(code == 37){ // left arrow
SB.previous();
}else if(code == 39){ // right arrow
SB.next();
}else if(code == 32){ // space bar
SB[(typeof slide_timer == 'number' ? 'pause' : 'play')]();
}
};

/**
 * Toggles the visibility of the "loading" layer.
 *
 * @param Boolean onTrue to toggle on, false to toggle off
 * @param FunctioncbThe callback function to call when toggling
 *completes
 * @returnvoid
 * @private
 */
var toggleLoading = function(on, cb){
var loading = SL.get('shadowbox_loading');
if(on){
loading.style.display = '';
if(typeof cb == 'function') cb();
}else{
var p = gallery[current].player;
var anim = (p == 'img' || p == 'html'); // fade on images & html
var fn = function(){
loading.style.display = 'none';
clearOpacity(loading);
if(typeof cb == 'function') cb();
};
if(anim){
animate(loading, 'opacity', 0, options.fadeDuration, fn);
}else{
fn();
}
}
};

/**
 * Sets the top of the container element. This is only necessary in IE6
 * where the container uses absolute positioning instead of fixed.
 *
 * @returnvoid
 * @private
 */
var fixTop = function(){
SL.get('shadowbox_container').style.top = document.documentElement.scrollTop + 'px';
};

/**
 * Sets the height of the overlay element to the full viewport height. This
 * is only necessary in IE6 where the container uses absolute positioning
 * instead of fixed, thus restricting the size of the overlay element.
 *
 * @returnvoid
 * @private
 */
var fixHeight = function(){
SL.get('shadowbox_overlay').style.height = SL.getViewportHeight() + 'px';
};

/**
 * Determines if there is a next piece to display in the current gallery.
 *
 * @returnboolTrue if there is another piece, false otherwise
 * @private
 */
var hasNext = function(){
return gallery.length > 1 && (current != gallery.length - 1 || options.continuous);
};

/**
 * Toggles the visibility of #shadowbox_container and sets its size (if on
 * IE6). Also toggles the visibility of elements (<select>, <object>, and
 * <embed>) that are troublesome for semi-transparent modal overlays. IE has
 * problems with <select> elements, while Firefox has trouble with
 * <object>s.
 *
 * @param FunctioncbA callback to call after toggling on, absent
 *when toggling off
 * @returnvoid
 * @private
 */
var toggleVisible = function(cb){
var els, v = (cb) ? 'hidden' : 'visible';
var hide = ['select', 'object', 'embed']; // tags to hide
for(var i = 0; i < hide.length; ++i){
els = document.getElementsByTagName(hide[i]);
for(var j = 0, len = els.length; j < len; ++j){
els[j].style.visibility = v;
}
}

// resize & show container
var so = SL.get('shadowbox_overlay');
var sc = SL.get('shadowbox_container');
var sb = SL.get('shadowbox');
if(cb){
// set overlay color/opacity
SL.setStyle(so, {
backgroundColor: options.overlayColor,
opacity: 0
});
if(!options.modal) SL.addEvent(so, 'click', SB.close);
if(ltIE7){
// fix container top & overlay height before showing
fixTop();
fixHeight();
SL.addEvent(window, 'scroll', fixTop);
}

// fade in animation
sb.style.display = 'none'; // will be cleared in loadContent()
sc.style.visibility = 'visible';
animate(so, 'opacity', parseFloat(options.overlayOpacity), options.fadeDuration, cb);
}else{
SL.removeEvent(so, 'click', SB.close);
if(ltIE7) SL.removeEvent(window, 'scroll', fixTop);

// fade out effect
sb.style.display = 'none';
animate(so, 'opacity', 0, options.fadeDuration, function(){
sc.style.visibility = 'hidden';
sb.style.display = '';
clearOpacity(so);
});
}
};

/**
 * Initializes the Shadowbox environment. Loads the skin (if necessary),
 * compiles the player matching regular expressions, and sets up the
 * window resize listener.
 *
 * @param Objectopts(optional) The default options to use
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.init = function(opts){
// don't initialize twice
if(initialized) return;

// make sure language is loaded
if(typeof SB.LANG == 'undefined'){
SB.raise('No Shadowbox language loaded');
return;
}
// make sure skin is loaded
if(typeof SB.SKIN == 'undefined'){
SB.raise('No Shadowbox skin loaded');
return;
}

// apply custom options
apply(options, opts || {});

// add markup
var markup = SB.SKIN.markup.replace(/\{(\w+)\}/g, function(m, p){
return SB.LANG[p];
});
var bd = document.body || document.documentElement;
SL.append(bd, markup);

// several fixes for IE6
if(ltIE7){
// give the container absolute positioning
SL.setStyle(SL.get('shadowbox_container'), 'position', 'absolute');
// give shadowbox_body "layout"...whatever that is
SL.get('shadowbox_body').style.zoom = 1;
// use AlphaImageLoader for transparent PNG support
var png = SB.SKIN.png_fix;
if(png && png.constructor == Array){
for(var i = 0; i < png.length; ++i){
var el = SL.get(png[i]);
if(el){
var match = SL.getStyle(el, 'background-image').match(/url\("(.*\.png)"\)/);
if(match){
SL.setStyle(el, {
backgroundImage: 'none',
filter: 'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src=' + match[1] + ',sizingMethod=scale);'
});
}
}
}
}
}

// compile file type regular expressions here for speed
for(var e in options.ext){
RE[e] = new RegExp('\.(' + options.ext[e].join('|') + ')\s*$', 'i');
}

// set up window resize event handler
var id;
SL.addEvent(window, 'resize', function(){
// use 50 ms event buffering to prevent jerky window resizing
if(id){
clearTimeout(id);
id = null;
}
id = setTimeout(function(){
if(ltIE7) fixHeight();
resizeContent();
}, 50);
});

if(!options.skipSetup) SB.setup();
initialized = true;
};

/**
 * Dynamically loads the specified skin for use with Shadowbox. If the skin
 * is included already in the page via the appropriate <script> and <link>
 * tags, this function does not need to be called. Otherwise, this function
 * must be called before window.onload.
 *
 * @param StringskinThe directory where the skin is located
 * @param Stringdir The directory where the Shadowbox skin
 *files are located
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.loadSkin = function(skin, dir){
if(!(/\/$/.test(dir))) dir += '/';
skin = dir + skin + '/';

// Safari 2.0 fails using DOM, use document.write instead
document.write('<link rel="stylesheet" type="text/css" href="' + skin + 'skin.css">');
document.write('<scr' + 'ipt type="text/javascript" src="' + skin + 'skin.js"><\/script>');
};

/**
 * Dynamically loads the specified language file to be used with Shadowbox.
 * If the language file is included already in the page via the appropriate
 * <script> tag, this function does not need to be called. Otherwise, this
 * function must be called before window.onload.
 *
 * @param StringlangThe language abbreviation (e.g. en)
 * @param Stringdir The directory where the Shadowbox
 *language file(s) is located
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.loadLanguage = function(lang, dir){
if(!(/\/$/.test(dir))) dir += '/';

// Safari 2.0 fails using DOM, use document.write instead
document.write('<scr' + 'ipt type="text/javascript" src="' + dir + 'shadowbox-' + lang + '.js"><\/script>');
};

/**
 * Dynamically loads the specified player(s) to be used with Shadowbox. If
 * the needed player(s) is already included in the page via the appropriate
 * <script> tag(s), this function does not need to be called. Otherwise,
 * this function must be called before window.onload.
 *
 * @param Array players The player(s) to load
 * @param Stringdir The director where the Shadowbox player
 *file(s) is located
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.loadPlayer = function(players, dir){
if(typeof players == 'string') players = [players];
if(!(/\/$/.test(dir))) dir += '/';

for(var i = 0, len = players.length; i < len; ++i){
// Safari 2.0 fails using DOM, use document.write instead
document.write('<scr' + 'ipt type="text/javascript" src="' + dir + 'shadowbox-' + players[i] + '.js"><\/script>');
}
};

/**
 * Sets up listeners on the given links that will trigger Shadowbox. If no
 * links are given, this method will set up every anchor element on the page
 * with the appropriate rel attribute. Note: Because AREA elements do not
 * support the rel attribute, they must be explicitly passed to this method.
 *
 * @param Array links An array (or array-like) list of anchor
 *and/or area elements to set up
 * @param ObjectoptsSome options to use for the given links
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.setup = function(links, opts){
// get links if none specified
if(!links){
var links = [];
var a = document.getElementsByTagName('a'), rel;
for(var i = 0, len = a.length; i < len; ++i){
rel = a[i].getAttribute('rel');
if(rel && RE.rel.test(rel)) links[links.length] = a[i];
}
}else if(!links.length){
links = [links]; // one link
}

var link;
for(var i = 0, len = links.length; i < len; ++i){
link = links[i];
if(typeof link.shadowboxCacheKey == 'undefined'){
// assign cache key expando
// use integer primitive to avoid memory leak in IE
link.shadowboxCacheKey = cache.length;
SL.addEvent(link, 'click', handleClick); // add listener
}
cache[link.shadowboxCacheKey] = this.buildCacheObj(link, opts);
}
};

/**
 * Builds an object from the original link element data to store in cache.
 * These objects contain (most of) the following keys:
 *
 * - el: the link element
 * - title: the linked file title
 * - player: the player to use for the linked file
 * - content: the linked file's URL
 * - gallery: the gallery the file belongs to (optional)
 * - height: the height of the linked file (only necessary for movies)
 * - width: the width of the linked file (only necessary for movies)
 * - options: custom options to use (optional)
 *
 * @param HTMLElement linkThe link element to process
 * @returnObjectAn object representing the link
 * @public
 * @static
 */
Shadowbox.buildCacheObj = function(link, opts){
var href = link.href; // don't use getAttribute() here
var o = {
el: link,
title:link.getAttribute('title'),
player: getPlayer(href),
options:apply({}, opts || {}), // break the reference
content:href
};

// remove link-level options from top-level options
var opt, l_opts = ['player', 'title', 'height', 'width', 'gallery'];
for(var i = 0, len = l_opts.length; i < len; ++i){
opt = l_opts[i];
if(typeof o.options[opt] != 'undefined'){
o[opt] = o.options[opt];
delete o.options[opt];
}
}

// HTML options always trump JavaScript options, so do these last
var rel = link.getAttribute('rel');
if(rel){
// extract gallery name from shadowbox[name] format
var match = rel.match(RE.gallery);
if(match) o.gallery = escape(match[2]);

// other parameters
var params = rel.split(';');
for(var i = 0, len = params.length; i < len; ++i){
match = params[i].match(RE.param);
if(match){
if(match[1] == 'options'){
eval('apply(o.options, ' + match[2] + ')');
}else{
o[match[1]] = match[2];
}
}
}
}

return o;
};

/**
 * Applies the given set of options to those currently in use. Note: Options
 * will be reset on Shadowbox.open() so this function is only useful after
 * it has already been called (while Shadowbox is open).
 *
 * @param ObjectoptsThe options to apply
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.applyOptions = function(opts){
if(opts){
// use apply here to break references
default_options = apply({}, options); // store default options
options = apply(options, opts); // apply options
}
};

/**
 * Reverts Shadowbox' options to the last default set in use before
 * Shadowbox.applyOptions() was called.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.revertOptions = function(){
if(default_options){
options = default_options; // revert to default options
default_options = null; // erase for next time
}
};

/**
 * Opens the given object in Shadowbox. This object may be either an
 * anchor/area element, or an object similar to the one created by
 * Shadowbox.buildCacheObj().
 *
 * @param mixed obj The object or link element that defines
 *what to display
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.open = function(obj, opts){
// revert options
this.revertOptions();

// is it a link?
if(isLink(obj)){
if(typeof obj.shadowboxCacheKey == 'undefined' || typeof cache[obj.shadowboxCacheKey] == 'undefined'){
// link element that hasn't been set up before
// create on-the-fly object
obj = this.buildCacheObj(obj, opts);
}else{
// link element that has been set up before, get from cache
obj = cache[obj.shadowboxCacheKey];
}
}

// is it already a gallery?
if(obj.constructor == Array){
gallery = obj;
current = 0;
}else{
// create a copy so it doesn't get modified later
var copy = apply({}, obj);

// is it part of a gallery?
if(!obj.gallery){ // single item, no gallery
gallery = [copy];
current = 0;
}else{
current = null; // reset current
gallery = []; // clear the current gallery
var ci;
for(var i = 0, len = cache.length; i < len; ++i){
ci = cache[i];
if(ci.gallery){
if(ci.content == obj.content
&& ci.gallery == obj.gallery
&& ci.title == obj.title){ // compare content, gallery, & title
current = gallery.length; // key element found
}
if(ci.gallery == obj.gallery){
gallery.push(apply({}, ci));
}
}
}
// if not found in cache, prepend to front of gallery
if(current == null){
gallery.unshift(copy);
current = 0;
}
}
}

obj = gallery[current];

// apply custom options
if(obj.options || opts){
// use apply here to break references
this.applyOptions(apply(apply({}, obj.options || {}), opts || {}));
}

// filter gallery for unsupported elements
var match, r;
for(var i = 0, len = gallery.length; i < len; ++i){
r = false; // remove the element?
if(gallery[i].player == 'unsupported'){ // don't support this at all
r = true;
}else if(match = RE.unsupported.exec(gallery[i].player)){ // handle unsupported elements
if(options.handleUnsupported == 'link'){
gallery[i].player = 'html';
// generate a link to the appropriate plugin download page(s)
var s, a, oe = options.errors;
switch(match[1]){
case 'qtwmp':
s = 'either';
a = [oe.qt.url, oe.qt.name, oe.wmp.url, oe.wmp.name];
break;
case 'qtf4m':
s = 'shared';
a = [oe.qt.url, oe.qt.name, oe.f4m.url, oe.f4m.name];
break;
default:
s = 'single';
if(match[1] == 'swf' || match[1] == 'flv') match[1] = 'fla';
a = [oe[match[1]].url, oe[match[1]].name];
}
var msg = SB.LANG.errors[s].replace(/\{(\d+)\}/g, function(m, i){
return a[i];
});
gallery[i].content = '<div class="shadowbox_message">' + msg + '</div>';
}else{
r = true;
}
}else if(gallery[i].player == 'inline'){ // handle inline elements
// retrieve the innerHTML of the inline element
var match = RE.inline.exec(gallery[i].content);
if(match){
var el;
if(el = SL.get(match[1])){
gallery[i].content = el.innerHTML;
}else{
SB.raise('Cannot find element with id ' + match[1]);
}
}else{
SB.raise('Cannot find element id for inline content');
}
}
if(r){
gallery.splice(i, 1); // remove the element from the gallery
if(i < current){
--current;
}else if(i == current){
// if current is unsupported, look for supported neighbor
current = i > 0 ? current - 1 : i;
}
--i; // decrement to account for splice
len = gallery.length; // gallery.length has changed!
}
}

// anything left?
if(gallery.length){
// fire onOpen hook
if(options.onOpen && typeof options.onOpen == 'function'){
options.onOpen(obj);
}

if(!activated){
// set initial dimensions & load
setDimensions(options.initialHeight, options.initialWidth);
adjustHeight(dims.inner_h, dims.top, false);
adjustWidth(dims.width, false);
toggleVisible(loadContent);
} else {
loadContent();
}

activated = true;
}
};

/**
 * Jumps to the piece in the current gallery with index num.
 *
 * @param Numbernum The gallery index to view
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.change = function(num){
if(!gallery) return; // no current gallery
if(!gallery[num]){ // index does not exist
if(!options.continuous){
return;
}else{
num = num < 0 ? (gallery.length - 1) : 0; // loop
}
}

if(typeof slide_timer == 'number'){
clearTimeout(slide_timer);
slide_timer = null;
slide_delay = slide_start = 0; // reset slideshow variables
}
current = num; // update current

if(options.onChange && typeof options.onChange == 'function'){
options.onChange(gallery[current]); // fire onChange handler
}

loadContent();
};

/**
 * Jumps to the next piece in the gallery.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.next = function(){
this.change(current + 1);
};

/**
 * Jumps to the previous piece in the gallery.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.previous = function(){
this.change(current - 1);
};

/**
 * Sets the timer for the next image in the slideshow to be displayed.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.play = function(){
if(!hasNext()) return;
if(!slide_delay) slide_delay = options.slideshowDelay * 1000;
if(slide_delay){
slide_start = new Date().getTime();
slide_timer = setTimeout(function(){
slide_delay = slide_start = 0; // reset slideshow
SB.next();
}, slide_delay);

// change play nav to pause
toggleNav('play', false);
toggleNav('pause', true);
}
};

/**
 * Pauses the current slideshow.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.pause = function(){
if(typeof slide_timer == 'number'){
var time = new Date().getTime();
slide_delay = Math.max(0, slide_delay - (time - slide_start));

// any delay left on current slide? if so, stop the timer
if(slide_delay){
clearTimeout(slide_timer);
slide_timer = 'paused';
}

// change pause nav to play
toggleNav('pause', false);
toggleNav('play', true);
}
};

/**
 * Deactivates Shadowbox.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.close = function(){
if(!activated) return; // already closed

// stop listening for keys
listenKeys(false);
// hide
toggleVisible(false);
// remove the content
if(content){
content.remove();
content = null;
}

// clear slideshow variables
if(typeof slide_timer == 'number') clearTimeout(slide_timer);
slide_timer = null;
slide_delay = 0;

// fire onClose handler
if(options.onClose && typeof options.onClose == 'function'){
options.onClose(gallery[current]);
}

activated = false;
};

/**
 * Clears Shadowbox' cache and removes listeners and expandos from all
 * cached link elements. May be used to completely reset Shadowbox in case
 * links on a page change.
 *
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.clearCache = function(){
for(var i = 0, len = cache.length; i < len; ++i){
if(cache[i].el){
SL.removeEvent(cache[i].el, 'click', handleClick);
delete cache[i].el.shadowboxCacheKey; // remove expando
}
}
cache = [];
};

/**
 * Gets an object that lists which plugins are supported by the client. The
 * keys of this object will be:
 *
 * - fla: Adobe Flash Player
 * - qt: QuickTime Player
 * - wmp: Windows Media Player
 * - f4m: Flip4Mac QuickTime Player
 *
 * @returnObjectThe plugins object
 * @public
 * @static
 */
Shadowbox.getPlugins = function(){
return plugins;
};

/**
 * Gets the current options object in use.
 *
 * @returnObjectThe options object
 * @public
 * @static
 */
Shadowbox.getOptions = function(){
return options;
};

/**
 * Gets the current gallery object.
 *
 * @returnObjectThe current gallery item
 * @public
 * @static
 */
Shadowbox.getCurrent = function(){
return gallery[current];
};

/**
 * Gets the current version number of Shadowbox.
 *
 * @returnStringThe current version
 * @public
 * @static
 */
Shadowbox.getVersion = function(){
return version;
};

/**
 * Returns an object containing information about the current client
 * configuration.
 *
 * @returnObjectThe object containing client data
 * @public
 * @static
 */
Shadowbox.getClient = function(){
return client;
};

/**
 * Returns the current content object in use.
 *
 * @returnObjectThe current content object
 * @public
 * @static
 */
Shadowbox.getContent = function(){
return content;
};

/**
 * Gets the current dimensions of Shadowbox as calculated by
 * setDimensions().
 *
 * @returnObjectThe current dimensions of Shadowbox
 * @public
 * @static
 */
Shadowbox.getDimensions = function(){
return dims;
};

/**
 * Handles all Shadowbox exceptions (errors). Calls the exception
 * handler callback if one is present (see handleException option) or
 * throws a new exception.
 *
 * @param Stringe The error message
 * @returnvoid
 * @public
 * @static
 */
Shadowbox.raise = function(e){
if(typeof options.handleException == 'function'){
options.handleException(e);
}else{
throw e;
}
};

})();if(typeof Shadowbox=='undefined'){throw'Unable to load Shadowbox skin, base library not found.';}
Shadowbox.SKIN={markup:'<div id="shadowbox_container">'+'<div id="shadowbox_overlay"></div>'+'<div id="shadowbox">'+'<div id="shadowbox_title">'+'<div id="shadowbox_title_inner"></div>'+'</div>'+'<div id="shadowbox_body">'+'<div id="shadowbox_body_inner"></div>'+'<div id="shadowbox_loading">'+'<div id="shadowbox_loading_indicator"></div>'+'<span><a onclick="Shadowbox.close();">{cancel}</a></span>'+'</div>'+'</div>'+'<div id="shadowbox_info">'+'<div id="shadowbox_info_inner">'+'<div id="shadowbox_counter"></div>'+'<div id="shadowbox_nav">'+'<a id="shadowbox_nav_close" title="{close}" onclick="Shadowbox.close()"></a>'+'<a id="shadowbox_nav_next" title="{next}" onclick="Shadowbox.next()"></a>'+'<a id="shadowbox_nav_play" title="{play}" onclick="Shadowbox.play()"></a>'+'<a id="shadowbox_nav_pause" title="{pause}" onclick="Shadowbox.pause()"></a>'+'<a id="shadowbox_nav_previous" title="{previous}" onclick="Shadowbox.previous()"></a>'+'</div>'+'<div class="shadowbox_clear"></div>'+'</div>'+'</div>'+'</div>'+'</div>',png_fix:['shadowbox_nav_close','shadowbox_nav_next','shadowbox_nav_play','shadowbox_nav_pause','shadowbox_nav_previous']};if(typeof Shadowbox=='undefined'){throw'Unable to load Shadowbox language file, base library not found.';}
Shadowbox.LANG={code:'de',of:'von',loading:'ladend',cancel:'Abbrechen',next:'Nächste',previous:'Vorige',play:'Abspielen',pause:'Pause',close:'Schließen',errors:{single:'Um den Inhalt anzeigen zu können muss die Browser-Erweiterung <a href="{0}">{1}</a> installiert werden.',shared:'Um den Inhalt anzeigen zu können müssen die beiden Browser-Erweiterungen <a href="{0}">{1}</a> und <a href="{2}">{3}</a> installiert werden.',either:'Um den Inhalt anzeigen zu können muss eine der beiden Browser-Erweiterungen <a href="{0}">{1}</a> oder <a href="{2}">{3}</a> installiert werden.'}};(function(){var SB=Shadowbox;var SL=SB.lib;var C=SB.getClient();var drag;var draggable;var drag_id='shadowbox_drag_layer';var preloader;var resetDrag=function(){drag={x:0,y:0,start_x:null,start_y:null};};var toggleDrag=function(on,h,w){if(on){resetDrag();var styles=['position:absolute','height:'+h+'px','width:'+w+'px','cursor:'+(C.isGecko?'-moz-grab':'move'),'background-color:'+(C.isIE?'#fff;filter:alpha(opacity=0)':'transparent')];SL.append(SL.get('shadowbox_body_inner'),'<div id="'+drag_id+'" style="'+styles.join(';')+'"></div>');SL.addEvent(SL.get(drag_id),'mousedown',listenDrag);}else{var d=SL.get(drag_id);if(d){SL.removeEvent(d,'mousedown',listenDrag);SL.remove(d);}}};var listenDrag=function(e){SL.preventDefault(e);var coords=SL.getPageXY(e);drag.start_x=coords[0];drag.start_y=coords[1];draggable=SL.get('shadowbox_content');SL.addEvent(document,'mousemove',positionDrag);SL.addEvent(document,'mouseup',unlistenDrag);if(C.isGecko)SL.setStyle(SL.get(drag_id),'cursor','-moz-grabbing');};var unlistenDrag=function(){SL.removeEvent(document,'mousemove',positionDrag);SL.removeEvent(document,'mouseup',unlistenDrag);if(C.isGecko)SL.setStyle(SL.get(drag_id),'cursor','-moz-grab');};var positionDrag=function(e){var content=SB.getContent();var dims=SB.getDimensions();var coords=SL.getPageXY(e);var move_x=coords[0]-drag.start_x;drag.start_x+=move_x;drag.x=Math.max(Math.min(0,drag.x+move_x),dims.inner_w-content.width);SL.setStyle(draggable,'left',drag.x+'px');var move_y=coords[1]-drag.start_y;drag.start_y+=move_y;drag.y=Math.max(Math.min(0,drag.y+move_y),dims.inner_h-content.height);SL.setStyle(draggable,'top',drag.y+'px');};Shadowbox.img=function(id,obj){this.id=id;this.obj=obj;this.resizable=true;this.ready=false;var self=this;preloader=new Image();preloader.onload=function(){self.height=self.obj.height?parseInt(self.obj.height,10):preloader.height;self.width=self.obj.width?parseInt(self.obj.width,10):preloader.width;self.ready=true;preloader.onload='';preloader=null;};preloader.src=obj.content;};Shadowbox.img.prototype={markup:function(dims){return{tag:'img',id:this.id,height:dims.resize_h,width:dims.resize_w,src:this.obj.content,style:'position:absolute'};},onLoad:function(){var dims=SB.getDimensions();if(dims.drag&&SB.getOptions().handleOversize=='drag'){toggleDrag(true,dims.resize_h,dims.resize_w);}},remove:function(){var el=SL.get(this.id);if(el)SL.remove(el);toggleDrag(false);if(preloader){preloader.onload='';preloader=null;}}};})();window.onload = Shadowbox.init;