1/*! 2 * Shadowbox.js, version 3.0.3 3 * http://shadowbox-js.com/ 4 * 5 * Copyright 2007-2010, Michael J. I. Jackson 6 * Date: 2015-06-23 17:44:16 +0200 7 */ 8(function(window, undefined) { 9/** 10 * The Shadowbox object. 11 * 12 * @type {Object} 13 * @public 14 */ 15var S = { 16 17 /** 18 * The current version of Shadowbox. 19 * 20 * @type {String} 21 * @public 22 */ 23 version: "3.0.3" 24 25} 26 27var ua = navigator.userAgent.toLowerCase(); 28 29// operating system detection 30if (ua.indexOf('windows') > -1 || ua.indexOf('win32') > -1) { 31 S.isWindows = true; 32} else if (ua.indexOf('macintosh') > -1 || ua.indexOf('mac os x') > -1) { 33 S.isMac = true; 34} else if (ua.indexOf('linux') > -1) { 35 S.isLinux = true; 36} 37 38// browser detection -- deprecated. the goal is to use object detection 39// instead of the user agent string 40S.isIE = ua.indexOf('msie') > -1; 41S.isIE6 = ua.indexOf('msie 6') > -1; 42S.isIE7 = ua.indexOf('msie 7') > -1; 43S.isGecko = ua.indexOf('gecko') > -1 && ua.indexOf('safari') == -1; 44S.isWebKit = ua.indexOf('applewebkit/') > -1; 45 46var inlineId = /#(.+)$/, 47 galleryName = /^(light|shadow)box\[(.*?)\]/i, 48 inlineParam = /\s*([a-z_]*?)\s*=\s*(.+)\s*/, 49 fileExtension = /[0-9a-z]+$/i, 50 scriptPath = /(.+\/)shadowbox\.js/i; 51 52/** 53 * True if Shadowbox is currently open, false otherwise. 54 * 55 * @type {Boolean} 56 * @private 57 */ 58var open = false, 59 60/** 61 * True if Shadowbox has been initialized, false otherwise. 62 * 63 * @type {Boolean} 64 * @private 65 */ 66initialized = false, 67 68/** 69 * The previous set of options that were used before Shadowbox.applyOptions was 70 * called. 71 * 72 * @type {Object} 73 * @private 74 */ 75lastOptions = {}, 76 77/** 78 * The delay in milliseconds that the current gallery uses. 79 * 80 * @type {Number} 81 * @private 82 */ 83slideDelay = 0, 84 85/** 86 * The time at which the current slideshow frame appeared. 87 * 88 * @type {Number} 89 * @private 90 */ 91slideStart, 92 93/** 94 * The timeout id for the slideshow transition function. 95 * 96 * @type {Number} 97 * @private 98 */ 99slideTimer; 100 101/** 102 * The index of the current object in the gallery array. 103 * 104 * @type {Number} 105 * @public 106 */ 107S.current = -1; 108 109/** 110 * The current dimensions of Shadowbox. 111 * 112 * @type {Object} 113 * @public 114 */ 115S.dimensions = null; 116 117/** 118 * Easing function used for animations. Based on a cubic polynomial. 119 * 120 * @param {Number} state The state of the animation (% complete) 121 * @return {Number} The adjusted easing value 122 * @public 123 */ 124S.ease = function(state) { 125 return 1 + Math.pow(state - 1, 3); 126} 127 128/** 129 * An object containing names of plugins and links to their respective download pages. 130 * 131 * @type {Object} 132 * @public 133 */ 134S.errorInfo = { 135 fla: { 136 name: "Flash", 137 url: "http://www.adobe.com/products/flashplayer/" 138 }, 139 qt: { 140 name: "QuickTime", 141 url: "http://www.apple.com/quicktime/download/" 142 }, 143 wmp: { 144 name: "Windows Media Player", 145 url: "http://www.microsoft.com/windows/windowsmedia/" 146 }, 147 f4m: { 148 name: "Flip4Mac", 149 url: "http://www.flip4mac.com/wmv_download.htm" 150 } 151}; 152 153/** 154 * The content objects in the current set. 155 * 156 * @type {Array} 157 * @public 158 */ 159S.gallery = []; 160 161/** 162 * A function that will be called as soon as the DOM is ready. 163 * 164 * @type {Function} 165 * @public 166 */ 167S.onReady = noop; 168 169/** 170 * The URL path to the Shadowbox script. 171 * 172 * @type {String} 173 * @public 174 */ 175S.path = null; 176 177/** 178 * The current player object. 179 * 180 * @type {Object} 181 * @public 182 */ 183S.player = null; 184 185/** 186 * The id to use for the Shadowbox player element. 187 * 188 * @type {String} 189 * @public 190 */ 191S.playerId = "sb-player"; 192 193/** 194 * Various options that control Shadowbox' behavior. 195 * 196 * @type {Object} 197 * @public 198 */ 199S.options = { 200 201 /** 202 * True to enable animations. 203 * 204 * @type {Boolean} 205 */ 206 animate: true, 207 208 /** 209 * True to enable opacity animations. 210 * 211 * @type {Boolean} 212 */ 213 animateFade: true, 214 215 /** 216 * True to automatically play movies when the load. 217 * 218 * @type {Boolean} 219 */ 220 autoplayMovies: true, 221 222 /** 223 * True to enable the user to skip to the first item in a gallery from the last using 224 * next. 225 * 226 * @type {Boolean} 227 */ 228 continuous: false, 229 230 /** 231 * True to enable keyboard navigation. 232 * 233 * @type {Boolean} 234 */ 235 enableKeys: true, 236 237 /** 238 * Parameters to pass to flash <object>'s. 239 * 240 * @type {Object} 241 */ 242 flashParams: { 243 bgcolor: "#000000", 244 allowfullscreen: true 245 }, 246 247 /** 248 * Variables to pass to flash <object>'s. 249 * 250 * @type {Object} 251 */ 252 flashVars: {}, 253 254 /** 255 * The minimum required Flash version. 256 * 257 * Note: The default is 9.0.115. This is the minimum version suggested by 258 * the JW FLV player. 259 * 260 * @type {String} 261 */ 262 flashVersion: "9.0.115", 263 264 /** 265 * Determines how oversized content is handled. If set to "resize" the
266 * content will be resized while preserving aspect ratio. If "drag" will display 267 * the image at its original resolution but it will be draggable. If "none" will 268 * display the content at its original resolution but it may be cropped. 269 * 270 * @type {String} 271 */ 272 handleOversize: "resize", 273 274 /** 275 * Determines how unsupported content is handled. If set to "remove" will 276 * remove the content from the gallery. If "link" will display a helpful 277 * link to a page where the necessary browser plugin can be installed. 278 * 279 * @type {String} 280 */ 281 handleUnsupported: "link", 282 283 /** 284 * A hook function to be fired when changing from one gallery item to the 285 * next. Is passed the item that is about to be displayed as its only argument. 286 * 287 * @type {Function} 288 */ 289 onChange: noop, 290 291 /** 292 * A hook function to be fired when closing. Is passed the most recent item 293 * as its only argument. 294 * 295 * @type {Function} 296 */ 297 onClose: noop, 298 299 /** 300 * A hook funciton to be fires when content is finished loading. Is passed the 301 * current gallery item as its only argument. 302 * 303 * @type {Function} 304 */ 305 onFinish: noop, 306 307 /** 308 * A hook function to be fired when opening. Is passed the current gallery item 309 * as its only argument. 310 * 311 * @type {Function} 312 */ 313 onOpen: noop, 314 315 /** 316 * True to enable movie controls on movie players. 317 * 318 * @type {Boolean} 319 */ 320 showMovieControls: true, 321 322 /** 323 * True to skip calling setup during init. 324 * 325 * @type {Boolean} 326 */ 327 skipSetup: false, 328 329 /** 330 * The delay (in seconds) to use when displaying a gallery in slideshow mode. Setting 331 * this option to any value other than 0 will trigger slideshow mode. 332 * 333 * @type {Number} 334 */ 335 slideshowDelay: 0, 336 337 /** 338 * The ammount of padding (in pixels) to maintain around the edge of the viewport at all 339 * times. 340 * 341 * @type {Number} 342 */ 343 viewportPadding: 20 344 345}; 346 347/** 348 * Gets the object that is currently being displayed. 349 * 350 * @return {Object} 351 * @public 352 */ 353S.getCurrent = function() { 354 return S.current > -1 ? S.gallery[S.current] : null; 355} 356 357/** 358 * Returns true if there is another object to display after the current. 359 * 360 * @return {Boolean} 361 * @public 362 */ 363S.hasNext = function() { 364 return S.gallery.length > 1 && (S.current != S.gallery.length - 1 || S.options.continuous); 365} 366 367/** 368 * Returns true if Shadowbox is currently open. 369 * 370 * @return {Boolean} 371 * @public 372 */ 373S.isOpen = function() { 374 return open; 375} 376 377/** 378 * Returns true if Shadowbox is currently paused. 379 * 380 * @return {Boolean} 381 * @public 382 */ 383S.isPaused = function() { 384 return slideTimer == "pause"; 385} 386 387/** 388 * Applies the given set of options to Shadowbox' options. May be undone with revertOptions(). 389 * 390 * @param {Object} options 391 * @public 392 */ 393S.applyOptions = function(options) { 394 lastOptions = apply({}, S.options); 395 apply(S.options, options); 396} 397 398/** 399 * Reverts to whatever the options were before applyOptions() was called. 400 * 401 * @public 402 */ 403S.revertOptions = function() { 404 apply(S.options, lastOptions); 405} 406 407/** 408 * Initializes the Shadowbox environment. If options are given here, they 409 * will override the defaults. A callback may be provided that will be called 410 * when the document is ready. This function can be used for setting up links 411 * using Shadowbox.setup. 412 * 413 * @param {Object} options 414 * @param {Function} callback 415 * @public 416 */ 417S.init = function(options, callback) { 418 if (initialized) 419 return; 420 421 initialized = true; 422 423 if (S.skin.options) 424 apply(S.options, S.skin.options); 425 426 if (options) 427 apply(S.options, options); 428 429 if (!S.path) { 430 // determine script path automatically 431 var path, scripts = document.getElementsByTagName("script"); 432 for (var i = 0, len = scripts.length; i < len; ++i) { 433 path = scriptPath.exec(scripts[i].src); 434 if (path) { 435 S.path = path[1]; 436 break; 437 } 438 } 439 } 440 441 if (callback) 442 S.onReady = callback; 443 444 bindLoad(); 445} 446 447/** 448 * Opens the given object in Shadowbox. This object may be any of the following: 449 * 450 * - A URL specifying the location of some content to display 451 * - An HTML link object (A or AREA tag) that links to some content 452 * - A custom object similar to one produced by Shadowbox.makeObject 453 * - An array of any of the above 454 *
455 * Note: When a single link object is given, Shadowbox will automatically search 456 * for other cached link objects that have been set up in the same gallery and 457 * display them all together. 458 * 459 * @param {mixed} obj 460 * @public 461 */ 462S.open = function(obj) { 463 if (open) 464 return; 465 466 var gc = S.makeGallery(obj); 467 S.gallery = gc[0]; 468 S.current = gc[1]; 469 470 obj = S.getCurrent(); 471 472 if (obj == null) 473 return; 474 475 S.applyOptions(obj.options || {}); 476 477 filterGallery(); 478 479 // anything left to display? 480 if (S.gallery.length) { 481 obj = S.getCurrent(); 482 483 if (S.options.onOpen(obj) === false) 484 return; 485 486 open = true; 487 488 S.skin.onOpen(obj, load); 489 } 490} 491 492/** 493 * Closes Shadowbox. 494 * 495 * @public 496 */ 497S.close = function() { 498 if (!open) 499 return; 500 501 open = false; 502 503 if (S.player) { 504 S.player.remove(); 505 S.player = null; 506 } 507 508 if (typeof slideTimer == "number") { 509 clearTimeout(slideTimer); 510 slideTimer = null; 511 } 512 slideDelay = 0; 513 514 listenKeys(false); 515 516 S.options.onClose(S.getCurrent()); 517 518 S.skin.onClose(); 519 520 S.revertOptions(); 521} 522 523/** 524 * Starts a slideshow when a gallery is being displayed. Is called automatically 525 * when the slideshowDelay option is set to anything other than 0. 526 * 527 * @public 528 */ 529S.play = function() { 530 if (!S.hasNext()) 531 return; 532 533 if (!slideDelay) 534 slideDelay = S.options.slideshowDelay * 1000; 535 536 if (slideDelay) { 537 slideStart = now(); 538 slideTimer = setTimeout(function(){ 539 slideDelay = slideStart = 0; // reset slideshow 540 S.next(); 541 }, slideDelay); 542 543 if(S.skin.onPlay) 544 S.skin.onPlay(); 545 } 546} 547 548/** 549 * Pauses a slideshow on the current object. 550 * 551 * @public 552 */ 553S.pause = function() { 554 if (typeof slideTimer != "number") 555 return; 556 557 slideDelay = Math.max(0, slideDelay - (now() - slideStart)); 558 559 // if there's any time left on current slide, pause the timer 560 if (slideDelay) { 561 clearTimeout(slideTimer); 562 slideTimer = "pause"; 563 564 if(S.skin.onPause) 565 S.skin.onPause(); 566 } 567} 568 569/** 570 * Changes Shadowbox to display the item in the gallery specified by index. 571 * 572 * @param {Number} index 573 * @public 574 */ 575S.change = function(index) { 576 if (!(index in S.gallery)) { 577 if (S.options.continuous) { 578 index = (index < 0 ? S.gallery.length + index : 0); // loop 579 if (!(index in S.gallery)) 580 return; 581 } else { 582 return; 583 } 584 } 585 586 S.current = index; 587 588 if (typeof slideTimer == "number") { 589 clearTimeout(slideTimer); 590 slideTimer = null; 591 slideDelay = slideStart = 0; 592 } 593 594 S.options.onChange(S.getCurrent()); 595 596 load(true); 597} 598 599/** 600 * Advances to the next item in the gallery. 601 * 602 * @public 603 */ 604S.next = function() { 605 S.change(S.current + 1); 606} 607 608/** 609 * Rewinds to the previous gallery item. 610 * 611 * @public 612 */ 613S.previous = function() { 614 S.change(S.current - 1); 615} 616 617/** 618 * Calculates the dimensions for Shadowbox. 619 * 620 * @param {Number} height The height of the object 621 * @param {Number} width The width of the object 622 * @param {Number} maxHeight The maximum available height 623 * @param {Number} maxWidth The maximum available width 624 * @param {Number} topBottom The extra top/bottom required for borders/toolbars 625 * @param {Number} leftRight The extra left/right required for borders/toolbars 626 * @param {Number} padding The amount of padding (in pixels) to maintain around 627 * the edge of the viewport 628 * @param {Boolean} preserveAspect True to preserve the original aspect ratio when the 629 * given dimensions are too large 630 * @return {Object} The new dimensions object 631 * @public 632 */ 633S.setDimensions = function(height, width, maxHeight, maxWidth, topBottom, leftRight, padding, preserveAspect) { 634 var originalHeight = height, 635 originalWidth = width; 636 637 // constrain height/width to max 638 var extraHeight = 2 * padding + topBottom; 639 if (height + extraHeight > maxHeight) 640 height = maxHeight - extraHeight; 641 var extraWidth = 2 * padding + leftRight; 642 if (width + extraWidth > maxWidth) 643 width = maxWidth - extraWidth; 644 645 // determine if object is oversized 646 var changeHeight = (originalHeight - height) / originalHeight, 647 changeWidth = (originalWidth - width) / originalWidth, 648 oversized = (changeHeight > 0 || changeWidth > 0); 649 650 // adjust height/width if too large 651 if (preserveAspect && oversized) { 652 // preserve aspect ratio according to greatest change 653 if (changeHeight > changeWidth) { 654 width = Math.round((originalWidth / originalHeight) * height); 655 } else if (changeWidth > changeHeight) { 656 height = Math.round((originalHeight / originalWidth) * width); 657 } 658 } 659 660 S.dimensions = { 661 height: height + topBottom, 662 width: width + leftRight, 663 innerHeight: height, 664 innerWidth: width, 665 top: Math.floor((maxHeight - (height + extraHeight)) / 2 + padding), 666 left: Math.floor((maxWidth - (width + extraWidth)) / 2 + padding), 667 oversized: oversized 668 }; 669 670 return S.dimensions; 671} 672 673/** 674 * Returns an array with two elements. The first is an array of objects that 675 * constitutes the gallery, and the second is the index of the given object in 676 * that array. 677 * 678 * @param {mixed} obj 679 * @return {Array} An array containing the gallery and current index 680 * @public 681 */ 682S.makeGallery = function(obj) { 683 var gallery = [], current = -1; 684 685 if (typeof obj == "string") 686 obj = [obj]; 687 688 if (typeof obj.length == "number") { 689 each(obj, function(i, o) { 690 if (o.content) { 691 gallery[i] = o; 692 } else { 693 gallery[i] = {content: o}; 694 } 695 }); 696 current = 0; 697 } else { 698 if (obj.tagName) { 699 // check the cache for this object before building one on the fly 700 var cacheObj = S.getCache(obj); 701 obj = cacheObj ? cacheObj : S.makeObject(obj); 702 } 703 704 if (obj.gallery) { 705 // gallery object, build gallery from cached gallery objects 706 gallery = []; 707 708 var o; 709 for (var key in S.cache) { 710 o = S.cache[key]; 711 if (o.gallery && o.gallery == obj.gallery) { 712 if (current == -1 && o.content == obj.content) 713 current = gallery.length; 714 gallery.push(o); 715 } 716 } 717 718 if (current == -1) { 719 gallery.unshift(obj); 720 current = 0; 721 } 722 } else { 723 // single object, no gallery 724 gallery = [obj]; 725 current = 0; 726 } 727 } 728 729 // use apply to break references to each gallery object here because 730 // the code may modify certain properties of these objects from here 731 // on out and we want to preserve the original in case the same object 732 // is used again in a future call 733 each(gallery, function(i, o) { 734 gallery[i] = apply({}, o); 735 }); 736 737 return [gallery, current]; 738} 739 740/** 741 * Extracts parameters from a link element and returns an object c
741ontaining 742 * (most of) the following keys: 743 * 744 * - content: The URL of the linked to content 745 * - player: The abbreviated name of the player to use for the object (can automatically 746 * be determined in most cases) 747 * - title: The title to use for the object (optional) 748 * - gallery: The name of the gallery the object belongs to (optional) 749 * - height: The height of the object (in pixels, only required for movies and Flash) 750 * - width: The width of the object (in pixels, only required for movies and Flash) 751 * - options: A set of options to use for this object (optional) 752 * - link: A reference to the original link element 753 * 754 * A custom set of options may be passed in here that will be applied when 755 * this object is displayed. However, any options that are specified in 756 * the link's HTML markup will trump options given here. 757 * 758 * @param {HTMLElement} link 759 * @param {Object} options 760 * @return {Object} An object representing the link 761 * @public 762 */ 763S.makeObject = function(link, options) { 764 var obj = { 765 // accessing the href attribute directly here (instead of using 766 // getAttribute) should give a full URL instead of a relative one 767 content: link.href, 768 title: link.getAttribute("title") || "", 769 link: link 770 }; 771 772 // remove link-level options from top-level options 773 if (options) { 774 options = apply({}, options); 775 each(["player", "title", "height", "width", "gallery"], function(i, o) { 776 if (typeof options[o] != "undefined") { 777 obj[o] = options[o]; 778 delete options[o]; 779 } 780 }); 781 obj.options = options; 782 } else { 783 obj.options = {}; 784 } 785 786 if (!obj.player) 787 obj.player = S.getPlayer(obj.content); 788 789 // HTML options always trump JavaScript options, so do these last 790 var rel = link.getAttribute("rel"); 791 if (rel) { 792 // extract gallery name from shadowbox[name] format 793 var match = rel.match(galleryName); 794 if (match) 795 obj.gallery = escape(match[2]); 796 797 // extract any other parameters 798 each(rel.split(';'), function(i, p) { 799 match = p.match(inlineParam); 800 if (match) 801 obj[match[1]] = match[2]; 802 }); 803 } 804 805 return obj; 806} 807 808/** 809 * Attempts to automatically determine the correct player to use for an object based 810 * on its content attribute. Defaults to "iframe" when the content type cannot 811 * automatically be determined. 812 * 813 * @param {String} content The content attribute of the object 814 * @return {String} The name of the player to use 815 * @public 816 */ 817S.getPlayer = function(content) { 818 if (content.indexOf("#") > -1 && content.indexOf(document.location.href) == 0) 819 return "inline"; 820 821 // strip query string for player detection purposes 822 var q = content.indexOf("?"); 823 if (q > -1) 824 content = content.substring(0, q); 825 826 // get file extension 827 var ext, m = content.match(fileExtension); 828 if (m) 829 ext = m[0].toLowerCase(); 830 831 if (ext) { 832 if (S.img && S.img.ext.indexOf(ext) > -1) 833 return "img"; 834 if (S.swf && S.swf.ext.indexOf(ext) > -1) 835 return "swf"; 836 if (S.flv && S.flv.ext.indexOf(ext) > -1) 837 return "flv"; 838 if (S.qt && S.qt.ext.indexOf(ext) > -1) { 839 if (S.wmp && S.wmp.ext.indexOf(ext) > -1) { 840 return "qtwmp"; // can be played by either QuickTime or Windows Media Player 841 } else { 842 return "qt"; 843 } 844 } 845 if (S.wmp && S.wmp.ext.indexOf(ext) > -1) 846 return "wmp"; 847 } 848 849 return "iframe"; 850} 851 852/** 853 * Filters the current gallery for unsupported objects. 854 * 855 * @private 856 */ 857function filterGallery() { 858 var err = S.errorInfo, plugins = S.plugins, obj, remove, needed, 859 m, format, replace, inlineEl, flashVersion; 860 861 for (var i = 0; i < S.gallery.length; ++i) { 862 obj = S.gallery[i] 863 864 remove = false; // remove the object? 865 needed = null; // what plugins are needed? 866 867 switch (obj.player) { 868 case "flv": 869 case "swf": 870 if (!plugins.fla) 871 needed = "fla"; 872 break; 873 case "qt": 874 if (!plugins.qt) 875 needed = "qt"; 876 break; 877 case "wmp": 878 if (S.isMac) { 879 if (plugins.qt && plugins.f4m) { 880 obj.player = "qt"; 881 } else { 882 needed = "qtf4m"; 883 } 884 } else if (!plugins.wmp) { 885 needed = "wmp"; 886 } 887 break; 888 case "qtwmp": 889 if (plugins.qt) { 890 obj.player = "qt"; 891 } else if (plugins.wmp) { 892 obj.player = "wmp"; 893 } else { 894 needed = "qtwmp"; 895 } 896 break; 897 } 898 899 // handle unsupported elements 900 if (needed) { 901 if (S.options.handleUnsupported == "link") { 902 // generate a link to the appropriate plugin download page(s) 903 switch (needed) { 904 case "qtf4m": 905 format = "shared"; 906 replace = [err.qt.url, err.qt.name, err.f4m.url, err.f4m.name]; 907 break; 908 case "qtwmp": 909 format = "either"; 910 replace = [err.qt.url, err.qt.name, err.wmp.url, err.wmp.name]; 911 break; 912 default: 913 format = "single"; 914 replace = [err[needed].url, err[needed].name]; 915 } 916 917 obj.player = "html"; 918 obj.content = '<div class="sb-message">' + sprintf(S.lang.errors[format], replace) + '</div>'; 919 } else { 920 remove = true; 921 } 922 } else if (obj.player == "inline") { 923 // inline element, retrieve innerHTML 924 m = inlineId.exec(obj.content); 925 if (m) { 926 inlineEl = get(m[1]); 927 if (inlineEl) { 928 obj.content = inlineEl.innerHTML; 929 } else { 930 // cannot find element with id 931 remove = true; 932 } 933 } else { 934 // cannot determine element id from content string 935 remove = true; 936 } 937 } else if (obj.player == "swf" || obj.player == "flv") { 938 flashVersion = (obj.options && obj.options.flashVersion) || S.options.flashVersion; 939 940 if (S.flash && !S.flash.hasFlashPlayerVersion(flashVersion)) { 941 // express install will be triggered because the client does not meet the
942 // minimum required version of Flash. set height and width to those of expressInstall.swf 943 obj.width = 310; 944 // minimum height is 127, but +20 pixels on top and bottom looks better 945 obj.height = 177; 946 } 947 } 948 949 if (remove) { 950 S.gallery.splice(i, 1); 951 952 if (i < S.current) { 953 --S.current; // maintain integrity of S.current 954 } else if (i == S.current) { 955 S.current = i > 0 ? i - 1 : i; // look for supported neighbor 956 } 957 958 // decrement index for next loop 959 --i; 960 } 961 } 962} 963 964/** 965 * Sets up a listener on the document for keydown events. 966 * 967 * @param {Boolean} on True to enable the listener, false to disable 968 * @private 969 */ 970function listenKeys(on) { 971 if (!S.options.enableKeys) 972 return; 973 974 (on ? addEvent : removeEvent)(document, "keydown", handleKey); 975} 976 977/** 978 * A listener function that is fired when a key is pressed. 979 * 980 * @param {Event} e The keydown event 981 * @private 982 */ 983function handleKey(e) { 984 // don't handle events with modifier keys 985 if (e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) 986 return; 987 988 var code = keyCode(e), handler; 989 990 switch (code) { 991 case 81: // q 992 case 88: // x 993 case 27: // esc 994 handler = S.close; 995 break; 996 case 37: // left 997 handler = S.previous; 998 break; 999 case 39: // right 1000 handler = S.next; 1001 break; 1002 case 32: // space 1003 handler = typeof slideTimer == "number" ? S.pause : S.play; 1004 break; 1005 } 1006 1007 if (handler) { 1008 preventDefault(e); 1009 handler(); 1010 } 1011} 1012 1013/** 1014 * Loads the current object. 1015 * 1016 * @param {Boolean} True if changing from a previous object 1017 * @private 1018 */ 1019function load(changing) { 1020 listenKeys(false); 1021 1022 var obj = S.getCurrent(); 1023 1024 // determine player, inline is really just html 1025 var player = (obj.player == "inline" ? "html" : obj.player); 1026 1027 if (typeof S[player] != "function") 1028 throw "unknown player " + player; 1029 1030 if (changing) { 1031 S.player.remove(); 1032 S.revertOptions(); 1033 S.applyOptions(obj.options || {}); 1034 } 1035 1036 S.player = new S[player](obj, S.playerId); 1037 1038 // preload neighboring gallery images 1039 if (S.gallery.length > 1) { 1040 var next = S.gallery[S.current + 1] || S.gallery[0]; 1041 if (next.player == "img") { 1042 var a = new Image(); 1043 a.src = next.content; 1044 } 1045 var prev = S.gallery[S.current - 1] || S.gallery[S.gallery.length - 1]; 1046 if (prev.player == "img") { 1047 var b = new Image(); 1048 b.src = prev.content; 1049 } 1050 } 1051 1052 S.skin.onLoad(changing, waitReady); 1053} 1054 1055/** 1056 * Waits until the current object is ready to be displayed. 1057 * 1058 * @private 1059 */ 1060function waitReady() { 1061 if (!open) 1062 return; 1063 1064 if (typeof S.player.ready != "undefined") { 1065 // wait for content to be ready before loading 1066 var timer = setInterval(function() { 1067 if (open) { 1068 if (S.player.ready) { 1069 clearInterval(timer); 1070 timer = null; 1071 S.skin.onReady(show); 1072 } 1073 } else { 1074 clearInterval(timer); 1075 timer = null; 1076 } 1077 }, 10); 1078 } else { 1079 S.skin.onReady(show); 1080 } 1081} 1082 1083/** 1084 * Displays the current object. 1085 * 1086 * @private 1087 */ 1088function show() { 1089 if (!open) 1090 return; 1091 1092 S.player.append(S.skin.body, S.dimensions); 1093 1094 S.skin.onShow(finish); 1095} 1096 1097/** 1098 * Finishes up any remaining tasks after the object is displayed. 1099 * 1100 * @private 1101 */ 1102function finish() { 1103 if (!open) 1104 return; 1105 1106 if (S.player.onLoad) 1107 S.player.onLoad(); 1108 1109 S.options.onFinish(S.getCurrent()); 1110 1111 if (!S.isPaused()) 1112 S.play(); // kick off next slide 1113 1114 listenKeys(true); 1115} 1116if (!Array.prototype.indexOf) { 1117 Array.prototype.indexOf = function(obj, from) { 1118 var len = this.length >>> 0; 1119 1120 from = from || 0; 1121 if (from < 0) 1122 from += len; 1123 1124 for (; from < len; ++from) { 1125 if (from in this && this[from] === obj) 1126 return from; 1127 } 1128 1129 return -1; 1130 } 1131} 1132 1133/** 1134 * Gets the current time in milliseconds. 1135 * 1136 * @return {Number} 1137 * @private 1138 */ 1139function now() { 1140 return (new Date).getTime(); 1141} 1142 1143/** 1144 * Applies all properties of extension to original. 1145 * 1146 * @param {Object} original 1147 * @param {Object} extension 1148 * @return {Object} The original object 1149 * @private 1150 */ 1151function apply(original, extension) { 1152 for (var property in extension) 1153 original[property] = extension[property]; 1154 return original; 1155} 1156 1157/** 1158 * Calls the given callback function for each element in obj. Note: obj must be an array-like 1159 * object. 1160 * 1161 * @param {Array|mixed} obj 1162 * @param {Function} callback 1163 * @private 1164 */ 1165function each(obj, callback) { 1166 var i = 0, len = obj.length; 1167 for (var value = obj[0]; i < len && callback.call(value, i, value) !== false; value = obj[++i]) {} 1168} 1169 1170/** 1171 * Formats a string with the elements in the replacement array. The string should contain 1172 * tokens in the format {n} where n corresponds to the index of property name of the replacement 1173 * in the replace object. 1174 * 1175 * Example: 1176 * 1177 * format('Hello {0}', ['World']); // "Hello World" 1178 * format('Hello {world}', {world: "World"}); // "Hello World" 1179 *
1180 * @param {String} str The format spec string 1181 * @param {Array|Object} replace The array/object of replacement values 1182 * @return {String} The formatted string 1183 * @private 1184 */ 1185function sprintf(str, replace) { 1186 return str.replace(/\{(\w+?)\}/g, function(match, i) { 1187 return replace[i]; 1188 }); 1189} 1190 1191/** 1192 * A no-op function. 1193 * 1194 * @private 1195 */ 1196function noop() {} 1197 1198/** 1199 * Gets the element with the given id. 1200 * 1201 * @param {String} id 1202 * @return {HTMLElement} 1203 * @private 1204 */ 1205function get(id) { 1206 return document.getElementById(id); 1207} 1208 1209/** 1210 * Removes an element from the DOM. 1211 * 1212 * @param {HTMLElement} el The element to remove 1213 * @private 1214 */ 1215function remove(el) { 1216 el.parentNode.removeChild(el); 1217} 1218 1219/** 1220 * True if this browser supports opacity. 1221 * 1222 * @type {Boolean} 1223 * @private 1224 */ 1225var supportsOpacity = true, 1226 1227/** 1228 * True if the browser supports fixed positioning. 1229 * 1230 * @type {Boolean} 1231 * @private 1232 */ 1233supportsFixed = true; 1234 1235/** 1236 * Checks the level of support the browser provides. Should be called when 1237 * the DOM is ready to be manipulated. 1238 * 1239 * @private 1240 */ 1241function checkSupport() { 1242 var body = document.body, 1243 div = document.createElement("div"); 1244 1245 // detect opacity support 1246 supportsOpacity = typeof div.style.opacity === "string"; 1247 1248 // detect support for fixed positioning 1249 div.style.position = "fixed"; 1250 div.style.margin = 0; 1251 div.style.top = "20px"; 1252 body.appendChild(div, body.firstChild); 1253 supportsFixed = div.offsetTop == 20; 1254 body.removeChild(div); 1255} 1256 1257/** 1258 * Gets the computed value of the style on the given element. 1259 * 1260 * Note: This function is not safe for retrieving float values or non-pixel values 1261 * in IE. 1262 * 1263 * @param {HTMLElement} el The element 1264 * @param {String} style The camel-cased name of the style 1265 * @return {mixed} The computed value of the given style 1266 * @public 1267 */ 1268S.getStyle = (function() { 1269 var opacity = /opacity=([^)]*)/, 1270 getComputedStyle = document.defaultView && document.defaultView.getComputedStyle; 1271 1272 return function(el, style) { 1273 var ret; 1274 1275 if (!supportsOpacity && style == "opacity" && el.currentStyle) { 1276 ret = opacity.test(el.currentStyle.filter || "") ? (parseFloat(RegExp.$1) / 100) + "" : ""; 1277 return ret === "" ? "1" : ret; 1278 } 1279 1280 if (getComputedStyle) { 1281 var computedStyle = getComputedStyle(el, null); 1282 1283 if (computedStyle) 1284 ret = computedStyle[style]; 1285 1286 if (style == "opacity" && ret == "") 1287 ret = "1"; 1288 } else { 1289 ret = el.currentStyle[style]; 1290 } 1291 1292 return ret; 1293 } 1294})(); 1295 1296/** 1297 * Appends an HTML fragment to the given element. 1298 * 1299 * @param {HTMLElement} el 1300 * @param {String} html The HTML fragment to use 1301 * @public 1302 */ 1303S.appendHTML = function(el, html) { 1304 if (el.insertAdjacentHTML) { 1305 el.insertAdjacentHTML("BeforeEnd", html); 1306 } else if (el.lastChild) { 1307 var range = el.ownerDocument.createRange(); 1308 range.setStartAfter(el.lastChild); 1309 var frag = range.createContextualFragment(html); 1310 el.appendChild(frag); 1311 } else { 1312 el.innerHTML = html; 1313 } 1314} 1315 1316/** 1317 * Gets the window size. The dimension may be either "Height" or "Width". 1318 * 1319 * @param {String} dimension 1320 * @return {Number} 1321 * @public 1322 */ 1323S.getWindowSize = function(dimension) { 1324 if (document.compatMode === "CSS1Compat") 1325 return document.documentElement["client" + dimension]; 1326 1327 return document.body["client" + dimension]; 1328} 1329 1330/** 1331 * Sets an element's opacity. 1332 * 1333 * @param {HTMLElement} el 1334 * @param {Number} opacity 1335 * @public 1336 */ 1337S.setOpacity = function(el, opacity) { 1338 var style = el.style; 1339 if (supportsOpacity) { 1340 style.opacity = (opacity == 1 ? "" : opacity); 1341 } else { 1342 style.zoom = 1; // trigger hasLayout 1343 if (opacity == 1) { 1344 if (typeof style.filter == "string" && (/alpha/i).test(style.filter)) 1345 style.filter = style.filter.replace(/\s*[\w\.]*alpha\([^\)]*\);?/gi, ""); 1346 } else { 1347 style.filter = (style.filter || "").replace(/\s*[\w\.]*alpha\([^\)]*\)/gi, "") + 1348 " alpha(opacity=" + (opacity * 100) + ")"; 1349 } 1350 } 1351} 1352 1353/** 1354 * Clears the opacity setting on the given element. Needed for some cases in IE. 1355 * 1356 * @param {HTMLElement} el 1357 * @public 1358 */ 1359S.clearOpacity = function(el) { 1360 S.setOpacity(el, 1); 1361} 1362/** 1363 * The base adapter for Shadowbox. 1364 */ 1365 1366/** 1367 * Gets the target of the given event. The event object passed will be 1368 * the same object that is passed to listeners registered with 1369 * addEvent(). 1370 * 1371 * @param {Event} e The event object 1372 * @return {HTMLElement} The event's target element 1373 * @private 1374 */ 1375function getTarget(e) { 1376 var target = e.target ? e.target : e.srcElement; 1377 return target.nodeType == 3 ? target.parentNode : target; 1378} 1379 1380/** 1381 * Gets the page X/Y coordinates of the mouse event in an [x, y] array. 1382 * The page coordinates should be relative to the document, and not the 1383 * viewport. The event object provided here will be the same object that 1384 * is passed to listeners registered with addEvent(). 1385 * 1386 * @param {Event} e The event object 1387 * @return {Array} The page X/Y coordinates 1388 * @private 1389 */ 1390function getPageXY(e) { 1391 var x = e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)), 1392 y = e.pageY || (e.clientY + (document.documentElement.scrollTop || document.body.scrollTop)); 1393 return [x, y]; 1394} 1395 1396/** 1397 * Prevents the event's default behavior. The event object passed will 1398 * be the same object that is passed to listeners registered with 1399 * addEvent(). 1400 * 1401 * @param {Event} e The event object 1402 * @private 1403 */ 1404function preventDefault(e) { 1405 e.preventDefault(); 1406} 1407 1408/** 1409 * Gets the key code of the given event object (keydown). The event 1410 * object here will be the same object that is passed to listeners 1411 * registered with addEvent(). 1412 * 1413 * @param {Event} e The event object 1414 * @return {Number} The key code of the event 1415 * @private 1416 */ 1417function keyCode(e) { 1418 return e.which ? e.which : e.keyCode; 1419} 1420 1421// Event handling functions below modified from original by Dean Edwards 1422// http://dean.edwards.name/my/events.js 1423 1424/** 1425 * Adds an event handler to the given element. The handler should be called 1426 * in the scope of the element with the event object as its only argument. 1427 * 1428 * @param {HTMLElement} el The element to listen to
1429 * @param {String} type The type of the event to add 1430 * @param {Function} handler The event handler function 1431 * @private 1432 */ 1433function addEvent(el, type, handler) { 1434 if (el.addEventListener) { 1435 el.addEventListener(type, handler, false); 1436 } else { 1437 if (el.nodeType === 3 || el.nodeType === 8) 1438 return; 1439 1440 if (el.setInterval && (el !== window && !el.frameElement)) 1441 el = window; 1442 1443 if (!handler.__guid) 1444 handler.__guid = addEvent.guid++; 1445 1446 if (!el.events) 1447 el.events = {}; 1448 1449 var handlers = el.events[type]; 1450 if (!handlers) { 1451 handlers = el.events[type] = {}; 1452 1453 if (el["on" + type]) 1454 handlers[0] = el["on" + type]; 1455 } 1456 1457 handlers[handler.__guid] = handler; 1458 1459 el["on" + type] = addEvent.handleEvent; 1460 } 1461} 1462 1463addEvent.guid = 1; 1464 1465addEvent.handleEvent = function(event) { 1466 var result = true; 1467 event = event || addEvent.fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); 1468 var handlers = this.events[event.type]; 1469 1470 for (var i in handlers) { 1471 this.__handleEvent = handlers[i]; 1472 if (this.__handleEvent(event) === false) 1473 result = false;
1474 } 1475 1476 return result; 1477} 1478 1479addEvent.preventDefault = function() { 1480 this.returnValue = false; 1481} 1482 1483addEvent.stopPropagation = function() { 1484 this.cancelBubble = true; 1485} 1486 1487addEvent.fixEvent = function(e) { 1488 e.preventDefault = addEvent.preventDefault; 1489 e.stopPropagation = addEvent.stopPropagation; 1490 return e; 1491} 1492 1493/** 1494 * Removes an event handler from the given element. 1495 * 1496 * @param {HTMLElement} el The DOM element to stop listening to 1497 * @param {String} type The type of the event to remove 1498 * @param {Function} handler The event handler function 1499 * @private 1500 */ 1501function removeEvent(el, type, handler) { 1502 if (el.removeEventListener) { 1503 el.removeEventListener(type, handler, false); 1504 } else { 1505 if (el.events && el.events[type]) 1506 delete el.events[type][handler.__guid]; 1507 } 1508} 1509// The code in this file is adapted for Shadowbox from the jQuery JavaScript library 1510 1511/** 1512 * True if Shadowbox has been loaded into the DOM, false otherwise. 1513 * 1514 * @type {Boolean} 1515 * @private 1516 */ 1517var loaded = false, 1518 1519/** 1520 * The callback function for the DOMContentLoaded browser event. 1521 * 1522 * @type {Function} 1523 * @private 1524 */ 1525DOMContentLoaded; 1526 1527if (document.addEventListener) { 1528 DOMContentLoaded = function() { 1529 document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false); 1530 S.load(); 1531 } 1532} else if (document.attachEvent) { 1533 DOMContentLoaded = function() { 1534 if (document.readyState === "complete") { 1535 document.detachEvent("onreadystatechange", DOMContentLoaded); 1536 S.load(); 1537 } 1538 } 1539} 1540 1541/** 1542 * A DOM ready check for IE. 1543 * 1544 * @private 1545 */ 1546function doScrollCheck() { 1547 if (loaded) 1548 return; 1549 1550 try { 1551 document.documentElement.doScroll("left"); 1552 } catch (e) { 1553 setTimeout(doScrollCheck, 1); 1554 return; 1555 } 1556 1557 S.load(); 1558} 1559 1560/** 1561 * Waits for the DOM to be ready before firing the given callback function. 1562 * 1563 * @param {Function} callback 1564 * @private 1565 */ 1566function bindLoad() { 1567 if (document.readyState === "complete") 1568 return S.load(); 1569 1570 if (document.addEventListener) { 1571 document.addEventListener("DOMContentLoaded", DOMContentLoaded, false); 1572 window.addEventListener("load", S.load, false); 1573 } else if (document.attachEvent) { 1574 document.attachEvent("onreadystatechange", DOMContentLoaded); 1575 window.attachEvent("onload", S.load); 1576 1577 var topLevel = false; 1578 try { 1579 topLevel = window.frameElement === null; 1580 } catch (e) {} 1581 1582 if (document.documentElement.doScroll && topLevel) 1583 doScrollCheck(); 1584 } 1585} 1586 1587/** 1588 * Loads the Shadowbox code into the DOM. Is called automatically when the document 1589 * is ready. 1590 * 1591 * @public 1592 */ 1593S.load = function() { 1594 if (loaded) 1595 return; 1596 1597 if (!document.body) 1598 return setTimeout(S.load, 13); 1599 1600 loaded = true; 1601 1602 checkSupport(); 1603 1604 S.onReady(); 1605 1606 if (!S.options.skipSetup) 1607 S.setup(); 1608 1609 S.skin.init(); 1610} 1611/** 1612 * Contains plugin support information. Each property of this object is a 1613 * boolean indicating whether that plugin is supported. Keys are: 1614 * 1615 * - fla: Flash player 1616 * - qt: QuickTime player 1617 * - wmp: Windows Media player 1618 * - f4m: Flip4Mac plugin 1619 * 1620 * @type {Object} 1621 * @public 1622 */ 1623S.plugins = {}; 1624 1625if (navigator.plugins && navigator.plugins.length) { 1626 var names = []; 1627 each(navigator.plugins, function(i, p) { 1628 names.push(p.name); 1629 }); 1630 names = names.join(','); 1631 1632 var f4m = names.indexOf('Flip4Mac') > -1; 1633 1634 S.plugins = { 1635 fla: names.indexOf('Shockwave Flash') > -1, 1636 qt: names.indexOf('QuickTime') > -1, 1637 wmp: !f4m && names.indexOf('Windows Media') > -1, // if it's Flip4Mac, it's not really WMP 1638 f4m: f4m 1639 }; 1640} else { 1641 var detectPlugin = function(name) { 1642 var axo; 1643 try { 1644 axo = new ActiveXObject(name); 1645 } catch(e) {} 1646 return !!axo; 1647 } 1648 1649 S.plugins = { 1650 fla: detectPlugin('ShockwaveFlash.ShockwaveFlash'), 1651 qt: detectPlugin('QuickTime.QuickTime'), 1652 wmp: detectPlugin('wmplayer.ocx'), 1653 f4m: false 1654 }; 1655} 1656// used to match the rel attribute of links 1657var relAttr = /^(light|shadow)box/i, 1658 1659/** 1660 * The name of the expando property that Shadowbox uses on HTML elements 1661 * to store the cache index of that element. 1662 * 1663 * @type {String} 1664 * @private 1665 */ 1666expando = "shadowboxCacheKey", 1667 1668/** 1669 * A unique id counter. 1670 * 1671 * @type {Number} 1672 * @private 1673 */ 1674cacheKey = 1; 1675 1676/** 1677 * Contains all link objects that have been cached. 1678 * 1679 * @type {Object} 1680 * @public 1681 */ 1682S.cache = {}; 1683 1684/** 1685 * Resolves a link selector. The selector may be omitted to select all anchor elements 1686 * on the page with rel="shadowbox" or, if Shadowbox.find is used, it may be a single CSS 1687 * selector or an array of [selector, [context]]. 1688 * 1689 * @param {mixed} selector 1690 * @return {Array} An array of matching link elements 1691 * @public 1692 */ 1693S.select = function(selector) { 1694 var links = []; 1695 1696 if (!selector) { 1697 var rel; 1698 each(document.getElementsByTagName("a"), function(i, el) { 1699 rel = el.getAttribute("rel"); 1700 if (rel && relAttr.test(rel)) 1701 links.push(el); 1702 }); 1703 } else { 1704 var length = selector.length; 1705 if (length) { 1706 if (typeof selector == "string") { 1707 if (S.find) 1708 links = S.find(selector); // css selector 1709 } else if (length == 2 && typeof selector[0] == "string" && selector[1].nodeType) { 1710 if (S.find) 1711 links = S.find(selector[0], selector[1]); // css selector + context 1712 } else { 1713 // array of links (or node list) 1714 for (var i = 0; i < length; ++i) 1715 links[i] = selector[i]; 1716 } 1717 } else { 1718 links.push(selector); // single link 1719 } 1720 } 1721 1722 return links; 1723} 1724 1725/** 1726 * Adds all links specified by the given selector to the cache. If no selector 1727 * is provided, will select every anchor element on the page with rel="shadowbox". 1728 *
1729 * Note: Options given here apply only to links selected by the given selector. 1730 * Also, because <area> elements do not support the rel attribute, they must be 1731 * explicitly passed to this method. 1732 * 1733 * @param {mixed} selector 1734 * @param {Object} options Some options to use for the given links 1735 * @public 1736 */ 1737S.setup = function(selector, options) { 1738 each(S.select(selector), function(i, link) { 1739 S.addCache(link, options); 1740 }); 1741} 1742 1743/** 1744 * Removes all links specified by the given selector from the cache. 1745 * 1746 * @param {mixed} selector 1747 * @public 1748 */ 1749S.teardown = function(selector) { 1750 each(S.select(selector), function(i, link) { 1751 S.removeCache(link); 1752 }); 1753} 1754 1755/** 1756 * Adds the given link element to the cache with the given options. 1757 * 1758 * @param {HTMLElement} link 1759 * @param {Object} options 1760 * @public 1761 */ 1762S.addCache = function(link, options) { 1763 var key = link[expando]; 1764 1765 if (key == undefined) { 1766 key = cacheKey++; 1767 // assign cache key expando, use integer primitive to avoid memory leak in IE 1768 link[expando] = key; 1769 // add onclick listener 1770 addEvent(link, "click", handleClick); 1771 } 1772 1773 S.cache[key] = S.makeObject(link, options); 1774} 1775 1776/** 1777 * Removes the given link element from the cache. 1778 * 1779 * @param {HTMLElement} link 1780 * @public 1781 */ 1782S.removeCache = function(link) { 1783 removeEvent(link, "click", handleClick); 1784 delete S.cache[link[expando]]; 1785 link[expando] = null; 1786} 1787 1788/** 1789 * Gets the object from cache representative of the given link element (if there is one). 1790 * 1791 * @param {HTMLElement} link 1792 * @return {Object} 1793 * @public 1794 */ 1795S.getCache = function(link) { 1796 var key = link[expando]; 1797 return (key in S.cache && S.cache[key]); 1798} 1799 1800/** 1801 * Removes all onclick listeners from elements that have previously been setup with 1802 * Shadowbox and clears all objects from cache. 1803 * 1804 * @public 1805 */ 1806S.clearCache = function() { 1807 for (var key in S.cache) 1808 S.removeCache(S.cache[key].link); 1809 1810 S.cache = {}; 1811} 1812 1813/** 1814 * Handles all clicks on links that have been set up to work with Shadowbox 1815 * and cancels the default event behavior when appropriate. 1816 * 1817 * @param {Event} e The click event 1818 * @private 1819 */ 1820function handleClick(e) { 1821 //preventDefault(e); // good for debugging 1822 1823 S.open(this); 1824 1825 if (S.gallery.length) 1826 preventDefault(e); 1827} 1828/*! 1829 * Sizzle CSS Selector Engine - v1.0 1830 * Copyright 2009, The Dojo Foundation 1831 * Released under the MIT, BSD, and GPL Licenses. 1832 * More information: http://sizzlejs.com/ 1833 * 1834 * Modified for inclusion in Shadowbox.js 1835 */ 1836S.find = (function(){ 1837 1838var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, 1839 done = 0, 1840 toString = Object.prototype.toString, 1841 hasDuplicate = false, 1842 baseHasDuplicate = true; 1843 1844// Here we check if the JavaScript engine is using some sort of 1845// optimization where it does not always call our comparision 1846// function. If that is the case, discard the hasDuplicate value. 1847// Thus far that includes Google Chrome. 1848[0, 0].sort(function(){ 1849 baseHasDuplicate = false; 1850 return 0; 1851}); 1852 1853var Sizzle = function(selector, context, results, seed) { 1854 results = results || []; 1855 var origContext = context = context || document; 1856 1857 if ( context.nodeType !== 1 && context.nodeType !== 9 ) { 1858 return []; 1859 } 1860 1861 if ( !selector || typeof selector !== "string" ) { 1862 return results; 1863 } 1864 1865 var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context), 1866 soFar = selector; 1867 1868 // Reset the position of the chunker regexp (start from head) 1869 while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { 1870 soFar = m[3]; 1871 1872 parts.push( m[1] ); 1873 1874 if ( m[2] ) { 1875 extra = m[3]; 1876 break; 1877 } 1878 } 1879 1880 if ( parts.length > 1 && origPOS.exec( selector ) ) { 1881 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { 1882 set = posProcess( parts[0] + parts[1], context ); 1883 } else { 1884 set = Expr.relative[ parts[0] ] ? 1885 [ context ] : 1886 Sizzle( parts.shift(), context ); 1887 1888 while ( parts.length ) { 1889 selector = parts.shift(); 1890 1891 if ( Expr.relative[ selector ] ) { 1892 selector += parts.shift(); 1893 } 1894 1895 set = posProcess( selector, set ); 1896 } 1897 } 1898 } else { 1899 // Take a shortcut and set the context if the root selector is an ID 1900 // (but not if it'll be faster if the inner selector is an ID) 1901 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && 1902 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { 1903 var ret = Sizzle.find( parts.shift(), context, contextXML ); 1904 context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; 1905 } 1906 1907 if ( context ) { 1908 var ret = seed ? 1909 { expr: parts.pop(), set: makeArray(seed) } : 1910 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); 1911 set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; 1912 1913 if ( parts.length > 0 ) { 1914 checkSet = makeArray(set); 1915 } else { 1916 prune = false;
1917 } 1918 1919 while ( parts.length ) { 1920 var cur = parts.pop(), pop = cur; 1921 1922 if ( !Expr.relative[ cur ] ) { 1923 cur = ""; 1924 } else { 1925 pop = parts.pop(); 1926 } 1927 1928 if ( pop == null ) { 1929 pop = context; 1930 } 1931 1932 Expr.relative[ cur ]( checkSet, pop, contextXML ); 1933 } 1934 } else { 1935 checkSet = parts = []; 1936 } 1937 } 1938 1939 if ( !checkSet ) { 1940 checkSet = set; 1941 } 1942 1943 if ( !checkSet ) { 1944 throw "Syntax error, unrecognized expression: " + (cur || selector); 1945 } 1946 1947 if ( toString.call(checkSet) === "[object Array]" ) { 1948 if ( !prune ) { 1949 results.push.apply( results, checkSet ); 1950 } else if ( context && context.nodeType === 1 ) { 1951 for ( var i = 0; checkSet[i] != null; i++ ) { 1952 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { 1953 results.push( set[i] ); 1954 } 1955 } 1956 } else { 1957 for ( var i = 0; checkSet[i] != null; i++ ) { 1958 if ( checkSet[i] && checkSet[i].nodeType === 1 ) { 1959 results.push( set[i] ); 1960 } 1961 } 1962 } 1963 } else { 1964 makeArray( checkSet, results ); 1965 } 1966 1967 if ( extra ) { 1968 Sizzle( extra, origContext, results, seed ); 1969 Sizzle.uniqueSort( results ); 1970 } 1971 1972 return results; 1973}; 1974 1975Sizzle.uniqueSort = function(results){ 1976 if ( sortOrder ) { 1977 hasDuplicate = baseHasDuplicate; 1978 results.sort(sortOrder); 1979 1980 if ( hasDuplicate ) { 1981 for ( var i = 1; i < results.length; i++ ) { 1982 if ( results[i] === results[i-1] ) { 1983 results.splice(i--, 1); 1984 } 1985 } 1986 } 1987 } 1988 1989 return results; 1990}; 1991 1992Sizzle.matches = function(expr, set){ 1993 return Sizzle(expr, null, null, set); 1994}; 1995 1996Sizzle.find = function(expr, context, isXML){ 1997 var set, match; 1998 1999 if ( !expr ) { 2000 return []; 2001 } 2002 2003 for ( var i = 0, l = Expr.order.length; i < l; i++ ) { 2004 var type = Expr.order[i], match; 2005 2006 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { 2007 var left = match[1]; 2008 match.splice(1,1); 2009 2010 if ( left.substr( left.length - 1 ) !== "\\" ) { 2011 match[1] = (match[1] || "").replace(/\\/g, ""); 2012 set = Expr.find[ type ]( match, context, isXML ); 2013 if ( set != null ) { 2014 expr = expr.replace( Expr.match[ type ], "" ); 2015 break; 2016 } 2017 } 2018 } 2019 } 2020 2021 if ( !set ) { 2022 set = context.getElementsByTagName("*"); 2023 } 2024 2025 return {set: set, expr: expr}; 2026}; 2027 2028Sizzle.filter = function(expr, set, inplace, not){ 2029 var old = expr, result = [], curLoop = set, match, anyFound, 2030 isXMLFilter = set && set[0] && isXML(set[0]); 2031 2032 while ( expr && set.length ) { 2033 for ( var type in Expr.filter ) { 2034 if ( (match = Expr.match[ type ].exec( expr )) != null ) { 2035 var filter = Expr.filter[ type ], found, item; 2036 anyFound = false; 2037 2038 if ( curLoop === result ) { 2039 result = []; 2040 } 2041 2042 if ( Expr.preFilter[ type ] ) { 2043 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); 2044 2045 if ( !match ) { 2046 anyFound = found = true; 2047 } else if ( match === true ) { 2048 continue; 2049 } 2050 } 2051 2052 if ( match ) { 2053 for ( var i = 0; (item = curLoop[i]) != null; i++ ) { 2054 if ( item ) { 2055 found = filter( item, match, i, curLoop ); 2056 var pass = not ^ !!found; 2057 2058 if ( inplace && found != null ) { 2059 if ( pass ) { 2060 anyFound = true; 2061 } else { 2062 curLoop[i] = false; 2063 } 2064 } else if ( pass ) { 2065 result.push( item ); 2066 anyFound = true; 2067 } 2068 } 2069 } 2070 } 2071 2072 if ( found !== undefined ) { 2073 if ( !inplace ) { 2074 curLoop = result; 2075 } 2076 2077 expr = expr.replace( Expr.match[ type ], "" ); 2078 2079 if ( !anyFound ) { 2080 return []; 2081 } 2082 2083 break; 2084 } 2085 } 2086 } 2087 2088 // Improper expression 2089 if ( expr === old ) { 2090 if ( anyFound == null ) { 2091 throw "Syntax error, unrecognized expression: " + expr; 2092 } else { 2093 break; 2094 } 2095 } 2096 2097 old = expr; 2098 } 2099 2100 return curLoop; 2101}; 2102 2103var Expr = Sizzle.selectors = { 2104 order: [ "ID", "NAME", "TAG" ], 2105 match: { 2106 ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, 2107 CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, 2108 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, 2109 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, 2110 TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, 2111 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
2112 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, 2113 PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ 2114 }, 2115 leftMatch: {}, 2116 attrMap: { 2117 "class": "className", 2118 "for": "htmlFor" 2119 }, 2120 attrHandle: { 2121 href: function(elem){ 2122 return elem.getAttribute("href"); 2123 } 2124 }, 2125 relative: { 2126 "+": function(checkSet, part){ 2127 var isPartStr = typeof part === "string", 2128 isTag = isPartStr && !/\W/.test(part), 2129 isPartStrNotTag = isPartStr && !isTag; 2130 2131 if ( isTag ) { 2132 part = part.toLowerCase(); 2133 } 2134 2135 for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { 2136 if ( (elem = checkSet[i]) ) { 2137 while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} 2138 2139 checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? 2140 elem || false : 2141 elem === part; 2142 } 2143 } 2144 2145 if ( isPartStrNotTag ) { 2146 Sizzle.filter( part, checkSet, true ); 2147 } 2148 }, 2149 ">": function(checkSet, part){ 2150 var isPartStr = typeof part === "string"; 2151 2152 if ( isPartStr && !/\W/.test(part) ) { 2153 part = part.toLowerCase(); 2154 2155 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 2156 var elem = checkSet[i]; 2157 if ( elem ) { 2158 var parent = elem.parentNode; 2159 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; 2160 } 2161 } 2162 } else { 2163 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 2164 var elem = checkSet[i]; 2165 if ( elem ) { 2166 checkSet[i] = isPartStr ? 2167 elem.parentNode : 2168 elem.parentNode === part; 2169 } 2170 } 2171 2172 if ( isPartStr ) { 2173 Sizzle.filter( part, checkSet, true ); 2174 } 2175 } 2176 }, 2177 "": function(checkSet, part, isXML){ 2178 var doneName = done++, checkFn = dirCheck; 2179 2180 if ( typeof part === "string" && !/\W/.test(part) ) { 2181 var nodeCheck = part = part.toLowerCase(); 2182 checkFn = dirNodeCheck; 2183 } 2184 2185 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); 2186 }, 2187 "~": function(checkSet, part, isXML){ 2188 var doneName = done++, checkFn = dirCheck; 2189 2190 if ( typeof part === "string" && !/\W/.test(part) ) { 2191 var nodeCheck = part = part.toLowerCase(); 2192 checkFn = dirNodeCheck; 2193 } 2194 2195 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); 2196 } 2197 }, 2198 find: { 2199 ID: function(match, context, isXML){ 2200 if ( typeof context.getElementById !== "undefined" && !isXML ) { 2201 var m = context.getElementById(match[1]); 2202 return m ? [m] : []; 2203 } 2204 }, 2205 NAME: function(match, context){ 2206 if ( typeof context.getElementsByName !== "undefined" ) { 2207 var ret = [], results = context.getElementsByName(match[1]); 2208 2209 for ( var i = 0, l = results.length; i < l; i++ ) { 2210 if ( results[i].getAttribute("name") === match[1] ) { 2211 ret.push( results[i] ); 2212 } 2213 } 2214 2215 return ret.length === 0 ? null : ret; 2216 } 2217 }, 2218 TAG: function(match, context){ 2219 return context.getElementsByTagName(match[1]); 2220 } 2221 }, 2222 preFilter: { 2223 CLASS: function(match, curLoop, inplace, result, not, isXML){ 2224 match = " " + match[1].replace(/\\/g, "") + " "; 2225 2226 if ( isXML ) { 2227 return match; 2228 } 2229 2230 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { 2231 if ( elem ) { 2232 if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { 2233 if ( !inplace ) { 2234 result.push( elem ); 2235 } 2236 } else if ( inplace ) { 2237 curLoop[i] = false; 2238 } 2239 } 2240 } 2241 2242 return false; 2243 }, 2244 ID: function(match){ 2245 return match[1].replace(/\\/g, ""); 2246 }, 2247 TAG: function(match, curLoop){ 2248 return match[1].toLowerCase(); 2249 }, 2250 CHILD: function(match){ 2251 if ( match[1] === "nth" ) { 2252 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' 2253 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( 2254 match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || 2255 !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); 2256 2257 // calculate the numbers (first)n+(last) including if they are negative 2258 match[2] = (test[1] + (test[2] || 1)) - 0; 2259 match[3] = test[3] - 0; 2260 } 2261 2262 // TODO: Move to normal caching system 2263 match[0] = done++; 2264 2265 return match; 2266 }, 2267 ATTR: function(match, curLoop, inplace, result, not, isXML){ 2268 var name = match[1].replace(/\\/g, ""); 2269 2270 if ( !isXML && Expr.attrMap[name] ) { 2271 match[1] = Expr.attrMap[name]; 2272 } 2273 2274 if ( match[2] === "~=" ) { 2275 match[4] = " " + match[4] + " "; 2276 } 2277 2278 return match; 2279 }, 2280 PSEUDO: function(match, curLoop, inplace, result, not){
2281 if ( match[1] === "not" ) { 2282 // If we're dealing with a complex expression, or a simple one 2283 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { 2284 match[3] = Sizzle(match[3], null, null, curLoop); 2285 } else { 2286 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); 2287 if ( !inplace ) { 2288 result.push.apply( result, ret ); 2289 } 2290 return false; 2291 } 2292 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { 2293 return true; 2294 } 2295 2296 return match; 2297 }, 2298 POS: function(match){ 2299 match.unshift( true ); 2300 return match; 2301 } 2302 }, 2303 filters: { 2304 enabled: function(elem){ 2305 return elem.disabled === false && elem.type !== "hidden"; 2306 }, 2307 disabled: function(elem){ 2308 return elem.disabled === true; 2309 }, 2310 checked: function(elem){ 2311 return elem.checked === true; 2312 }, 2313 selected: function(elem){ 2314 // Accessing this property makes selected-by-default 2315 // options in Safari work properly 2316 elem.parentNode.selectedIndex; 2317 return elem.selected === true; 2318 }, 2319 parent: function(elem){ 2320 return !!elem.firstChild; 2321 }, 2322 empty: function(elem){ 2323 return !elem.firstChild; 2324 }, 2325 has: function(elem, i, match){ 2326 return !!Sizzle( match[3], elem ).length; 2327 }, 2328 header: function(elem){ 2329 return /h\d/i.test( elem.nodeName ); 2330 }, 2331 text: function(elem){ 2332 return "text" === elem.type; 2333 }, 2334 radio: function(elem){ 2335 return "radio" === elem.type; 2336 }, 2337 checkbox: function(elem){ 2338 return "checkbox" === elem.type; 2339 }, 2340 file: function(elem){ 2341 return "file" === elem.type; 2342 }, 2343 password: function(elem){ 2344 return "password" === elem.type; 2345 }, 2346 submit: function(elem){ 2347 return "submit" === elem.type; 2348 }, 2349 image: function(elem){ 2350 return "image" === elem.type; 2351 }, 2352 reset: function(elem){ 2353 return "reset" === elem.type; 2354 }, 2355 button: function(elem){ 2356 return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; 2357 }, 2358 input: function(elem){ 2359 return /input|select|textarea|button/i.test(elem.nodeName); 2360 } 2361 }, 2362 setFilters: { 2363 first: function(elem, i){ 2364 return i === 0; 2365 }, 2366 last: function(elem, i, match, array){ 2367 return i === array.length - 1; 2368 }, 2369 even: function(elem, i){ 2370 return i % 2 === 0; 2371 }, 2372 odd: function(elem, i){ 2373 return i % 2 === 1; 2374 }, 2375 lt: function(elem, i, match){ 2376 return i < match[3] - 0; 2377 }, 2378 gt: function(elem, i, match){ 2379 return i > match[3] - 0; 2380 }, 2381 nth: function(elem, i, match){ 2382 return match[3] - 0 === i; 2383 }, 2384 eq: function(elem, i, match){ 2385 return match[3] - 0 === i; 2386 } 2387 }, 2388 filter: { 2389 PSEUDO: function(elem, match, i, array){ 2390 var name = match[1], filter = Expr.filters[ name ]; 2391 2392 if ( filter ) { 2393 return filter( elem, i, match, array ); 2394 } else if ( name === "contains" ) { 2395 return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; 2396 } else if ( name === "not" ) { 2397 var not = match[3]; 2398 2399 for ( var i = 0, l = not.length; i < l; i++ ) { 2400 if ( not[i] === elem ) { 2401 return false; 2402 } 2403 } 2404 2405 return true; 2406 } else { 2407 throw "Syntax error, unrecognized expression: " + name; 2408 } 2409 }, 2410 CHILD: function(elem, match){ 2411 var type = match[1], node = elem; 2412 switch (type) { 2413 case 'only': 2414 case 'first': 2415 while ( (node = node.previousSibling) ) { 2416 if ( node.nodeType === 1 ) { 2417 return false; 2418 } 2419 } 2420 if ( type === "first" ) { 2421 return true; 2422 } 2423 node = elem; 2424 case 'last': 2425 while ( (node = node.nextSibling) ) { 2426 if ( node.nodeType === 1 ) { 2427 return false; 2428 } 2429 } 2430 return true; 2431 case 'nth': 2432 var first = match[2], last = match[3]; 2433 2434 if ( first === 1 && last === 0 ) { 2435 return true; 2436 } 2437 2438 var doneName = match[0], 2439 parent = elem.parentNode; 2440 2441 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { 2442 var count = 0; 2443 for ( node = parent.firstChild; node; node = node.nextSibling ) { 2444 if ( node.nodeType === 1 ) { 2445 node.nodeIndex = ++count; 2446 } 2447 } 2448 parent.sizcache = doneName; 2449 } 2450 2451 var diff = elem.nodeIndex - last; 2452 if ( first === 0 ) { 2453 return diff === 0; 2454 } else { 2455 return ( diff % first === 0 && diff / first >= 0 ); 2456 } 2457 } 2458 }, 2459 ID: function(elem, match){ 2460 return elem.nodeType === 1 && elem.getAttribute("id") === match; 2461 }, 2462 TAG: function(elem, match){ 2463 return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; 2464 }, 2465 CLASS: function(elem, match){ 2466 return (" " + (elem.className || elem.getAttribute("class")) + " ") 2467 .indexOf( match ) > -1; 2468 }, 2469 ATTR: function(elem, match){ 2470 var name = match[1], 2471 result = Expr.attrHandle[ name ] ? 2472 Expr.attrHandle[ name ]( elem ) : 2473 elem[ name ] != null ? 2474 elem[ name ] : 2475 elem.getAttribute( name ), 2476 value = result + "", 2477 type = match[2], 2478 check = match[4]; 2479 2480 return result == null ? 2481 type === "!=" : 2482 type === "=" ? 2483 value === check : 2484 type === "*=" ? 2485 value.indexOf(check) >= 0 : 2486 type === "~=" ? 2487 (" " + value + " ").indexOf(check) >= 0 : 2488 !check ? 2489 value && result !== false : 2490 type === "!=" ? 2491 value !== check : 2492 type === "^=" ? 2493 value.indexOf(check) === 0 : 2494 type === "$=" ? 2495 value.substr(value.length - check.length) === check : 2496 type === "|=" ? 2497 value === check || value.substr(0, check.length + 1) === check + "-" : 2498 false; 2499 },
2500 POS: function(elem, match, i, array){ 2501 var name = match[2], filter = Expr.setFilters[ name ]; 2502 2503 if ( filter ) { 2504 return filter( elem, i, match, array ); 2505 } 2506 } 2507 } 2508}; 2509 2510var origPOS = Expr.match.POS; 2511 2512for ( var type in Expr.match ) { 2513 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); 2514 Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source ); 2515} 2516 2517var makeArray = function(array, results) { 2518 array = Array.prototype.slice.call( array, 0 ); 2519 2520 if ( results ) { 2521 results.push.apply( results, array ); 2522 return results; 2523 } 2524 2525 return array; 2526}; 2527 2528// Perform a simple check to determine if the browser is capable of 2529// converting a NodeList to an array using builtin methods. 2530try { 2531 Array.prototype.slice.call( document.documentElement.childNodes, 0 ); 2532 2533// Provide a fallback method if it does not work 2534} catch(e){ 2535 makeArray = function(array, results) { 2536 var ret = results || []; 2537 2538 if ( toString.call(array) === "[object Array]" ) { 2539 Array.prototype.push.apply( ret, array ); 2540 } else { 2541 if ( typeof array.length === "number" ) { 2542 for ( var i = 0, l = array.length; i < l; i++ ) { 2543 ret.push( array[i] ); 2544 } 2545 } else { 2546 for ( var i = 0; array[i]; i++ ) { 2547 ret.push( array[i] ); 2548 } 2549 } 2550 } 2551 2552 return ret; 2553 }; 2554} 2555 2556var sortOrder; 2557 2558if ( document.documentElement.compareDocumentPosition ) { 2559 sortOrder = function( a, b ) { 2560 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { 2561 if ( a == b ) { 2562 hasDuplicate = true; 2563 } 2564 return a.compareDocumentPosition ? -1 : 1; 2565 } 2566 2567 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; 2568 if ( ret === 0 ) { 2569 hasDuplicate = true; 2570 } 2571 return ret; 2572 }; 2573} else if ( "sourceIndex" in document.documentElement ) { 2574 sortOrder = function( a, b ) { 2575 if ( !a.sourceIndex || !b.sourceIndex ) { 2576 if ( a == b ) { 2577 hasDuplicate = true; 2578 } 2579 return a.sourceIndex ? -1 : 1; 2580 } 2581 2582 var ret = a.sourceIndex - b.sourceIndex; 2583 if ( ret === 0 ) { 2584 hasDuplicate = true; 2585 } 2586 return ret; 2587 }; 2588} else if ( document.createRange ) { 2589 sortOrder = function( a, b ) { 2590 if ( !a.ownerDocument || !b.ownerDocument ) { 2591 if ( a == b ) { 2592 hasDuplicate = true; 2593 } 2594 return a.ownerDocument ? -1 : 1; 2595 } 2596 2597 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); 2598 aRange.setStart(a, 0); 2599 aRange.setEnd(a, 0); 2600 bRange.setStart(b, 0); 2601 bRange.setEnd(b, 0); 2602 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); 2603 if ( ret === 0 ) { 2604 hasDuplicate = true; 2605 } 2606 return ret; 2607 }; 2608} 2609 2610// Utility function for retreiving the text value of an array of DOM nodes 2611function getText( elems ) { 2612 var ret = "", elem; 2613 2614 for ( var i = 0; elems[i]; i++ ) { 2615 elem = elems[i]; 2616 2617 // Get the text from text nodes and CDATA nodes 2618 if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 2619 ret += elem.nodeValue; 2620 2621 // Traverse everything else, except comment nodes 2622 } else if ( elem.nodeType !== 8 ) { 2623 ret += getText( elem.childNodes ); 2624 } 2625 } 2626 2627 return ret; 2628} 2629 2630// Check to see if the browser returns elements by name when 2631// querying by getElementById (and provide a workaround) 2632(function(){ 2633 // We're going to inject a fake input element with a specified name 2634 var form = document.createElement("div"), 2635 id = "script" + (new Date).getTime(); 2636 form.innerHTML = "<a name='" + id + "'/>"; 2637 2638 // Inject it into the root element, check its status, and remove it quickly 2639 var root = document.documentElement; 2640 root.insertBefore( form, root.firstChild ); 2641 2642 // The workaround has to do additional checks after a getElementById 2643 // Which slows things down for other browsers (hence the branching) 2644 if ( document.getElementById( id ) ) { 2645 Expr.find.ID = function(match, context, isXML){ 2646 if ( typeof context.getElementById !== "undefined" && !isXML ) { 2647 var m = context.getElementById(match[1]); 2648 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; 2649 } 2650 }; 2651 2652 Expr.filter.ID = function(elem, match){ 2653 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); 2654 return elem.nodeType === 1 && node && node.nodeValue === match; 2655 }; 2656 } 2657 2658 root.removeChild( form ); 2659 root = form = null; // release memory in IE 2660})(); 2661 2662(function(){ 2663 // Check to see if the browser returns only elements 2664 // when doing getElementsByTagName("*") 2665 2666 // Create a fake element 2667 var div = document.createElement("div"); 2668 div.appendChild( document.createComment("") ); 2669 2670 // Make sure no comments are found 2671 if ( div.getElementsByTagName("*").length > 0 ) { 2672 Expr.find.TAG = function(match, context){ 2673 var results = context.getElementsByTagName(match[1]); 2674 2675 // Filter out possible comments 2676 if ( match[1] === "*" ) { 2677 var tmp = []; 2678 2679 for ( var i = 0; results[i]; i++ ) { 2680 if ( results[i].nodeType === 1 ) { 2681 tmp.push( results[i] ); 2682 } 2683 } 2684 2685 results = tmp; 2686 } 2687 2688 return results; 2689 }; 2690 } 2691 2692 // Check to see if an attribute returns normalized href attributes 2693 div.innerHTML = "<a href='#'></a>"; 2694 if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && 2695 div.firstChild.getAttribute("href") !== "#" ) { 2696 Expr.attrHandle.href = function(elem){ 2697 return elem.getAttribute("href", 2); 2698 }; 2699 } 2700 2701 div = null; // release memory in IE 2702})(); 2703 2704if ( document.querySelectorAll ) { 2705 (function(){
2706 var oldSizzle = Sizzle, div = document.createElement("div"); 2707 div.innerHTML = "<p class='TEST'></p>"; 2708 2709 // Safari can't handle uppercase or unicode characters when 2710 // in quirks mode. 2711 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { 2712 return; 2713 } 2714 2715 Sizzle = function(query, context, extra, seed){ 2716 context = context || document; 2717 2718 // Only use querySelectorAll on non-XML documents 2719 // (ID selectors don't work in non-HTML documents) 2720 if ( !seed && context.nodeType === 9 && !isXML(context) ) { 2721 try { 2722 return makeArray( context.querySelectorAll(query), extra ); 2723 } catch(e){} 2724 } 2725 2726 return oldSizzle(query, context, extra, seed); 2727 }; 2728 2729 for ( var prop in oldSizzle ) { 2730 Sizzle[ prop ] = oldSizzle[ prop ]; 2731 } 2732 2733 div = null; // release memory in IE 2734 })(); 2735} 2736 2737(function(){ 2738 var div = document.createElement("div"); 2739 2740 div.innerHTML = "<div class='test e'></div><div class='test'></div>"; 2741 2742 // Opera can't find a second classname (in 9.6) 2743 // Also, make sure that getElementsByClassName actually exists 2744 if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { 2745 return; 2746 } 2747 2748 // Safari caches class attributes, doesn't catch changes (in 3.2) 2749 div.lastChild.className = "e"; 2750 2751 if ( div.getElementsByClassName("e").length === 1 ) { 2752 return; 2753 } 2754 2755 Expr.order.splice(1, 0, "CLASS"); 2756 Expr.find.CLASS = function(match, context, isXML) { 2757 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { 2758 return context.getElementsByClassName(match[1]); 2759 } 2760 }; 2761 2762 div = null; // release memory in IE 2763})(); 2764 2765function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { 2766 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 2767 var elem = checkSet[i]; 2768 if ( elem ) { 2769 elem = elem[dir]; 2770 var match = false; 2771 2772 while ( elem ) { 2773 if ( elem.sizcache === doneName ) { 2774 match = checkSet[elem.sizset]; 2775 break; 2776 } 2777 2778 if ( elem.nodeType === 1 && !isXML ){ 2779 elem.sizcache = doneName; 2780 elem.sizset = i; 2781 } 2782 2783 if ( elem.nodeName.toLowerCase() === cur ) { 2784 match = elem; 2785 break; 2786 } 2787 2788 elem = elem[dir]; 2789 } 2790 2791 checkSet[i] = match; 2792 } 2793 } 2794} 2795 2796function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { 2797 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 2798 var elem = checkSet[i]; 2799 if ( elem ) { 2800 elem = elem[dir]; 2801 var match = false; 2802 2803 while ( elem ) { 2804 if ( elem.sizcache === doneName ) { 2805 match = checkSet[elem.sizset]; 2806 break; 2807 } 2808 2809 if ( elem.nodeType === 1 ) { 2810 if ( !isXML ) { 2811 elem.sizcache = doneName; 2812 elem.sizset = i; 2813 } 2814 if ( typeof cur !== "string" ) { 2815 if ( elem === cur ) { 2816 match = true; 2817 break; 2818 } 2819 2820 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { 2821 match = elem; 2822 break; 2823 } 2824 } 2825 2826 elem = elem[dir]; 2827 } 2828 2829 checkSet[i] = match; 2830 } 2831 } 2832} 2833 2834var contains = document.compareDocumentPosition ? function(a, b){ 2835 return a.compareDocumentPosition(b) & 16; 2836} : function(a, b){ 2837 return a !== b && (a.contains ? a.contains(b) : true); 2838}; 2839 2840var isXML = function(elem){ 2841 // documentElement is verified for cases where it doesn't yet exist 2842 // (such as loading iframes in IE - #4833) 2843 var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; 2844 return documentElement ? documentElement.nodeName !== "HTML" : false; 2845}; 2846 2847var posProcess = function(selector, context){ 2848 var tmpSet = [], later = "", match, 2849 root = context.nodeType ? [context] : context; 2850 2851 // Position selectors must be done after the filter 2852 // And so must :not(positional) so we move all PSEUDOs to the end 2853 while ( (match = Expr.match.PSEUDO.exec( selector )) ) { 2854 later += match[0]; 2855 selector = selector.replace( Expr.match.PSEUDO, "" ); 2856 } 2857 2858 selector = Expr.relative[selector] ? selector + "*" : selector; 2859 2860 for ( var i = 0, l = root.length; i < l; i++ ) { 2861 Sizzle( selector, root[i], tmpSet ); 2862 } 2863 2864 return Sizzle.filter( later, tmpSet ); 2865}; 2866 2867// EXPOSE 2868 2869//window.Sizzle = Sizzle; 2870return Sizzle; 2871 2872})(); 2873/*! 2874 * SWFObject v2.1 <http://code.google.com/p/swfobject/> 2875 * Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis 2876 * This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 2877 * 2878 * Modified for inclusion in Shadowbox.js 2879 */ 2880S.flash = (function(){ 2881 2882var swfobject = function() { 2883 2884 var UNDEF = "undefined", 2885 OBJECT = "object", 2886 SHOCKWAVE_FLASH = "Shockwave Flash", 2887 SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", 2888 FLASH_MIME_TYPE = "application/x-shockwave-flash", 2889 EXPRESS_INSTALL_ID = "SWFObjectExprInst", 2890 2891 win = window, 2892 doc = document, 2893 nav = navigator, 2894 2895 domLoadFnArr = [], 2896 regObjArr = [], 2897 objIdArr = [], 2898 listenersArr = [], 2899 script, 2900 timer = null, 2901 storedAltContent = null, 2902 storedAltContentId = null, 2903 isDomLoaded = false, 2904 isExpressInstallActive = false;
2905 2906 /* Centralized function for browser feature detection 2907 - Proprietary feature detection (conditional compiling) is used to detect Internet Explorer's features 2908 - User agent string detection is only used when no alternative is possible 2909 - Is executed directly for optimal performance 2910 */ 2911 var ua = function() { 2912 var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF, 2913 playerVersion = [0,0,0], 2914 d = null; 2915 if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) { 2916 d = nav.plugins[SHOCKWAVE_FLASH].description; 2917 if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+ 2918 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); 2919 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10); 2920 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10); 2921 playerVersion[2] = /r/.test(d) ? parseInt(d.replace(/^.*r(.*)$/, "$1"), 10) : 0; 2922 } 2923 } 2924 else if (typeof win.ActiveXObject != UNDEF) { 2925 var a = null, fp6Crash = false; 2926 try { 2927 a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7"); 2928 } 2929 catch(e) { 2930 try { 2931 a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".6"); 2932 playerVersion = [6,0,21]; 2933 a.AllowScriptAccess = "always"; // Introduced in fp6.0.47 2934 } 2935 catch(e) { 2936 if (playerVersion[0] == 6) { 2937 fp6Crash = true; 2938 } 2939 } 2940 if (!fp6Crash) { 2941 try { 2942 a = new ActiveXObject(SHOCKWAVE_FLASH_AX); 2943 } 2944 catch(e) {} 2945 } 2946 } 2947 if (!fp6Crash && a) { // a will return null when ActiveX is disabled 2948 try { 2949 d = a.GetVariable("$version"); // Will crash fp6.0.21/23/29 2950 if (d) { 2951 d = d.split(" ")[1].split(","); 2952 playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 2953 } 2954 } 2955 catch(e) {} 2956 } 2957 } 2958 var u = nav.userAgent.toLowerCase(), 2959 p = nav.platform.toLowerCase(), 2960 webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit 2961 ie = false, 2962 windows = p ? /win/.test(p) : /win/.test(u), 2963 mac = p ? /mac/.test(p) : /mac/.test(u); 2964 /*@cc_on 2965 ie = true; 2966 @if (@_win32) 2967 windows = true; 2968 @elif (@_mac) 2969 mac = true; 2970 @end 2971 @*/ 2972 return { w3cdom:w3cdom, pv:playerVersion, webkit:webkit, ie:ie, win:windows, mac:mac }; 2973 }(); 2974 2975 /* Cross-browser onDomLoad 2976 - Based on Dean Edwards' solution: http://dean.edwards.name/weblog/2006/06/again/ 2977 - Will fire an event as soon as the DOM of a page is loaded (supported by Gecko based browsers - like Firefox -, IE, Opera9+, Safari) 2978 */ 2979 var onDomLoad = function() { 2980 if (!ua.w3cdom) { 2981 return; 2982 } 2983 addDomLoadEvent(main); 2984 if (ua.ie && ua.win) { 2985 try { // Avoid a possible Operation Aborted error 2986 doc.write("<scr" + "ipt id=__ie_ondomload defer=true src=//:></scr" + "ipt>"); // String is split into pieces to avoid Norton AV to add code that can cause errors 2987 script = getElementById("__ie_ondomload"); 2988 if (script) { 2989 addListener(script, "onreadystatechange", checkReadyState); 2990 } 2991 } 2992 catch(e) {} 2993 } 2994 if (ua.webkit && typeof doc.readyState != UNDEF) { 2995 timer = setInterval(function() { if (/loaded|complete/.test(doc.readyState)) { callDomLoadFunctions(); }}, 10); 2996 } 2997 if (typeof doc.addEventListener != UNDEF) { 2998 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, null); 2999 } 3000 addLoadEvent(callDomLoadFunctions); 3001 }(); 3002 3003 function checkReadyState() { 3004 if (script.readyState == "complete") { 3005 script.parentNode.removeChild(script); 3006 callDomLoadFunctions(); 3007 } 3008 } 3009 3010 function callDomLoadFunctions() { 3011 if (isDomLoaded) { 3012 return; 3013 } 3014 if (ua.ie && ua.win) { // Test if we can really add elements to the DOM; we don't want to fire it too early 3015 var s = createElement("span"); 3016 try { // Avoid a possible Operation Aborted error 3017 var t = doc.getElementsByTagName("body")[0].appendChild(s); 3018 t.parentNode.removeChild(t); 3019 } 3020 catch (e) { 3021 return; 3022 } 3023 } 3024 isDomLoaded = true; 3025 if (timer) { 3026 clearInterval(timer); 3027 timer = null; 3028 } 3029 var dl = domLoadFnArr.length; 3030 for (var i = 0; i < dl; i++) { 3031 domLoadFnArr[i](); 3032 } 3033 } 3034 3035 function addDomLoadEvent(fn) { 3036 if (isDomLoaded) { 3037 fn(); 3038 } 3039 else { 3040 domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+ 3041 } 3042 } 3043 3044 /* Cross-browser onload 3045 - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/ 3046 - Will fire an event as soon as a web page including all of its assets are loaded 3047 */ 3048 function addLoadEvent(fn) { 3049 if (typeof win.addEventListener != UNDEF) { 3050 win.addEventListener("load", fn, false); 3051 } 3052 else if (typeof doc.addEventListener != UNDEF) { 3053 doc.addEventListener("load", fn, false); 3054 } 3055 else if (typeof win.attachEvent != UNDEF) { 3056 addListener(win, "onload", fn); 3057 } 3058 else if (typeof win.onload == "function") { 3059 var fnOld = win.onload; 3060 win.onload = function() { 3061 fnOld(); 3062 fn(); 3063 }; 3064 } 3065 else { 3066 win.onload = fn; 3067 } 3068 } 3069 3070 /* Main function 3071 - Will preferably execute onDomLoad, otherwise onload (as a fallback) 3072 */ 3073 function main() { // Static publishing only 3074 var rl = regObjArr.length; 3075 for (var i = 0; i < rl; i++) { // For each registered object element 3076 var id = regObjArr[i].id; 3077 if (ua.pv[0] > 0) { 3078 var obj = getElementById(id); 3079 if (obj) { 3080 regObjArr[i].width = obj.getAttribute("width") ? obj.getAttribute("width") : "0"; 3081 regObjArr[i].height = obj.getAttribute("height") ? obj.getAttribute("height") : "0"; 3082 if (hasPlayerVersion(regObjArr[i].swfVersion)) { // Flash plug-in version >= Flash content version: Houston, we have a match! 3083 if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements 3084 fixParams(obj); 3085 } 3086 setVisibility(id, true); 3087 } 3088 else if (regObjArr[i].expressInstall && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) { // Show the Adobe Express Install dialog if set by the web page author and if supported (fp6.0.65+ on Win/Mac OS only) 3089 showExpressInstall(regObjArr[i]); 3090 } 3091 else { // Flash plug-in and Flash content version mismatch: display alternative content instead of Flash content 3092 displayAltContent(obj); 3093 } 3094 } 3095 } 3096 else { // If no fp is installed, we let the object element do its job (show alternative content) 3097 setVisibility(id, true); 3098 } 3099 } 3100 } 3101 3102 /* Fix nested param elements, which are ignored by older webkit engines 3103 - This includes Safari up to and including version 1.2.2 on Mac OS 10.3 3104 - Fall back to the proprietary embed element 3105 */ 3106 function fixParams(obj) { 3107 var nestedObj = obj.getElementsByTagName(OBJECT)[0];
3108 if (nestedObj) { 3109 var e = createElement("embed"), a = nestedObj.attributes; 3110 if (a) { 3111 var al = a.length; 3112 for (var i = 0; i < al; i++) { 3113 if (a[i].nodeName == "DATA") { 3114 e.setAttribute("src", a[i].nodeValue); 3115 } 3116 else { 3117 e.setAttribute(a[i].nodeName, a[i].nodeValue); 3118 } 3119 } 3120 } 3121 var c = nestedObj.childNodes; 3122 if (c) { 3123 var cl = c.length; 3124 for (var j = 0; j < cl; j++) { 3125 if (c[j].nodeType == 1 && c[j].nodeName == "PARAM") { 3126 e.setAttribute(c[j].getAttribute("name"), c[j].getAttribute("value")); 3127 } 3128 } 3129 } 3130 obj.parentNode.replaceChild(e, obj); 3131 } 3132 } 3133 3134 /* Show the Adobe Express Install dialog 3135 - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75 3136 */ 3137 function showExpressInstall(regObj) { 3138 isExpressInstallActive = true; 3139 var obj = getElementById(regObj.id); 3140 if (obj) { 3141 if (regObj.altContentId) { 3142 var ac = getElementById(regObj.altContentId); 3143 if (ac) { 3144 storedAltContent = ac; 3145 storedAltContentId = regObj.altContentId; 3146 } 3147 } 3148 else { 3149 storedAltContent = abstractAltContent(obj); 3150 } 3151 if (!(/%$/.test(regObj.width)) && parseInt(regObj.width, 10) < 310) { 3152 regObj.width = "310"; 3153 } 3154 if (!(/%$/.test(regObj.height)) && parseInt(regObj.height, 10) < 137) { 3155 regObj.height = "137"; 3156 } 3157 doc.title = doc.title.slice(0, 47) + " - Flash Player Installation"; 3158 var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", 3159 dt = doc.title, 3160 fv = "MMredirectURL=" + win.location + "&MMplayerType=" + pt + "&MMdoctitle=" + dt, 3161 replaceId = regObj.id; 3162 // For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element 3163 // In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work 3164 if (ua.ie && ua.win && obj.readyState != 4) { 3165 var newObj = createElement("div"); 3166 replaceId += "SWFObjectNew"; 3167 newObj.setAttribute("id", replaceId); 3168 obj.parentNode.insertBefore(newObj, obj); // Insert placeholder div that will be replaced by the object element that loads expressinstall.swf 3169 obj.style.display = "none"; 3170 var fn = function() { 3171 obj.parentNode.removeChild(obj); 3172 }; 3173 addListener(win, "onload", fn); 3174 } 3175 createSWF({ data:regObj.expressInstall, id:EXPRESS_INSTALL_ID, width:regObj.width, height:regObj.height }, { flashvars:fv }, replaceId); 3176 } 3177 } 3178 3179 /* Functions to abstract and display alternative content 3180 */ 3181 function displayAltContent(obj) { 3182 if (ua.ie && ua.win && obj.readyState != 4) { 3183 // For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element 3184 // In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work 3185 var el = createElement("div"); 3186 obj.parentNode.insertBefore(el, obj); // Insert placeholder div that will be replaced by the alternative content 3187 el.parentNode.replaceChild(abstractAltContent(obj), el); 3188 obj.style.display = "none"; 3189 var fn = function() { 3190 obj.parentNode.removeChild(obj); 3191 }; 3192 addListener(win, "onload", fn); 3193 } 3194 else { 3195 obj.parentNode.replaceChild(abstractAltContent(obj), obj); 3196 } 3197 } 3198 3199 function abstractAltContent(obj) { 3200 var ac = createElement("div"); 3201 if (ua.win && ua.ie) { 3202 ac.innerHTML = obj.innerHTML; 3203 } 3204 else { 3205 var nestedObj = obj.getElementsByTagName(OBJECT)[0];
3206 if (nestedObj) { 3207 var c = nestedObj.childNodes; 3208 if (c) { 3209 var cl = c.length; 3210 for (var i = 0; i < cl; i++) { 3211 if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) { 3212 ac.appendChild(c[i].cloneNode(true)); 3213 } 3214 } 3215 } 3216 } 3217 } 3218 return ac; 3219 } 3220 3221 /* Cross-browser dynamic SWF creation 3222 */ 3223 function createSWF(attObj, parObj, id) { 3224 var r, el = getElementById(id); 3225 if (el) { 3226 if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content 3227 attObj.id = id; 3228 } 3229 if (ua.ie && ua.win) { // IE, the object element and W3C DOM methods do not combine: fall back to outerHTML 3230 var att = ""; 3231 for (var i in attObj) { 3232 if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries, like Object.prototype.toJSONString = function() {} 3233 if (i.toLowerCase() == "data") { 3234 parObj.movie = attObj[i]; 3235 } 3236 else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 3237 att += ' class="' + attObj[i] + '"'; 3238 } 3239 else if (i.toLowerCase() != "classid") { 3240 att += ' ' + i + '="' + attObj[i] + '"'; 3241 } 3242 } 3243 } 3244 var par = ""; 3245 for (var j in parObj) { 3246 if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries 3247 par += '<param name="' + j + '" value="' + parObj[j] + '" />'; 3248 } 3249 } 3250 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>'; 3251 objIdArr[objIdArr.length] = attObj.id; // Stored to fix object 'leaks' on unload (dynamic publishing only) 3252 r = getElementById(attObj.id); 3253 } 3254 else if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements: fall back to the proprietary embed element 3255 var e = createElement("embed"); 3256 e.setAttribute("type", FLASH_MIME_TYPE); 3257 for (var k in attObj) { 3258 if (attObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries 3259 if (k.toLowerCase() == "data") { 3260 e.setAttribute("src", attObj[k]); 3261 } 3262 else if (k.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 3263 e.setAttribute("class", attObj[k]); 3264 } 3265 else if (k.toLowerCase() != "classid") { // Filter out IE specific attribute 3266 e.setAttribute(k, attObj[k]); 3267 } 3268 } 3269 } 3270 for (var l in parObj) { 3271 if (parObj[l] != Object.prototype[l]) { // Filter out prototype additions from other potential libraries 3272 if (l.toLowerCase() != "movie") { // Filter out IE specific param element 3273 e.setAttribute(l, parObj[l]); 3274 } 3275 } 3276 } 3277 el.parentNode.replaceChild(e, el); 3278 r = e; 3279 } 3280 else { // Well-behaving browsers 3281 var o = createElement(OBJECT); 3282 o.setAttribute("type", FLASH_MIME_TYPE); 3283 for (var m in attObj) { 3284 if (attObj[m] != Object.prototype[m]) { // Filter out prototype additions from other potential libraries 3285 if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 3286 o.setAttribute("class", attObj[m]); 3287 } 3288 else if (m.toLowerCase() != "classid") { // Filter out IE specific attribute 3289 o.setAttribute(m, attObj[m]); 3290 } 3291 } 3292 } 3293 for (var n in parObj) { 3294 if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // Filter out prototype additions from other potential libraries and IE specific param element 3295 createObjParam(o, n, parObj[n]); 3296 } 3297 } 3298 el.parentNode.replaceChild(o, el); 3299 r = o; 3300 } 3301 } 3302 return r; 3303 } 3304 3305 function createObjParam(el, pName, pValue) { 3306 var p = createElement("param"); 3307 p.setAttribute("name", pName); 3308 p.setAttribute("value", pValue); 3309 el.appendChild(p); 3310 } 3311 3312 /* Cross-browser SWF removal 3313 - Especially needed to safely and completely remove a SWF in Internet Explorer 3314 */ 3315 function removeSWF(id) { 3316 var obj = getElementById(id); 3317 if (obj && (obj.nodeName == "OBJECT" || obj.nodeName == "EMBED")) { 3318 if (ua.ie && ua.win) { 3319 if (obj.readyState == 4) { 3320 removeObjectInIE(id); 3321 } 3322 else { 3323 win.attachEvent("onload", function() { 3324 removeObjectInIE(id); 3325 }); 3326 } 3327 } 3328 else { 3329 obj.parentNode.removeChild(obj); 3330 } 3331 } 3332 } 3333 3334 function removeObjectInIE(id) { 3335 var obj = getElementById(id); 3336 if (obj) { 3337 for (var i in obj) { 3338 if (typeof obj[i] == "function") { 3339 obj[i] = null; 3340 } 3341 } 3342 obj.parentNode.removeChild(obj); 3343 } 3344 } 3345 3346 /* Functions to optimize JavaScript compression 3347 */ 3348 function getElementById(id) { 3349 var el = null; 3350 try { 3351 el = doc.getElementById(id); 3352 } 3353 catch (e) {} 3354 return el; 3355 } 3356 3357 function createElement(el) { 3358 return doc.createElement(el); 3359 } 3360 3361 /* Updated attachEvent function for Internet Explorer 3362 - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks 3363 */
3364 function addListener(target, eventType, fn) { 3365 target.attachEvent(eventType, fn); 3366 listenersArr[listenersArr.length] = [target, eventType, fn]; 3367 } 3368 3369 /* Flash Player and SWF content version matching 3370 */ 3371 function hasPlayerVersion(rv) { 3372 var pv = ua.pv, v = rv.split("."); 3373 v[0] = parseInt(v[0], 10); 3374 v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0" 3375 v[2] = parseInt(v[2], 10) || 0; 3376 return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false; 3377 } 3378 3379 /* Cross-browser dynamic CSS creation 3380 - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php 3381 */ 3382 function createCSS(sel, decl) { 3383 if (ua.ie && ua.mac) { 3384 return; 3385 } 3386 var h = doc.getElementsByTagName("head")[0], s = createElement("style"); 3387 s.setAttribute("type", "text/css"); 3388 s.setAttribute("media", "screen"); 3389 if (!(ua.ie && ua.win) && typeof doc.createTextNode != UNDEF) { 3390 s.appendChild(doc.createTextNode(sel + " {" + decl + "}")); 3391 } 3392 h.appendChild(s); 3393 if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) { 3394 var ls = doc.styleSheets[doc.styleSheets.length - 1]; 3395 if (typeof ls.addRule == OBJECT) { 3396 ls.addRule(sel, decl); 3397 } 3398 } 3399 } 3400 3401 function setVisibility(id, isVisible) { 3402 var v = isVisible ? "visible" : "hidden"; 3403 if (isDomLoaded && getElementById(id)) { 3404 getElementById(id).style.visibility = v; 3405 } 3406 else { 3407 createCSS("#" + id, "visibility:" + v); 3408 } 3409 } 3410 3411 /* Filter to avoid XSS attacks 3412 */ 3413 function urlEncodeIfNecessary(s) { 3414 var regex = /[\\\"<>\.;]/; 3415 var hasBadChars = regex.exec(s) != null; 3416 return hasBadChars ? encodeURIComponent(s) : s; 3417 } 3418 3419 /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only) 3420 */ 3421 var cleanup = function() { 3422 if (ua.ie && ua.win) { 3423 window.attachEvent("onunload", function() { 3424 // remove listeners to avoid memory leaks 3425 var ll = listenersArr.length; 3426 for (var i = 0; i < ll; i++) { 3427 listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]); 3428 } 3429 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect 3430 var il = objIdArr.length; 3431 for (var j = 0; j < il; j++) { 3432 removeSWF(objIdArr[j]); 3433 } 3434 // cleanup library's main closures to avoid memory leaks 3435 for (var k in ua) { 3436 ua[k] = null; 3437 } 3438 ua = null; 3439 for (var l in swfobject) { 3440 swfobject[l] = null; 3441 } 3442 swfobject = null; 3443 }); 3444 } 3445 }(); 3446 3447 3448 return { 3449 /* Public API 3450 - Reference: http://code.google.com/p/swfobject/wiki/SWFObject_2_0_documentation 3451 */ 3452 registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr) { 3453 if (!ua.w3cdom || !objectIdStr || !swfVersionStr) { 3454 return; 3455 } 3456 var regObj = {}; 3457 regObj.id = objectIdStr; 3458 regObj.swfVersion = swfVersionStr; 3459 regObj.expressInstall = xiSwfUrlStr ? xiSwfUrlStr : false; 3460 regObjArr[regObjArr.length] = regObj; 3461 setVisibility(objectIdStr, false); 3462 }, 3463 3464 getObjectById: function(objectIdStr) { 3465 var r = null; 3466 if (ua.w3cdom) { 3467 var o = getElementById(objectIdStr); 3468 if (o) { 3469 var n = o.getElementsByTagName(OBJECT)[0]; 3470 if (!n || (n && typeof o.SetVariable != UNDEF)) { 3471 r = o; 3472 } 3473 else if (typeof n.SetVariable != UNDEF) { 3474 r = n; 3475 } 3476 } 3477 } 3478 return r; 3479 }, 3480 3481 embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj) { 3482 if (!ua.w3cdom || !swfUrlStr || !replaceElemIdStr || !widthStr || !heightStr || !swfVersionStr) { 3483 return; 3484 } 3485 widthStr += ""; // Auto-convert to string 3486 heightStr += ""; 3487 if (hasPlayerVersion(swfVersionStr)) { 3488 setVisibility(replaceElemIdStr, false); 3489 var att = {}; 3490 if (attObj && typeof attObj === OBJECT) { 3491 for (var i in attObj) { 3492 if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries 3493 att[i] = attObj[i]; 3494 } 3495 } 3496 } 3497 att.data = swfUrlStr; 3498 att.width = widthStr; 3499 att.height = heightStr; 3500 var par = {}; 3501 if (parObj && typeof parObj === OBJECT) { 3502 for (var j in parObj) { 3503 if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries 3504 par[j] = parObj[j]; 3505 } 3506 } 3507 } 3508 if (flashvarsObj && typeof flashvarsObj === OBJECT) { 3509 for (var k in flashvarsObj) { 3510 if (flashvarsObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries 3511 if (typeof par.flashvars != UNDEF) { 3512 par.flashvars += "&" + k + "=" + flashvarsObj[k]; 3513 } 3514 else { 3515 par.flashvars = k + "=" + flashvarsObj[k]; 3516 } 3517 } 3518 } 3519 } 3520 addDomLoadEvent(function() { 3521 createSWF(att, par, replaceElemIdStr); 3522 if (att.id == replaceElemIdStr) { 3523 setVisibility(replaceElemIdStr, true); 3524 } 3525 }); 3526 } 3527 else if (xiSwfUrlStr && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) { 3528 isExpressInstallActive = true; // deferred execution 3529 setVisibility(replaceElemIdStr, false); 3530 addDomLoadEvent(function() { 3531 var regObj = {}; 3532 regObj.id = regObj.altContentId = replaceElemIdStr; 3533 regObj.width = widthStr; 3534 regObj.height = heightStr; 3535 regObj.expressInstall = xiSwfUrlStr; 3536 showExpressInstall(regObj); 3537 }); 3538 } 3539 }, 3540 3541 getFlashPlayerVersion: function() { 3542 return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] }; 3543 }, 3544 3545 hasFlashPlayerVersion: hasPlayerVersion, 3546 3547 createSWF: function(attObj, parObj, replaceElemIdStr) { 3548 if (ua.w3cdom) { 3549 return createSWF(attObj, parObj, replaceElemIdStr); 3550 } 3551 else { 3552 return undefined; 3553 } 3554 }, 3555 3556 removeSWF: function(objElemIdStr) { 3557 if (ua.w3cdom) { 3558 removeSWF(objElemIdStr); 3559 } 3560 }, 3561 3562 createCSS: function(sel, decl) { 3563 if (ua.w3cdom) { 3564 createCSS(sel, decl); 3565 } 3566 }, 3567 3568 addDomLoadEvent: addDomLoadEvent, 3569 3570 addLoadEvent: addLoadEvent, 3571 3572 getQueryParamValue: function(param) { 3573 var q = doc.location.search || doc.location.hash; 3574 if (param == null) { 3575 return urlEncodeIfNecessary(q); 3576 } 3577 if (q) { 3578 var pairs = q.substring(1).split("&"); 3579 for (var i = 0; i < pairs.length; i++) { 3580 if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) { 3581 return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1))); 3582 } 3583 } 3584 } 3585 return ""; 3586 }, 3587 3588 // For internal usage only 3589 expressInstallCallback: function() { 3590 if (isExpressInstallActive && storedAltContent) { 3591 var obj = getElementById(EXPRESS_INSTALL_ID); 3592 if (obj) { 3593 obj.parentNode.replaceChild(storedAltContent, obj); 3594 if (storedAltContentId) { 3595 setVisibility(storedAltContentId, true); 3596 if (ua.ie && ua.win) { 3597 storedAltContent.style.display = "block"; 3598 } 3599 } 3600 storedAltContent = null; 3601 storedAltContentId = null; 3602 isExpressInstallActive = false;
3603 } 3604 } 3605 } 3606 }; 3607}(); 3608 3609return swfobject; 3610 3611})(); 3612/** 3613 * The English language translation for Shadowbox. 3614 */ 3615 3616S.lang = { 3617 code: 'en', 3618 of: 'of', 3619 loading: 'loading', 3620 cancel: 'Cancel', 3621 next: 'Next', 3622 previous: 'Previous', 3623 play: 'Play', 3624 pause: 'Pause', 3625 close: 'Close', 3626 errors: { 3627 single: 'You must install the <a href="{0}">{1}</a> browser plugin to view this content.', 3628 shared: 'You must install both the <a href="{0}">{1}</a> and <a href="{2}">{3}</a> browser plugins to view this content.', 3629 either: 'You must install either the <a href="{0}">{1}</a> or the <a href="{2}">{3}</a> browser plugin to view this content.' 3630 } 3631} 3632/** 3633 * The FLV player for Shadowbox. 3634 */ 3635 3636/** 3637 * The height (in pixels) of the JW FLV player controller. 3638 * 3639 * @type {Number} 3640 * @private 3641 */ 3642var jwControllerHeight = 20; 3643 3644/** 3645 * Constructor. The Flash video player class for Shadowbox. 3646 * 3647 * @constructor 3648 * @param {Object} obj The content object 3649 * @param {String} id The player id 3650 * @public 3651 */ 3652S.flv = function(obj, id) { 3653 this.obj = obj; 3654 this.id = id; 3655 3656 // height/width default to 300 pixels 3657 this.height = obj.height ? parseInt(obj.height, 10) : 300; 3658 if (S.options.showMovieControls) 3659 this.height += jwControllerHeight; 3660 this.width = obj.width ? parseInt(obj.width, 10) : 300; 3661} 3662 3663S.flv.ext = ["flv", "m4v"]; 3664 3665S.flv.prototype = { 3666 3667 /** 3668 * Appends this movie to the document. 3669 * 3670 * @param {HTMLElement} body The body element 3671 * @param {Object} dims The current Shadowbox dimensions 3672 * @public 3673 */ 3674 append: function(body, dims) { 3675 // append temporary content element to replace 3676 var tmp = document.createElement('div'); 3677 tmp.id = this.id; 3678 body.appendChild(tmp); 3679 3680 var height = dims.innerHeight, 3681 width = dims.innerWidth, 3682 swf = S.path + "player.swf", 3683 version = S.options.flashVersion, 3684 express = S.path + "expressInstall.swf", 3685 flashvars = apply({ 3686 file: this.obj.content, 3687 height: height, 3688 width: width, 3689 autostart: (S.options.autoplayMovies ? "true" : "false"), 3690 controlbar: (S.options.showMovieControls ? "bottom" : "none"), 3691 backcolor: "0x000000", 3692 frontcolor: "0xCCCCCC", 3693 lightcolor: "0x557722" 3694 }, S.options.flashVars), 3695 params = S.options.flashParams; 3696 3697 S.flash.embedSWF(swf, this.id, width, height, version, express, flashvars, params); 3698 }, 3699 3700 /** 3701 * Removes this movie from the document. 3702 * 3703 * @public 3704 */ 3705 remove: function() { 3706 // call express install callback here in case express install is 3707 // active and user has not selected anything 3708 S.flash.expressInstallCallback(); 3709 S.flash.removeSWF(this.id); 3710 }, 3711 3712 /** 3713 * Called when the window is resized. 3714 * 3715 * @public 3716 */ 3717 onWindowResize: function() { 3718 var dims = S.dimensions, 3719 el = get(this.id); 3720 el.height = dims.innerHeight; 3721 el.width = dims.innerWidth; 3722 } 3723 3724} 3725/** 3726 * The HTML player for Shadowbox. 3727 */ 3728 3729/** 3730 * Constructor. The HTML player class for Shadowbox. 3731 * 3732 * @constructor 3733 * @param {Object} obj The content object 3734 * @param {String} id The player id 3735 * @public 3736 */ 3737S.html = function(obj, id) { 3738 this.obj = obj; 3739 this.id = id; 3740 3741 // height defaults to 300, width defaults to 500 3742 this.height = obj.height ? parseInt(obj.height, 10) : 300; 3743 this.width = obj.width ? parseInt(obj.width, 10) : 500; 3744} 3745 3746S.html.prototype = { 3747 3748 /** 3749 * Appends this object to the DOM. 3750 * 3751 * @param {HTMLElement} body The body element 3752 * @param {Object} dims The current Shadowbox dimensions 3753 * @public 3754 */ 3755 append: function(body, dims) { 3756 var div = document.createElement("div"); 3757 div.id = this.id; 3758 div.className = "html"; // give special class to enable scrolling 3759 div.innerHTML = this.obj.content; 3760 3761 body.appendChild(div); 3762 }, 3763 3764 /** 3765 * Removes this object from the DOM. 3766 * 3767 * @public 3768 */ 3769 remove: function() {
3770 var el = get(this.id); 3771 if (el) 3772 remove(el); 3773 } 3774 3775} 3776/** 3777 * The iframe player for Shadowbox. 3778 */ 3779 3780/** 3781 * Constructor. The iframe player class for Shadowbox. 3782 * 3783 * @constructor 3784 * @param {Object} obj The content object 3785 * @param {String} id The player id 3786 * @public 3787 */ 3788S.iframe = function(obj, id) { 3789 this.obj = obj; 3790 this.id = id; 3791 3792 // height/width default to full viewport height/width 3793 var overlay = get("sb-overlay"); 3794 this.height = obj.height ? parseInt(obj.height, 10) : overlay.offsetHeight; 3795 this.width = obj.width ? parseInt(obj.width, 10) : overlay.offsetWidth; 3796} 3797 3798S.iframe.prototype = { 3799 3800 /** 3801 * Appends this iframe to the DOM. 3802 * 3803 * @param {HTMLElement} body The body element 3804 * @param {Object} dims The current Shadowbox dimensions 3805 * @public 3806 */ 3807 append: function(body, dims) { 3808 var html = '<iframe id="' + this.id + '" name="' + this.id + '" height="100%" ' + 3809 'width="100%" frameborder="0" marginwidth="0" marginheight="0" ' + 3810 'style="visibility:hidden" onload="this.style.visibility=\'visible\'" ' + 3811 'scrolling="auto"'; 3812 3813 if (S.isIE) { 3814 // prevent brief whiteout while loading iframe source 3815 html += ' allowtransparency="true"'; 3816 3817 // prevent "secure content" warning for https on IE6 3818 // see http://www.zachleat.com/web/2007/04/24/adventures-in-i-frame-shims-or-how-i-learned-to-love-the-bomb/ 3819 if (S.isIE6) 3820 html += ' src="javascript:false;document.write(\'\');"'; 3821 } 3822 3823 html += '></iframe>'; 3824 3825 // use innerHTML method of insertion here instead of appendChild 3826 // because IE renders frameborder otherwise 3827 body.innerHTML = html; 3828 }, 3829 3830 /** 3831 * Removes this iframe from the DOM. 3832 * 3833 * @public 3834 */ 3835 remove: function() { 3836 var el = get(this.id); 3837 if (el) { 3838 remove(el); 3839 if (S.isGecko) 3840 delete window.frames[this.id]; // needed for Firefox 3841 } 3842 }, 3843 3844 /** 3845 * An optional callback function to process after this content has been loaded. 3846 * 3847 * @public 3848 */ 3849 onLoad: function() { 3850 var win = S.isIE ? get(this.id).contentWindow : window.frames[this.id]; 3851 win.location.href = this.obj.content; 3852 } 3853 3854} 3855/** 3856 * The image player for Shadowbox. 3857 */ 3858 3859/** 3860 * Resource used to preload images. It's class-level so that when a new image is requested, 3861 * the same resource can be reassigned, cancelling the original's callback. 3862 * 3863 * @type {Image} 3864 * @private 3865 */ 3866var pre, 3867 3868/** 3869 * The id to use for the drag proxy element. 3870 * 3871 * @type {String} 3872 * @private 3873 */ 3874proxyId = "sb-drag-proxy", 3875 3876/** 3877 * Keeps track of 4 floating values (x, y, startx, & starty) that are used in the drag calculations. 3878 * 3879 * @type {Object} 3880 * @private 3881 */ 3882dragData, 3883 3884/** 3885 * The transparent element that is used to listen for drag events. 3886 * 3887 * @type {HTMLElement} 3888 * @private 3889 */ 3890dragProxy, 3891 3892/** 3893 * The draggable element. 3894 * 3895 * @type {HTMLElement} 3896 * @private 3897 */ 3898dragTarget; 3899 3900/** 3901 * Resets the class drag variable. 3902 * 3903 * @private 3904 */ 3905function resetDrag() { 3906 dragData = { 3907 x: 0, 3908 y: 0, 3909 startX: null, 3910 startY: null 3911 }; 3912} 3913 3914/** 3915 * Updates the drag proxy dimensions. 3916 * 3917 * @private 3918 */ 3919function updateProxy() { 3920 var dims = S.dimensions; 3921 apply(dragProxy.style, { 3922 height: dims.innerHeight + "px", 3923 width: dims.innerWidth + "px" 3924 }); 3925} 3926 3927/** 3928 * Enables a transparent drag layer on top of images. 3929 * 3930 * @private 3931 */ 3932function enableDrag() { 3933 resetDrag(); 3934 3935 // add transparent proxy layer to prevent browser dragging of actual image 3936 var style = [ 3937 "position:absolute", 3938 "cursor:" + (S.isGecko ? "-moz-grab" : "move"), 3939 "background-color:" + (S.isIE ? "#fff;filter:alpha(opacity=0)" : "transparent") 3940 ].join(";"); 3941 S.appendHTML(S.skin.body, '<div id="' + proxyId + '" style="' + style + '"></div>'); 3942 3943 dragProxy = get(proxyId); 3944 updateProxy(); 3945 3946 addEvent(dragProxy, "mousedown", startDrag); 3947} 3948 3949/** 3950 * Disables the drag layer. 3951 * 3952 * @private 3953 */ 3954function disableDrag() { 3955 if (dragProxy) { 3956 removeEvent(dragProxy, "mousedown", startDrag); 3957 remove(dragProxy); 3958 dragProxy = null; 3959 } 3960 3961 dragTarget = null; 3962} 3963 3964/** 3965 * Sets up a drag listener on the document. 3966 * 3967 * @param {Event} e The mousedown event 3968 * @private 3969 */ 3970function startDrag(e) { 3971 // prevent browser dragging 3972 preventDefault(e); 3973 3974 var xy = getPageXY(e); 3975 dragData.startX = xy[0]; 3976 dragData.startY = xy[1]; 3977 3978 dragTarget = get(S.player.id); 3979 3980 addEvent(document, "mousemove", positionDrag); 3981 addEvent(document, "mouseup", endDrag); 3982 3983 if (S.isGecko) 3984 dragProxy.style.cursor = "-moz-grabbing"; 3985} 3986 3987/** 3988 * Positions an oversized image on drag. 3989 * 3990 * @param {Event} e The mousemove event 3991 * @private 3992 */ 3993function positionDrag(e) { 3994 var player = S.player, 3995 dims = S.dimensions, 3996 xy = getPageXY(e); 3997 3998 var moveX = xy[0] - dragData.startX; 3999 dragData.startX += moveX; 4000 dragData.x = Math.max(Math.min(0, dragData.x + moveX), dims.innerWidth - player.width); 4001 4002 var moveY = xy[1] - dragData.startY; 4003 dragData.startY += moveY; 4004 dragData.y = Math.max(Math.min(0, dragData.y + moveY), dims.innerHeight - player.height); 4005 4006 apply(dragTarget.style, { 4007 left: dragData.x + "px", 4008 top: dragData.y + "px" 4009 }); 4010} 4011 4012/** 4013 * Removes the drag listener from the document. 4014 * 4015 * @private 4016 */
4017function endDrag() { 4018 removeEvent(document, "mousemove", positionDrag); 4019 removeEvent(document, "mouseup", endDrag); 4020 4021 if (S.isGecko) 4022 dragProxy.style.cursor = "-moz-grab"; 4023} 4024 4025/** 4026 * Constructor. The image player class for Shadowbox. 4027 * 4028 * @constructor 4029 * @param {Object} obj The content object 4030 * @param {String} id The player id 4031 * @public 4032 */ 4033S.img = function(obj, id) { 4034 this.obj = obj; 4035 this.id = id; 4036 4037 // preload the image 4038 this.ready = false; 4039 var self = this; 4040 pre = new Image(); 4041 pre.onload = function() { 4042 // height/width defaults to image height/width 4043 self.height = obj.height ? parseInt(obj.height, 10) : pre.height; 4044 self.width = obj.width ? parseInt(obj.width, 10) : pre.width; 4045 4046 // ready to go 4047 self.ready = true; 4048 4049 // clean up to prevent memory leak in IE 4050 pre.onload = null; 4051 pre = null; 4052 } 4053 pre.src = obj.content; 4054} 4055 4056S.img.ext = ["bmp", "gif", "jpg", "jpeg", "png"]; 4057 4058S.img.prototype = { 4059 4060 /** 4061 * Appends this image to the document. 4062 * 4063 * @param {HTMLElement} body The body element 4064 * @param {Object} dims The current Shadowbox dimensions 4065 * @public 4066 */ 4067 append: function(body, dims) { 4068 var img = document.createElement("img"); 4069 img.id = this.id; 4070 img.src = this.obj.content; 4071 img.style.position = "absolute"; 4072 4073 var height, width; 4074 if (dims.oversized && S.options.handleOversize == "resize") { 4075 height = dims.innerHeight; 4076 width = dims.innerWidth; 4077 } else { 4078 height = this.height; 4079 width = this.width; 4080 } 4081 4082 // need to use setAttribute here for IE's sake 4083 img.setAttribute("height", height); 4084 img.setAttribute("width", width); 4085 4086 body.appendChild(img); 4087 }, 4088 4089 /** 4090 * Removes this image from the document. 4091 * 4092 * @public 4093 */ 4094 remove: function() { 4095 var el = get(this.id); 4096 if (el) 4097 remove(el); 4098 4099 disableDrag(); 4100 4101 // prevent old image requests from loading 4102 if (pre) { 4103 pre.onload = null; 4104 pre = null; 4105 } 4106 }, 4107 4108 /** 4109 * An optional callback function to process after this content has been 4110 * loaded. 4111 * 4112 * @public 4113 */ 4114 onLoad: function() { 4115 var dims = S.dimensions; 4116 4117 // listen for drag when image is oversized 4118 if (dims.oversized && S.options.handleOversize == "drag") 4119 enableDrag(); 4120 }, 4121 4122 /** 4123 * Called when the window is resized. 4124 * 4125 * @public 4126 */ 4127 onWindowResize: function() { 4128 var dims = S.dimensions; 4129 4130 switch (S.options.handleOversize) { 4131 case "resize": 4132 var el = get(this.id); 4133 el.height = dims.innerHeight; 4134 el.width = dims.innerWidth; 4135 break; 4136 case "drag": 4137 if (dragTarget) { 4138 var top = parseInt(S.getStyle(dragTarget, "top")), 4139 left = parseInt(S.getStyle(dragTarget, "left")); 4140 4141 // fix positioning when viewport is enlarged 4142 if (top + this.height < dims.innerHeight) 4143 dragTarget.style.top = dims.innerHeight - this.height + "px"; 4144 if (left + this.width < dims.innerWidth) 4145 dragTarget.style.left = dims.innerWidth - this.width + "px"; 4146 4147 updateProxy(); 4148 } 4149 break; 4150 } 4151 } 4152 4153} 4154/** 4155 * The QuickTime player for Shadowbox. 4156 */ 4157 4158/** 4159 * The height (in pixels) of the QuickTime controller. 4160 * 4161 * @type {Number} 4162 * @private 4163 */ 4164var qtControllerHeight = 16; 4165 4166/** 4167 * Constructor. The QuickTime player class for Shadowbox. 4168 * 4169 * @param {Object} obj The content object 4170 * @param {String} id The player id 4171 * @public 4172 */ 4173S.qt = function(obj, id) { 4174 this.obj = obj; 4175 this.id = id; 4176 4177 // height/width default to 300 pixels 4178 this.height = obj.height ? parseInt(obj.height, 10) : 300; 4179 if (S.options.showMovieControls) 4180 this.height += qtControllerHeight; 4181 this.width = obj.width ? parseInt(obj.width, 10) : 300; 4182} 4183 4184S.qt.ext = ["dv", "mov", "moov", "movie", "mp4", "avi", "mpg", "mpeg"]; 4185 4186S.qt.prototype = { 4187 4188 /** 4189 * Appends this movie to the document. 4190 * 4191 * @param {HTMLElement} body The body element 4192 * @param {Object} dims The current Shadowbox dimensions 4193 * @public 4194 */ 4195 append: function(body, dims) { 4196 var opt = S.options, 4197 autoplay = String(opt.autoplayMovies), 4198 controls = String(opt.showMovieControls); 4199 4200 var html = "<object", 4201 movie = { 4202 id: this.id, 4203 name: this.id, 4204 height: this.height, 4205 width: this.width, 4206 kioskmode: "true" 4207 }; 4208 4209 if (S.isIE) { 4210 movie.classid = "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"; 4211 movie.codebase = "http://www.apple.com/qtactivex/qtplugin.cab#ver
4211sion=6,0,2,0"; 4212 } else { 4213 movie.type = "video/quicktime"; 4214 movie.data = this.obj.content; 4215 } 4216 4217 for (var m in movie) 4218 html += " " + m + '="' + movie[m] + '"'; 4219 html += ">"; 4220 4221 var params = { 4222 src: this.obj.content, 4223 scale: "aspect", 4224 controller: controls, 4225 autoplay: autoplay 4226 }; 4227 4228 for (var p in params) 4229 html += '<param name="' + p + '" value="' + params[p] + '">'; 4230 html += "</object>"; 4231 4232 body.innerHTML = html; 4233 }, 4234 4235 /** 4236 * Removes this movie from the DOM. 4237 * 4238 * @public 4239 */ 4240 remove: function() { 4241 try { 4242 document[this.id].Stop(); // stop QT video stream 4243 } catch(e) {} 4244 4245 var el = get(this.id); 4246 if (el) 4247 remove(el); 4248 } 4249 4250} 4251/** 4252 * The SWF player for Shadowbox. 4253 */ 4254 4255/** 4256 * Constructor. The SWF movie player class for Shadowbox. 4257 * 4258 * @constructor 4259 * @param {Object} obj The content object 4260 * @param {String} id The player id 4261 * @public 4262 */ 4263S.swf = function(obj, id) { 4264 this.obj = obj; 4265 this.id = id; 4266 4267 // height/width default to 300 pixels 4268 this.height = obj.height ? parseInt(obj.height, 10) : 300; 4269 this.width = obj.width ? parseInt(obj.width, 10) : 300; 4270} 4271 4272S.swf.ext = ["swf"]; 4273 4274S.swf.prototype = { 4275 4276 /** 4277 * Appends this swf to the document. 4278 * 4279 * @param {HTMLElement} body The body element 4280 * @param {Object} dims The current Shadowbox dimensions 4281 * @public 4282 */ 4283 append: function(body, dims){ 4284 // append temporary content element to replace 4285 var tmp = document.createElement("div"); 4286 tmp.id = this.id; 4287 body.appendChild(tmp); 4288 4289 var height = dims.innerHeight, 4290 width = dims.innerWidth, 4291 swf = this.obj.content, 4292 version = S.options.flashVersion, 4293 express = S.path + "expressInstall.swf", 4294 flashvars = S.options.flashVars, 4295 params = S.options.flashParams; 4296 4297 S.flash.embedSWF(swf, this.id, width, height, version, express, flashvars, params); 4298 }, 4299 4300 /** 4301 * Removes this swf from the document. 4302 * 4303 * @public 4304 */ 4305 remove: function() { 4306 // call express install callback here in case express install is 4307 // active and user has not selected anything 4308 S.flash.expressInstallCallback(); 4309 S.flash.removeSWF(this.id); 4310 }, 4311 4312 /** 4313 * Called when the window is resized. 4314 * 4315 * @public 4316 */ 4317 onWindowResize: function() { 4318 var dims = S.dimensions, 4319 el = get(this.id); 4320 el.height = dims.innerHeight; 4321 el.width = dims.innerWidth; 4322 } 4323 4324} 4325/** 4326 * The WMP player for Shadowbox. 4327 */ 4328 4329/** 4330 * The height (in pixels) of the Windows Media Player controller. 4331 * 4332 * @type {Number} 4333 * @private 4334 */ 4335var wmpControllerHeight = (S.isIE ? 70 : 45); 4336 4337/** 4338 * Constructor. The Windows Media player class for Shadowbox. 4339 * 4340 * @param {Object} obj The content object 4341 * @param {String} id The player id 4342 * @public 4343 */ 4344S.wmp = function(obj, id) { 4345 this.obj = obj; 4346 this.id = id; 4347 4348 // height/width default to 300 pixels 4349 this.height = obj.height ? parseInt(obj.height, 10) : 300; 4350 if (S.options.showMovieControls) 4351 this.height += wmpControllerHeight; 4352 this.width = obj.width ? parseInt(obj.width, 10) : 300; 4353} 4354 4355S.wmp.ext = ["asf", "avi", "mpg", "mpeg", "wm", "wmv"]; 4356 4357S.wmp.prototype = { 4358 4359 /** 4360 * Appends this movie to the document. 4361 * 4362 * @param {HTMLElement} body The body element 4363 * @param {Object} dims The current Shadowbox dimensions 4364 * @public 4365 */ 4366 append: function(body, dims) { 4367 var opt = S.options, 4368 autoplay = opt.autoplayMovies ? 1 : 0; 4369 4370 var movie = '<object id="' + this.id + 4371 '" name="' + this.id + 4372 '" height="' + this.height + 4373 '" width="' + this.width + '"',
4374 params = { autostart: opt.autoplayMovies ? 1 : 0 }; 4375 4376 if (S.isIE) { 4377 // movie += ' type="application/x-oleobject"'; 4378 movie += ' classid="clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6"'; 4379 params.url = this.obj.content; 4380 params.uimode = opt.showMovieControls ? "full" : "none"; 4381 } else { 4382 movie += ' type="video/x-ms-wmv"'; 4383 movie += ' data="' + this.obj.content + '"' 4384 params.showcontrols = opt.showMovieControls ? 1 : 0; 4385 } 4386 4387 movie += ">"; 4388 4389 for (var p in params) 4390 movie += '<param name="' + p + '" value="' + params[p] + '">'; 4391 4392 movie += "</object>"; 4393 4394 body.innerHTML = movie; 4395 }, 4396 4397 /** 4398 * Removes this movie from the document. 4399 * 4400 * @return void 4401 * @public 4402 */ 4403 remove: function(){ 4404 if (S.isIE) { 4405 try { 4406 window[this.id].controls.stop(); // stop the movie 4407 window[this.id].URL = "movie" + now() + ".wmv"; // force player refresh 4408 window[this.id] = function(){}; // remove from window object 4409 } catch(e) {} 4410 } 4411 4412 var el = get(this.id); 4413 if (el) { 4414 // using setTimeout here prevents browser crashes with WMP 4415 setTimeout(function() { 4416 remove(el); 4417 }, 10); 4418 } 4419 } 4420 4421} 4422/** 4423 * Keeps track of whether or not the overlay is activated. 4424 * 4425 * @type {Boolean} 4426 * @private 4427 */ 4428var overlayOn = false, 4429 4430/** 4431 * A cache of elements that are troublesome for modal overlays. 4432 * 4433 * @type {Array} 4434 * @private 4435 */ 4436visibilityCache = [], 4437 4438/** 4439 * Id's of elements that need transparent PNG support. 4440 * 4441 * @type {Array} 4442 * @private 4443 */ 4444pngIds = [ 4445 "sb-nav-close", 4446 "sb-nav-next", 4447 "sb-nav-play", 4448 "sb-nav-pause", 4449 "sb-nav-previous" 4450], 4451 4452/** 4453 * The container element. 4454 * 4455 * @type {HTMLElement} 4456 * @private 4457 */ 4458container, 4459 4460/** 4461 * The overlay element. 4462 * 4463 * @type {HTMLElement} 4464 * @private 4465 */ 4466overlay, 4467 4468/** 4469 * The wrapper element. 4470 * 4471 * @type {HTMLElement} 4472 * @private 4473 */ 4474wrapper, 4475 4476/** 4477 * True if the window resize event is allowed to fire. 4478 * 4479 * @type {Boolean} 4480 * @private 4481 */ 4482doWindowResize = true; 4483 4484/** 4485 * Animates the given property of el to the given value over a specified duration. If a 4486 * callback is provided, it will be called when the animation is finished. 4487 * 4488 * @param {HTMLElement} el 4489 * @param {String} property 4490 * @param {mixed} to 4491 * @param {Number} duration 4492 * @param {Function} callback 4493 * @private 4494 */ 4495function animate(el, property, to, duration, callback) { 4496 var isOpacity = (property == "opacity"), 4497 anim = isOpacity ? S.setOpacity : function(el, value) { 4498 // default unit is px for properties other than opacity 4499 el.style[property] = "" + 4500 value + "px"; 4501 }; 4502 4503 if (duration == 0 || (!isOpacity && !S.options.animate) || (isOpacity && !S.options.animateFade)) { 4504 anim(el, to); 4505 if (callback) 4506 callback(); 4507 return; 4508 } 4509 4510 var from = parseFloat(S.getStyle(el, property)) || 0; 4511 var delta = to - from; 4512 if (delta == 0) { 4513 if (callback) 4514 callback(); 4515 return; // nothing to animate 4516 } 4517 4518 duration *= 1000; // convert to milliseconds 4519 4520 var begin = now(), 4521 ease = S.ease, 4522 end = begin + duration, 4523 time; 4524 4525 var interval = setInterval(function() { 4526 time = now(); 4527 if (time >= end) { 4528 clearInterval(interval); 4529 interval = null; 4530 anim(el, to); 4531 if (callback) 4532 callback(); 4533 } else { 4534 anim(el, from + ease((time - begin) / duration) * delta); 4535 } 4536 }, 10); // 10 ms interval is minimum on WebKit 4537} 4538 4539/** 4540 * Sets the size of the container element. 4541 * 4542 * @private 4543 */ 4544function setSize() { 4545 container.style.height = S.getWindowSize("Height") + "px"; 4546 container.style.width = S.getWindowSize("Width") + "px"; 4547} 4548 4549/** 4550 * Sets the top of the container element. This is only necessary in browsers that 4551 * don't support fixed positioning, such as IE6. 4552 * 4553 * @private 4554 */ 4555function setPosition() { 4556 container.style.top = document.documentElement.scrollTop + "px"; 4557 container.style.left = document.documentElement.scrollLeft + "px"; 4558} 4559
4560/** 4561 * Toggles the visibility of elements that are troublesome for overlays. 4562 * 4563 * @param {Boolean} on True to make visible, false to hide 4564 * @private 4565 */ 4566function toggleTroubleElements(on) { 4567 if (on) { 4568 each(visibilityCache, function(i, el){ 4569 el[0].style.visibility = el[1] || ''; 4570 }); 4571 } else { 4572 visibilityCache = []; 4573 each(S.options.troubleElements, function(i, tag) { 4574 each(document.getElementsByTagName(tag), function(j, el) { 4575 visibilityCache.push([el, el.style.visibility]); 4576 el.style.visibility = "hidden"; 4577 }); 4578 }); 4579 } 4580} 4581 4582/** 4583 * Toggles the display of the nav control with the given id. 4584 * 4585 * @param {String} id The id of the navigation control 4586 * @param {Boolean} on True to toggle on, false to toggle off 4587 * @private 4588 */ 4589function toggleNav(id, on) { 4590 var el = get("sb-nav-" + id); 4591 if (el) 4592 el.style.display = on ? "" : "none"; 4593} 4594 4595/** 4596 * Toggles the visibility of the loading layer. 4597 * 4598 * @param {Boolean} on True to toggle on, false to toggle off 4599 * @param {Function} callback The callback to use when finished 4600 * @private 4601 */ 4602function toggleLoading(on, callback) { 4603 var loading = get("sb-loading"), 4604 playerName = S.getCurrent().player, 4605 anim = (playerName == "img" || playerName == "html"); // fade on images & html 4606 4607 if (on) { 4608 S.setOpacity(loading, 0); 4609 loading.style.display = "block"; 4610 4611 var wrapped = function() { 4612 S.clearOpacity(loading); 4613 if (callback) 4614 callback(); 4615 } 4616 4617 if (anim) { 4618 animate(loading, "opacity", 1, S.options.fadeDuration, wrapped); 4619 } else { 4620 wrapped(); 4621 } 4622 } else { 4623 var wrapped = function() { 4624 loading.style.display = "none"; 4625 S.clearOpacity(loading); 4626 if (callback) 4627 callback(); 4628 } 4629 4630 if (anim) { 4631 animate(loading, "opacity", 0, S.options.fadeDuration, wrapped); 4632 } else { 4633 wrapped(); 4634 } 4635 } 4636} 4637 4638/** 4639 * Builds the content for the title and information bars. 4640 * 4641 * @param {Function} callback The callback to use when finished 4642 * @private 4643 */ 4644function buildBars(callback) { 4645 var obj = S.getCurrent(); 4646 4647 get("sb-title-inner").innerHTML = obj.title || ""; 4648 4649 // build the nav 4650 var close, next, play, pause, previous; 4651 if (S.options.displayNav) { 4652 close = true; 4653 var len = S.gallery.length; 4654 if (len > 1) { 4655 if (S.options.continuous) { 4656 next = previous = true; 4657 } else { 4658 next = (len - 1) > S.current; // not last in gallery, show next 4659 previous = S.current > 0; // not first in gallery, show previous 4660 } 4661 } 4662 // in a slideshow? 4663 if (S.options.slideshowDelay > 0 && S.hasNext()) { 4664 pause = !S.isPaused(); 4665 play = !pause; 4666 } 4667 } else { 4668 close = next = play = pause = previous = false; 4669 } 4670 toggleNav("close", close); 4671 toggleNav("next", next); 4672 toggleNav("play", play); 4673 toggleNav("pause", pause); 4674 toggleNav("previous", previous); 4675 4676 // build the counter 4677 var counter = ""; 4678 if (S.options.displayCounter && S.gallery.length > 1) { 4679 var len = S.gallery.length; 4680 if (S.options.counterType == "skip") { 4681 // limit the counter? 4682 var i = 0, 4683 end = len, 4684 limit = parseInt(S.options.counterLimit) || 0; 4685 4686 if (limit < len && limit > 2) { // support large galleries 4687 var h = Math.floor(limit / 2); 4688 i = S.current - h; 4689 if (i < 0) 4690 i += len; 4691 end = S.current + (limit - h); 4692 if (end > len) 4693 end -= len; 4694 } 4695 4696 while (i != end) { 4697 if (i == len) 4698 i = 0; 4699 counter += '<a onclick="Shadowbox.change(' + i + ');"' 4700 if (i == S.current) 4701 counter += ' class="sb-counter-current"'; 4702 counter += ">" + (++i) + "</a>"; 4703 } 4704 } else { 4705 counter = [S.current + 1, S.lang.of, len].join(' '); 4706 } 4707 } 4708 4709 get("sb-counter").innerHTML = counter; 4710 4711 callback(); 4712} 4713 4714/** 4715 * Shows the title and info bars. 4716 * 4717 * @param {Function} callback The callback to use when finished 4718 * @private 4719 */ 4720function showBars(callback) { 4721 var titleInner = get("sb-title-inner"), 4722 infoInner = get("sb-info-inner"), 4723 duration = 0.35; 4724 4725 // clear visibility before animating into view 4726 titleInner.style.visibility = infoInner.style.visibility = ""; 4727 4728 if (titleInner.innerHTML != "") 4729 animate(titleInner, "marginTop", 0, duration); 4730 animate(infoInner, "marginTop", 0, duration, callback); 4731} 4732 4733/** 4734 * Hides the title and info bars. 4735 * 4736 * @param {Boolean} anim True to animate the transition 4737 * @param {Function} callback The callback to use when finished 4738 * @private 4739 */ 4740function hideBars(anim, callback) { 4741 var title = get("sb-title"), 4742 info = get("sb-info"),
4743 titleHeight = title.offsetHeight, 4744 infoHeight = info.offsetHeight, 4745 titleInner = get("sb-title-inner"), 4746 infoInner = get("sb-info-inner"), 4747 duration = (anim ? 0.35 : 0); 4748 4749 animate(titleInner, "marginTop", titleHeight, duration); 4750 animate(infoInner, "marginTop", infoHeight * -1, duration, function() { 4751 titleInner.style.visibility = infoInner.style.visibility = "hidden"; 4752 callback(); 4753 }); 4754} 4755 4756/** 4757 * Adjusts the height of #sb-wrapper-inner and centers #sb-wrapper vertically 4758 * in the viewport. 4759 * 4760 * @param {Number} height The height (in pixels) 4761 * @param {Number} top The top (in pixels) 4762 * @param {Boolean} anim True to animate the transition 4763 * @param {Function} callback The callback to use when finished 4764 * @private 4765 */ 4766function adjustHeight(height, top, anim, callback) { 4767 var wrapperInner = get("sb-wrapper-inner"), 4768 duration = (anim ? S.options.resizeDuration : 0); 4769 4770 animate(wrapper, "top", top, duration); 4771 animate(wrapperInner, "height", height, duration, callback); 4772} 4773 4774/** 4775 * Adjusts the width and left position of #sb-wrapper. 4776 * 4777 * @param {Number} width The width (in pixels) 4778 * @param {Number} left The left (in pixels) 4779 * @param {Boolean} anim True to animate the transition 4780 * @param {Function} callback The callback to use when finished 4781 * @private 4782 */ 4783function adjustWidth(width, left, anim, callback) { 4784 var duration = (anim ? S.options.resizeDuration : 0); 4785 4786 animate(wrapper, "left", left, duration); 4787 animate(wrapper, "width", width, duration, callback); 4788} 4789 4790/** 4791 * Calculates the dimensions for Shadowbox. 4792 * 4793 * @param {Number} height The content height 4794 * @param {Number} width The content width 4795 * @return {Object} The new dimensions object 4796 * @private 4797 */ 4798function setDimensions(height, width) { 4799 var bodyInner = get("sb-body-inner"), 4800 height = parseInt(height), 4801 width = parseInt(width), 4802 topBottom = wrapper.offsetHeight - bodyInner.offsetHeight, 4803 leftRight = wrapper.offsetWidth - bodyInner.offsetWidth, 4804 4805 // overlay should provide proper window dimensions here 4806 maxHeight = overlay.offsetHeight, 4807 maxWidth = overlay.offsetWidth, 4808 4809 // default to the default viewport padding 4810 padding = parseInt(S.options.viewportPadding) || 20, 4811 4812 // only preserve aspect ratio if there is something to display and 4813 // it's not draggable 4814 preserveAspect = (S.player && S.options.handleOversize != "drag"); 4815 4816 return S.setDimensions(height, width, maxHeight, maxWidth, topBottom, leftRight, padding, preserveAspect); 4817} 4818 4819/** 4820 * The Shadowbox.skin object. 4821 * 4822 * @type {Object} 4823 * @public 4824 */ 4825var K = {}; 4826 4827/** 4828 * The HTML markup to use. 4829 * 4830 * @type {String} 4831 * @public 4832 */ 4833K.markup = "" + 4834'<div id="sb-container">' + 4835 '<div id="sb-overlay"></div>' + 4836 '<div id="sb-wrapper">' + 4837 '<div id="sb-title">' + 4838 '<div id="sb-title-inner"></div>' + 4839 '</div>' + 4840 '<div id="sb-wrapper-inner">' + 4841 '<div id="sb-body">' + 4842 '<div id="sb-body-inner"></div>' + 4843 '<div id="sb-loading">' + 4844 '<div id="sb-loading-inner"><span>{loading}</span></div>' + 4845 '</div>' + 4846 '</div>' + 4847 '</div>' + 4848 '<div id="sb-info">' + 4849 '<div id="sb-info-inner">' + 4850 '<div id="sb-counter"></div>' + 4851 '<div id="sb-nav">' + 4852 '<a id="sb-nav-close" title="{close}" onclick="Shadowbox.close()"></a>' + 4853 '<a id="sb-nav-next" title="{next}" onclick="Shadowbox.next()"></a>' + 4854 '<a id="sb-nav-play" title="{play}" onclick="Shadowbox.play()"></a>' + 4855 '<a id="sb-nav-pause" title="{pause}" onclick="Shadowbox.pause()"></a>' + 4856 '<a id="sb-nav-previous" title="{previous}" onclick="Shadowbox.previous()"></a>' + 4857 '</div>' + 4858 '</div>' + 4859 '</div>' + 4860 '</div>' + 4861'</div>'; 4862 4863/** 4864 * Various options that control the behavior of Shadowbox' skin. 4865 * 4866 * @type {Object} 4867 * @public 4868 */ 4869K.options = { 4870 4871 /** 4872 * The sequence of the resizing animations. "hw" will resize height, then width. "wh" resizes 4873 * width, then height. "sync" resizes both simultaneously. 4874 * 4875 * @type {String} 4876 */ 4877 animSequence: "sync", 4878 4879 /** 4880 * The limit to the number of counter links that are displayed in a "skip"-style counter. 4881 * 4882 * @type {Number} 4883 */ 4884 counterLimit: 10, 4885 4886 /**
4887 * The counter type to use. May be either "default" or "skip". A skip counter displays a 4888 * link for each object in the gallery. 4889 * 4890 * @type {String} 4891 */ 4892 counterType: "default", 4893 4894 /** 4895 * True to display the gallery counter. 4896 * 4897 * @type {Boolean} 4898 */ 4899 displayCounter: true, 4900 4901 /** 4902 * True to show the navigation controls. 4903 * 4904 * @type {Boolean} 4905 */ 4906 displayNav: true, 4907 4908 /** 4909 * The duration (in seconds) of opacity animations. 4910 * 4911 * @type {Number} 4912 */ 4913 fadeDuration: 0.35, 4914 4915 /** 4916 * The initial height (in pixels). 4917 * 4918 * @type {Number} 4919 */ 4920 initialHeight: 160, 4921 4922 /** 4923 * The initial width (in pixels). 4924 * 4925 * @type {Number} 4926 */ 4927 initialWidth: 320, 4928 4929 /** 4930 * True to trigger Shadowbox.close when the overlay is clicked. 4931 * 4932 * @type {Boolean} 4933 */ 4934 modal: false, 4935 4936 /** 4937 * The color (in hex) to use for the overlay. 4938 * 4939 * @type {String} 4940 */ 4941 overlayColor: "#000", 4942 4943 /** 4944 * The opacity to use for the overlay. 4945 * 4946 * @type {Number} 4947 */ 4948 overlayOpacity: 0.5, 4949 4950 /** 4951 * The duration (in seconds) to use for resizing animations. 4952 * 4953 * @type {Number} 4954 */ 4955 resizeDuration: 0.35, 4956 4957 /** 4958 * True to show the overlay, false to hide it. 4959 * 4960 * @type {Boolean} 4961 */ 4962 showOverlay: true, 4963 4964 /** 4965 * Names of elements that should be hidden when the overlay is enabled. 4966 * 4967 * @type {String} 4968 */ 4969 troubleElements: ["select", "object", "embed", "canvas"] 4970 4971}; 4972 4973/** 4974 * Initialization function. Called immediately after this skin's markup has been 4975 * appended to the document with all of the necessary language replacements done. 4976 * 4977 * @public 4978 */ 4979K.init = function() { 4980 S.appendHTML(document.body, sprintf(K.markup, S.lang)); 4981 4982 K.body = get("sb-body-inner"); 4983 4984 // cache oft-used elements 4985 container = get("sb-container"); 4986 overlay = get("sb-overlay"); 4987 wrapper = get("sb-wrapper"); 4988 4989 // use absolute positioning in browsers that don't support fixed 4990 if (!supportsFixed) 4991 container.style.position = "absolute"; 4992 4993 if (!supportsOpacity) { 4994 // support transparent PNG's via AlphaImageLoader 4995 var el, m, re = /url\("(.*\.png)"\)/; 4996 each(pngIds, function(i, id) { 4997 el = get(id); 4998 if (el) { 4999 m = S.getStyle(el, "backgroundImage").match(re); 5000 if (m) { 5001 el.style.backgroundImage = "none"; 5002 el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src=" + 5003 m[1] + ",sizingMethod=scale);"; 5004 } 5005 } 5006 }); 5007 } 5008 5009 // add window resize event handler, use 10 ms buffer to prevent jerky resizing 5010 var timer; 5011 addEvent(window, "resize", function() { 5012 if (timer) { 5013 clearTimeout(timer); 5014 timer = null; 5015 } 5016 5017 if (open) 5018 timer = setTimeout(K.onWindowResize, 10); 5019 }); 5020} 5021 5022/** 5023 * Called when Shadowbox opens. 5024 * 5025 * @param {Object} obj The object to open 5026 * @param {Function} callback The callback to use when finished 5027 * @public 5028 */ 5029K.onOpen = function(obj, callback) { 5030 // prevent window resize events from firing until we're finished 5031 doWindowResize = false; 5032 5033 container.style.display = "block"; 5034 5035 setSize(); 5036 5037 var dims = setDimensions(S.options.initialHeight, S.options.initialWidth); 5038 adjustHeight(dims.innerHeight, dims.top); 5039 adjustWidth(dims.width, dims.left); 5040 5041 if (S.options.showOverlay) { 5042 overlay.style.backgroundColor = S.options.overlayColor; 5043 S.setOpacity(overlay, 0); 5044 5045 if (!S.options.modal) 5046 addEvent(overlay, "click", S.close); 5047 5048 overlayOn = true; 5049 } 5050 5051 if (!supportsFixed) { 5052 setPosition(); 5053 addEvent(window, "scroll", setPosition); 5054 } 5055 5056 toggleTroubleElements(); 5057 container.style.visibility = "visible"; 5058 5059 if (overlayOn) { 5060 animate(overlay, "opacity", S.options.overlayOpacity, S.options.fadeDuration, callback); 5061 } else { 5062 callback(); 5063 } 5064} 5065 5066/** 5067 * Called when a new object is being loaded. 5068 * 5069 * @param {Boolean} changing True if the content is changing from some 5070 * previous object 5071 * @param {Function} callback The callback to use when finished 5072 * @public 5073 */
5074K.onLoad = function(changing, callback) { 5075 toggleLoading(true); 5076 5077 // make sure the body doesn't have any children 5078 while (K.body.firstChild) 5079 remove(K.body.firstChild); 5080 5081 hideBars(changing, function() { 5082 if (!open) 5083 return; 5084 5085 if (!changing) 5086 wrapper.style.visibility = "visible"; 5087 5088 buildBars(callback); 5089 }); 5090} 5091 5092/** 5093 * Called when the content is ready to be loaded (e.g. when the image has finished 5094 * loading). Should resize the content box and make any other necessary adjustments. 5095 * 5096 * @param {Function} callback The callback to use when finished 5097 * @public 5098 */ 5099K.onReady = function(callback) { 5100 if (!open) 5101 return; 5102 5103 var player = S.player, 5104 dims = setDimensions(player.height, player.width); 5105 5106 var wrapped = function() { 5107 showBars(callback); 5108 } 5109 5110 switch (S.options.animSequence) { 5111 case "hw": 5112 adjustHeight(dims.innerHeight, dims.top, true, function() { 5113 adjustWidth(dims.width, dims.left, true, wrapped); 5114 }); 5115 break; 5116 case "wh": 5117 adjustWidth(dims.width, dims.left, true, function() { 5118 adjustHeight(dims.innerHeight, dims.top, true, wrapped); 5119 }); 5120 break; 5121 default: // sync 5122 adjustWidth(dims.width, dims.left, true); 5123 adjustHeight(dims.innerHeight, dims.top, true, wrapped); 5124 } 5125} 5126 5127/** 5128 * Called when the content is loaded into the box and is ready to be displayed. 5129 * 5130 * @param {Function} callback The callback to use when finished 5131 * @public 5132 */ 5133K.onShow = function(callback) { 5134 toggleLoading(false, callback); 5135 5136 // re-enable window resize events 5137 doWindowResize = true; 5138} 5139 5140/** 5141 * Called in Shadowbox.close. 5142 * 5143 * @public 5144 */ 5145K.onClose = function() { 5146 if (!supportsFixed) 5147 removeEvent(window, "scroll", setPosition); 5148 5149 removeEvent(overlay, "click", S.close); 5150 5151 wrapper.style.visibility = "hidden"; 5152 5153 var callback = function() { 5154 container.style.visibility = "hidden"; 5155 container.style.display = "none"; 5156 toggleTroubleElements(true); 5157 } 5158 5159 if (overlayOn) { 5160 animate(overlay, "opacity", 0, S.options.fadeDuration, callback); 5161 } else { 5162 callback(); 5163 } 5164} 5165 5166/** 5167 * Called in Shadowbox.play. 5168 * 5169 * @public 5170 */ 5171K.onPlay = function() { 5172 toggleNav("play", false); 5173 toggleNav("pause", true); 5174} 5175 5176/** 5177 * Called in Shadowbox.pause. 5178 * 5179 * @public 5180 */ 5181K.onPause = function() { 5182 toggleNav("pause", false); 5183 toggleNav("play", true); 5184} 5185 5186/** 5187 * Called when the window is resized. 5188 * 5189 * @public 5190 */ 5191K.onWindowResize = function() { 5192 if (!doWindowResize) 5193 return; 5194 5195 setSize(); 5196 5197 var player = S.player, 5198 dims = setDimensions(player.height, player.width); 5199 5200 // adjust width first to eliminate horizontal scroll bar 5201 adjustWidth(dims.width, dims.left); 5202 adjustHeight(dims.innerHeight, dims.top); 5203 5204 if (player.onWindowResize) 5205 player.onWindowResize(); 5206} 5207 5208S.skin = K; 5209// expose 5210window['Shadowbox'] = S; 5211 5212})(window);
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.