~jstys-z/helioviewer.org/client5

« back to all changes in this revision

Viewing changes to lib/shadowbox-2.0/src/shadowbox.js

  • Committer: V. Keith Hughitt
  • Date: 2009-03-26 19:20:57 UTC
  • Revision ID: hughitt1@kore-20090326192057-u0x8rf8sf5lmmnwh
nightly build 03-26-2009: Using alpha-channel JPEG 2000 dataset

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * The Shadowbox class.
3
 
 *
4
 
 * This file is part of Shadowbox.
5
 
 *
6
 
 * Shadowbox is an online media viewer application that supports all of the
7
 
 * web's most popular media publishing formats. Shadowbox is written entirely
8
 
 * in JavaScript and CSS and is highly customizable. Using Shadowbox, website
9
 
 * authors can showcase a wide assortment of media in all major browsers without
10
 
 * navigating users away from the linking page.
11
 
 *
12
 
 * Shadowbox is released under version 3.0 of the Creative Commons Attribution-
13
 
 * Noncommercial-Share Alike license. This means that it is absolutely free
14
 
 * for personal, noncommercial use provided that you 1) make attribution to the
15
 
 * author and 2) release any derivative work under the same or a similar
16
 
 * license.
17
 
 *
18
 
 * If you wish to use Shadowbox for commercial purposes, licensing information
19
 
 * can be found at http://mjijackson.com/shadowbox/.
20
 
 *
21
 
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
22
 
 * @copyright   2007-2008 Michael J. I. Jackson
23
 
 * @license     http://creativecommons.org/licenses/by-nc-sa/3.0/
24
 
 * @version     SVN: $Id: shadowbox.js 108 2008-07-11 04:19:01Z mjijackson $
25
 
 */
26
 
 
27
 
if(typeof Shadowbox == 'undefined'){
28
 
    throw 'Unable to load Shadowbox, no base library adapter found';
29
 
}
30
 
 
31
 
/**
32
 
 * The Shadowbox class. Used to display different media on a web page using a
33
 
 * Lightbox-like effect.
34
 
 *
35
 
 * Useful resources:
36
 
 *
37
 
 * - http://www.alistapart.com/articles/byebyeembed
38
 
 * - http://www.w3.org/TR/html401/struct/objects.html
39
 
 * - http://www.dyn-web.com/dhtml/iframes/
40
 
 * - http://www.apple.com/quicktime/player/specs.html
41
 
 * - http://www.apple.com/quicktime/tutorials/embed2.html
42
 
 * - http://www.howtocreate.co.uk/wrongWithIE/?chapter=navigator.plugins
43
 
 * - http://msdn.microsoft.com/en-us/library/ms532969.aspx
44
 
 * - http://support.microsoft.com/kb/316992
45
 
 *
46
 
 * @class       Shadowbox
47
 
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
48
 
 * @singleton
49
 
 */
50
 
(function(){
51
 
 
52
 
    /**
53
 
     * The current version of Shadowbox.
54
 
     *
55
 
     * @var         String
56
 
     * @private
57
 
     */
58
 
    var version = '2.0';
59
 
 
60
 
    /**
61
 
     * Contains the default options for Shadowbox.
62
 
     *
63
 
     * @var         Object
64
 
     * @private
65
 
     */
66
 
    var options = {
67
 
 
68
 
        /**
69
 
         * Enable all animations besides fades.
70
 
         *
71
 
         * @var     Boolean
72
 
         */
73
 
        animate:            true,
74
 
 
75
 
        /**
76
 
         * Enable fade animations.
77
 
         *
78
 
         * @var     Boolean
79
 
         */
80
 
        animateFade:        true,
81
 
 
82
 
        /**
83
 
         * Specifies the sequence of the height and width animations. May be
84
 
         * 'wh' (width then height), 'hw' (height then width), or 'sync' (both
85
 
         * at the same time). Of course this will only work if animate is true.
86
 
         *
87
 
         * @var     String
88
 
         */
89
 
        animSequence:       'wh',
90
 
 
91
 
        /**
92
 
         * The path to flvplayer.swf.
93
 
         *
94
 
         * @var     String
95
 
         */
96
 
        flvPlayer:          'flvplayer.swf',
97
 
 
98
 
        /**
99
 
         * Listen to the overlay for clicks. If the user clicks the overlay,
100
 
         * it will trigger Shadowbox.close().
101
 
         *
102
 
         * @var     Boolean
103
 
         */
104
 
        modal:              false,
105
 
 
106
 
        /**
107
 
         * The color to use for the modal overlay (in hex).
108
 
         *
109
 
         * @var     String
110
 
         */
111
 
        overlayColor:       '#000',
112
 
 
113
 
        /**
114
 
         * The opacity to use for the modal overlay.
115
 
         *
116
 
         * @var     Number
117
 
         */
118
 
        overlayOpacity:     0.8,
119
 
 
120
 
        /**
121
 
         * The default background color to use for Flash movies (in hex).
122
 
         *
123
 
         * @var     String
124
 
         */
125
 
        flashBgColor:       '#000000',
126
 
 
127
 
        /**
128
 
         * Automatically play movies.
129
 
         *
130
 
         * @var     Boolean
131
 
         */
132
 
        autoplayMovies:     true,
133
 
 
134
 
        /**
135
 
         * Enable movie controllers on movie players.
136
 
         *
137
 
         * @var     Boolean
138
 
         */
139
 
        showMovieControls:  true,
140
 
 
141
 
        /**
142
 
         * A delay (in seconds) to use for slideshows. If set to anything other
143
 
         * than 0, this value determines an interval at which Shadowbox will
144
 
         * automatically proceed to the next piece in the gallery.
145
 
         *
146
 
         * @var     Number
147
 
         */
148
 
        slideshowDelay:     0,
149
 
 
150
 
        /**
151
 
         * The duration of the resizing animations (in seconds).
152
 
         *
153
 
         * @var     Number
154
 
         */
155
 
        resizeDuration:     0.55,
156
 
 
157
 
        /**
158
 
         * The duration of the fading animations (in seconds).
159
 
         *
160
 
         * @var     Number
161
 
         */
162
 
        fadeDuration:       0.35,
163
 
 
164
 
        /**
165
 
         * Show the navigation controls.
166
 
         *
167
 
         * @var     Boolean
168
 
         */
169
 
        displayNav:         true,
170
 
 
171
 
        /**
172
 
         * Enable continuous galleries. When this is true, users will be able
173
 
         * to skip to the first gallery image from the last using next and vice
174
 
         * versa.
175
 
         *
176
 
         * @var     Boolean
177
 
         */
178
 
        continuous:         false,
179
 
 
180
 
        /**
181
 
         * Display the gallery counter.
182
 
         *
183
 
         * @var     Boolean
184
 
         */
185
 
        displayCounter:     true,
186
 
 
187
 
        /**
188
 
         * This option may be either 'default' or 'skip'. The default counter is
189
 
         * a simple '1 of 5' message. The skip counter displays a link for each
190
 
         * piece in the gallery that enables a user to skip directly to any
191
 
         * piece.
192
 
         *
193
 
         * @var     String
194
 
         */
195
 
        counterType:        'default',
196
 
 
197
 
        /**
198
 
         * Limits the number of counter links that will be displayed in a "skip"
199
 
         * style counter. If the actual number of gallery elements is greater
200
 
         * than this value, the counter will be restrained to the elements
201
 
         * immediately preceeding and following the current element.
202
 
         *
203
 
         * @var     Number
204
 
         */
205
 
        counterLimit:       10,
206
 
 
207
 
        /**
208
 
         * The amount of padding to maintain around the viewport edge (in
209
 
         * pixels). This only applies when the image is very large and takes up
210
 
         * the entire viewport.
211
 
         *
212
 
         * @var     Number
213
 
         */
214
 
        viewportPadding:    20,
215
 
 
216
 
        /**
217
 
         * How to handle content that is too large to display in its entirety
218
 
         * (and is resizable). A value of 'resize' will resize the content while
219
 
         * preserving aspect ratio and display it at the smaller resolution. If
220
 
         * the content is an image, a value of 'drag' will display the image at
221
 
         * its original resolution but it will be draggable within Shadowbox. A
222
 
         * value of 'none' will display the content at its original resolution
223
 
         * but it may be cropped.
224
 
         *
225
 
         * @var     String
226
 
         */
227
 
        handleOversize:     'resize',
228
 
 
229
 
        /**
230
 
         * An exception handling function that will be called whenever
231
 
         * Shadowbox should throw an exception. Will be passed the error
232
 
         * message as its first argument.
233
 
         *
234
 
         * @var     Function
235
 
         */
236
 
        handleException:    null,
237
 
 
238
 
        /**
239
 
         * The mode to use when handling unsupported media. May be either
240
 
         * 'remove' or 'link'. If it is 'remove', the unsupported gallery item
241
 
         * will merely be removed from the gallery. If it is the only item in
242
 
         * the gallery, the link will simply be followed. If it is 'link', a
243
 
         * link will be provided to the appropriate plugin page in place of the
244
 
         * gallery element.
245
 
         *
246
 
         * @var     String
247
 
         */
248
 
        handleUnsupported:  'link',
249
 
 
250
 
        /**
251
 
         * The initial height of Shadowbox (in pixels).
252
 
         *
253
 
         * @var     Number
254
 
         */
255
 
        initialHeight:      160,
256
 
 
257
 
        /**
258
 
         * The initial width of Shadowbox (in pixels).
259
 
         *
260
 
         * @var     Number
261
 
         */
262
 
        initialWidth:       320,
263
 
 
264
 
        /**
265
 
         * Enable keyboard control.
266
 
         *
267
 
         * @var     Boolean
268
 
         */
269
 
        enableKeys:         true,
270
 
 
271
 
        /**
272
 
         * A hook function to be fired when Shadowbox opens. The single argument
273
 
         * will be the current gallery element.
274
 
         *
275
 
         * @var     Function
276
 
         */
277
 
        onOpen:             null,
278
 
 
279
 
        /**
280
 
         * A hook function to be fired when Shadowbox finishes loading its
281
 
         * content. The single argument will be the current gallery element on
282
 
         * display.
283
 
         *
284
 
         * @var     Function
285
 
         */
286
 
        onFinish:           null,
287
 
 
288
 
        /**
289
 
         * A hook function to be fired when Shadowbox changes from one gallery
290
 
         * element to the next. The single argument will be the current gallery
291
 
         * element that is about to be displayed.
292
 
         *
293
 
         * @var     Function
294
 
         */
295
 
        onChange:           null,
296
 
 
297
 
        /**
298
 
         * A hook function that will be fired when Shadowbox closes. The single
299
 
         * argument will be the gallery element most recently displayed.
300
 
         *
301
 
         * @var     Function
302
 
         */
303
 
        onClose:            null,
304
 
 
305
 
        /**
306
 
         * Skips calling Shadowbox.setup() in init(). This means that it must
307
 
         * be called later manually.
308
 
         *
309
 
         * @var     Boolean
310
 
         */
311
 
        skipSetup:          false,
312
 
 
313
 
        /**
314
 
         * An object containing names of plugins and links to their respective
315
 
         * download pages.
316
 
         *
317
 
         * @var     Object
318
 
         */
319
 
        errors:         {
320
 
 
321
 
            fla:        {
322
 
                name:   'Flash',
323
 
                url:    'http://www.adobe.com/products/flashplayer/'
324
 
            },
325
 
 
326
 
            qt:         {
327
 
                name:   'QuickTime',
328
 
                url:    'http://www.apple.com/quicktime/download/'
329
 
            },
330
 
 
331
 
            wmp:        {
332
 
                name:   'Windows Media Player',
333
 
                url:    'http://www.microsoft.com/windows/windowsmedia/'
334
 
            },
335
 
 
336
 
            f4m:        {
337
 
                name:   'Flip4Mac',
338
 
                url:    'http://www.flip4mac.com/wmv_download.htm'
339
 
            }
340
 
 
341
 
        },
342
 
 
343
 
        /**
344
 
         * A map of players to the file extensions they support. Each member of
345
 
         * this object is the name of a player (with one exception), whose value
346
 
         * is an array of file extensions that player will "play". The one
347
 
         * exception to this rule is the "qtwmp" member, which contains extensions
348
 
         * that may be played using either QuickTime or Windows Media Player.
349
 
         *
350
 
         * - img: Image file extensions
351
 
         * - swf: Flash SWF file extensions
352
 
         * - flv: Flash video file extensions (will be played by JW FLV player)
353
 
         * - qt: Movie file extensions supported by QuickTime
354
 
         * - wmp: Movie file extensions supported by Windows Media Player
355
 
         * - qtwmp: Movie file extensions supported by both QuickTime and Windows Media Player
356
 
         * - iframe: File extensions that will be display in an iframe
357
 
         *
358
 
         * IMPORTANT: If this object is to be modified, it must be copied in its
359
 
         * entirety and tweaked because it is not merged recursively with the
360
 
         * default. Also, any modifications must be passed into Shadowbox.init
361
 
         * for speed reasons.
362
 
         *
363
 
         * @var     Object      ext
364
 
         */
365
 
        ext:     {
366
 
            img:        ['png', 'jpg', 'jpeg', 'gif', 'bmp'],
367
 
            swf:        ['swf'],
368
 
            flv:        ['flv'],
369
 
            qt:         ['dv', 'mov', 'moov', 'movie', 'mp4'],
370
 
            wmp:        ['asf', 'wm', 'wmv'],
371
 
            qtwmp:      ['avi', 'mpg', 'mpeg'],
372
 
            iframe:     ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'pl', 'php',
373
 
                        'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml',
374
 
                        'txt', 'vbs']
375
 
        }
376
 
 
377
 
    };
378
 
 
379
 
    // shorthand
380
 
    var SB = Shadowbox;
381
 
    var SL = SB.lib;
382
 
 
383
 
    /**
384
 
     * Stores the default set of options in case a custom set of options is used
385
 
     * on a link-by-link basis so we can restore them later.
386
 
     *
387
 
     * @var         Object
388
 
     * @private
389
 
     */
390
 
    var default_options;
391
 
 
392
 
    /**
393
 
     * An object containing some regular expressions we'll need later. Compiled
394
 
     * up front for speed.
395
 
     *
396
 
     * @var         Object
397
 
     * @private
398
 
     */
399
 
    var RE = {
400
 
        domain:         /:\/\/(.*?)[:\/]/, // domain prefix
401
 
        inline:         /#(.+)$/, // inline element id
402
 
        rel:            /^(light|shadow)box/i, // rel attribute format
403
 
        gallery:        /^(light|shadow)box\[(.*?)\]/i, // rel attribute format for gallery link
404
 
        unsupported:    /^unsupported-(\w+)/, // unsupported media type
405
 
        param:          /\s*([a-z_]*?)\s*=\s*(.+)\s*/, // rel string parameter
406
 
        empty:          /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i // elements that don't have children
407
 
    };
408
 
 
409
 
    /**
410
 
     * A cache of options for links that have been set up for use with
411
 
     * Shadowbox.
412
 
     *
413
 
     * @var         Array
414
 
     * @private
415
 
     */
416
 
    var cache = [];
417
 
 
418
 
    /**
419
 
     * An array containing the gallery objects currently being viewed. In the
420
 
     * case of non-gallery items, this will only hold one object.
421
 
     *
422
 
     * @var         Array
423
 
     * @private
424
 
     */
425
 
    var gallery;
426
 
 
427
 
    /**
428
 
     * The array index of the current gallery that is currently being viewed.
429
 
     *
430
 
     * @var         Number
431
 
     * @private
432
 
     */
433
 
    var current;
434
 
 
435
 
    /**
436
 
     * The current content object.
437
 
     *
438
 
     * @var         Object
439
 
     * @private
440
 
     */
441
 
    var content;
442
 
 
443
 
    /**
444
 
     * The id to use for content objects.
445
 
     *
446
 
     * @var         String
447
 
     * @private
448
 
     */
449
 
    var content_id = 'shadowbox_content';
450
 
 
451
 
    /**
452
 
     * Holds the current dimensions of Shadowbox as calculated by
453
 
     * setDimensions(). Contains the following properties:
454
 
     *
455
 
     * - height: The total height of #shadowbox
456
 
     * - width: The total width of #shadowbox
457
 
     * - inner_h: The height of #shadowbox_body
458
 
     * - inner_w: The width of #shadowbox_body
459
 
     * - top: The top to use for #shadowbox
460
 
     * - resize_h: The height to use for resizable content
461
 
     * - resize_w: The width to use for resizable content
462
 
     * - drag: True if dragging should be enabled (oversized image)
463
 
     *
464
 
     * @var         Object
465
 
     * @private
466
 
     */
467
 
    var dims;
468
 
 
469
 
    /**
470
 
     * Keeps track of whether or not Shadowbox has been initialized. We never
471
 
     * want to initialize twice.
472
 
     *
473
 
     * @var         Boolean
474
 
     * @private
475
 
     */
476
 
    var initialized = false;
477
 
 
478
 
    /**
479
 
     * Keeps track of whether or not Shadowbox is activated.
480
 
     *
481
 
     * @var         Boolean
482
 
     * @private
483
 
     */
484
 
    var activated = false;
485
 
 
486
 
    /**
487
 
     * The timeout id for the slideshow transition function.
488
 
     *
489
 
     * @var         Number
490
 
     * @private
491
 
     */
492
 
    var slide_timer;
493
 
 
494
 
    /**
495
 
     * Keeps track of the time at which the current slideshow frame was
496
 
     * displayed.
497
 
     *
498
 
     * @var         Number
499
 
     * @private
500
 
     */
501
 
    var slide_start;
502
 
 
503
 
    /**
504
 
     * The delay on which the next slide will display.
505
 
     *
506
 
     * @var         Number
507
 
     * @private
508
 
     */
509
 
    var slide_delay = 0;
510
 
 
511
 
    /**
512
 
     * These parameters for simple browser detection. Adapted from Ext.js.
513
 
     *
514
 
     * @var         Object
515
 
     * @private
516
 
     */
517
 
    var ua = navigator.userAgent.toLowerCase();
518
 
    var client = {
519
 
        isStrict:   document.compatMode == 'CSS1Compat',
520
 
        isOpera:    ua.indexOf('opera') > -1,
521
 
        isIE:       ua.indexOf('msie') > -1,
522
 
        isIE7:      ua.indexOf('msie 7') > -1,
523
 
        isSafari:   /webkit|khtml/.test(ua),
524
 
        isWindows:  ua.indexOf('windows') != -1 || ua.indexOf('win32') != -1,
525
 
        isMac:      ua.indexOf('macintosh') != -1 || ua.indexOf('mac os x') != -1,
526
 
        isLinux:    ua.indexOf('linux') != -1
527
 
    };
528
 
    client.isBorderBox = client.isIE && !client.isStrict;
529
 
    client.isSafari3 = client.isSafari && !!(document.evaluate);
530
 
    client.isGecko = ua.indexOf('gecko') != -1 && !client.isSafari;
531
 
 
532
 
    /**
533
 
     * You're not sill using IE6 are you?
534
 
     *
535
 
     * @var         Boolean
536
 
     * @private
537
 
     */
538
 
    var ltIE7 = client.isIE && !client.isIE7;
539
 
 
540
 
    /**
541
 
     * Contains plugin support information. Each property of this object is a
542
 
     * boolean indicating whether that plugin is supported.
543
 
     *
544
 
     * - fla: Flash player
545
 
     * - qt: QuickTime player
546
 
     * - wmp: Windows Media player
547
 
     * - f4m: Flip4Mac plugin
548
 
     *
549
 
     * @var         Object
550
 
     * @private
551
 
     */
552
 
    var plugins;
553
 
 
554
 
    // detect plugin support
555
 
    if(navigator.plugins && navigator.plugins.length){
556
 
        var detectPlugin = function(plugin_name){
557
 
            var detected = false;
558
 
            for (var i = 0, len = navigator.plugins.length; i < len; ++i){
559
 
                if(navigator.plugins[i].name.indexOf(plugin_name) > -1){
560
 
                    detected = true;
561
 
                    break;
562
 
                }
563
 
            }
564
 
            return detected;
565
 
        };
566
 
        var f4m = detectPlugin('Flip4Mac');
567
 
        plugins = {
568
 
            fla:    detectPlugin('Shockwave Flash'),
569
 
            qt:     detectPlugin('QuickTime'),
570
 
            wmp:    !f4m && detectPlugin('Windows Media'), // if it's Flip4Mac, it's not really WMP
571
 
            f4m:    f4m
572
 
        };
573
 
    }else{
574
 
        var detectPlugin = function(plugin_name){
575
 
            var detected = false;
576
 
            try{
577
 
                var axo = new ActiveXObject(plugin_name);
578
 
                if(axo) detected = true;
579
 
            }catch(e){}
580
 
            return detected;
581
 
        };
582
 
        plugins = {
583
 
            fla:    detectPlugin('ShockwaveFlash.ShockwaveFlash'),
584
 
            qt:     detectPlugin('QuickTime.QuickTime'),
585
 
            wmp:    detectPlugin('wmplayer.ocx'),
586
 
            f4m:    false
587
 
        };
588
 
    }
589
 
 
590
 
    /**
591
 
     * Applies all properties of e to o.
592
 
     *
593
 
     * @param   Object      o       The original object
594
 
     * @param   Object      e       The extension object
595
 
     * @return  Object              The original object with all properties
596
 
     *                              of the extension object applied
597
 
     * @private
598
 
     */
599
 
    var apply = function(o, e){
600
 
        for(var p in e) o[p] = e[p];
601
 
        return o;
602
 
    };
603
 
 
604
 
    /**
605
 
     * Determines if the given object is an anchor/area element.
606
 
     *
607
 
     * @param   mixed       el      The object to check
608
 
     * @return  Boolean             True if the object is a link element
609
 
     * @private
610
 
     */
611
 
    var isLink = function(el){
612
 
        return el && typeof el.tagName == 'string' && (el.tagName.toUpperCase() == 'A' || el.tagName.toUpperCase() == 'AREA');
613
 
    };
614
 
 
615
 
    /**
616
 
     * Gets the height of the viewport in pixels. Note: This function includes
617
 
     * scrollbars in Safari 3.
618
 
     *
619
 
     * @return  Number          The height of the viewport
620
 
     * @public
621
 
     * @static
622
 
     */
623
 
    SL.getViewportHeight = function(){
624
 
        var h = window.innerHeight; // Safari
625
 
        var mode = document.compatMode;
626
 
        if((mode || client.isIE) && !client.isOpera){
627
 
            h = client.isStrict ? document.documentElement.clientHeight : document.body.clientHeight;
628
 
        }
629
 
        return h;
630
 
    };
631
 
 
632
 
    /**
633
 
     * Gets the width of the viewport in pixels. Note: This function includes
634
 
     * scrollbars in Safari 3.
635
 
     *
636
 
     * @return  Number          The width of the viewport
637
 
     * @public
638
 
     * @static
639
 
     */
640
 
    SL.getViewportWidth = function(){
641
 
        var w = window.innerWidth; // Safari
642
 
        var mode = document.compatMode;
643
 
        if(mode || client.isIE){
644
 
            w = client.isStrict ? document.documentElement.clientWidth : document.body.clientWidth;
645
 
        }
646
 
        return w;
647
 
    };
648
 
 
649
 
    /**
650
 
     * Creates an HTML string from an object representing HTML elements. Based
651
 
     * on Ext.DomHelper's createHtml.
652
 
     *
653
 
     * @param   Object      obj     The HTML definition object
654
 
     * @return  String              An HTML string
655
 
     * @public
656
 
     * @static
657
 
     */
658
 
    SL.createHTML = function(obj){
659
 
        var html = '<' + obj.tag;
660
 
        for(var attr in obj){
661
 
            if(attr == 'tag' || attr == 'html' || attr == 'children') continue;
662
 
            if(attr == 'cls'){
663
 
                html += ' class="' + obj['cls'] + '"';
664
 
            }else{
665
 
                html += ' ' + attr + '="' + obj[attr] + '"';
666
 
            }
667
 
        }
668
 
        if(RE.empty.test(obj.tag)){
669
 
            html += '/>';
670
 
        }else{
671
 
            html += '>';
672
 
            var cn = obj.children;
673
 
            if(cn){
674
 
                for(var i = 0, len = cn.length; i < len; ++i){
675
 
                    html += this.createHTML(cn[i]);
676
 
                }
677
 
            }
678
 
            if(obj.html) html += obj.html;
679
 
            html += '</' + obj.tag + '>';
680
 
        }
681
 
        return html;
682
 
    };
683
 
 
684
 
    /**
685
 
     * Easing function used for animations. Based on a cubic polynomial.
686
 
     *
687
 
     * @param   Number      x       The state of the animation (% complete)
688
 
     * @return  Number              The adjusted easing value
689
 
     * @private
690
 
     * @static
691
 
     */
692
 
    var ease = function(x){
693
 
        return 1 + Math.pow(x - 1, 3);
694
 
    };
695
 
 
696
 
    /**
697
 
     * Animates any numeric (not color) style of the given element from its
698
 
     * current state to the given value. Defaults to using pixel-based
699
 
     * measurements.
700
 
     *
701
 
     * @param   HTMLElement     el      The DOM element to animate
702
 
     * @param   String          p       The property to animate (in camelCase)
703
 
     * @param   mixed           to      The value to animate to
704
 
     * @param   Number          d       The duration of the animation (in
705
 
     *                                  seconds)
706
 
     * @param   Function        cb      A callback function to call when the
707
 
     *                                  animation completes
708
 
     * @return  void
709
 
     * @private
710
 
     * @static
711
 
     */
712
 
    var animate = function(el, p, to, d, cb){
713
 
        var from = parseFloat(SL.getStyle(el, p));
714
 
        if(isNaN(from)) from = 0;
715
 
 
716
 
        if(from == to){
717
 
            if(typeof cb == 'function') cb();
718
 
            return; // nothing to animate
719
 
        }
720
 
 
721
 
        var delta = to - from;
722
 
        var op = p == 'opacity';
723
 
        var unit = op ? '' : 'px'; // default unit is px
724
 
        var fn = function(ease){
725
 
            SL.setStyle(el, p, from + ease * delta + unit);
726
 
        };
727
 
 
728
 
        // cancel the animation here if set in the options
729
 
        if(!options.animate && !op || op && !options.animateFade){
730
 
            fn(1);
731
 
            if(typeof cb == 'function') cb();
732
 
            return;
733
 
        }
734
 
 
735
 
        d *= 1000; // convert to milliseconds
736
 
        var begin = new Date().getTime();
737
 
        var end = begin + d;
738
 
 
739
 
        var timer = setInterval(function(){
740
 
            var time = new Date().getTime();
741
 
            if(time >= end){ // end of animation
742
 
                clearInterval(timer);
743
 
                fn(1);
744
 
                if(typeof cb == 'function') cb();
745
 
            }else{
746
 
                fn(ease((time - begin) / d));
747
 
            }
748
 
        }, 10); // 10 ms interval is minimum on WebKit
749
 
    };
750
 
 
751
 
    /**
752
 
     * A utility function used by the fade functions to clear the opacity
753
 
     * style setting of the given element. Required in some cases for IE.
754
 
     *
755
 
     * @param   HTMLElement     el      The DOM element
756
 
     * @return  void
757
 
     * @private
758
 
     */
759
 
    var clearOpacity = function(el){
760
 
        var s = el.style;
761
 
        if(client.isIE){
762
 
            if(typeof s.filter == 'string' && (/alpha/i).test(s.filter)){
763
 
                // careful not to overwrite other filters!
764
 
                s.filter = s.filter.replace(/[\w\.]*alpha\(.*?\);?/i, '');
765
 
            }
766
 
        }else{
767
 
            s.opacity = '';
768
 
            s['-moz-opacity'] = '';
769
 
            s['-khtml-opacity'] = '';
770
 
        }
771
 
    };
772
 
 
773
 
    /**
774
 
     * Gets the computed height of the given element, including padding and
775
 
     * borders.
776
 
     *
777
 
     * @param   HTMLElement     el  The element
778
 
     * @return  Number              The computed height of the element
779
 
     * @private
780
 
     */
781
 
    var getComputedHeight = function(el){
782
 
        var h = Math.max(el.offsetHeight, el.clientHeight);
783
 
        if(!h){
784
 
            h = parseInt(SL.getStyle(el, 'height'), 10) || 0;
785
 
            if(!client.isBorderBox){
786
 
                h += parseInt(SL.getStyle(el, 'padding-top'), 10)
787
 
                    + parseInt(SL.getStyle(el, 'padding-bottom'), 10)
788
 
                    + parseInt(SL.getStyle(el, 'border-top-width'), 10)
789
 
                    + parseInt(SL.getStyle(el, 'border-bottom-width'), 10);
790
 
            }
791
 
        }
792
 
        return h;
793
 
    };
794
 
 
795
 
    /**
796
 
     * Determines the player needed to display the file at the given URL. If
797
 
     * the file type is not supported, the return value will be 'unsupported'.
798
 
     * If the file type is not supported but the correct player can be
799
 
     * determined, the return value will be 'unsupported-*' where * will be the
800
 
     * player abbreviation (e.g. 'qt' = QuickTime).
801
 
     *
802
 
     * @param   String          url     The url of the file
803
 
     * @return  String                  The name of the player to use
804
 
     * @private
805
 
     */
806
 
    var getPlayer = function(url){
807
 
        var m = url.match(RE.domain);
808
 
        var d = m && document.domain == m[1]; // same domain
809
 
        if(url.indexOf('#') > -1 && d) return 'inline';
810
 
        var q = url.indexOf('?');
811
 
        if(q > -1) url = url.substring(0, q); // strip query string for player detection purposes
812
 
        if(RE.img.test(url)) return 'img';
813
 
        if(RE.swf.test(url)) return plugins.fla ? 'swf' : 'unsupported-swf';
814
 
        if(RE.flv.test(url)) return plugins.fla ? 'flv' : 'unsupported-flv';
815
 
        if(RE.qt.test(url)) return plugins.qt ? 'qt' : 'unsupported-qt';
816
 
        if(RE.wmp.test(url)){
817
 
            if(plugins.wmp) return 'wmp';
818
 
            if(plugins.f4m) return 'qt';
819
 
            if(client.isMac) return plugins.qt ? 'unsupported-f4m' : 'unsupported-qtf4m';
820
 
            return 'unsupported-wmp';
821
 
        }else if(RE.qtwmp.test(url)){
822
 
            if(plugins.qt) return 'qt';
823
 
            if(plugins.wmp) return 'wmp';
824
 
            return client.isMac ? 'unsupported-qt' : 'unsupported-qtwmp';
825
 
        }else if(!d || RE.iframe.test(url)){
826
 
            return 'iframe';
827
 
        }
828
 
        return 'unsupported'; // same domain, not supported
829
 
    };
830
 
 
831
 
    /**
832
 
     * Handles all clicks on links that have been set up to work with Shadowbox
833
 
     * and cancels the default event behavior when appropriate.
834
 
     *
835
 
     * @param   {Event}         ev          The click event object
836
 
     * @return  void
837
 
     * @private
838
 
     */
839
 
    var handleClick = function(ev){
840
 
        // get anchor/area element
841
 
        var link;
842
 
        if(isLink(this)){
843
 
            link = this; // jQuery, Prototype, YUI
844
 
        }else{
845
 
            link = SL.getTarget(ev); // Ext, standalone
846
 
            while(!isLink(link) && link.parentNode){
847
 
                link = link.parentNode;
848
 
            }
849
 
        }
850
 
 
851
 
        //SL.preventDefault(ev); // good for debugging
852
 
 
853
 
        if(link){
854
 
            SB.open(link);
855
 
            if(gallery.length) SL.preventDefault(ev); // stop event
856
 
        }
857
 
    };
858
 
 
859
 
    /**
860
 
     * Toggles the display of the nav control with the given id on and off.
861
 
     *
862
 
     * @param   String      id      The id of the navigation control
863
 
     * @param   Boolean     on      True to toggle on, false to toggle off
864
 
     * @return  void
865
 
     * @private
866
 
     */
867
 
    var toggleNav = function(id, on){
868
 
        var el = SL.get('shadowbox_nav_' + id);
869
 
        if(el) el.style.display = on ? '' : 'none';
870
 
    };
871
 
 
872
 
    /**
873
 
     * Builds the content for the title and information bars.
874
 
     *
875
 
     * @param   Function    cb      A callback function to execute after the
876
 
     *                              bars are built
877
 
     * @return  void
878
 
     * @private
879
 
     */
880
 
    var buildBars = function(cb){
881
 
        var obj = gallery[current];
882
 
        var title_i = SL.get('shadowbox_title_inner');
883
 
 
884
 
        // build the title
885
 
        title_i.innerHTML = obj.title || '';
886
 
 
887
 
        // build the nav
888
 
        var nav = SL.get('shadowbox_nav');
889
 
        if(nav){
890
 
            var c, n, pl, pa, p;
891
 
 
892
 
            // need to build the nav?
893
 
            if(options.displayNav){
894
 
                c = true;
895
 
                // next & previous links
896
 
                var len = gallery.length;
897
 
                if(len > 1){
898
 
                    if(options.continuous){
899
 
                        n = p = true; // show both
900
 
                    }else{
901
 
                        n = (len - 1) > current; // not last in gallery, show next
902
 
                        p = current > 0; // not first in gallery, show previous
903
 
                    }
904
 
                }
905
 
                // in a slideshow?
906
 
                if(options.slideshowDelay > 0 && hasNext()){
907
 
                    pa = slide_timer != 'paused';
908
 
                    pl = !pa;
909
 
                }
910
 
            }else{
911
 
                c = n = pl = pa = p = false;
912
 
            }
913
 
 
914
 
            toggleNav('close', c);
915
 
            toggleNav('next', n);
916
 
            toggleNav('play', pl);
917
 
            toggleNav('pause', pa);
918
 
            toggleNav('previous', p);
919
 
        }
920
 
 
921
 
        // build the counter
922
 
        var counter = SL.get('shadowbox_counter');
923
 
        if(counter){
924
 
            var co = '';
925
 
 
926
 
            // need to build the counter?
927
 
            if(options.displayCounter && gallery.length > 1){
928
 
                if(options.counterType == 'skip'){
929
 
                    // limit the counter?
930
 
                    var i = 0, len = gallery.length, end = len;
931
 
                    var limit = parseInt(options.counterLimit);
932
 
                    if(limit < len){ // support large galleries
933
 
                        var h = Math.round(limit / 2);
934
 
                        i = current - h;
935
 
                        if(i < 0) i += len;
936
 
                        end = current + (limit - h);
937
 
                        if(end > len) end -= len;
938
 
                    }
939
 
                    while(i != end){
940
 
                        if(i == len) i = 0;
941
 
                        co += '<a onclick="Shadowbox.change(' + i + ');"';
942
 
                        if(i == current) co += ' class="shadowbox_counter_current"';
943
 
                        co += '>' + (++i) + '</a>';
944
 
                    }
945
 
                }else{ // default
946
 
                    co = (current + 1) + ' ' + SB.LANG.of + ' ' + len;
947
 
                }
948
 
            }
949
 
 
950
 
            counter.innerHTML = co;
951
 
        }
952
 
 
953
 
        cb();
954
 
    };
955
 
 
956
 
    /**
957
 
     * Hides the title and info bars.
958
 
     *
959
 
     * @param   Boolean     anim    True to animate the transition
960
 
     * @param   Function    cb      A callback function to execute after the
961
 
     *                              animation completes
962
 
     * @return  void
963
 
     * @private
964
 
     */
965
 
    var hideBars = function(anim, cb){
966
 
        var obj = gallery[current];
967
 
        var title = SL.get('shadowbox_title');
968
 
        var info = SL.get('shadowbox_info');
969
 
        var title_i = SL.get('shadowbox_title_inner');
970
 
        var info_i = SL.get('shadowbox_info_inner');
971
 
 
972
 
        // build bars after they are hidden
973
 
        var fn = function(){
974
 
            buildBars(cb);
975
 
        };
976
 
 
977
 
        var title_h = getComputedHeight(title);
978
 
        var info_h = getComputedHeight(info) * -1;
979
 
        if(anim){
980
 
            // animate the transition
981
 
            animate(title_i, 'margin-top', title_h, 0.35);
982
 
            animate(info_i, 'margin-top', info_h, 0.35, fn);
983
 
        }else{
984
 
            SL.setStyle(title_i, 'margin-top', title_h + 'px');
985
 
            SL.setStyle(info_i, 'margin-top', info_h + 'px');
986
 
            fn();
987
 
        }
988
 
    };
989
 
 
990
 
    /**
991
 
     * Shows the title and info bars.
992
 
     *
993
 
     * @param   Function    cb      A callback function to execute after the
994
 
     *                              animation completes
995
 
     * @return  void
996
 
     * @private
997
 
     */
998
 
    var showBars = function(cb){
999
 
        var title_i = SL.get('shadowbox_title_inner');
1000
 
        var info_i = SL.get('shadowbox_info_inner');
1001
 
        var t = title_i.innerHTML != ''; // is there a title to display?
1002
 
 
1003
 
        if(t) animate(title_i, 'margin-top', 0, 0.35);
1004
 
        animate(info_i, 'margin-top', 0, 0.35, cb);
1005
 
    };
1006
 
 
1007
 
    /**
1008
 
     * Loads the Shadowbox with the current piece.
1009
 
     *
1010
 
     * @return  void
1011
 
     * @private
1012
 
     */
1013
 
    var loadContent = function(){
1014
 
        var obj = gallery[current];
1015
 
        if(!obj) return; // invalid
1016
 
 
1017
 
        var changing = false;
1018
 
        if(content){
1019
 
            content.remove(); // remove old content first
1020
 
            changing = true; // changing from some previous content
1021
 
        }
1022
 
 
1023
 
        // determine player, inline is really just HTML
1024
 
        var p = obj.player == 'inline' ? 'html' : obj.player;
1025
 
 
1026
 
        // make sure player is loaded
1027
 
        if(typeof SB[p] != 'function'){
1028
 
            SB.raise('Unknown player ' + obj.player);
1029
 
        }
1030
 
        content = new SB[p](content_id, obj); // instantiate new content object
1031
 
 
1032
 
        listenKeys(false); // disable the keyboard temporarily
1033
 
        toggleLoading(true);
1034
 
 
1035
 
        hideBars(changing, function(){ // if changing, animate the bars transition
1036
 
            if(!content) return;
1037
 
 
1038
 
            // if opening, clear #shadowbox display
1039
 
            if(!changing){
1040
 
                 SL.get('shadowbox').style.display = '';
1041
 
            }
1042
 
 
1043
 
            var fn = function(){
1044
 
                resizeContent(function(){
1045
 
                    if(!content) return;
1046
 
 
1047
 
 
1048
 
                    showBars(function(){
1049
 
                        if(!content) return;
1050
 
 
1051
 
                        // append content just before hiding the loading layer
1052
 
                        SL.get('shadowbox_body_inner').innerHTML = SL.createHTML(content.markup(dims));
1053
 
 
1054
 
                        toggleLoading(false, function(){
1055
 
                            if(!content) return;
1056
 
 
1057
 
                            if(typeof content.onLoad == 'function'){
1058
 
                                content.onLoad(); // call onLoad callback if present
1059
 
                            }
1060
 
                            if(options.onFinish && typeof options.onFinish == 'function'){
1061
 
                                options.onFinish(gallery[current]); // fire onFinish handler
1062
 
                            }
1063
 
                            if(slide_timer != 'paused'){
1064
 
                                SB.play(); // kick off next slide
1065
 
                            }
1066
 
                            listenKeys(true); // re-enable the keyboard
1067
 
                        });
1068
 
                    });
1069
 
                });
1070
 
            };
1071
 
 
1072
 
            if(typeof content.ready != 'undefined'){ // does the object have a ready property?
1073
 
                var id = setInterval(function(){ // if so, wait for the object to be ready
1074
 
                    if(content){
1075
 
                        if(content.ready){
1076
 
                            clearInterval(id); // clean up
1077
 
                            id = null;
1078
 
                            fn();
1079
 
                        }
1080
 
                    }else{ // content has been removed
1081
 
                        clearInterval(id);
1082
 
                        id = null;
1083
 
                    }
1084
 
                }, 100);
1085
 
            }else{
1086
 
                fn();
1087
 
            }
1088
 
        });
1089
 
 
1090
 
        // preload neighboring gallery images
1091
 
        if(gallery.length > 1){
1092
 
            var next = gallery[current + 1] || gallery[0];
1093
 
            if(next.player == 'img'){
1094
 
                var a = new Image();
1095
 
                a.src = next.content;
1096
 
            }
1097
 
            var prev = gallery[current - 1] || gallery[gallery.length - 1];
1098
 
            if(prev.player == 'img'){
1099
 
                var b = new Image();
1100
 
                b.src = prev.content;
1101
 
            }
1102
 
        }
1103
 
    };
1104
 
 
1105
 
    /**
1106
 
     * Calculates the dimensions for Shadowbox, taking into account the borders
1107
 
     * and surrounding elements of the shadowbox_body. If the height/width
1108
 
     * combination is too large for Shadowbox and handleOversize option is set
1109
 
     * to 'resize', the resized dimensions will be returned (preserving the
1110
 
     * original aspect ratio). Otherwise, the originally calculated dimensions
1111
 
     * will be used. Stores all dimensions in the private dims variable.
1112
 
     *
1113
 
     * @param   Number      height      The content player height
1114
 
     * @param   Number      width       The content player width
1115
 
     * @param   Boolean     resizable   True if the content is able to be
1116
 
     *                                  resized. Defaults to false.
1117
 
     * @return  void
1118
 
     * @private
1119
 
     */
1120
 
    var setDimensions = function(height, width, resizable){
1121
 
        resizable = resizable || false;
1122
 
 
1123
 
        var sb = SL.get('shadowbox_body');
1124
 
        var h = height = parseInt(height);
1125
 
        var w = width = parseInt(width);
1126
 
        var view_h = SL.getViewportHeight();
1127
 
        var view_w = SL.getViewportWidth();
1128
 
 
1129
 
        // calculate the max width
1130
 
        var border_w = parseInt(SL.getStyle(sb, 'border-left-width'), 10)
1131
 
            + parseInt(SL.getStyle(sb, 'border-right-width'), 10);
1132
 
        var extra_w = border_w + 2 * options.viewportPadding;
1133
 
        if(w + extra_w >= view_w){
1134
 
            w = view_w - extra_w;
1135
 
        }
1136
 
 
1137
 
        // calculate the max height
1138
 
        var border_h = parseInt(SL.getStyle(sb, 'border-top-width'), 10)
1139
 
            + parseInt(SL.getStyle(sb, 'border-bottom-width'), 10);
1140
 
        var bar_h = getComputedHeight(SL.get('shadowbox_title'))
1141
 
            + getComputedHeight(SL.get('shadowbox_info'));
1142
 
        var extra_h = border_h + 2 * options.viewportPadding + bar_h;
1143
 
        if(h + extra_h >= view_h){
1144
 
            h = view_h - extra_h;
1145
 
        }
1146
 
 
1147
 
        // handle oversized content
1148
 
        var drag = false;
1149
 
        var resize_h = height;
1150
 
        var resize_w = width;
1151
 
        var handle = options.handleOversize;
1152
 
        if(resizable && (handle == 'resize' || handle == 'drag')){
1153
 
            var change_h = (height - h) / height;
1154
 
            var change_w = (width - w) / width;
1155
 
            if(handle == 'resize'){
1156
 
                if(change_h > change_w){
1157
 
                    w = Math.round((width / height) * h);
1158
 
                }else if(change_w > change_h){
1159
 
                    h = Math.round((height / width) * w);
1160
 
                }
1161
 
                // adjust resized height or width accordingly
1162
 
                resize_w = w;
1163
 
                resize_h = h;
1164
 
            }else{
1165
 
                // drag on oversized images only
1166
 
                var link = gallery[current];
1167
 
                if(link) drag = link.player == 'img' && (change_h > 0 || change_w > 0);
1168
 
            }
1169
 
        }
1170
 
 
1171
 
        // update dims
1172
 
        dims = {
1173
 
            height:     h + border_h + bar_h,
1174
 
            width:      w + border_w,
1175
 
            inner_h:    h,
1176
 
            inner_w:    w,
1177
 
            top:        (view_h - (h + extra_h)) / 2 + options.viewportPadding,
1178
 
            resize_h:   resize_h,
1179
 
            resize_w:   resize_w,
1180
 
            drag:       drag
1181
 
        };
1182
 
    };
1183
 
 
1184
 
    /**
1185
 
     * Resizes Shadowbox to the given height and width. If the callback
1186
 
     * parameter is given, the transition will be animated and the callback
1187
 
     * function will be called when the animation completes. Note: The private
1188
 
     * content variable must be updated before calling this function.
1189
 
     *
1190
 
     * @param   Function    cb      A callback function to execute after the
1191
 
     *                              content has been resized
1192
 
     * @return  void
1193
 
     * @private
1194
 
     */
1195
 
    var resizeContent = function(cb){
1196
 
        if(!content) return; // no content
1197
 
 
1198
 
        // set new dimensions
1199
 
        setDimensions(content.height, content.width, content.resizable);
1200
 
 
1201
 
        if(cb){
1202
 
            switch(options.animSequence){
1203
 
                case 'hw':
1204
 
                    adjustHeight(dims.inner_h, dims.top, true, function(){
1205
 
                        adjustWidth(dims.width, true, cb);
1206
 
                    });
1207
 
                    break;
1208
 
                case 'wh':
1209
 
                    adjustWidth(dims.width, true, function(){
1210
 
                        adjustHeight(dims.inner_h, dims.top, true, cb);
1211
 
                    });
1212
 
                    break;
1213
 
                case 'sync':
1214
 
                default:
1215
 
                    adjustWidth(dims.width, true);
1216
 
                    adjustHeight(dims.inner_h, dims.top, true, cb);
1217
 
            }
1218
 
        }else{ // window resize
1219
 
            adjustWidth(dims.width, false);
1220
 
            adjustHeight(dims.inner_h, dims.top, false);
1221
 
            var c = SL.get(content_id);
1222
 
            if(c){
1223
 
                // resize resizable content when in resize mode
1224
 
                if(content.resizable && options.handleOversize == 'resize'){
1225
 
                    c.height = dims.resize_h;
1226
 
                    c.width = dims.resize_w;
1227
 
                }
1228
 
                // fix draggable positioning if enlarging viewport
1229
 
                if(gallery[current].player == 'img' && options.handleOversize == 'drag'){
1230
 
                    var top = parseInt(SL.getStyle(c, 'top'));
1231
 
                    if(top + content.height < dims.inner_h){
1232
 
                        SL.setStyle(c, 'top', dims.inner_h - content.height + 'px');
1233
 
                    }
1234
 
                    var left = parseInt(SL.getStyle(c, 'left'));
1235
 
                    if(left + content.width < dims.inner_w){
1236
 
                        SL.setStyle(c, 'left', dims.inner_w - content.width + 'px');
1237
 
                    }
1238
 
                }
1239
 
            }
1240
 
        }
1241
 
    };
1242
 
 
1243
 
    /**
1244
 
     * Adjusts the height of #shadowbox_body and centers #shadowbox vertically
1245
 
     * in the viewport.
1246
 
     *
1247
 
     * @param   Number      height      The height to use for #shadowbox_body
1248
 
     * @param   Number      top         The top to use for #shadowbox
1249
 
     * @param   Boolean     anim        True to animate the transition
1250
 
     * @param   Function    cb          A callback to use when the animation
1251
 
     *                                  completes
1252
 
     * @return  void
1253
 
     * @private
1254
 
     */
1255
 
    var adjustHeight = function(height, top, anim, cb){
1256
 
        height = parseInt(height);
1257
 
 
1258
 
        // adjust the height
1259
 
        var sb = SL.get('shadowbox_body');
1260
 
        if(anim){
1261
 
            animate(sb, 'height', height, options.resizeDuration);
1262
 
        }else{
1263
 
            SL.setStyle(sb, 'height', height + 'px');
1264
 
        }
1265
 
 
1266
 
        // adjust the top
1267
 
        var s = SL.get('shadowbox');
1268
 
        if(anim){
1269
 
            animate(s, 'top', top, options.resizeDuration, cb);
1270
 
        }else{
1271
 
            SL.setStyle(s, 'top', top + 'px');
1272
 
            if(typeof cb == 'function') cb();
1273
 
        }
1274
 
    };
1275
 
 
1276
 
    /**
1277
 
     * Adjusts the width of #shadowbox.
1278
 
     *
1279
 
     * @param   Number      width       The width to use for #shadowbox
1280
 
     * @param   Boolean     anim        True to animate the transition
1281
 
     * @param   Function    cb          A callback to use when the animation
1282
 
     *                                  completes
1283
 
     * @return  void
1284
 
     * @private
1285
 
     */
1286
 
    var adjustWidth = function(width, anim, cb){
1287
 
        width = parseInt(width);
1288
 
 
1289
 
        // adjust the width
1290
 
        var s = SL.get('shadowbox');
1291
 
        if(anim){
1292
 
            animate(s, 'width', width, options.resizeDuration, cb);
1293
 
        }else{
1294
 
            SL.setStyle(s, 'width', width + 'px');
1295
 
            if(typeof cb == 'function') cb();
1296
 
        }
1297
 
    };
1298
 
 
1299
 
    /**
1300
 
     * Sets up a listener on the document for keystrokes.
1301
 
     *
1302
 
     * @param   Boolean     on      True to enable the listener, false to turn
1303
 
     *                              it off
1304
 
     * @return  void
1305
 
     * @private
1306
 
     */
1307
 
    var listenKeys = function(on){
1308
 
        if(!options.enableKeys) return;
1309
 
        SL[(on ? 'add' : 'remove') + 'Event'](document, 'keydown', handleKey);
1310
 
    };
1311
 
 
1312
 
    /**
1313
 
     * A listener function that is fired when a key is pressed.
1314
 
     *
1315
 
     * @param   mixed       e       The event object
1316
 
     * @return  void
1317
 
     * @private
1318
 
     */
1319
 
    var handleKey = function(e){
1320
 
        var code = SL.keyCode(e);
1321
 
 
1322
 
        // attempt to prevent default key action
1323
 
        SL.preventDefault(e);
1324
 
 
1325
 
        if(code == 81 || code == 88 || code == 27){ // q, x, or esc
1326
 
            SB.close();
1327
 
        }else if(code == 37){ // left arrow
1328
 
            SB.previous();
1329
 
        }else if(code == 39){ // right arrow
1330
 
            SB.next();
1331
 
        }else if(code == 32){ // space bar
1332
 
            SB[(typeof slide_timer == 'number' ? 'pause' : 'play')]();
1333
 
        }
1334
 
    };
1335
 
 
1336
 
    /**
1337
 
     * Toggles the visibility of the "loading" layer.
1338
 
     *
1339
 
     * @param   Boolean     on      True to toggle on, false to toggle off
1340
 
     * @param   Function    cb      The callback function to call when toggling
1341
 
     *                              completes
1342
 
     * @return  void
1343
 
     * @private
1344
 
     */
1345
 
    var toggleLoading = function(on, cb){
1346
 
        var loading = SL.get('shadowbox_loading');
1347
 
        if(on){
1348
 
            loading.style.display = '';
1349
 
            if(typeof cb == 'function') cb();
1350
 
        }else{
1351
 
            var p = gallery[current].player;
1352
 
            var anim = (p == 'img' || p == 'html'); // fade on images & html
1353
 
            var fn = function(){
1354
 
                loading.style.display = 'none';
1355
 
                clearOpacity(loading);
1356
 
                if(typeof cb == 'function') cb();
1357
 
            };
1358
 
            if(anim){
1359
 
                animate(loading, 'opacity', 0, options.fadeDuration, fn);
1360
 
            }else{
1361
 
                fn();
1362
 
            }
1363
 
        }
1364
 
    };
1365
 
 
1366
 
    /**
1367
 
     * Sets the top of the container element. This is only necessary in IE6
1368
 
     * where the container uses absolute positioning instead of fixed.
1369
 
     *
1370
 
     * @return  void
1371
 
     * @private
1372
 
     */
1373
 
    var fixTop = function(){
1374
 
        SL.get('shadowbox_container').style.top = document.documentElement.scrollTop + 'px';
1375
 
    };
1376
 
 
1377
 
    /**
1378
 
     * Sets the height of the overlay element to the full viewport height. This
1379
 
     * is only necessary in IE6 where the container uses absolute positioning
1380
 
     * instead of fixed, thus restricting the size of the overlay element.
1381
 
     *
1382
 
     * @return  void
1383
 
     * @private
1384
 
     */
1385
 
    var fixHeight = function(){
1386
 
        SL.get('shadowbox_overlay').style.height = SL.getViewportHeight() + 'px';
1387
 
    };
1388
 
 
1389
 
    /**
1390
 
     * Determines if there is a next piece to display in the current gallery.
1391
 
     *
1392
 
     * @return  bool            True if there is another piece, false otherwise
1393
 
     * @private
1394
 
     */
1395
 
    var hasNext = function(){
1396
 
        return gallery.length > 1 && (current != gallery.length - 1 || options.continuous);
1397
 
    };
1398
 
 
1399
 
    /**
1400
 
     * Toggles the visibility of #shadowbox_container and sets its size (if on
1401
 
     * IE6). Also toggles the visibility of elements (<select>, <object>, and
1402
 
     * <embed>) that are troublesome for semi-transparent modal overlays. IE has
1403
 
     * problems with <select> elements, while Firefox has trouble with
1404
 
     * <object>s.
1405
 
     *
1406
 
     * @param   Function    cb      A callback to call after toggling on, absent
1407
 
     *                              when toggling off
1408
 
     * @return  void
1409
 
     * @private
1410
 
     */
1411
 
    var toggleVisible = function(cb){
1412
 
        var els, v = (cb) ? 'hidden' : 'visible';
1413
 
        var hide = ['select', 'object', 'embed']; // tags to hide
1414
 
        for(var i = 0; i < hide.length; ++i){
1415
 
            els = document.getElementsByTagName(hide[i]);
1416
 
            for(var j = 0, len = els.length; j < len; ++j){
1417
 
                els[j].style.visibility = v;
1418
 
            }
1419
 
        }
1420
 
 
1421
 
        // resize & show container
1422
 
        var so = SL.get('shadowbox_overlay');
1423
 
        var sc = SL.get('shadowbox_container');
1424
 
        var sb = SL.get('shadowbox');
1425
 
        if(cb){
1426
 
            // set overlay color/opacity
1427
 
            SL.setStyle(so, {
1428
 
                backgroundColor: options.overlayColor,
1429
 
                opacity: 0
1430
 
            });
1431
 
            if(!options.modal) SL.addEvent(so, 'click', SB.close);
1432
 
            if(ltIE7){
1433
 
                // fix container top & overlay height before showing
1434
 
                fixTop();
1435
 
                fixHeight();
1436
 
                SL.addEvent(window, 'scroll', fixTop);
1437
 
            }
1438
 
 
1439
 
            // fade in animation
1440
 
            sb.style.display = 'none'; // will be cleared in loadContent()
1441
 
            sc.style.visibility = 'visible';
1442
 
            animate(so, 'opacity', parseFloat(options.overlayOpacity), options.fadeDuration, cb);
1443
 
        }else{
1444
 
            SL.removeEvent(so, 'click', SB.close);
1445
 
            if(ltIE7) SL.removeEvent(window, 'scroll', fixTop);
1446
 
 
1447
 
            // fade out effect
1448
 
            sb.style.display = 'none';
1449
 
            animate(so, 'opacity', 0, options.fadeDuration, function(){
1450
 
                sc.style.visibility = 'hidden';
1451
 
                sb.style.display = '';
1452
 
                clearOpacity(so);
1453
 
            });
1454
 
        }
1455
 
    };
1456
 
 
1457
 
    /**
1458
 
     * Initializes the Shadowbox environment. Loads the skin (if necessary),
1459
 
     * compiles the player matching regular expressions, and sets up the
1460
 
     * window resize listener.
1461
 
     *
1462
 
     * @param   Object      opts    (optional) The default options to use
1463
 
     * @return  void
1464
 
     * @public
1465
 
     * @static
1466
 
     */
1467
 
    Shadowbox.init = function(opts){
1468
 
        // don't initialize twice
1469
 
        if(initialized) return;
1470
 
 
1471
 
        // make sure language is loaded
1472
 
        if(typeof SB.LANG == 'undefined'){
1473
 
            SB.raise('No Shadowbox language loaded');
1474
 
            return;
1475
 
        }
1476
 
        // make sure skin is loaded
1477
 
        if(typeof SB.SKIN == 'undefined'){
1478
 
            SB.raise('No Shadowbox skin loaded');
1479
 
            return;
1480
 
        }
1481
 
 
1482
 
        // apply custom options
1483
 
        apply(options, opts || {});
1484
 
 
1485
 
        // add markup
1486
 
        var markup = SB.SKIN.markup.replace(/\{(\w+)\}/g, function(m, p){
1487
 
            return SB.LANG[p];
1488
 
        });
1489
 
        var bd = document.body || document.documentElement;
1490
 
        SL.append(bd, markup);
1491
 
 
1492
 
        // several fixes for IE6
1493
 
        if(ltIE7){
1494
 
            // give the container absolute positioning
1495
 
            SL.setStyle(SL.get('shadowbox_container'), 'position', 'absolute');
1496
 
            // give shadowbox_body "layout"...whatever that is
1497
 
            SL.get('shadowbox_body').style.zoom = 1;
1498
 
            // use AlphaImageLoader for transparent PNG support
1499
 
            var png = SB.SKIN.png_fix;
1500
 
            if(png && png.constructor == Array){
1501
 
                for(var i = 0; i < png.length; ++i){
1502
 
                    var el = SL.get(png[i]);
1503
 
                    if(el){
1504
 
                        var match = SL.getStyle(el, 'background-image').match(/url\("(.*\.png)"\)/);
1505
 
                        if(match){
1506
 
                            SL.setStyle(el, {
1507
 
                                backgroundImage: 'none',
1508
 
                                filter: 'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src=' + match[1] + ',sizingMethod=scale);'
1509
 
                            });
1510
 
                        }
1511
 
                    }
1512
 
                }
1513
 
            }
1514
 
        }
1515
 
 
1516
 
        // compile file type regular expressions here for speed
1517
 
        for(var e in options.ext){
1518
 
            RE[e] = new RegExp('\.(' + options.ext[e].join('|') + ')\s*$', 'i');
1519
 
        }
1520
 
 
1521
 
        // set up window resize event handler
1522
 
        var id;
1523
 
        SL.addEvent(window, 'resize', function(){
1524
 
            // use 50 ms event buffering to prevent jerky window resizing
1525
 
            if(id){
1526
 
                clearTimeout(id);
1527
 
                id = null;
1528
 
            }
1529
 
            id = setTimeout(function(){
1530
 
                if(ltIE7) fixHeight();
1531
 
                resizeContent();
1532
 
            }, 50);
1533
 
        });
1534
 
 
1535
 
        if(!options.skipSetup) SB.setup();
1536
 
        initialized = true;
1537
 
    };
1538
 
 
1539
 
    /**
1540
 
     * Dynamically loads the specified skin for use with Shadowbox. If the skin
1541
 
     * is included already in the page via the appropriate <script> and <link>
1542
 
     * tags, this function does not need to be called. Otherwise, this function
1543
 
     * must be called before window.onload.
1544
 
     *
1545
 
     * @param   String      skin        The directory where the skin is located
1546
 
     * @param   String      dir         The directory where the Shadowbox skin
1547
 
     *                                  files are located
1548
 
     * @return  void
1549
 
     * @public
1550
 
     * @static
1551
 
     */
1552
 
    Shadowbox.loadSkin = function(skin, dir){
1553
 
        if(!(/\/$/.test(dir))) dir += '/';
1554
 
        skin = dir + skin + '/';
1555
 
 
1556
 
        // Safari 2.0 fails using DOM, use document.write instead
1557
 
        document.write('<link rel="stylesheet" type="text/css" href="' + skin + 'skin.css">');
1558
 
        document.write('<scr' + 'ipt type="text/javascript" src="' + skin + 'skin.js"><\/script>');
1559
 
    };
1560
 
 
1561
 
    /**
1562
 
     * Dynamically loads the specified language file to be used with Shadowbox.
1563
 
     * If the language file is included already in the page via the appropriate
1564
 
     * <script> tag, this function does not need to be called. Otherwise, this
1565
 
     * function must be called before window.onload.
1566
 
     *
1567
 
     * @param   String      lang        The language abbreviation (e.g. en)
1568
 
     * @param   String      dir         The directory where the Shadowbox
1569
 
     *                                  language file(s) is located
1570
 
     * @return  void
1571
 
     * @public
1572
 
     * @static
1573
 
     */
1574
 
    Shadowbox.loadLanguage = function(lang, dir){
1575
 
        if(!(/\/$/.test(dir))) dir += '/';
1576
 
 
1577
 
        // Safari 2.0 fails using DOM, use document.write instead
1578
 
        document.write('<scr' + 'ipt type="text/javascript" src="' + dir + 'shadowbox-' + lang + '.js"><\/script>');
1579
 
    };
1580
 
 
1581
 
    /**
1582
 
     * Dynamically loads the specified player(s) to be used with Shadowbox. If
1583
 
     * the needed player(s) is already included in the page via the appropriate
1584
 
     * <script> tag(s), this function does not need to be called. Otherwise,
1585
 
     * this function must be called before window.onload.
1586
 
     *
1587
 
     * @param   Array       players     The player(s) to load
1588
 
     * @param   String      dir         The director where the Shadowbox player
1589
 
     *                                  file(s) is located
1590
 
     * @return  void
1591
 
     * @public
1592
 
     * @static
1593
 
     */
1594
 
    Shadowbox.loadPlayer = function(players, dir){
1595
 
        if(typeof players == 'string') players = [players];
1596
 
        if(!(/\/$/.test(dir))) dir += '/';
1597
 
 
1598
 
        for(var i = 0, len = players.length; i < len; ++i){
1599
 
            // Safari 2.0 fails using DOM, use document.write instead
1600
 
            document.write('<scr' + 'ipt type="text/javascript" src="' + dir + 'shadowbox-' + players[i] + '.js"><\/script>');
1601
 
        }
1602
 
    };
1603
 
 
1604
 
    /**
1605
 
     * Sets up listeners on the given links that will trigger Shadowbox. If no
1606
 
     * links are given, this method will set up every anchor element on the page
1607
 
     * with the appropriate rel attribute. Note: Because AREA elements do not
1608
 
     * support the rel attribute, they must be explicitly passed to this method.
1609
 
     *
1610
 
     * @param   Array       links       An array (or array-like) list of anchor
1611
 
     *                                  and/or area elements to set up
1612
 
     * @param   Object      opts        Some options to use for the given links
1613
 
     * @return  void
1614
 
     * @public
1615
 
     * @static
1616
 
     */
1617
 
    Shadowbox.setup = function(links, opts){
1618
 
        // get links if none specified
1619
 
        if(!links){
1620
 
            var links = [];
1621
 
            var a = document.getElementsByTagName('a'), rel;
1622
 
            for(var i = 0, len = a.length; i < len; ++i){
1623
 
                rel = a[i].getAttribute('rel');
1624
 
                if(rel && RE.rel.test(rel)) links[links.length] = a[i];
1625
 
            }
1626
 
        }else if(!links.length){
1627
 
            links = [links]; // one link
1628
 
        }
1629
 
 
1630
 
        var link;
1631
 
        for(var i = 0, len = links.length; i < len; ++i){
1632
 
            link = links[i];
1633
 
            if(typeof link.shadowboxCacheKey == 'undefined'){
1634
 
                // assign cache key expando
1635
 
                // use integer primitive to avoid memory leak in IE
1636
 
                link.shadowboxCacheKey = cache.length;
1637
 
                SL.addEvent(link, 'click', handleClick); // add listener
1638
 
            }
1639
 
            cache[link.shadowboxCacheKey] = this.buildCacheObj(link, opts);
1640
 
        }
1641
 
    };
1642
 
 
1643
 
    /**
1644
 
     * Builds an object from the original link element data to store in cache.
1645
 
     * These objects contain (most of) the following keys:
1646
 
     *
1647
 
     * - el: the link element
1648
 
     * - title: the linked file title
1649
 
     * - player: the player to use for the linked file
1650
 
     * - content: the linked file's URL
1651
 
     * - gallery: the gallery the file belongs to (optional)
1652
 
     * - height: the height of the linked file (only necessary for movies)
1653
 
     * - width: the width of the linked file (only necessary for movies)
1654
 
     * - options: custom options to use (optional)
1655
 
     *
1656
 
     * @param   HTMLElement     link    The link element to process
1657
 
     * @return  Object                  An object representing the link
1658
 
     * @public
1659
 
     * @static
1660
 
     */
1661
 
    Shadowbox.buildCacheObj = function(link, opts){
1662
 
        var href = link.href; // don't use getAttribute() here
1663
 
        var o = {
1664
 
            el:         link,
1665
 
            title:      link.getAttribute('title'),
1666
 
            player:     getPlayer(href),
1667
 
            options:    apply({}, opts || {}), // break the reference
1668
 
            content:    href
1669
 
        };
1670
 
 
1671
 
        // remove link-level options from top-level options
1672
 
        var opt, l_opts = ['player', 'title', 'height', 'width', 'gallery'];
1673
 
        for(var i = 0, len = l_opts.length; i < len; ++i){
1674
 
            opt = l_opts[i];
1675
 
            if(typeof o.options[opt] != 'undefined'){
1676
 
                o[opt] = o.options[opt];
1677
 
                delete o.options[opt];
1678
 
            }
1679
 
        }
1680
 
 
1681
 
        // HTML options always trump JavaScript options, so do these last
1682
 
        var rel = link.getAttribute('rel');
1683
 
        if(rel){
1684
 
            // extract gallery name from shadowbox[name] format
1685
 
            var match = rel.match(RE.gallery);
1686
 
            if(match) o.gallery = escape(match[2]);
1687
 
 
1688
 
            // other parameters
1689
 
            var params = rel.split(';');
1690
 
            for(var i = 0, len = params.length; i < len; ++i){
1691
 
                match = params[i].match(RE.param);
1692
 
                if(match){
1693
 
                    if(match[1] == 'options'){
1694
 
                        eval('apply(o.options, ' + match[2] + ')');
1695
 
                    }else{
1696
 
                        o[match[1]] = match[2];
1697
 
                    }
1698
 
                }
1699
 
            }
1700
 
        }
1701
 
 
1702
 
        return o;
1703
 
    };
1704
 
 
1705
 
    /**
1706
 
     * Applies the given set of options to those currently in use. Note: Options
1707
 
     * will be reset on Shadowbox.open() so this function is only useful after
1708
 
     * it has already been called (while Shadowbox is open).
1709
 
     *
1710
 
     * @param   Object      opts        The options to apply
1711
 
     * @return  void
1712
 
     * @public
1713
 
     * @static
1714
 
     */
1715
 
    Shadowbox.applyOptions = function(opts){
1716
 
        if(opts){
1717
 
            // use apply here to break references
1718
 
            default_options = apply({}, options); // store default options
1719
 
            options = apply(options, opts); // apply options
1720
 
        }
1721
 
    };
1722
 
 
1723
 
    /**
1724
 
     * Reverts Shadowbox' options to the last default set in use before
1725
 
     * Shadowbox.applyOptions() was called.
1726
 
     *
1727
 
     * @return  void
1728
 
     * @public
1729
 
     * @static
1730
 
     */
1731
 
    Shadowbox.revertOptions = function(){
1732
 
        if(default_options){
1733
 
            options = default_options; // revert to default options
1734
 
            default_options = null; // erase for next time
1735
 
        }
1736
 
    };
1737
 
 
1738
 
    /**
1739
 
     * Opens the given object in Shadowbox. This object may be either an
1740
 
     * anchor/area element, or an object similar to the one created by
1741
 
     * Shadowbox.buildCacheObj().
1742
 
     *
1743
 
     * @param   mixed       obj         The object or link element that defines
1744
 
     *                                  what to display
1745
 
     * @return  void
1746
 
     * @public
1747
 
     * @static
1748
 
     */
1749
 
    Shadowbox.open = function(obj, opts){
1750
 
        // revert options
1751
 
        this.revertOptions();
1752
 
 
1753
 
        // is it a link?
1754
 
        if(isLink(obj)){
1755
 
            if(typeof obj.shadowboxCacheKey == 'undefined' || typeof cache[obj.shadowboxCacheKey] == 'undefined'){
1756
 
                // link element that hasn't been set up before
1757
 
                // create on-the-fly object
1758
 
                obj = this.buildCacheObj(obj, opts);
1759
 
            }else{
1760
 
                // link element that has been set up before, get from cache
1761
 
                obj = cache[obj.shadowboxCacheKey];
1762
 
            }
1763
 
        }
1764
 
 
1765
 
        // is it already a gallery?
1766
 
        if(obj.constructor == Array){
1767
 
            gallery = obj;
1768
 
            current = 0;
1769
 
        }else{
1770
 
            // create a copy so it doesn't get modified later
1771
 
            var copy = apply({}, obj);
1772
 
 
1773
 
            // is it part of a gallery?
1774
 
            if(!obj.gallery){ // single item, no gallery
1775
 
                gallery = [copy];
1776
 
                current = 0;
1777
 
            }else{
1778
 
                current = null; // reset current
1779
 
                gallery = []; // clear the current gallery
1780
 
                var ci;
1781
 
                for(var i = 0, len = cache.length; i < len; ++i){
1782
 
                    ci = cache[i];
1783
 
                    if(ci.gallery){
1784
 
                        if(ci.content == obj.content
1785
 
                            && ci.gallery == obj.gallery
1786
 
                            && ci.title == obj.title){ // compare content, gallery, & title
1787
 
                                current = gallery.length; // key element found
1788
 
                        }
1789
 
                        if(ci.gallery == obj.gallery){
1790
 
                            gallery.push(apply({}, ci));
1791
 
                        }
1792
 
                    }
1793
 
                }
1794
 
                // if not found in cache, prepend to front of gallery
1795
 
                if(current == null){
1796
 
                    gallery.unshift(copy);
1797
 
                    current = 0;
1798
 
                }
1799
 
            }
1800
 
        }
1801
 
 
1802
 
        obj = gallery[current];
1803
 
 
1804
 
        // apply custom options
1805
 
        if(obj.options || opts){
1806
 
            // use apply here to break references
1807
 
            this.applyOptions(apply(apply({}, obj.options || {}), opts || {}));
1808
 
        }
1809
 
 
1810
 
        // filter gallery for unsupported elements
1811
 
        var match, r;
1812
 
        for(var i = 0, len = gallery.length; i < len; ++i){
1813
 
            r = false; // remove the element?
1814
 
            if(gallery[i].player == 'unsupported'){ // don't support this at all
1815
 
                r = true;
1816
 
            }else if(match = RE.unsupported.exec(gallery[i].player)){ // handle unsupported elements
1817
 
                if(options.handleUnsupported == 'link'){
1818
 
                    gallery[i].player = 'html';
1819
 
                    // generate a link to the appropriate plugin download page(s)
1820
 
                    var s, a, oe = options.errors;
1821
 
                    switch(match[1]){
1822
 
                        case 'qtwmp':
1823
 
                            s = 'either';
1824
 
                            a = [oe.qt.url, oe.qt.name, oe.wmp.url, oe.wmp.name];
1825
 
                        break;
1826
 
                        case 'qtf4m':
1827
 
                            s = 'shared';
1828
 
                            a = [oe.qt.url, oe.qt.name, oe.f4m.url, oe.f4m.name];
1829
 
                        break;
1830
 
                        default:
1831
 
                            s = 'single';
1832
 
                            if(match[1] == 'swf' || match[1] == 'flv') match[1] = 'fla';
1833
 
                            a = [oe[match[1]].url, oe[match[1]].name];
1834
 
                    }
1835
 
                    var msg = SB.LANG.errors[s].replace(/\{(\d+)\}/g, function(m, i){
1836
 
                        return a[i];
1837
 
                    });
1838
 
                    gallery[i].content = '<div class="shadowbox_message">' + msg + '</div>';
1839
 
                }else{
1840
 
                    r = true;
1841
 
                }
1842
 
            }else if(gallery[i].player == 'inline'){ // handle inline elements
1843
 
                // retrieve the innerHTML of the inline element
1844
 
                var match = RE.inline.exec(gallery[i].content);
1845
 
                if(match){
1846
 
                    var el;
1847
 
                    if(el = SL.get(match[1])){
1848
 
                        gallery[i].content = el.innerHTML;
1849
 
                    }else{
1850
 
                        SB.raise('Cannot find element with id ' + match[1]);
1851
 
                    }
1852
 
                }else{
1853
 
                    SB.raise('Cannot find element id for inline content');
1854
 
                }
1855
 
            }
1856
 
            if(r){
1857
 
                gallery.splice(i, 1); // remove the element from the gallery
1858
 
                if(i < current){
1859
 
                    --current;
1860
 
                }else if(i == current){
1861
 
                    // if current is unsupported, look for supported neighbor
1862
 
                    current = i > 0 ? current - 1 : i;
1863
 
                }
1864
 
                --i; // decrement to account for splice
1865
 
                len = gallery.length; // gallery.length has changed!
1866
 
            }
1867
 
        }
1868
 
 
1869
 
        // anything left?
1870
 
        if(gallery.length){
1871
 
            // fire onOpen hook
1872
 
            if(options.onOpen && typeof options.onOpen == 'function'){
1873
 
                options.onOpen(obj);
1874
 
            }
1875
 
 
1876
 
            if(!activated){
1877
 
                // set initial dimensions & load
1878
 
                setDimensions(options.initialHeight, options.initialWidth);
1879
 
                adjustHeight(dims.inner_h, dims.top, false);
1880
 
                adjustWidth(dims.width, false);
1881
 
                toggleVisible(loadContent);
1882
 
            } else {
1883
 
                loadContent();
1884
 
            }
1885
 
 
1886
 
            activated = true;
1887
 
        }
1888
 
    };
1889
 
 
1890
 
    /**
1891
 
     * Jumps to the piece in the current gallery with index num.
1892
 
     *
1893
 
     * @param   Number      num     The gallery index to view
1894
 
     * @return  void
1895
 
     * @public
1896
 
     * @static
1897
 
     */
1898
 
    Shadowbox.change = function(num){
1899
 
        if(!gallery) return; // no current gallery
1900
 
        if(!gallery[num]){ // index does not exist
1901
 
            if(!options.continuous){
1902
 
                return;
1903
 
            }else{
1904
 
                num = num < 0 ? (gallery.length - 1) : 0; // loop
1905
 
            }
1906
 
        }
1907
 
 
1908
 
        if(typeof slide_timer == 'number'){
1909
 
            clearTimeout(slide_timer);
1910
 
            slide_timer = null;
1911
 
            slide_delay = slide_start = 0; // reset slideshow variables
1912
 
        }
1913
 
        current = num; // update current
1914
 
 
1915
 
        if(options.onChange && typeof options.onChange == 'function'){
1916
 
            options.onChange(gallery[current]); // fire onChange handler
1917
 
        }
1918
 
 
1919
 
        loadContent();
1920
 
    };
1921
 
 
1922
 
    /**
1923
 
     * Jumps to the next piece in the gallery.
1924
 
     *
1925
 
     * @return  void
1926
 
     * @public
1927
 
     * @static
1928
 
     */
1929
 
    Shadowbox.next = function(){
1930
 
        this.change(current + 1);
1931
 
    };
1932
 
 
1933
 
    /**
1934
 
     * Jumps to the previous piece in the gallery.
1935
 
     *
1936
 
     * @return  void
1937
 
     * @public
1938
 
     * @static
1939
 
     */
1940
 
    Shadowbox.previous = function(){
1941
 
        this.change(current - 1);
1942
 
    };
1943
 
 
1944
 
    /**
1945
 
     * Sets the timer for the next image in the slideshow to be displayed.
1946
 
     *
1947
 
     * @return  void
1948
 
     * @public
1949
 
     * @static
1950
 
     */
1951
 
    Shadowbox.play = function(){
1952
 
        if(!hasNext()) return;
1953
 
        if(!slide_delay) slide_delay = options.slideshowDelay * 1000;
1954
 
        if(slide_delay){
1955
 
            slide_start = new Date().getTime();
1956
 
            slide_timer = setTimeout(function(){
1957
 
                slide_delay = slide_start = 0; // reset slideshow
1958
 
                SB.next();
1959
 
            }, slide_delay);
1960
 
 
1961
 
            // change play nav to pause
1962
 
            toggleNav('play', false);
1963
 
            toggleNav('pause', true);
1964
 
        }
1965
 
    };
1966
 
 
1967
 
    /**
1968
 
     * Pauses the current slideshow.
1969
 
     *
1970
 
     * @return  void
1971
 
     * @public
1972
 
     * @static
1973
 
     */
1974
 
    Shadowbox.pause = function(){
1975
 
        if(typeof slide_timer == 'number'){
1976
 
            var time = new Date().getTime();
1977
 
            slide_delay = Math.max(0, slide_delay - (time - slide_start));
1978
 
 
1979
 
            // any delay left on current slide? if so, stop the timer
1980
 
            if(slide_delay){
1981
 
                clearTimeout(slide_timer);
1982
 
                slide_timer = 'paused';
1983
 
            }
1984
 
 
1985
 
            // change pause nav to play
1986
 
            toggleNav('pause', false);
1987
 
            toggleNav('play', true);
1988
 
        }
1989
 
    };
1990
 
 
1991
 
    /**
1992
 
     * Deactivates Shadowbox.
1993
 
     *
1994
 
     * @return  void
1995
 
     * @public
1996
 
     * @static
1997
 
     */
1998
 
    Shadowbox.close = function(){
1999
 
        if(!activated) return; // already closed
2000
 
 
2001
 
        // stop listening for keys
2002
 
        listenKeys(false);
2003
 
        // hide
2004
 
        toggleVisible(false);
2005
 
        // remove the content
2006
 
        if(content){
2007
 
            content.remove();
2008
 
            content = null;
2009
 
        }
2010
 
 
2011
 
        // clear slideshow variables
2012
 
        if(typeof slide_timer == 'number') clearTimeout(slide_timer);
2013
 
        slide_timer = null;
2014
 
        slide_delay = 0;
2015
 
 
2016
 
        // fire onClose handler
2017
 
        if(options.onClose && typeof options.onClose == 'function'){
2018
 
            options.onClose(gallery[current]);
2019
 
        }
2020
 
 
2021
 
        activated = false;
2022
 
    };
2023
 
 
2024
 
    /**
2025
 
     * Clears Shadowbox' cache and removes listeners and expandos from all
2026
 
     * cached link elements. May be used to completely reset Shadowbox in case
2027
 
     * links on a page change.
2028
 
     *
2029
 
     * @return  void
2030
 
     * @public
2031
 
     * @static
2032
 
     */
2033
 
    Shadowbox.clearCache = function(){
2034
 
        for(var i = 0, len = cache.length; i < len; ++i){
2035
 
            if(cache[i].el){
2036
 
                SL.removeEvent(cache[i].el, 'click', handleClick);
2037
 
                delete cache[i].el.shadowboxCacheKey; // remove expando
2038
 
            }
2039
 
        }
2040
 
        cache = [];
2041
 
    };
2042
 
 
2043
 
    /**
2044
 
     * Gets an object that lists which plugins are supported by the client. The
2045
 
     * keys of this object will be:
2046
 
     *
2047
 
     * - fla: Adobe Flash Player
2048
 
     * - qt: QuickTime Player
2049
 
     * - wmp: Windows Media Player
2050
 
     * - f4m: Flip4Mac QuickTime Player
2051
 
     *
2052
 
     * @return  Object          The plugins object
2053
 
     * @public
2054
 
     * @static
2055
 
     */
2056
 
    Shadowbox.getPlugins = function(){
2057
 
        return plugins;
2058
 
    };
2059
 
 
2060
 
    /**
2061
 
     * Gets the current options object in use.
2062
 
     *
2063
 
     * @return  Object          The options object
2064
 
     * @public
2065
 
     * @static
2066
 
     */
2067
 
    Shadowbox.getOptions = function(){
2068
 
        return options;
2069
 
    };
2070
 
 
2071
 
    /**
2072
 
     * Gets the current gallery object.
2073
 
     *
2074
 
     * @return  Object          The current gallery item
2075
 
     * @public
2076
 
     * @static
2077
 
     */
2078
 
    Shadowbox.getCurrent = function(){
2079
 
        return gallery[current];
2080
 
    };
2081
 
 
2082
 
    /**
2083
 
     * Gets the current version number of Shadowbox.
2084
 
     *
2085
 
     * @return  String          The current version
2086
 
     * @public
2087
 
     * @static
2088
 
     */
2089
 
    Shadowbox.getVersion = function(){
2090
 
        return version;
2091
 
    };
2092
 
 
2093
 
    /**
2094
 
     * Returns an object containing information about the current client
2095
 
     * configuration.
2096
 
     *
2097
 
     * @return  Object          The object containing client data
2098
 
     * @public
2099
 
     * @static
2100
 
     */
2101
 
    Shadowbox.getClient = function(){
2102
 
        return client;
2103
 
    };
2104
 
 
2105
 
    /**
2106
 
     * Returns the current content object in use.
2107
 
     *
2108
 
     * @return  Object          The current content object
2109
 
     * @public
2110
 
     * @static
2111
 
     */
2112
 
    Shadowbox.getContent = function(){
2113
 
        return content;
2114
 
    };
2115
 
 
2116
 
    /**
2117
 
     * Gets the current dimensions of Shadowbox as calculated by
2118
 
     * setDimensions().
2119
 
     *
2120
 
     * @return  Object          The current dimensions of Shadowbox
2121
 
     * @public
2122
 
     * @static
2123
 
     */
2124
 
    Shadowbox.getDimensions = function(){
2125
 
        return dims;
2126
 
    };
2127
 
 
2128
 
    /**
2129
 
     * Handles all Shadowbox exceptions (errors). Calls the exception
2130
 
     * handler callback if one is present (see handleException option) or
2131
 
     * throws a new exception.
2132
 
     *
2133
 
     * @param   String      e       The error message
2134
 
     * @return  void
2135
 
     * @public
2136
 
     * @static
2137
 
     */
2138
 
    Shadowbox.raise = function(e){
2139
 
        if(typeof options.handleException == 'function'){
2140
 
            options.handleException(e);
2141
 
        }else{
2142
 
            throw e;
2143
 
        }
2144
 
    };
2145
 
 
2146
 
})();
 
 
b'\\ No newline at end of file'