[Midnightbsd-cvs] www: shadowbox.js: for new site revision
smultron at midnightbsd.org
smultron at midnightbsd.org
Thu Aug 21 15:22:58 EDT 2008
Log Message:
-----------
for new site revision
Added Files:
-----------
www/shadowbox/src:
shadowbox.js (r1.1)
-------------- next part --------------
--- /dev/null
+++ shadowbox/src/shadowbox.js
@@ -0,0 +1,2146 @@
+/**
+ * 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/.
+ *
+ * @author Michael J. I. Jackson <mjijackson at 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
+ * @author Michael J. I. Jackson <mjijackson at 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: '../shadowbox/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 Object ext
+ */
+ 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 Object o The original object
+ * @param Object e The extension object
+ * @return Object The 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 el The object to check
+ * @return Boolean 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.
+ *
+ * @return Number The 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.
+ *
+ * @return Number The 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 Object obj The HTML definition object
+ * @return String An 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 Number x The state of the animation (% complete)
+ * @return Number The 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 el The DOM element to animate
+ * @param String p The property to animate (in camelCase)
+ * @param mixed to The value to animate to
+ * @param Number d The duration of the animation (in
+ * seconds)
+ * @param Function cb A callback function to call when the
+ * animation completes
+ * @return void
+ * @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 el The DOM element
+ * @return void
+ * @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 el The element
+ * @return Number The 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 String url The url of the file
+ * @return String The 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} ev The click event object
+ * @return void
+ * @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 String id The id of the navigation control
+ * @param Boolean on True to toggle on, false to toggle off
+ * @return void
+ * @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 Function cb A callback function to execute after the
+ * bars are built
+ * @return void
+ * @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 anim True to animate the transition
+ * @param Function cb A callback function to execute after the
+ * animation completes
+ * @return void
+ * @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 Function cb A callback function to execute after the
+ * animation completes
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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 Number height The content player height
+ * @param Number width The content player width
+ * @param Boolean resizable True if the content is able to be
+ * resized. Defaults to false.
+ * @return void
+ * @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 Function cb A callback function to execute after the
+ * content has been resized
+ * @return void
+ * @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 Number height The height to use for #shadowbox_body
+ * @param Number top The top to use for #shadowbox
+ * @param Boolean anim True to animate the transition
+ * @param Function cb A callback to use when the animation
+ * completes
+ * @return void
+ * @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 Number width The width to use for #shadowbox
+ * @param Boolean anim True to animate the transition
+ * @param Function cb A callback to use when the animation
+ * completes
+ * @return void
+ * @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 on True to enable the listener, false to turn
+ * it off
+ * @return void
+ * @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
+ * @return void
+ * @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 on True to toggle on, false to toggle off
+ * @param Function cb The callback function to call when toggling
+ * completes
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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.
+ *
+ * @return bool True 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 Function cb A callback to call after toggling on, absent
+ * when toggling off
+ * @return void
+ * @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 Object opts (optional) The default options to use
+ * @return void
+ * @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 String skin The directory where the skin is located
+ * @param String dir The directory where the Shadowbox skin
+ * files are located
+ * @return void
+ * @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 String lang The language abbreviation (e.g. en)
+ * @param String dir The directory where the Shadowbox
+ * language file(s) is located
+ * @return void
+ * @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 String dir The director where the Shadowbox player
+ * file(s) is located
+ * @return void
+ * @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 Object opts Some options to use for the given links
+ * @return void
+ * @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 link The link element to process
+ * @return Object An 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 Object opts The options to apply
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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
+ * @return void
+ * @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 Number num The gallery index to view
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @public
+ * @static
+ */
+ Shadowbox.next = function(){
+ this.change(current + 1);
+ };
+
+ /**
+ * Jumps to the previous piece in the gallery.
+ *
+ * @return void
+ * @public
+ * @static
+ */
+ Shadowbox.previous = function(){
+ this.change(current - 1);
+ };
+
+ /**
+ * Sets the timer for the next image in the slideshow to be displayed.
+ *
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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
+ *
+ * @return Object The plugins object
+ * @public
+ * @static
+ */
+ Shadowbox.getPlugins = function(){
+ return plugins;
+ };
+
+ /**
+ * Gets the current options object in use.
+ *
+ * @return Object The options object
+ * @public
+ * @static
+ */
+ Shadowbox.getOptions = function(){
+ return options;
+ };
+
+ /**
+ * Gets the current gallery object.
+ *
+ * @return Object The current gallery item
+ * @public
+ * @static
+ */
+ Shadowbox.getCurrent = function(){
+ return gallery[current];
+ };
+
+ /**
+ * Gets the current version number of Shadowbox.
+ *
+ * @return String The current version
+ * @public
+ * @static
+ */
+ Shadowbox.getVersion = function(){
+ return version;
+ };
+
+ /**
+ * Returns an object containing information about the current client
+ * configuration.
+ *
+ * @return Object The object containing client data
+ * @public
+ * @static
+ */
+ Shadowbox.getClient = function(){
+ return client;
+ };
+
+ /**
+ * Returns the current content object in use.
+ *
+ * @return Object The current content object
+ * @public
+ * @static
+ */
+ Shadowbox.getContent = function(){
+ return content;
+ };
+
+ /**
+ * Gets the current dimensions of Shadowbox as calculated by
+ * setDimensions().
+ *
+ * @return Object The 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 String e The error message
+ * @return void
+ * @public
+ * @static
+ */
+ Shadowbox.raise = function(e){
+ if(typeof options.handleException == 'function'){
+ options.handleException(e);
+ }else{
+ throw e;
+ }
+ };
+
+})();
\ No newline at end of file
More information about the Midnightbsd-cvs
mailing list