1/** 2 * A media-viewer script for web pages that allows content to be viewed without 3 * navigating away from the original linking page. 4 * 5 * This file is part of Shadowbox. 6 * 7 * Shadowbox is free software: you can redistribute it and/or modify it under 8 * the terms of the GNU Lesser General Public License as published by the Free 9 * Software Foundation, either version 3 of the License, or (at your option) 10 * any later version. 11 * 12 * Shadowbox is distributed in the hope that it will be useful, but WITHOUT ANY 13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with Shadowbox. If not, see <http://www.gnu.org/licenses/>. 19 * 20 * @author Michael J. I. Jackson <[email protected]> 21 * @copyright 2007 Michael J. I. Jackson 22 * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0 23 * @version SVN: $Id: shadowbox.js 75 2008-02-21 16:51:29Z mjijackson $ 24 */ 25 26if(typeof Shadowbox == 'undefined'){ 27 throw 'Unable to load Shadowbox, no base library adapter found.'; 28} 29 30/** 31 * The Shadowbox class. Used to display different media on a web page using a 32 * Lightbox-like effect. 33 * 34 * Useful resources: 35 * - http://www.alistapart.com/articles/byebyeembed 36 * - http://www.w3.org/TR/html401/struct/objects.html 37 * - http://www.dyn-web.com/dhtml/iframes/ 38 * - http://support.microsoft.com/kb/316992 39 * - http://www.apple.com/quicktime/player/specs.html 40 * - http://www.howtocreate.co.uk/wrongWithIE/?chapter=navigator.plugins 41 * 42 * @class Shadowbox 43 * @author Michael J. I. Jackson <[email protected]> 44 * @singleton 45 */ 46(function(){ 47 48 /** 49 * The current version of Shadowbox. 50 *
51 * @property {String} version 52 * @private 53 */ 54 var version = '1.0'; 55 56 /** 57 * Contains the default options for Shadowbox. This object is almost 58 * entirely customizable. 59 * 60 * @property {Object} options 61 * @private 62 */ 63 var options = { 64 65 /** 66 * A base URL that will be prepended to the loadingImage, flvPlayer, and 67 * overlayBgImage options to save on typing. 68 * 69 * @var {String} assetURL 70 */ 71 assetURL: '', 72 73 /** 74 * The path to the image to display while loading. 75 * 76 * @var {String} loadingImage 77 */ 78 loadingImage: 'images/loading2.gif', 79 80 /** 81 * Enable animations. 82 * 83 * @var {Boolean} animate 84 */ 85 animate: true, 86 87 /** 88 * Specifies the sequence of the height and width animations. May be 89 * 'wh' (width then height), 'hw' (height then width), or 'sync' (both 90 * at the same time). Of course this will only work if animate is true. 91 * 92 * @var {String} animSequence 93 */ 94 animSequence: 'wh', 95 96 /** 97 * The path to flvplayer.swf. 98 * 99 * @var {String} flvPlayer 100 */ 101 flvPlayer: 'flvplayer.swf', 102 103 /** 104 * The background color and opacity of the overlay. Note: When viewing 105 * movie files on FF Mac, the default background image will be used 106 * because that browser has problems displaying movies above layers 107 * that aren't 100% opaque. 108 * 109 * @var {String} overlayColor 110 */ 111 overlayColor: '#000000', 112 113 /** 114 * The background opacity to use for the overlay. 115 * 116 * @var {Number} overlayOpacity 117 */ 118 overlayOpacity: 0.50, 119 120 /** 121 * A background image to use for browsers such as FF Mac that don't 122 * support displaying movie content over backgrounds that aren't 100% 123 * opaque. 124 * 125 * @var {String} overlayBgImage 126 */ 127 overlayBgImage: 'images/overlay-85.png', 128 129 /** 130 * Listen to the overlay for clicks. If the user clicks the overlay, 131 * it will trigger Shadowbox.close(). 132 * 133 * @var {Boolean} listenOverlay 134 */ 135 listenOverlay: true, 136 137 /** 138 * Automatically play movies. 139 * 140 * @var {Boolean} autoplayMovies 141 */ 142 autoplayMovies: true, 143 144 /** 145 * Enable movie controllers on movie players. 146 * 147 * @var {Boolean} showMovieControls 148 */ 149 showMovieControls: true, 150 151 /** 152 * The duration of the resizing animations (in seconds). 153 * 154 * @var {Number} resizeDuration 155 */ 156 resizeDuration: 0.35, 157 158 /** 159 * The duration of the overlay fade animation (in seconds). 160 * 161 * @var {Number} fadeDuration 162 */ 163 fadeDuration: 0.35, 164 165 /** 166 * Show the navigation controls. 167 * 168 * @var {Boolean} displayNav 169 */ 170 displayNav: true, 171 172 /** 173 * Enable continuous galleries. When this is true, users will be able 174 * to skip to the first gallery image from the last using next and vice 175 * versa. 176 * 177 * @var {Boolean} continuous 178 */ 179 continuous: false, 180 181 /** 182 * Display the gallery counter. 183 * 184 * @var {Boolean} displayCounter 185 */ 186 displayCounter: true, 187 188 /** 189 * This option may be either 'default' or 'skip'. The default counter is 190 * a simple '1 of 5' message. The skip counter displays a link for each 191 * piece in the gallery that enables a user to skip directly to any 192 * piece. 193 * 194 * @var {String} counterType 195 */ 196 counterType: 'default', 197 198 /** 199 * The amount of padding to maintain around the viewport edge (in 200 * pixels). This only applies when the image is very large and takes up 201 * the entire viewport. 202 * 203 * @var {Number} viewportPadding 204 */ 205 viewportPadding: 1, 206 207 /** 208 * How to handle images that are too large for the viewport. 'resize' 209 * will resize the image while preserving aspect ratio and display it at 210 * the smaller resolution. 'drag' will display the image at its native 211 * resolution but it will be draggable within the Shadowbox. 'none' will 212 * display the image at its native resolution but it may be cropped. 213 * 214 * @var {String} handleLgImages 215 */ 216 handleLgImages: 'resize',
217 218 /** 219 * The initial height of Shadowbox (in pixels). 220 * 221 * @var {Number} initialHeight 222 */ 223 initialHeight: 160, 224 225 /** 226 * The initial width of Shadowbox (in pixels). 227 * 228 * @var {Number} initialWidth 229 */ 230 initialWidth: 320, 231 232 /** 233 * Enable keyboard control. Note: If you disable the keys, you may want 234 * to change the visual styles for the navigation elements that suggest 235 * keyboard shortcuts. 236 * 237 * @var {Boolean} enableKeys 238 */ 239 enableKeys: true, 240 241 /** 242 * The keys used to control Shadowbox. Note: In order to use these, 243 * enableKeys must be true. Key values or key codes may be used. 244 * 245 * @var {Array} 246 */ 247 keysClose: [27], // c, q, or esc 248 keysNext: ['n', 39], // n or right arrow 249 keysPrev: ['p', 37], // p or left arrow 250 251 /** 252 * A hook function to be fired when Shadowbox opens. The single argument 253 * will be the current gallery element. 254 * 255 * @var {Function} 256 */ 257 onOpen: null, 258 259 /** 260 * A hook function to be fired when Shadowbox finishes loading its 261 * content. The single argument will be the current gallery element on 262 * display. 263 * 264 * @var {Function} 265 */ 266 onFinish: null, 267 268 /** 269 * A hook function to be fired when Shadowbox changes from one gallery 270 * element to the next. The single argument will be the current gallery 271 * element that is about to be displayed. 272 * 273 * @var {Function} 274 */ 275 onChange: null, 276 277 /** 278 * A hook function that will be fired when Shadowbox closes. The single 279 * argument will be the gallery element most recently displayed. 280 * 281 * @var {Function} 282 */ 283 onClose: null, 284 285 /** 286 * The mode to use when handling unsupported media. May be either 287 * 'remove' or 'link'. If it is 'remove', the unsupported gallery item 288 * will merely be removed from the gallery. If it is the only item in 289 * the gallery, the link will simply be followed. If it is 'link', a 290 * link will be provided to the appropriate plugin page in place of the 291 * gallery element. 292 * 293 * @var {String} handleUnsupported 294 */ 295 handleUnsupported: 'link', 296 297 /** 298 * Skips calling Shadowbox.setup() in init(). This means that it must 299 * be called later manually. 300 * 301 * @var {Boolean} skipSetup 302 */ 303 skipSetup: false, 304 305 /** 306 * Text messages to use for Shadowbox. These are provided so they may be 307 * translated into different languages. 308 * 309 * @var {Object} text 310 */ 311 text: { 312 313 cancel: 'Annulla', 314 315 loading: 'loading', 316 317 close: '<span class="shortcut" style="color:#ffffff; font-size:15px;">C</span><span style="color:#ffffff; font-size:15px;">hiudi</span>', 318 319 next: '<span class="shortcut" style="color:#ffffff">S</span><span style="color:#ffffff">uccessiva</span>', 320 321 prev: '<span class="shortcut" style="color:#ffffff">P</span><span style="color:#ffffff">recedente</span>', 322 323 errors: { 324 single: 'You must install the <a href="{0}">{1}</a> browser plugin to view this content.', 325 shared: 'You must install both the <a href="{0}">{1}</a> and <a href="{2}">{3}</a> browser plugins to view this content.', 326 either: 'You must install either the <a href="{0}">{1}</a> or the <a href="{2}">{3}</a> browser plugin to view this content.' 327 } 328 329 }, 330 331 /**
332 * An object containing names of plugins and links to their respective 333 * download pages. 334 * 335 * @var {Object} errors 336 */ 337 errors: { 338 339 fla: { 340 name: 'Flash', 341 url: 'http://www.adobe.com/products/flashplayer/' 342 }, 343 344 qt: { 345 name: 'QuickTime', 346 url: 'http://www.apple.com/quicktime/download/' 347 }, 348 349 wmp: { 350 name: 'Windows Media Player', 351 url: 'http://www.microsoft.com/windows/windowsmedia/' 352 }, 353 354 f4m: { 355 name: 'Flip4Mac', 356 url: 'http://www.flip4mac.com/wmv_download.htm' 357 } 358 359 }, 360 361 /** 362 * The HTML markup to use for Shadowbox. Note: The script depends on 363 * most of these elements being present, so don't modify this variable 364 * unless you know what you're doing. 365 * 366 * @var {Object} skin 367 */ 368 skin: { 369 370 main: '<div id="shadowbox_overlay"></div>' + 371 '<div id="shadowbox_container">' + 372 '<div id="shadowbox">' + 373 '<div id="shadowbox_title">' + 374 '<div id="shadowbox_title_inner"></div>' + 375 '</div>' + 376 '<div id="shadowbox_body">' + 377 '<div id="shadowbox_body_inner"></div>' + 378 '<div id="shadowbox_loading"></div>' + 379 '</div>' + 380 '<div id="shadowbox_toolbar">' + 381 '<div id="shadowbox_toolbar_inner"></div>' + 382 '</div>' + 383 '</div>' + 384 '</div>', 385 386 loading: '<img src="{0}" alt="{1}" />' + 387 '<span><a href="javascript:Shadowbox.close();">{2}</a></span>', 388 389 counter: '<div id="shadowbox_counter">{0}</div>', 390 391 close: '<div id="shadowbox_nav_close">' + 392 '<a href="javascript:Shadowbox.close();">{0}</a>' + 393 '</div>', 394 395 next: '<div id="shadowbox_nav_next">' + 396 '<a href="javascript:Shadowbox.next();">{0}</a>' + 397 '</div>', 398 399 prev: '<div id="shadowbox_nav_previous">' + 400 '<a href="javascript:Shadowbox.previous();">{0}</a>' + 401 '</div>' 402 403 }, 404 405 /** 406 * An object containing arrays of all supported file extensions. Each
407 * property of this object contains an array. If this object is to be 408 * modified, it must be done before calling init(). 409 * 410 * - img: Supported image file extensions 411 * - qt: Movie file extensions supported by QuickTime 412 * - wmp: Movie file extensions supported by Windows Media Player 413 * - qtwmp: Movie file extensions supported by both QuickTime and Windows Media Player 414 * - iframe: File extensions that will be display in an iframe 415 * 416 * @var {Object} ext 417 */ 418 ext: { 419 img: ['png', 'jpg', 'jpeg', 'gif', 'bmp'], 420 qt: ['dv', 'mov', 'moov', 'movie', 'mp4'], 421 wmp: ['asf', 'wm', 'wmv'], 422 qtwmp: ['avi', 'mpg', 'mpeg'], 423 iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'pl', 'php', 'pdf', 424 'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml', 'jsp', 425 'txt', 'vbs'] 426 } 427 428 }; 429 430 /** 431 * Stores the default set of options in case a custom set of options is used 432 * on a link-by-link basis so we can restore them later. 433 * 434 * @property {Object} default_options 435 * @private 436 */ 437 var default_options = null; 438 439 /** 440 * Shorthand for Shadowbox.lib. 441 * 442 * @property {Object} SL 443 * @private 444 */ 445 var SL = Shadowbox.lib; 446 447 /** 448 * An object containing some regular expressions we'll need later. Compiled 449 * up front for speed. 450 * 451 * @property {Object} RE 452 * @private 453 */ 454 var RE = { 455 resize: /(img|swf|flv)/, // file types to resize 456 overlay: /(img|iframe|html|inline)/, // content types to not use an overlay image for on FF Mac 457 swf: /\.swf\s*$/i, // swf file extension 458 flv: /\.flv\s*$/i, // flv file extension 459 domain: /:\/\/(.*?)[:\/]/, // domain prefix 460 inline: /#(.+)$/, // inline element id 461 rel: /^(light|shadow)box/i, // rel attribute format 462 gallery: /^(light|shadow)box\[(.*?)\]/i, // rel attribute format for gallery link 463 unsupported: /^unsupported-(\w+)/, // unsupported media type 464 param: /\s*([a-z_]*?)\s*=\s*(.+)\s*/, // rel string parameter 465 empty: /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i // elements that don't have children 466 }; 467 468 /** 469 * A cache of options for links that have been set up for use with 470 * Shadowbox. 471 * 472 * @property {Array} cache 473 * @private 474 */ 475 var cache = []; 476 477 /** 478 * An array of pieces currently being viewed. In the case of non-gallery 479 * pieces, this will only hold one object. 480 * 481 * @property {Array} current_gallery 482 * @private 483 */ 484 var current_gallery; 485 486 /** 487 * The array index of the current_gallery that is currently being viewed. 488 * 489 * @property {Number} current 490 * @private 491 */ 492 var current; 493 494 /** 495 * Keeps track of the current optimal height of the box. We use this so that 496 * if the user resizes the browser window to get a better view, and we're 497 * currently at a size smaller than the optimal, we can resize easily. 498 * 499 * @see resizeContent() 500 * @property {Number} optimal_height 501 * @private 502 */ 503 var optimal_height = options.initialHeight; 504 505 /** 506 * Keeps track of the current optimal width of the box. See optimal_height 507 * explanation (above). 508 * 509 * @property {Number} optimal_width 510 * @private 511 */ 512 var optimal_width = options.initialWidth; 513 514 /** 515 * Keeps track of the current height of the box. This is useful in drag 516 * calculations. 517 * 518 * @property {Number} current_height 519 * @private 520 */ 521 var current_height = 0; 522 523 /** 524 * Keeps track of the current width of the box. Useful in drag calculations. 525 * 526 * @property {Number} current_width 527 * @private 528 */ 529 var current_width = 0; 530 531 /** 532 * Resource used to preload images. It's class-level so that when a new 533 * image is requested, the same resource can be reassigned, cancelling 534 * the original's callback. 535 * 536 * @property {HTMLElement} preloader 537 * @private 538 */ 539 var preloader; 540 541 /** 542 * Keeps track of whether or not Shadowbox has been initialized. We never 543 * want to initialize twice. 544 * 545 * @property {Boolean} initialized 546 * @private 547 */ 548 var initialized = false; 549 550 /** 551 * Keeps track of whether or not Shadowbox is activated. 552 * 553 * @property {Boolean} activated 554 * @private 555 */ 556 var activated = false; 557 558 /** 559 * Keeps track of 4 floating values (x, y, start_x, & start_y) that are used 560 * in the drag calculations. 561 * 562 * @property {Object} drag 563 * @private 564 */ 565 var drag; 566 567 /** 568 * Holds the draggable element so we don't have to fetch it every time 569 * the mouse moves. 570 * 571 * @property {HTMLElement} draggable 572 * @private 573 */ 574 var draggable; 575 576 /** 577 * Keeps track of whether or not we're currently using the overlay 578 * background image to display the current gallery. We do this because we 579 * use different methods for fading the overlay in and out. The color fill 580 * overlay fades in and out nicely, but the image overlay stutters. By 581 * keeping track of the type of overlay in use, we don't have to check again 582 * what type of overlay we're using when it's time to get rid of it later. 583 * 584 * @property {Boolean} overlay_img_needed 585 * @private 586 */ 587 var overlay_img_needed; 588 589 /** 590 * These parameters for simple browser detection. Used in Ext.js. 591 * 592 * @ignore 593 */ 594 var ua = navigator.userAgent.toLowerCase(); 595 var isStrict = document.compatMode == 'CSS1Compat', 596 isOpera = ua.indexOf("opera") > -1, 597 isIE = ua.indexOf('msie') > -1, 598 isIE7 = ua.indexOf('msie 7') > -1, 599 isBorderBox = isIE && !isStrict, 600 isSafari = (/webkit|khtml/).test(ua), 601 isSafari3 = isSafari && !!(document.evaluate), 602 isGecko = !isSafari && ua.indexOf('gecko') > -1, 603 isWindows = (ua.indexOf('windows') != -1 || ua.indexOf('win32') != -1), 604 isMac = (ua.indexOf('macintosh') != -1 || ua.indexOf('mac os x') != -1), 605 isLinux = (ua.indexOf('linux') != -1); 606 607 /** 608 * Do we need to hack the position to make Shadowbox appear fixed? We could 609 * hack this using CSS, but let's just get over all the hacks and let IE6 610 * users get what they deserve! Down with hacks! Hmm...now that I think 611 * about it, I should just flash all kinds of alerts and annoying popups on 612 * their screens, and then redirect them to some foreign spyware site that 613 * will upload a nasty virus... 614 * 615 * @property {Boolean} absolute_pos 616 * @private 617 */ 618 var absolute_pos = isIE && !isIE7; 619 620 /** 621 * Contains plugin support information. Each property of this object is a 622 * boolean indicating whether that plugin is supported. 623 * 624 * - fla: Flash player 625 * - qt: QuickTime player 626 * - wmp: Windows Media player 627 * - f4m: Flip4Mac plugin 628 * 629 * @property {Object} plugins 630 * @private 631 */ 632 var plugins = null; 633 634 // detect plugin support 635 if(navigator.plugins && navigator.plugins.length){ 636 var detectPlugin = function(plugin_name){ 637 var detected = false; 638 for (var i = 0, len = navigator.plugins.length; i < len; ++i){ 639 if(navigator.plugins[i].name.indexOf(plugin_name) > -1){ 640 detected = true; 641 break; 642 } 643 } 644 return detected; 645 }; 646 var f4m = detectPlugin('Flip4Mac'); 647 var plugins = { 648 fla: detectPlugin('Shockwave Flash'), 649 qt: detectPlugin('QuickTime'), 650 wmp: !f4m && detectPlugin('Windows Media'), // if it's Flip4Mac, it's not really WMP 651 f4m: f4m 652 }; 653 }else{ 654 var detectPlugin = function(plugin_name){ 655 var detected = false; 656 try { 657 var axo = new ActiveXObject(plugin_name); 658 if(axo){ 659 detected = true; 660 } 661 } catch (e) {} 662 return detected; 663 }; 664 var plugins = { 665 fla: detectPlugin('ShockwaveFlash.ShockwaveFlash'), 666 qt: detectPlugin('QuickTime.QuickTime'), 667 wmp: detectPlugin('wmplayer.ocx'), 668 f4m: false 669 }; 670 } 671 672 /** 673 * Applies all properties of e to o. This function is recursive so that if 674 * any properties of e are themselves objects, those objects will be applied 675 * to objects with the same key that may exist in o. 676 * 677 * @param {Object} o The original object 678 * @param {Object} e The extension object 679 * @return {Object} The original object with all properties 680 * of the extension object applied (deep) 681 * @private 682 */ 683 var apply = function(o, e){ 684 for(var p in e) o[p] = e[p]; 685 return o; 686 }; 687 688 /** 689 * Determines if the given object is an anchor/area element. 690 * 691 * @param {mixed} el The object to check 692 * @return {Boolean} True if the object is a link element 693 * @private 694 */ 695 var isLink = function(el){ 696 return typeof el.tagName == 'string' && (el.tagName.toUpperCase() == 'A' || el.tagName.toUpperCase() == 'AREA'); 697 }; 698 699 /** 700 * Gets the height of the viewport in pixels. Note: This function includes 701 * scrollbars in Safari 3. 702 * 703 * @return {Number} The height of the viewport 704 * @public 705 * @static 706 */
707 SL.getViewportHeight = function(){ 708 var height = window.innerHeight; // Safari 709 var mode = document.compatMode; 710 if((mode || isIE) && !isOpera){ 711 height = isStrict ? document.documentElement.clientHeight : document.body.clientHeight; 712 } 713 return height; 714 }; 715 716 /** 717 * Gets the width of the viewport in pixels. Note: This function includes 718 * scrollbars in Safari 3. 719 * 720 * @return {Number} The width of the viewport 721 * @public 722 * @static 723 */ 724 SL.getViewportWidth = function(){ 725 var width = window.innerWidth; // Safari 726 var mode = document.compatMode; 727 if(mode || isIE){ 728 width = isStrict ? document.documentElement.clientWidth : document.body.clientWidth; 729 } 730 return width; 731 }; 732 733 /** 734 * Gets the height of the document (body and its margins) in pixels. 735 * 736 * @return {Number} The height of the document 737 * @public 738 * @static 739 */ 740 SL.getDocumentHeight = function(){ 741 var scrollHeight = isStrict ? document.documentElement.scrollHeight : document.body.scrollHeight; 742 return Math.max(scrollHeight, SL.getViewportHeight()); 743 }; 744 745 /** 746 * Gets the width of the document (body and its margins) in pixels. 747 * 748 * @return {Number} The width of the document 749 * @public 750 * @static 751 */ 752 SL.getDocumentWidth = function(){ 753 var scrollWidth = isStrict ? document.documentElement.scrollWidth : document.body.scrollWidth; 754 return Math.max(scrollWidth, SL.getViewportWidth()); 755 }; 756 757 /** 758 * A utility function used by the fade functions to clear the opacity 759 * style setting of the given element. Required in some cases for IE. 760 * Based on Ext.Element's clearOpacity. 761 * 762 * @param {HTMLElement} el The DOM element 763 * @return void 764 * @private 765 */ 766 var clearOpacity = function(el){ 767 if(isIE){ 768 if(typeof el.style.filter == 'string' && (/alpha/i).test(el.style.filter)){ 769 el.style.filter = ''; 770 } 771 }else{ 772 el.style.opacity = ''; 773 el.style['-moz-opacity'] = ''; 774 el.style['-khtml-opacity'] = ''; 775 } 776 }; 777 778 /** 779 * Fades the given element from 0 to the specified opacity. 780 * 781 * @param {HTMLElement} el The DOM element to fade 782 * @param {Number} endingOpacity The final opacity to animate to 783 * @param {Number} duration The duration of the animation 784 * (in seconds) 785 * @param {Function} callback A callback function to call 786 * when the animation completes 787 * @return void 788 * @private 789 */ 790 var fadeIn = function(el, endingOpacity, duration, callback){ 791 if(options.animate){ 792 SL.setStyle(el, 'opacity', 0); 793 el.style.visibility = 'visible'; 794 SL.animate(el, { 795 opacity: { to: endingOpacity } 796 }, duration, function(){ 797 if(endingOpacity == 1) clearOpacity(el); 798 if(typeof callback == 'function') callback(); 799 }); 800 }else{ 801 if(endingOpacity == 1){ 802 clearOpacity(el); 803 }else{ 804 SL.setStyle(el, 'opacity', endingOpacity); 805 } 806 el.style.visibility = 'visible'; 807 if(typeof callback == 'function') callback(); 808 } 809 }; 810 811 /** 812 * Fades the given element from its current opacity to 0. 813 * 814 * @param {HTMLElement} el The DOM element to fade 815 * @param {Number} duration The duration of the fade animation 816 * @param {Function} callback A callback function to call when 817 * the animation completes 818 * @return void 819 * @private 820 */ 821 var fadeOut = function(el, duration, callback){ 822 var cb = function(){ 823 el.style.visibility = 'hidden'; 824 clearOpacity(el); 825 if(typeof callback == 'function') callback(); 826 }; 827 if(options.animate){ 828 SL.animate(el, { 829 opacity: { to: 0 } 830 }, duration, cb); 831 }else{ 832 cb(); 833 } 834 }; 835 836 /** 837 * Appends an HTML fragment to the given element. 838 *
839 * @param {String/HTMLElement} el The element to append to 840 * @param {String} html The HTML fragment to use 841 * @return {HTMLElement} The newly appended element 842 * @private 843 */ 844 var appendHTML = function(el, html){ 845 el = SL.get(el); 846 if(el.insertAdjacentHTML){ 847 el.insertAdjacentHTML('BeforeEnd', html); 848 return el.lastChild; 849 } 850 if(el.lastChild){ 851 var range = el.ownerDocument.createRange(); 852 range.setStartAfter(el.lastChild); 853 var frag = range.createContextualFragment(html); 854 el.appendChild(frag); 855 return el.lastChild; 856 }else{ 857 el.innerHTML = html; 858 return el.lastChild; 859 } 860 }; 861 862 /** 863 * Overwrites the HTML of the given element. 864 * 865 * @param {String/HTMLElement} el The element to overwrite 866 * @param {String} html The new HTML to use 867 * @return {HTMLElement} The new firstChild element 868 * @private 869 */ 870 var overwriteHTML = function(el, html){ 871 el = SL.get(el); 872 el.innerHTML = html; 873 return el.firstChild; 874 }; 875 876 /** 877 * Gets either the offsetHeight or the height of the given element plus 878 * padding and borders (when offsetHeight is not available). Based on 879 * Ext.Element's getComputedHeight. 880 * 881 * @return {Number} The computed height of the element 882 * @private 883 */ 884 var getComputedHeight = function(el){ 885 var h = Math.max(el.offsetHeight, el.clientHeight); 886 if(!h){ 887 h = parseInt(SL.getStyle(el, 'height'), 10) || 0; 888 if(!isBorderBox){ 889 h += parseInt(SL.getStyle(el, 'padding-top'), 10) 890 + parseInt(SL.getStyle(el, 'padding-bottom'), 10) 891 + parseInt(SL.getStyle(el, 'border-top-width'), 10) 892 + parseInt(SL.getStyle(el, 'border-bottom-width'), 10); 893 } 894 } 895 return h; 896 }; 897 898 /** 899 * Gets either the offsetWidth or the width of the given element plus 900 * padding and borders (when offsetWidth is not available). Based on 901 * Ext.Element's getComputedWidth. 902 * 903 * @return {Number} The computed width of the element 904 * @private 905 */ 906 var getComputedWidth = function(el){ 907 var w = Math.max(el.offsetWidth, el.clientWidth); 908 if(!w){ 909 w = parseInt(SL.getStyle(el, 'width'), 10) || 0; 910 if(!isBorderBox){ 911 w += parseInt(SL.getStyle(el, 'padding-left'), 10) 912 + parseInt(SL.getStyle(el, 'padding-right'), 10) 913 + parseInt(SL.getStyle(el, 'border-left-width'), 10) 914 + parseInt(SL.getStyle(el, 'border-right-width'), 10); 915 } 916 } 917 return w; 918 }; 919 920 /** 921 * Determines the player needed to display the file at the given URL. If 922 * the file type is not supported, the return value will be 'unsupported'. 923 * If the file type is not supported but the correct player can be 924 * determined, the return value will be 'unsupported-*' where * will be the 925 * player abbreviation (e.g. 'qt' = QuickTime). 926 * 927 * @param {String} url The url of the file 928 * @return {String} The name of the player to use 929 * @private 930 */ 931 var getPlayerType = function(url){ 932 if(RE.img.test(url)) return 'img'; 933 var match = url.match(RE.domain); 934 var this_domain = match ? document.domain == match[1] : false; 935 if(url.indexOf('#') > -1 && this_domain) return 'inline'; 936 var q_index = url.indexOf('?'); 937 if(q_index > -1) url = url.substring(0, q_index); // strip query string for player detection purposes 938 if(RE.swf.test(url)) return plugins.fla ? 'swf' : 'unsupported-swf'; 939 if(RE.flv.test(url)) return plugins.fla ? 'flv' : 'unsupported-flv'; 940 if(RE.qt.test(url)) return plugins.qt ? 'qt' : 'unsupported-qt'; 941 if(RE.wmp.test(url)){ 942 if(plugins.wmp){ 943 return 'wmp'; 944 }else if(plugins.f4m){ 945 return 'qt'; 946 }else{ 947 return isMac ? (plugins.qt ? 'unsupported-f4m' : 'unsupported-qtf4m') : 'unsupported-wmp'; 948 } 949 }else if(RE.qtwmp.test(url)){ 950 if(plugins.qt){ 951 return 'qt'; 952 }else if(plugins.wmp){ 953 return 'wmp'; 954 }else{ 955 return isMac ? 'unsupported-qt' : 'unsupported-qtwmp'; 956 } 957 }else if(!this_domain || RE.iframe.test(url)){ 958 return 'iframe'; 959 } 960 return 'unsupported'; 961 }; 962 963 /** 964 * Handles all clicks on links that have been set up to work with Shadowbox 965 * and cancels the default event behavior when appropriate. 966 * 967 * @param {Event} ev The click event object 968 * @return void 969 * @private 970 */ 971 var handleClick = function(ev){ 972 // get anchor/area element 973 var link; 974 if(isLink(this)){ 975 link = this; // jQuery, Prototype, YUI 976 }else{ 977 link = SL.getTarget(ev); // Ext 978 while(!isLink(link) && link.parentNode){ 979 link = link.parentNode; 980 } 981 } 982 983 Shadowbox.open(link); 984 if(current_gallery.length) SL.preventDefault(ev); 985 }; 986 987 /** 988 * Sets up the current gallery for the given object. Modifies the current 989 * and current_gallery variables to contain the appropriate information. 990 * Also, checks to see if there are any gallery pieces that are not 991 * supported by the client's browser/plugins. If there are, they will be 992 * handled according to the handleUnsupported option. 993 * 994 * @param {Object} obj The content to get the gallery for 995 * @return void 996 * @private 997 */ 998 var setupGallery = function(obj){ 999 // create a copy so it doesn't get modified later 1000 var copy = apply({}, obj); 1001 1002 // is it part of a gallery? 1003 if(!obj.gallery){ // single item, no gallery 1004 current_gallery = [copy]; 1005 current = 0; 1006 }else{ 1007 current_gallery = []; // clear the current gallery 1008 var index, ci; 1009 for(var i = 0, len = cache.length; i < len; ++i){ 1010 ci = cache[i]; 1011 if(ci.gallery){ 1012 if(ci.content == obj.content 1013 && ci.gallery == obj.gallery 1014 && ci.title == obj.title){ // compare content, gallery, & title 1015 index = current_gallery.length; // key element found 1016 } 1017 if(ci.gallery == obj.gallery){ 1018 current_gallery.push(apply({}, ci)); 1019 } 1020 } 1021 } 1022 // if not found in cache, prepend to front of gallery 1023 if(index == null){ 1024 current_gallery.unshift(copy); 1025 index = 0; 1026 } 1027 current = index; 1028 } 1029 1030 // are any media in the current gallery supported? 1031 var match, r; 1032 for(var i = 0, len = current_gallery.length; i < len; ++i){ 1033 r = false; 1034 if(current_gallery[i].type == 'unsupported'){ // don't support this at all 1035 r = true; 1036 }else if(match = RE.unsupported.exec(current_gallery[i].type)){ // handle unsupported elements 1037 if(options.handleUnsupported == 'link'){ 1038 current_gallery[i].type = 'html'; 1039 // generate a link to the appropriate plugin download page(s) 1040 var m; 1041 switch(match[1]){ 1042 case 'qtwmp': 1043 m = String.format(options.text.errors.either, 1044 options.errors.qt.url, options.errors.qt.name, 1045 options.errors.wmp.url, options.errors.wmp.name); 1046 break; 1047 case 'qtf4m': 1048 m = String.format(options.text.errors.shared, 1049 options.errors.qt.url, options.errors.qt.name, 1050 options.errors.f4m.url, options.errors.f4m.name); 1051 break; 1052 default: 1053 if(match[1] == 'swf' || match[1] == 'flv') match[1] = 'fla'; 1054 m = String.format(options.text.errors.single, 1055 options.errors[match[1]].url, options.errors[match[1]].name); 1056 } 1057 current_gallery[i] = apply(current_gallery[i], { 1058 height: 160, // error messages are short so they 1059 width: 320, // only need a small box to display properly 1060 content: '<div class="shadowbox_message">' + m + '</div>' 1061 }); 1062 }else{ 1063 r = true; 1064 } 1065 }else if(current_gallery[i].type == 'inline'){ // handle inline elements 1066 // retrieve the innerHTML of the inline element
1067 var match = RE.inline.exec(current_gallery[i].content); 1068 if(match){ 1069 var el; 1070 if(el = SL.get(match[1])){ 1071 current_gallery[i].content = el.innerHTML; 1072 }else{ 1073 throw 'No element found with id ' + match[1]; 1074 } 1075 }else{ 1076 throw 'No element id found for inline content'; 1077 } 1078 } 1079 if(r){ 1080 // remove the element from the gallery 1081 current_gallery.splice(i, 1); 1082 if(i < current) --current; 1083 --i; 1084 } 1085 } 1086 }; 1087 1088 /** 1089 * Hides the title bar and toolbar and populates them with the proper 1090 * content. 1091 * 1092 * @return void 1093 * @private 1094 */ 1095 var buildBars = function(){ 1096 var link = current_gallery[current]; 1097 if(!link) return; // nothing to build 1098 1099 // build the title 1100 var title_i = SL.get('shadowbox_title_inner'); 1101 title_i.innerHTML = (link.title) ? link.title : ''; 1102 // empty the toolbar 1103 var tool_i = SL.get('shadowbox_toolbar_inner'); 1104 tool_i.innerHTML = ''; 1105 1106 // build the nav 1107 if(options.displayNav){ 1108 tool_i.innerHTML = String.format(options.skin.close, options.text.close); 1109 if(current_gallery.length > 1){ 1110 if(options.continuous){ 1111 // show both 1112 appendHTML(tool_i, String.format(options.skin.next, options.text.next)); 1113 appendHTML(tool_i, String.format(options.skin.prev, options.text.prev)); 1114 }else{ 1115 // not last in the gallery, show the next link 1116 if((current_gallery.length - 1) > current){ 1117 appendHTML(tool_i, String.format(options.skin.next, options.text.next)); 1118 } 1119 // not first in the gallery, show the previous link 1120 if(current > 0){ 1121 appendHTML(tool_i, String.format(options.skin.prev, options.text.prev)); 1122 } 1123 } 1124 } 1125 } 1126 1127 // build the counter 1128 if(current_gallery.length > 1 && options.displayCounter){ 1129 // append the counter div 1130 var counter = ''; 1131 if(options.counterType == 'skip'){ 1132 for(var i = 0, len = current_gallery.length; i < len; ++i){ 1133 counter += '<a href="javascript:Shadowbox.change(' + i + ');"'; 1134 if(i == current){ 1135 counter += ' class="shadowbox_counter_current"'; 1136 } 1137 counter += '>' + (i + 1) + '</a>'; 1138 } 1139 }else{ 1140 counter = (current + 1) + ' of ' + current_gallery.length; 1141 } 1142 appendHTML(tool_i, String.format(options.skin.counter, counter)); 1143 } 1144 }; 1145 1146 /** 1147 * Hides the title and tool bars. 1148 * 1149 * @param {Function} callback A function to call on finish 1150 * @return void 1151 * @private 1152 */ 1153 var hideBars = function(callback){ 1154 var title_m = getComputedHeight(SL.get('shadowbox_title')); 1155 var tool_m = 0 - getComputedHeight(SL.get('shadowbox_toolbar')); 1156 var title_i = SL.get('shadowbox_title_inner'); 1157 var tool_i = SL.get('shadowbox_toolbar_inner'); 1158 1159 if(options.animate && callback){ 1160 // animate the transition 1161 SL.animate(title_i, { 1162 marginTop: { to: title_m } 1163 }, 0.2); 1164 SL.animate(tool_i, { 1165 marginTop: { to: tool_m } 1166 }, 0.2, callback); 1167 }else{ 1168 SL.setStyle(title_i, 'marginTop', title_m + 'px'); 1169 SL.setStyle(tool_i, 'marginTop', tool_m + 'px'); 1170 } 1171 }; 1172 1173 /** 1174 * Shows the title and tool bars. 1175 * 1176 * @param {Function} callback A callback function to execute after 1177 * the animation completes 1178 * @return void 1179 * @private 1180 */ 1181 var showBars = function(callback){ 1182 var title_i = SL.get('shadowbox_title_inner'); 1183 if(options.animate){ 1184 if(title_i.innerHTML != ''){ 1185 SL.animate(title_i, { marginTop: { to: 0 } }, 0.35); 1186 } 1187 SL.animate(SL.get('shadowbox_toolbar_inner'), { 1188 marginTop: { to: 0 } 1189 }, 0.35, callback); 1190 }else{ 1191 if(title_i.innerHTML != ''){ 1192 SL.setStyle(title_i, 'margin-top', '0px'); 1193 } 1194 SL.setStyle(SL.get('shadowbox_toolbar_inner'), 'margin-top', '0px'); 1195 callback(); 1196 } 1197 }; 1198 1199 /** 1200 * Resets the class drag variable. 1201 * 1202 * @return void 1203 * @private 1204 */ 1205 var resetDrag = function(){ 1206 drag = { 1207 x: 0, 1208 y: 0, 1209 start_x: null, 1210 start_y: null 1211 }; 1212 }; 1213 1214 /** 1215 * Toggles the drag function on and off. 1216 * 1217 * @param {Boolean} on True to toggle on, false to toggle off 1218 * @return void 1219 * @private 1220 */ 1221 var toggleDrag = function(on){ 1222 if(on){ 1223 resetDrag(); 1224 // add drag layer to prevent browser dragging of actual image 1225 var styles = [ 1226 'position:absolute', 1227 'cursor:' + (isGecko ? '-moz-grab' : 'move') 1228 ]; 1229 // make drag layer transparent 1230 styles.push(isIE ? 'background-color:#fff;filter:alpha(opacity=0)' : 'background-color:transparent'); 1231 appendHTML('shadowbox_body_inner', '<div id="shadowbox_drag_layer" style="' + styles.join(';') + '"></div>'); 1232 SL.addEvent(SL.get('shadowbox_drag_layer'), 'mousedown', listenDrag); 1233 }else{ 1234 var d = SL.get('shadowbox_drag_layer'); 1235 if(d){ 1236 SL.removeEvent(d, 'mousedown', listenDrag); 1237 SL.remove(d); 1238 } 1239 } 1240 }; 1241 1242 /** 1243 * Sets up a drag listener on the document. Called when the mouse button is 1244 * pressed (mousedown). 1245 * 1246 * @param {mixed} ev The mousedown event 1247 * @return void 1248 * @private 1249 */ 1250 var listenDrag = function(ev){ 1251 drag.start_x = ev.clientX; 1252 drag.start_y = ev.clientY; 1253 draggable = SL.get('shadowbox_content'); 1254 SL.addEvent(document, 'mousemove', positionDrag); 1255 SL.addEvent(document, 'mouseup', unlistenDrag); 1256 if(isGecko) SL.setStyle(SL.get('shadowbox_drag_layer'), 'cursor', '-moz-grabbing'); 1257 }; 1258 1259 /** 1260 * Removes the drag listener. Called when the mouse button is released 1261 * (mouseup). 1262 * 1263 * @return void 1264 * @private 1265 */ 1266 var unlistenDrag = function(){ 1267 SL.removeEvent(document, 'mousemove', positionDrag); 1268 SL.removeEvent(document, 'mouseup', unlistenDrag); // clean up 1269 if(isGecko) SL.setStyle(SL.get('shadowbox_drag_layer'), 'cursor', '-moz-grab'); 1270 }; 1271 1272 /** 1273 * Positions an oversized image on drag. 1274 * 1275 * @param {mixed} ev The drag event 1276 * @return void 1277 * @private 1278 */ 1279 var positionDrag = function(ev){ 1280 var move_y = ev.clientY - drag.start_y; 1281 drag.start_y = drag.start_y + move_y; 1282 drag.y = Math.max(Math.min(0, drag.y + move_y), current_height - optimal_height); // y boundaries 1283 SL.setStyle(draggable, 'top', drag.y + 'px'); 1284 var move_x = ev.clientX - drag.start_x; 1285 drag.start_x = drag.start_x + move_x; 1286 drag.x = Math.max(Math.min(0, drag.x + move_x), current_width - optimal_width); // x boundaries 1287 SL.setStyle(draggable, 'left', drag.x + 'px'); 1288 }; 1289 1290 /** 1291 * Loads the Shadowbox with the current piece. 1292 * 1293 * @return void 1294 * @private 1295 */ 1296 var loadContent = function(){ 1297 var obj = current_gallery[current]; 1298 if(!obj) return; // invalid 1299 1300 buildBars();
1301 1302 switch(obj.type){ 1303 case 'img': 1304 // preload the image 1305 preloader = new Image(); 1306 preloader.onload = function(){ 1307 // images default to image height and width 1308 var h = obj.height ? parseInt(obj.height, 10) : preloader.height; 1309 var w = obj.width ? parseInt(obj.width, 10) : preloader.width; 1310 resizeContent(h, w, function(dims){ 1311 showBars(function(){ 1312 setContent({ 1313 tag: 'img', 1314 height: dims.i_height, 1315 width: dims.i_width, 1316 src: obj.content, 1317 style: 'position:absolute' 1318 }); 1319 if(dims.enableDrag && options.handleLgImages == 'drag'){ 1320 // listen for drag 1321 toggleDrag(true); 1322 SL.setStyle(SL.get('shadowbox_drag_layer'), { 1323 height: dims.i_height + 'px', 1324 width: dims.i_width + 'px' 1325 }); 1326 } 1327 finishContent(); 1328 }); 1329 }); 1330 1331 preloader.onload = function(){}; // clear onload for IE 1332 }; 1333 preloader.src = obj.content; 1334 break; 1335 1336 case 'swf': 1337 case 'flv': 1338 case 'qt': 1339 case 'wmp': 1340 var markup = Shadowbox.movieMarkup(obj); 1341 resizeContent(markup.height, markup.width, function(){ 1342 showBars(function(){ 1343 setContent(markup); 1344 finishContent(); 1345 }); 1346 }); 1347 break; 1348 1349 case 'iframe': 1350 // iframes default to full viewport height and width 1351 var h = obj.height ? parseInt(obj.height, 10) : SL.getViewportHeight(); 1352 var w = obj.width ? parseInt(obj.width, 10) : SL.getViewportWidth(); 1353 var content = { 1354 tag: 'iframe', 1355 name: 'shadowbox_content', 1356 height: '100%', 1357 width: '100%', 1358 frameborder: '0', 1359 marginwidth: '0', 1360 marginheight: '0', 1361 scrolling: 'auto', 1362 allowfullscreen: 'true' 1363 }; 1364 1365 resizeContent(h, w, function(dims){ 1366 showBars(function(){ 1367 setContent(content); 1368 var win = (isIE) 1369 ? SL.get('shadowbox_content').contentWindow 1370 : window.frames['shadowbox_content']; 1371 win.location = obj.content; 1372 finishContent(); 1373 }); 1374 }); 1375 break; 1376 1377 case 'html': 1378 case 'inline': 1379 // HTML content defaults to full viewport height and width 1380 var h = obj.height ? parseInt(obj.height, 10) : SL.getViewportHeight(); 1381 var w = obj.width ? parseInt(obj.width, 10) : SL.getViewportWidth(); 1382 var content = { 1383 tag: 'div', 1384 cls: 'html', /* give special class to make scrollable */ 1385 html: obj.content 1386 }; 1387 resizeContent(h, w, function(){ 1388 showBars(function(){ 1389 setContent(content); 1390 finishContent(); 1391 }); 1392 }); 1393 break; 1394 1395 default: 1396 // should never happen 1397 throw 'Shadowbox cannot open content of type ' + obj.type; 1398 } 1399 1400 // preload neighboring images 1401 if(current_gallery.length > 0){ 1402 var next = current_gallery[current + 1]; 1403 if(!next){ 1404 next = current_gallery[0]; 1405 } 1406 if(next.type == 'img'){
1407 var preload_next = new Image(); 1408 preload_next.src = next.href; 1409 } 1410 1411 var prev = current_gallery[current - 1]; 1412 if(!prev){ 1413 prev = current_gallery[current_gallery.length - 1]; 1414 } 1415 if(prev.type == 'img'){ 1416 var preload_prev = new Image(); 1417 preload_prev.src = prev.href; 1418 } 1419 } 1420 }; 1421 1422 /** 1423 * Removes old content and sets the new content of the Shadowbox. 1424 * 1425 * @param {Object} obj The content to set (appropriate to pass 1426 * directly to Shadowbox.createHTML()) 1427 * @return {HTMLElement} The newly appended element (or null if 1428 * none is provided) 1429 * @private 1430 */ 1431 var setContent = function(obj){ 1432 var id = 'shadowbox_content'; 1433 var content = SL.get(id); 1434 if(content){ 1435 // remove old content first 1436 switch(content.tagName.toUpperCase()){ 1437 case 'OBJECT': 1438 // if we're in a gallery (i.e. changing and there's a new 1439 // object) we want the LAST link object 1440 var link = current_gallery[(obj ? current - 1 : current)]; 1441 if(link.type == 'wmp' && isIE){ 1442 try{ 1443 shadowbox_content.controls.stop(); // stop the movie 1444 shadowbox_content.URL = 'non-existent.wmv'; // force player refresh 1445 window.shadowbox_content = function(){}; // remove from window 1446 }catch(e){} 1447 }else if(link.type == 'qt' && isSafari){ 1448 try{ 1449 document.shadowbox_content.Stop(); // stop QT movie 1450 }catch(e){} 1451 // stop QT audio stream for movies that have not yet loaded 1452 content.innerHTML = ''; 1453 // console.log(document.shadowbox_content); 1454 } 1455 setTimeout(function(){ // using setTimeout prevents browser crashes with WMP 1456 SL.remove(content); 1457 }, 10); 1458 break; 1459 case 'IFRAME': 1460 SL.remove(content); 1461 if(isGecko) delete window.frames[id]; // needed for Firefox 1462 break; 1463 default: 1464 SL.remove(content); 1465 } 1466 } 1467 if(obj){ 1468 if(!obj.id) obj.id = id; 1469 return appendHTML('shadowbox_body_inner', Shadowbox.createHTML(obj)); 1470 } 1471 return null; 1472 }; 1473 1474 /** 1475 * This function is used as the callback after the Shadowbox has been 1476 * positioned, resized, and loaded with content. 1477 * 1478 * @return void 1479 * @private 1480 */ 1481 var finishContent = function(){ 1482 var obj = current_gallery[current]; 1483 if(!obj) return; // invalid 1484 hideLoading(function(){ 1485 listenKeyboard(true); 1486 // fire onFinish handler 1487 if(options.onFinish && typeof options.onFinish == 'function'){ 1488 options.onFinish(obj); 1489 } 1490 }); 1491 }; 1492 1493 /** 1494 * Resizes and positions the content box using the given height and width. 1495 * If the callback parameter is missing, the transition will not be 1496 * animated. If the callback parameter is present, it will be passed the 1497 * new calculated dimensions object as its first parameter. Note: the height 1498 * and width here should represent the optimal height and width of the box. 1499 * 1500 * @param {Function} callback A callback function to use when the 1501 * resize completes 1502 * @return void 1503 * @private 1504 */ 1505 var resizeContent = function(height, width, callback){ 1506 // update optimal height and width 1507 optimal_height = height; 1508 optimal_width = width; 1509 var resizable = RE.resize.test(current_gallery[current].type); 1510 var dims = getDimensions(optimal_height, optimal_width, resizable); 1511 if(callback){ 1512 var cb = function(){ callback(dims); }; 1513 switch(options.animSequence){ 1514 case 'hw': 1515 adjustHeight(dims.height, dims.top, true, function(){ 1516 adjustWidth(dims.width, true, cb); 1517 }); 1518 break; 1519 case 'wh': 1520 adjustWidth(dims.width, true, function(){ 1521 adjustHeight(dims.height, dims.top, true, cb); 1522 }); 1523 break; 1524 default: // sync 1525 adjustWidth(dims.width, true); 1526 adjustHeight(dims.height, dims.top, true, cb); 1527 } 1528 }else{ // window resize 1529 adjustWidth(dims.width, false); 1530 adjustHeight(dims.height, dims.top, false); 1531 // resize content images & flash in 'resize' mode 1532 if(options.handleLgImages == 'resize' && resizable){ 1533 var content = SL.get('shadowbox_content'); 1534 if(content){ // may be animating, not present 1535 content.height = dims.i_height; 1536 content.width = dims.i_width; 1537 } 1538 } 1539 } 1540 }; 1541 1542 /** 1543 * Calculates the dimensions for Shadowbox, taking into account the borders, 1544 * margins, and surrounding elements of the shadowbox_body. If the image 1545 * is still to large for Shadowbox, and options.handleLgImages is 'resize',
1546 * the resized dimensions will be returned (preserving the original aspect 1547 * ratio). Otherwise, the originally calculated dimensions will be returned. 1548 * The returned object will have the following properties: 1549 * 1550 * - height: The height to use for shadowbox_body_inner 1551 * - width: The width to use for shadowbox 1552 * - i_height: The height to use for resizable content 1553 * - i_width: The width to use for resizable content 1554 * - top: The top to use for shadowbox 1555 * - enableDrag: True if dragging should be enabled (image is oversized) 1556 * 1557 * @param {Number} o_height The optimal height 1558 * @param {Number} o_width The optimal width 1559 * @param {Boolean} resizable True if the content is able to be 1560 * resized. Defaults to false. 1561 * @return {Object} The resize dimensions (see above) 1562 * @private 1563 */ 1564 var getDimensions = function(o_height, o_width, resizable){ 1565 if(typeof resizable == 'undefined') resizable = false; 1566 1567 var height = o_height = parseInt(o_height); 1568 var width = o_width = parseInt(o_width); 1569 var shadowbox_b = SL.get('shadowbox_body'); 1570 1571 // calculate the max height 1572 var view_height = SL.getViewportHeight(); 1573 var extra_height = parseInt(SL.getStyle(shadowbox_b, 'border-top-width'), 10) 1574 + parseInt(SL.getStyle(shadowbox_b, 'border-bottom-width'), 10) 1575 + parseInt(SL.getStyle(shadowbox_b, 'margin-top'), 10) 1576 + parseInt(SL.getStyle(shadowbox_b, 'margin-bottom'), 10) 1577 + getComputedHeight(SL.get('shadowbox_title')) 1578 + getComputedHeight(SL.get('shadowbox_toolbar')) 1579 + (2 * options.viewportPadding); 1580 if((height + extra_height) >= view_height){ 1581 height = view_height - extra_height; 1582 } 1583 1584 // calculate the max width 1585 var view_width = SL.getViewportWidth(); 1586 var extra_body_width = parseInt(SL.getStyle(shadowbox_b, 'border-left-width'), 10) 1587 + parseInt(SL.getStyle(shadowbox_b, 'border-right-width'), 10) 1588 + parseInt(SL.getStyle(shadowbox_b, 'margin-left'), 10) 1589 + parseInt(SL.getStyle(shadowbox_b, 'margin-right'), 10); 1590 var extra_width = extra_body_width + (2 * options.viewportPadding); 1591 if((width + extra_width) >= view_width){ 1592 width = view_width - extra_width; 1593 } 1594 1595 // handle oversized images & flash 1596 var enableDrag = false; 1597 var i_height = o_height; 1598 var i_width = o_width; 1599 var handle = options.handleLgImages; 1600 if(resizable && (handle == 'resize' || handle == 'drag')){ 1601 var change_h = (o_height - height) / o_height; 1602 var change_w = (o_width - width) / o_width; 1603 if(handle == 'resize'){ 1604 if(change_h > change_w){ 1605 width = Math.round((o_width / o_height) * height); 1606 }else if(change_w > change_h){ 1607 height = Math.round((o_height / o_width) * width); 1608 } 1609 // adjust image height or width accordingly 1610 i_width = width; 1611 i_height = height; 1612 }else{ 1613 // drag on oversized images only 1614 var link = current_gallery[current]; 1615 if(link) enableDrag = link.type == 'img' && (change_h > 0 || change_w > 0); 1616 } 1617 } 1618 1619 return { 1620 height: height, 1621 width: width + extra_body_width, 1622 i_height: i_height, 1623 i_width: i_width, 1624 top: ((view_height - (height + extra_height)) / 2) + options.viewportPadding, 1625 enableDrag: enableDrag 1626 }; 1627 }; 1628 1629 /** 1630 * Centers Shadowbox vertically in the viewport. Needs to be called on 1631 * scroll in IE6 because it does not support fixed positioning. 1632 * 1633 * @return void 1634 * @private 1635 */ 1636 var centerVertically = function(){ 1637 var shadowbox = SL.get('shadowbox'); 1638 var scroll = document.documentElement.scrollTop;
1639 var s_top = scroll + Math.round((SL.getViewportHeight() - (shadowbox.offsetHeight || 0)) / 2); 1640 SL.setStyle(shadowbox, 'top', s_top + 'px'); 1641 }; 1642 1643 /** 1644 * Adjusts the height of shadowbox_body_inner and centers Shadowbox 1645 * vertically in the viewport. 1646 * 1647 * @param {Number} height The height of shadowbox_body_inner 1648 * @param {Number} top The top of the Shadowbox 1649 * @param {Boolean} animate True to animate the transition 1650 * @param {Function} callback A callback to use when the animation completes 1651 * @return void 1652 * @private 1653 */ 1654 var adjustHeight = function(height, top, animate, callback){ 1655 height = parseInt(height); 1656 1657 // update current_height 1658 current_height = height; 1659 1660 // adjust the height 1661 var sbi = SL.get('shadowbox_body_inner'); 1662 if(animate && options.animate){ 1663 SL.animate(sbi, { 1664 height: { to: height } 1665 }, options.resizeDuration, callback); 1666 }else{ 1667 SL.setStyle(sbi, 'height', height + 'px'); 1668 if(typeof callback == 'function') callback(); 1669 } 1670 1671 // manually adjust the top because we're using fixed positioning in IE6 1672 if(absolute_pos){ 1673 // listen for scroll so we can adjust 1674 centerVertically(); 1675 SL.addEvent(window, 'scroll', centerVertically); 1676 1677 // add scroll to top 1678 top += document.documentElement.scrollTop; 1679 } 1680 1681 // adjust the top 1682 var shadowbox = SL.get('shadowbox'); 1683 if(animate && options.animate){ 1684 SL.animate(shadowbox, { 1685 top: { to: top } 1686 }, options.resizeDuration); 1687 }else{ 1688 SL.setStyle(shadowbox, 'top', top + 'px'); 1689 } 1690 }; 1691 1692 /** 1693 * Adjusts the width of shadowbox. 1694 * 1695 * @param {Number} width The width to use 1696 * @param {Boolean} animate True to animate the transition 1697 * @param {Function} callback A callback to use when the animation completes 1698 * @return void 1699 * @private 1700 */ 1701 var adjustWidth = function(width, animate, callback){ 1702 width = parseInt(width); 1703 1704 // update current_width 1705 current_width = width; 1706 1707 var shadowbox = SL.get('shadowbox'); 1708 if(animate && options.animate){ 1709 SL.animate(shadowbox, { 1710 width: { to: width } 1711 }, options.resizeDuration, callback); 1712 }else{ 1713 SL.setStyle(shadowbox, 'width', width + 'px'); 1714 if(typeof callback == 'function') callback(); 1715 } 1716 }; 1717 1718 /** 1719 * Sets up a listener on the document for keystrokes. 1720 * 1721 * @param {Boolean} on True to enable the listner, false to turn 1722 * it off 1723 * @return void 1724 * @private 1725 */ 1726 var listenKeyboard = function(on){ 1727 if(!options.enableKeys) return; 1728 if(on){ 1729 document.onkeydown = handleKey; 1730 }else{ 1731 document.onkeydown = ''; 1732 } 1733 }; 1734 1735 /** 1736 * Asserts the given key or code is present in the array of valid keys. 1737 * 1738 * @param {Array} valid An array of valid keys and codes 1739 * @param {String} key The character that was pressed 1740 * @param {Number} code The key code that was pressed 1741 * @return {Boolean} True if the key is valid 1742 * @private 1743 */ 1744 var assertKey = function(valid, key, code){ 1745 return (valid.indexOf(key) != -1 || valid.indexOf(code) != -1); 1746 }; 1747 1748 /** 1749 * A listener function that will act on a key pressed. 1750 * 1751 * @param {Event} e The event object 1752 * @return void 1753 * @private 1754 */ 1755 var handleKey = function(e){ 1756 var code = e ? e.which : event.keyCode; 1757 var key = String.fromCharCode(code).toLowerCase(); 1758 if(assertKey(options.keysClose, key, code)){ 1759 Shadowbox.close(); 1760 }else if(assertKey(options.keysPrev, key, code)){ 1761 Shadowbox.previous(); 1762 }else if(assertKey(options.keysNext, key, code)){ 1763 Shadowbox.next(); 1764 } 1765 }; 1766 1767 /** 1768 * Shows and hides elements that are troublesome for modal overlays. 1769 * 1770 * @param {Boolean} on True to show the elements, false otherwise 1771 * @return void 1772 * @private 1773 */ 1774 var toggleTroubleElements = function(on){ 1775 var vis = (on ? 'visible' : 'hidden'); 1776 var selects = document.getElementsByTagName('select'); 1777 for(i = 0, len = selects.length; i < len; ++i){ 1778 selects[i].style.visibility = vis; 1779 } 1780/* 1781 var objects = document.getElementsByTagName('object'); 1782 for(i = 0, len = objects.length; i < len; ++i){ 1783 objects[i].style.visibility = vis; 1784 } 1785 var embeds = document.getElementsByTagName('embed'); 1786 for(i = 0, len = embeds.length; i < len; ++i){ 1787 embeds[i].style.visibility = vis; 1788 } 1789 */ 1790 }; 1791 1792 /** 1793 * Fills the Shadowbox with the loading skin. 1794 * 1795 * @return void 1796 * @private 1797 */ 1798 var showLoading = function(){ 1799 var loading = SL.get('shadowbox_loading'); 1800 overwriteHTML(loading, String.format(options.skin.loading, 1801 options.assetURL + options.loadingImage, 1802 options.text.loading, 1803 options.text.cancel)); 1804 loading.style.visibility = 'visible'; 1805 }; 1806 1807 /** 1808 * Hides the Shadowbox loading skin. 1809 * 1810 * @param {Function} callback The callback function to call after 1811 * hiding the loading skin 1812 * @return void 1813 * @private 1814 */ 1815 var hideLoading = function(callback){ 1816 var t = current_gallery[current].type; 1817 var anim = (t == 'img' || t == 'html'); // fade on images & html 1818 var loading = SL.get('shadowbox_loading'); 1819 if(anim){
1820 fadeOut(loading, 0.35, callback); 1821 }else{ 1822 loading.style.visibility = 'hidden'; 1823 callback(); 1824 } 1825 }; 1826 1827 /** 1828 * Sets the size of the overlay to the size of the document. 1829 * 1830 * @return void 1831 * @private 1832 */ 1833 var resizeOverlay = function(){ 1834 var overlay = SL.get('shadowbox_overlay'); 1835 SL.setStyle(overlay, { 1836 height: '100%', 1837 width: '100%' 1838 }); 1839 SL.setStyle(overlay, 'height', SL.getDocumentHeight() + 'px'); 1840 if(!isSafari3){ 1841 // Safari3 includes vertical scrollbar in SL.getDocumentWidth()! 1842 // Leave overlay width at 100% for now... 1843 SL.setStyle(overlay, 'width', SL.getDocumentWidth() + 'px'); 1844 } 1845 }; 1846 1847 /** 1848 * Used to determine if the pre-made overlay background image is needed 1849 * instead of using the trasparent background overlay. A pre-made background 1850 * image is used for all but image pieces in FF Mac because it has problems 1851 * displaying correctly if the background layer is not 100% opaque. When 1852 * displaying a gallery, if any piece in the gallery meets these criteria, 1853 * the pre-made background image will be used. 1854 * 1855 * @return {Boolean} Whether or not an overlay image is needed 1856 * @private 1857 */
1858 var checkOverlayImgNeeded = function(){ 1859 if(!(isGecko && isMac)) return false; 1860 for(var i = 0, len = current_gallery.length; i < len; ++i){ 1861 if(!RE.overlay.exec(current_gallery[i].type)) return true; 1862 } 1863 return false; 1864 }; 1865 1866 /** 1867 * Activates (or deactivates) the Shadowbox overlay. If a callback function 1868 * is provided, we know we're activating. Otherwise, deactivate the overlay. 1869 * 1870 * @param {Function} callback A callback to call after activation 1871 * @return void 1872 * @private 1873 */ 1874 var toggleOverlay = function(callback){ 1875 var overlay = SL.get('shadowbox_overlay'); 1876 if(overlay_img_needed == null){ 1877 overlay_img_needed = checkOverlayImgNeeded(); 1878 } 1879 1880 if(callback){ 1881 resizeOverlay(); // size the overlay before showing 1882 if(overlay_img_needed){ 1883 SL.setStyle(overlay, { 1884 visibility: 'visible', 1885 backgroundColor: 'transparent', 1886 backgroundImage: 'url(' + options.assetURL + options.overlayBgImage + ')', 1887 backgroundRepeat: 'repeat', 1888 opacity: 1 1889 }); 1890 callback(); 1891 }else{ 1892 SL.setStyle(overlay, { 1893 visibility: 'visible', 1894 backgroundColor: options.overlayColor, 1895 backgroundImage: 'none' 1896 }); 1897 fadeIn(overlay, options.overlayOpacity, options.fadeDuration, 1898 callback); 1899 } 1900 }else{ 1901 if(overlay_img_needed){ 1902 SL.setStyle(overlay, 'visibility', 'hidden'); 1903 }else{ 1904 fadeOut(overlay, options.fadeDuration); 1905 } 1906 1907 // reset for next time 1908 overlay_img_needed = null; 1909 } 1910 }; 1911 1912 /** 1913 * Initializes the Shadowbox environment. Appends Shadowbox' HTML to the 1914 * document and sets up listeners on the window and overlay element. 1915 * 1916 * @param {Object} opts The default options to use 1917 * @return void 1918 * @public 1919 * @static 1920 */ 1921 Shadowbox.init = function(opts){ 1922 if(initialized) return; // don't initialize twice 1923 options = apply(options, opts || {}); 1924 1925 // add markup 1926 appendHTML(document.body, options.skin.main); 1927 1928 // compile file type regular expressions here for speed 1929 RE.img = new RegExp('\.(' + options.ext.img.join('|') + ')\s*$', 'i'); 1930 RE.qt = new RegExp('\.(' + options.ext.qt.join('|') + ')\s*$', 'i'); 1931 RE.wmp = new RegExp('\.(' + options.ext.wmp.join('|') + ')\s*$', 'i'); 1932 RE.qtwmp = new RegExp('\.(' + options.ext.qtwmp.join('|') + ')\s*$', 'i'); 1933 RE.iframe = new RegExp('\.(' + options.ext.iframe.join('|') + ')\s*$', 'i'); 1934 1935 // handle window resize events 1936 var id = null; 1937 var resize = function(){ 1938 clearInterval(id); 1939 id = null; 1940 resizeOverlay(); 1941 resizeContent(optimal_height, optimal_width); 1942 }; 1943 SL.addEvent(window, 'resize', function(){ 1944 if(activated){ 1945 // use event buffering to prevent jerky window resizing 1946 if(id){ 1947 clearInterval(id); 1948 id = null; 1949 } 1950 if(!id) id = setInterval(resize, 50); 1951 } 1952 }); 1953 1954 if(options.listenOverlay){ 1955 // add a listener to the overlay 1956 SL.addEvent(SL.get('shadowbox_overlay'), 'click', Shadowbox.close); 1957 } 1958 1959 // adjust some positioning if needed 1960 if(absolute_pos){ 1961 // give the container absolute positioning 1962 SL.setStyle(SL.get('shadowbox_container'), 'position', 'absolute'); 1963 // give shadowbox_body "layout"...whatever that is 1964 SL.setStyle('shadowbox_body', 'zoom', 1); 1965 // need to listen to the container element because it c
1965overs the top 1966 // half of the page 1967 SL.addEvent(SL.get('shadowbox_container'), 'click', function(e){ 1968 var target = SL.getTarget(e); 1969 if(target.id && target.id == 'shadowbox_container') Shadowbox.close(); 1970 }); 1971 } 1972 1973 // skip setup, will need to be done manually later 1974 if(!options.skipSetup) Shadowbox.setup(); 1975 initialized = true; 1976 }; 1977 1978 /** 1979 * Sets up listeners on the given links that will trigger Shadowbox. If no 1980 * links are given, this method will set up every anchor element on the page 1981 * with the appropriate rel attribute. Note: Because AREA elements do not 1982 * support the rel attribute, they must be explicitly passed to this method. 1983 * 1984 * @param {Array} links An array (or array-like) list of anchor 1985 * and/or area elements to set up 1986 * @param {Object} opts Some options to use for the given links 1987 * @return void 1988 * @public 1989 * @static 1990 */ 1991 Shadowbox.setup = function(links, opts){ 1992 // get links if none specified 1993 if(!links){ 1994 var links = []; 1995 var a = document.getElementsByTagName('a'), rel; 1996 for(var i = 0, len = a.length; i < len; ++i){ 1997 rel = a[i].getAttribute('rel'); 1998 if(rel && RE.rel.test(rel)) links[links.length] = a[i]; 1999 } 2000 }else if(!links.length){ 2001 links = [links]; // one link 2002 } 2003 2004 var link; 2005 for(var i = 0, len = links.length; i < len; ++i){ 2006 link = links[i]; 2007 if(typeof link.shadowboxCacheKey == 'undefined'){ 2008 // assign cache key expando 2009 // use integer primitive to avoid memory leak in IE 2010 link.shadowboxCacheKey = cache.length; 2011 SL.addEvent(link, 'click', handleClick); // add listener 2012 } 2013 cache[link.shadowboxCacheKey] = this.buildCacheObj(link, opts); 2014 } 2015 }; 2016 2017 /** 2018 * Builds an object from the original link element data to store in cache. 2019 * These objects contain (most of) the following keys: 2020 * 2021 * - el: the link element 2022 * - title: the linked file title 2023 * - type: the linked file type 2024 * - content: the linked file's URL 2025 * - gallery: the gallery the file belongs to (optional) 2026 * - height: the height of the linked file (only necessary for movies) 2027 * - width: the width of the linked file (only necessary for movies) 2028 * - options: custom options to use (optional) 2029 * 2030 * @param {HTMLElement} link The link element to process 2031 * @return {Object} An object representing the link 2032 * @public 2033 * @static 2034 */ 2035 Shadowbox.buildCacheObj = function(link, opts){ 2036 var href = link.href; // don't use getAttribute() here 2037 var o = { 2038 el: link, 2039 title: link.getAttribute('title'), 2040 type: getPlayerType(href), 2041 options: apply({}, opts || {}), // break the reference 2042 content: href 2043 }; 2044 2045 // remove link-level options from top-level options 2046 var opt, l_opts = ['title', 'type', 'height', 'width', 'gallery']; 2047 for(var i = 0, len = l_opts.length; i < len; ++i){ 2048 opt = l_opts[i]; 2049 if(typeof o.options[opt] != 'undefined'){ 2050 o[opt] = o.options[opt]; 2051 delete o.options[opt]; 2052 } 2053 } 2054 2055 // HTML options always trump JavaScript options, so do these last 2056 var rel = link.getAttribute('rel'); 2057 if(rel){ 2058 // extract gallery name from shadowbox[name] format 2059 var match = rel.match(RE.gallery); 2060 if(match) o.gallery = escape(match[2]); 2061 2062 // other parameters 2063 var params = rel.split(';'); 2064 for(var i = 0, len = params.length; i < len; ++i){ 2065 match = params[i].match(RE.param); 2066 if(match){ 2067 if(match[1] == 'options'){ 2068 eval('o.options = apply(o.options, ' + match[2] + ')'); 2069 }else{ 2070 o[match[1]] = match[2]; 2071 } 2072 } 2073 } 2074 } 2075 2076 return o; 2077 }; 2078 2079 /** 2080 * Applies the given set of options to those currently in use. Note: Options 2081 * will be reset on Shadowbox.open() so this function is only useful after 2082 * it has already been called (while Shadowbox is open). 2083 * 2084 * @param {Object} opts The options to apply 2085 * @return void 2086 * @public 2087 * @static 2088 */ 2089 Shadowbox.applyOptions = function(opts){ 2090 if(opts){ 2091 // use apply here to break references 2092 default_options = apply({}, options); // store default options 2093 options = apply(options, opts); // apply options 2094 } 2095 }; 2096 2097 /** 2098 * Reverts Shadowbox' options to the last default set in use before
2099 * Shadowbox.applyOptions() was called. 2100 * 2101 * @return void 2102 * @public 2103 * @static 2104 */ 2105 Shadowbox.revertOptions = function(){ 2106 if(default_options){ 2107 options = default_options; // revert to default options 2108 default_options = null; // erase for next time 2109 } 2110 }; 2111 2112 /** 2113 * Opens the given object in Shadowbox. This object may be either an 2114 * anchor/area element, or an object similar to the one created by 2115 * Shadowbox.buildCacheObj(). 2116 * 2117 * @param {mixed} obj The object or link element that defines 2118 * what to display 2119 * @return void 2120 * @public 2121 * @static 2122 */ 2123 Shadowbox.open = function(obj, opts){ 2124 if(activated) return; // already open 2125 activated = true; 2126 2127 // is it a link? 2128 if(isLink(obj)){ 2129 if(typeof obj.shadowboxCacheKey == 'undefined' || typeof cache[obj.shadowboxCacheKey] == 'undefined'){ 2130 // link element that hasn't been set up before 2131 // create an object on-the-fly 2132 obj = this.buildCacheObj(obj, opts); 2133 }else{ 2134 // link element that has been set up before, get from cache 2135 obj = cache[obj.shadowboxCacheKey]; 2136 } 2137 } 2138 2139 this.revertOptions(); 2140 if(obj.options || opts){ 2141 // use apply here to break references 2142 this.applyOptions(apply(apply({}, obj.options || {}), opts || {})); 2143 } 2144 2145 // update current & current_gallery 2146 setupGallery(obj); 2147 2148 // anything to display? 2149 if(current_gallery.length){ 2150 // fire onOpen hook 2151 if(options.onOpen && typeof options.onOpen == 'function'){ 2152 options.onOpen(obj); 2153 } 2154 2155 // display:block here helps with correct dimension calculations 2156 SL.setStyle(SL.get('shadowbox'), 'display', 'block'); 2157 2158 toggleTroubleElements(false); 2159 var dims = getDimensions(options.initialHeight, options.initialWidth); 2160 adjustHeight(dims.height, dims.top); 2161 adjustWidth(dims.width); 2162 hideBars(false); 2163 2164 // show the overlay and load the content 2165 toggleOverlay(function(){ 2166 SL.setStyle(SL.get('shadowbox'), 'visibility', 'visible'); 2167 showLoading(); 2168 loadContent(); 2169 }); 2170 } 2171 }; 2172 2173 /** 2174 * Jumps to the piece in the current gallery with index num. 2175 * 2176 * @param {Number} num The gallery index to view 2177 * @return void 2178 * @public 2179 * @static 2180 */ 2181 Shadowbox.change = function(num){ 2182 if(!current_gallery) return; // no current gallery 2183 if(!current_gallery[num]){ // index does not exist 2184 if(!options.continuous){ 2185 return; 2186 }else{ 2187 num = (num < 0) ? (current_gallery.length - 1) : 0; // loop 2188 } 2189 } 2190 2191 // update current 2192 current = num; 2193 2194 // stop listening for drag 2195 toggleDrag(false); 2196 // empty the content 2197 setContent(null); 2198 // turn this back on when done 2199 listenKeyboard(false); 2200 2201 // fire onChange handler 2202 if(options.onChange && typeof options.onChange == 'function'){ 2203 options.onChange(current_gallery[current]); 2204 } 2205 2206 showLoading(); 2207 hideBars(loadContent); 2208 }; 2209 2210 /** 2211 * Jumps to the next piece in the gallery. 2212 * 2213 * @return {Boolean} True if the gallery changed to next item, false 2214 * otherwise 2215 * @public 2216 * @static 2217 */ 2218 Shadowbox.next = function(){ 2219 return this.change(current + 1); 2220 }; 2221 2222 /** 2223 * Jumps to the previous piece in the gallery. 2224 * 2225 * @return {Boolean} True if the gallery changed to previous item, 2226 * false otherwise 2227 * @public 2228 * @static 2229 */ 2230 Shadowbox.previous = function(){ 2231 return this.change(current - 1); 2232 }; 2233 2234 /** 2235 * Deactivates Shadowbox. 2236 * 2237 * @return void 2238 * @public 2239 * @static 2240 */ 2241 Shadowbox.close = function(){ 2242 if(!activated) return; // already closed 2243 2244 // stop listening for keys 2245 listenKeyboard(false); 2246 // hide
2247 SL.setStyle(SL.get('shadowbox'), { 2248 display: 'none', 2249 visibility: 'hidden' 2250 }); 2251 // stop listening for scroll on IE 2252 if(absolute_pos) SL.removeEvent(window, 'scroll', centerVertically); 2253 // stop listening for drag 2254 toggleDrag(false); 2255 // empty the content 2256 setContent(null); 2257 // prevent old image requests from loading 2258 if(preloader){ 2259 preloader.onload = function(){}; 2260 preloader = null; 2261 } 2262 // hide the overlay 2263 toggleOverlay(false); 2264 // turn on trouble elements 2265 toggleTroubleElements(true); 2266 2267 // fire onClose handler 2268 if(options.onClose && typeof options.onClose == 'function'){ 2269 options.onClose(current_gallery[current]); 2270 } 2271 2272 activated = false; 2273 }; 2274 2275 /** 2276 * Clears Shadowbox' cache and removes listeners and expandos from all 2277 * cached link elements. May be used to completely reset Shadowbox in case 2278 * links on a page change. 2279 * 2280 * @return void 2281 * @public 2282 * @static 2283 */ 2284 Shadowbox.clearCache = function(){ 2285 for(var i = 0, len = cache.length; i < len; ++i){ 2286 if(cache[i].el){ 2287 SL.removeEvent(cache[i].el, 'click', handleClick); 2288 delete cache[i].shadowboxCacheKey; 2289 } 2290 } 2291 cache = []; 2292 }; 2293 2294 /** 2295 * Generates the markup necessary to embed the movie file with the given 2296 * link element. This markup will be browser-specific. Useful for generating 2297 * the media test suite. 2298 * 2299 * @param {HTMLElement} link The link to the media file 2300 * @return {Object} The proper markup to use (see above) 2301 * @public 2302 * @static 2303 */ 2304 Shadowbox.movieMarkup = function(obj){ 2305 // movies default to 300x300 pixels 2306 var h = obj.height ? parseInt(obj.height, 10) : 300; 2307 var w = obj.width ? parseInt(obj.width, 10) : 300; 2308 2309 var autoplay = options.autoplayMovies; 2310 var controls = options.showMovieControls; 2311 if(obj.options){ 2312 if(obj.options.autoplayMovies != null){ 2313 autoplay = obj.options.autoplayMovies; 2314 } 2315 if(obj.options.showMovieControls != null){ 2316 controls = obj.options.showMovieControls; 2317 } 2318 } 2319 2320 var markup = { 2321 tag: 'object', 2322 name: 'shadowbox_content' 2323 }; 2324 2325 switch(obj.type){ 2326 case 'swf': 2327 var dims = getDimensions(h, w, true); 2328 h = dims.height; 2329 w = dims.width; 2330 markup.type = 'application/x-shockwave-flash'; 2331 markup.data = obj.content; 2332 markup.children = [ 2333 { tag: 'param', name: 'movie', value: obj.content } 2334 ]; 2335 break; 2336 case 'flv': 2337 autoplay = autoplay ? 'true' : 'false'; 2338 var showicons = 'false'; 2339 var a = h/w; // aspect ratio 2340 if(controls){ 2341 showicons = 'true'; 2342 h += 20; // height of JW FLV player controller 2343 } 2344 var dims = getDimensions(h, h/a, true); // resize 2345 h = dims.height; 2346 w = (h-(controls?20:0))/a; // maintain aspect ratio 2347 var flashvars = [ 2348 'file=' + obj.content, 2349 'height=' + h, 2350 'width=' + w, 2351 'autostart=' + autoplay, 2352 'displayheight=' + (h - (controls?20:0)), 2353 'showicons=' + showicons, 2354 'backcolor=000000&frontcolor=CCCCCC&lightcolor=557722' 2355 ]; 2356 markup.type = 'application/x-shockwave-flash'; 2357 markup.data = options.assetURL + options.flvPlayer; 2358 markup.children = [ 2359 { tag: 'param', name: 'movie', value: options.assetURL + options.flvPlayer }, 2360 { tag: 'param', name: 'flashvars', value: flashvars.join('&') }, 2361 { tag: 'param', name: 'allowfullscreen', value: 'true' } 2362 ]; 2363 break; 2364 case 'qt': 2365 autoplay = autoplay ? 'true' : 'false';
2366 if(controls){ 2367 controls = 'true'; 2368 h += 16; // height of QuickTime controller 2369 }else{ 2370 controls = 'false'; 2371 } 2372 markup.children = [ 2373 { tag: 'param', name: 'src', value: obj.content }, 2374 { tag: 'param', name: 'scale', value: 'aspect' }, 2375 { tag: 'param', name: 'controller', value: controls }, 2376 { tag: 'param', name: 'autoplay', value: autoplay } 2377 ]; 2378 if(isIE){ 2379 markup.classid = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B'; 2380 markup.codebase = 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0'; 2381 }else{ 2382 markup.type = 'video/quicktime'; 2383 markup.data = obj.content; 2384 } 2385 break; 2386 case 'wmp': 2387 autoplay = autoplay ? 1 : 0; 2388 markup.children = [ 2389 { tag: 'param', name: 'autostart', value: autoplay } 2390 ]; 2391 if(isIE){ 2392 if(controls){ 2393 controls = 'full'; 2394 h += 70; // height of WMP controller in IE 2395 }else{ 2396 controls = 'none'; 2397 } 2398 // markup.type = 'application/x-oleobject'; 2399 markup.classid = 'clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6'; 2400 markup.children[markup.children.length] = { tag: 'param', name: 'url', value: obj.content }; 2401 markup.children[markup.children.length] = { tag: 'param', name: 'uimode', value: controls }; 2402 }else{ 2403 if(controls){ 2404 controls = 1; 2405 h += 45; // height of WMP controller in non-IE 2406 }else{ 2407 controls = 0; 2408 } 2409 markup.type = 'video/x-ms-wmv'; 2410 markup.data = obj.content; 2411 markup.children[markup.children.length] = { tag: 'param', name: 'showcontrols', value: controls }; 2412 } 2413 break; 2414 } 2415 2416 markup.height = h; // new height includes controller 2417 markup.width = w; 2418 2419 return markup; 2420 }; 2421 2422 /** 2423 * Creates an HTML string from an object representing HTML elements. Based 2424 * on Ext.DomHelper's createHtml. 2425 * 2426 * @param {Object} obj The HTML definition object 2427 * @return {String} An HTML string 2428 * @public 2429 * @static 2430 */ 2431 Shadowbox.createHTML = function(obj){ 2432 var html = '<' + obj.tag; 2433 for(var attr in obj){ 2434 if(attr == 'tag' || attr == 'html' || attr == 'children') continue; 2435 if(attr == 'cls'){ 2436 html += ' class="' + obj['cls'] + '"'; 2437 }else{ 2438 html += ' ' + attr + '="' + obj[attr] + '"'; 2439 } 2440 } 2441 if(RE.empty.test(obj.tag)){ 2442 html += '/>\n'; 2443 }else{ 2444 html += '>\n'; 2445 var cn = obj.children; 2446 if(cn){ 2447 for(var i = 0, len = cn.length; i < len; ++i){ 2448 html += this.createHTML(cn[i]); 2449 } 2450 } 2451 if(obj.html) html += obj.html; 2452 html += '</' + obj.tag + '>\n'; 2453 } 2454 return html; 2455 }; 2456 2457 /** 2458 * Gets an object that lists which plugins are supported by the client. The 2459 * keys of this object will be: 2460 * 2461 * - fla: Adobe Flash Player 2462 * - qt: QuickTime Player 2463 * - wmp: Windows Media Player 2464 * - f4m: Flip4Mac QuickTime Player 2465 * 2466 * @return {Object} The plugins object 2467 * @public 2468 * @static 2469 */ 2470 Shadowbox.getPlugins = function(){ 2471 return plugins; 2472 }; 2473 2474 /** 2475 * Gets the current options object in use. 2476 * 2477 * @return {Object} The options object 2478 * @public 2479 * @static 2480 */ 2481 Shadowbox.getOptions = function(){ 2482 return options; 2483 }; 2484 2485 /** 2486 * Gets the current gallery object. 2487 * 2488 * @return {Object} The current gallery item 2489 * @public 2490 * @static 2491 */ 2492 Shadowbox.getCurrent = function(){ 2493 return current_gallery[current]; 2494 }; 2495 2496 /** 2497 * Gets the current version number of Shadowbox. 2498 * 2499 * @return {String} The current version 2500 * @public 2501 * @static 2502 */ 2503 Shadowbox.getVersion = function(){ 2504 return version; 2505 }; 2506 2507})(); 2508 2509/** 2510 * Finds the index of the given object in this array. 2511 * 2512 * @param {mixed} o The object to search for 2513 * @return {Number} The index of the given object 2514 * @public 2515 */ 2516Array.prototype.indexOf = Array.prototype.indexOf || function(o){ 2517 for(var i = 0, len = this.length; i < len; ++i){ 2518 if(this[i] == o) return i; 2519 } 2520 return -1; 2521};
2522 2523/** 2524 * Formats a string with the given parameters. The string for format must have 2525 * placeholders that correspond to the numerical index of the arguments passed 2526 * in surrounded by curly braces (e.g. 'Some {0} string {1}'). 2527 * 2528 * @param {String} format The string to format 2529 * @param ... The parameters to put inside the string 2530 * @return {String} The string with the specified parameters 2531 * replaced 2532 * @public 2533 * @static 2534 */ 2535String.format = String.format || function(format){ 2536 var args = Array.prototype.slice.call(arguments, 1); 2537 return format.replace(/\{(\d+)\}/g, function(m, i){ 2538 return args[i]; 2539 }); 2540};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.