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