home *** CD-ROM | disk | FTP | other *** search
/ developer.apple.com / developer.apple.com.tar / developer.apple.com / global / scripts / overlay.js < prev    next >
Text File  |  2009-11-09  |  25KB  |  828 lines

  1.  
  2. if (typeof(AC) == "undefined") AC = {};
  3.  
  4.  
  5.  
  6. AC.OverlayInit = function(overlaysArray) {
  7.     for (var i=0; i<overlaysArray.length; i++) {
  8.         Event.observe(overlaysArray[i], 'beforePop', function(evt) {
  9.             var except = this.overlays.without(this.thisOverlay);
  10.             except.each(function(overlay) {
  11.                 overlay.close();
  12.             });
  13.         }.bind({ thisOverlay:overlaysArray[i], overlays:overlaysArray }));
  14.     }
  15. };
  16.  
  17.  
  18.  
  19. AC.Overlay = Class.create(
  20. {
  21.     controller: null,
  22.  
  23.     closeBtn: null,
  24.     overlay: null,
  25.     overlayShadow: null,
  26.  
  27.     overlayId: '',
  28.     overlayClasses: '',
  29.     overlayContents: '',
  30.     overlayShadowId: '',
  31.     overlayShadowClasses: '',
  32.     overlayShadowSrc: '',
  33.  
  34.     // -- empty functions --
  35.     buildContents:     function() {},  // called from createOverlay()
  36.     setItemAttributes: function() {},  // called from initialize()
  37.     prepPop:           function() {},  // called from onClick()
  38.  
  39.     // -- custom events --
  40.     //     - latch onto these events using this expample code:
  41.     //       `document.observe(this.id+':beforePop', this.handleBeforePop.bind(this));`
  42.     //     - using Event.fire rather than calling functions explicitly allows
  43.     //       multiple decendent classes to latch onto the same event.
  44.     //
  45.     //    beforePop    -- called from onClick()
  46.     //    afterPop     -- called from afterPop()
  47.     //    beforeClose  -- called from beforeClose()
  48.     //    afterClose   -- called from afterClose()
  49.  
  50.  
  51.     initialize: function(items, options) {
  52.         this.id = 'overlay'+AC.Overlay.counter;
  53.         AC.Overlay.counter++;
  54.         this.items = items;
  55.  
  56.         this.options = options || {};
  57.         if (this.options.overlayShadowSrc) this.overlayShadowSrc = this.options.overlayShadowSrc;
  58.         
  59.         if (this.items.length>0) {
  60.             this.createOverlay();
  61.             this.setDefaults();
  62.             this.setItemAttributes(); // empty by default
  63.         }
  64.  
  65.         if (location.hash) {
  66.             var initial = location.hash.substring(location.hash.indexOf('#')+1, location.hash.length);
  67.             initial = initial.toQueryParams();
  68.             this.showInitial(initial);
  69.         }
  70.     },
  71.  
  72.     createOverlay: function() {
  73.         this.closeBtn = Builder.node('a', {href:'#close', 'class':'close'}, 'Close');
  74.         Event.observe(this.closeBtn, 'click', this.close.bindAsEventListener(this));
  75.  
  76.         this.buildContents(); // empty by default
  77.  
  78.         this.overlay = Builder.node('div', {'id':this.overlayId, 'class':'overlay '+this.overlayClasses}, this.overlayContents);
  79.  
  80.         this.overlayshadow = Builder.node('div', {id:this.overlayShadowId, 'class':'overlayshadow '+this.overlayShadowClasses}, [
  81.             Builder.node('img', {src:this.overlayShadowSrc, alt:'', border:0})
  82.         ]);
  83.  
  84.         document.body.appendChild(this.overlayshadow);
  85.         document.body.appendChild(this.overlay);
  86.     },
  87.  
  88.     setDefaults: function() {
  89.         this.defaultWidth = this.overlay.offsetWidth;
  90.         this.padleft = parseInt(Element.getStyle(this.overlay, 'marginLeft').replace(/px/i,''));
  91.         this.padright = parseInt(Element.getStyle(this.overlay, 'marginRight').replace(/px/i,''));
  92.  
  93.         this.defaultHeight = this.overlay.offsetHeight;
  94.         this.padtop = parseInt(Element.getStyle(this.overlay, 'marginTop').replace(/px/,''));
  95.         this.padbottom = parseInt(Element.getStyle(this.overlay, 'marginBottom').replace(/px/,''));
  96.     },
  97.  
  98.     showInitial: function(index) {
  99.         index = Object.values(index);
  100.         index = parseInt(index[0]);
  101.  
  102.         // store the small size and position for later
  103.         this.width = 50;
  104.         this.left = this.windowSize().x+(this.windowSize().width/2);
  105.         this.height = 50;
  106.         this.top = this.windowSize().y+(this.windowSize().height/2)
  107.  
  108.         // do the movie or the image
  109.         if (this.items[index]) {
  110.             this.prepPop(null, this.items[index], index);
  111.         }
  112.     },
  113.  
  114.     setEvent: function(item, i) {
  115.         if (typeof(item) == 'object') {
  116.             if (item.nodeType == 1) Event.observe(item, 'click', this.onClick.bindAsEventListener(this, item, i));
  117.         }
  118.     },
  119.  
  120.     onClick: function(evt, item, i) {
  121.         this.setDimensions(evt, item, i);
  122.  
  123.         // stop the default event
  124.         evt.stop();
  125.  
  126.         // dispatch beforePop in case anything needs to be closed
  127.         document.fire(this.id+':beforePop');
  128.  
  129.         // do the image
  130.         this.prepPop(evt, item, i); // empty by default
  131.     },
  132.  
  133.     setDimensions: function(evt, item, i) {
  134.         // store the small size and position for later
  135.         this.width = (item.offsetWidth>80) ? 80 : item.offsetWidth;
  136.         this.left = evt.pageX || evt.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
  137.         this.left -= this.width/2;
  138.         this.left = this.left || document.body.getDimensions().width / 2;
  139.         this.height = item.offsetHeight;
  140.         this.top = evt.pageY || evt.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
  141.         this.top -= this.height/2;
  142.     },
  143.  
  144.     windowSize: function() {
  145.         var width = document.clientWidth || (document.documentElement.clientWidth || document.body.clientWidth);
  146.         var height = document.clientHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight);
  147.         var x = window.pageXOffset || (window.document.documentElement.scrollLeft || window.document.body.scrollLeft);
  148.         var y = window.pageYOffset || (window.document.documentElement.scrollTop || window.document.body.scrollTop);
  149.  
  150.         return {'width':width, 'height':height, 'x':x, 'y':y}
  151.     },
  152.  
  153.     setPopPosition: function() {
  154.         // set the position/offset of the image
  155.         var left, top = null;
  156.  
  157.         left = this.windowSize().x+(this.windowSize().width-this.defaultWidth-this.padleft-this.padright)/2;
  158.         if (this.windowSize().width<this.defaultWidth+this.padleft+this.padright) left = this.windowSize().x-(this.padtop-this.closeBtn.offsetWidth);
  159.  
  160.         top = this.windowSize().y+(this.windowSize().height-this.defaultHeight-this.padtop-this.padbottom)/2;
  161.         if (this.windowSize().height<this.defaultHeight+this.padtop+this.padbottom) top = this.windowSize().y-(this.padtop-this.closeBtn.offsetHeight);
  162.  
  163.         return { left:left, top:top };
  164.     },
  165.  
  166.     beforePop: function() {
  167.         Element.addClassName(this.overlay, 'isanim');
  168.         Element.addClassName(this.overlayshadow, 'isanim');
  169.     },
  170.  
  171.     pop: function(width, top, height, left, item, i) {
  172.         // prep the overlay/shadow for the effect
  173.         this.overlay.style.width = this.width+'px';
  174.         this.overlayshadow.style.width = this.width+'px';
  175.  
  176.         this.overlay.style.height = this.height+'px';
  177.         this.overlayshadow.style.height = this.height+'px';
  178.  
  179.         this.overlay.style.left = this.left+'px';
  180.         this.overlayshadow.style.left = this.left+'px';
  181.  
  182.         this.overlay.style.top = this.top+'px';
  183.         this.overlayshadow.style.top = this.top+'px';
  184.  
  185.         Element.setOpacity(this.overlay, 0);
  186.         Element.setOpacity(this.overlayshadow, 0);
  187.  
  188.         if (!AC.Detector.isMobile()) {
  189.             // do the craziness
  190.             new Effect.Parallel([
  191.                     new Effect.MoveBy(this.overlay, top-this.top, left-this.left, { sync:true }),
  192.                     new Effect.MoveBy(this.overlayshadow, top-this.top, left-this.left, { sync:true }),
  193.                     new Effect.Scale(this.overlay, (width/this.width)*100, { sync:true, scaleY:false, scaleContent:false }),
  194.                     new Effect.Scale(this.overlayshadow, ((width+this.padleft+this.padleft)/this.width)*100, { sync:true, scaleY:false, scaleContent:false }),
  195.                     new Effect.Scale(this.overlay, (height/this.height)*100, { sync:true, scaleX:false, scaleContent:false }),
  196.                     new Effect.Scale(this.overlayshadow, ((height+this.padtop+this.padbottom)/this.height)*100, { sync:true, scaleX:false, scaleContent:false }),
  197.                     new Effect.Appear(this.overlay, { sync:true }),
  198.                     new Effect.Appear(this.overlayshadow, { sync:true })
  199.                 ],
  200.                 { duration:.3, beforeStart:this.beforePop.bind(this), afterFinish:this.afterPop.bind(this, item, i) }
  201.             );
  202.         } else {
  203.             this.beforePop();
  204.  
  205.             this.overlay.style.left = parseInt(left)+'px';
  206.             this.overlayshadow.style.left = parseInt(left)+'px';
  207.  
  208.             this.overlay.style.top = parseInt(top)+'px';
  209.             this.overlayshadow.style.top = parseInt(top)+'px';
  210.  
  211.             this.afterPop(item, i);
  212.         }
  213.     },
  214.  
  215.     afterPop: function(item, id) {
  216.         this.setPoppedClass();
  217.  
  218.         // dispatch afterPop event in case anything else needs to be reset
  219.         document.fire(this.id+':afterPop', { item:item, id:id });
  220.  
  221.         this.resetOverlay();
  222.     },
  223.  
  224.     beforeClose: function() {
  225.         this.setIsanimClass();
  226.  
  227.         //dispatch beforeClose event in case anything else needs to be closed
  228.         document.fire(this.id+':beforeClose');
  229.     },
  230.  
  231.     resetOverlay: function() {
  232.         // reset the effect inline styles
  233.         this.overlay.style.width = '';
  234.         this.overlayshadow.style.width = '';
  235.  
  236.         this.overlay.style.height = '';
  237.         this.overlayshadow.style.height = '';
  238.  
  239.         Element.setOpacity(this.overlay, '');
  240.         Element.setOpacity(this.overlayshadow, '');
  241.     },
  242.  
  243.     setPoppedClass: function() {
  244.         Element.removeClassName(this.overlay, 'isanim');
  245.         Element.removeClassName(this.overlayshadow, 'isanim');
  246.         Element.addClassName(this.overlay, 'popped');
  247.         Element.addClassName(this.overlayshadow, 'popped');
  248.     },
  249.  
  250.     setIsanimClass: function() {
  251.         Element.addClassName(this.overlay, 'isanim');
  252.         Element.addClassName(this.overlayshadow, 'isanim');
  253.         Element.removeClassName(this.overlay, 'popped');
  254.         Element.removeClassName(this.overlayshadow, 'popped');
  255.     },
  256.  
  257.     close: function(evt) {
  258.         if (evt) evt.stop();
  259.  
  260.         var width = this.defaultWidth;
  261.         var left = this.overlay.offsetLeft;
  262.         var height = this.defaultHeight;
  263.         var top = this.overlay.offsetTop;
  264.  
  265.         if (!AC.Detector.isMobile()) {
  266.             // do the craziness
  267.             new Effect.Parallel([
  268.                 new Effect.MoveBy(this.overlay, this.top-top, this.left-left, { sync:true }),
  269.                 new Effect.MoveBy(this.overlayshadow, this.top-top, this.left-left, { sync:true }),
  270.                 new Effect.Scale(this.overlay, (this.width/width)*100, { sync:true, scaleY:false, scaleContent:false }),
  271.                 new Effect.Scale(this.overlayshadow, (this.width/(width+this.padleft+this.padleft))*100, { sync:true, scaleY:false, scaleContent:false }),
  272.                 new Effect.Scale(this.overlay, (this.height/height)*100, { sync:true,scaleX:false, scaleContent:false }),
  273.                 new Effect.Scale(this.overlayshadow, (this.height/(height+this.padtop+this.padbottom))*100, { sync:true, scaleX:false, scaleContent:false }),
  274.                 new Effect.Fade(this.overlay, { sync:true }),
  275.                 new Effect.Fade(this.overlayshadow, { sync:true })
  276.             ],
  277.             { duration:.3, beforeStart:this.beforeClose.bind(this), afterFinish:this.afterClose.bind(this) });
  278.         } else {
  279.             this.beforeClose();
  280.             this.afterClose();
  281.         }
  282.     },
  283.  
  284.     afterClose: function() {
  285.         Element.removeClassName(this.overlay, 'isanim');
  286.         Element.removeClassName(this.overlayshadow, 'isanim');
  287.  
  288.         // reset everything
  289.         this.overlay.style.width = '';
  290.         this.overlayshadow.style.width = '';
  291.  
  292.         this.overlay.style.height = '';
  293.         this.overlayshadow.style.height = '';
  294.  
  295.         this.overlay.style.left = '';
  296.         this.overlayshadow.style.left = '';
  297.  
  298.         this.overlay.style.top = '';
  299.         this.overlayshadow.style.top = '';
  300.  
  301.         this.overlay.style.display = '';
  302.         this.overlayshadow.style.display = '';
  303.  
  304.         // dispatch afterClose in case anything else needs to be reset
  305.         document.fire(this.id+':afterClose');
  306.  
  307.         if (AC.Detector.isWebKit()) this.fixSafarisScrollBars();
  308.     },
  309.  
  310.     fixSafarisScrollBars: function() {
  311.         scrollTo = 1;
  312.         window.scroll(this.windowSize().x+scrollTo, this.windowSize().y+scrollTo);
  313.         scrollTo = -scrollTo;
  314.         window.scroll(this.windowSize().x+scrollTo, this.windowSize().y+scrollTo);
  315.     }
  316. });
  317. AC.Overlay.counter = 0;
  318.  
  319.  
  320.  
  321. AC.ImageOverlay = Class.create( AC.Overlay,
  322. {
  323.     overlayId: 'ACOverlayImage',
  324.     overlayShadowId: 'ACOverlayImageShadow',
  325.     overlayShadowSrc: 'http://images.apple.com/global/elements/overlay/overlay_shadow20070807.png',
  326.  
  327.     buildContents: function() {
  328.         this.overlayimg = Builder.node('img', {'class':'overlayimg', border:0});
  329.         this.overlaynav = Builder.node('div', {'class':'overlaynav'});
  330.  
  331.         this.overlayContents = [
  332.             this.closeBtn,
  333.             this.overlayimg,
  334.             this.overlaynav
  335.         ];
  336.     },
  337.  
  338.     setItemAttributes: function() {
  339.         for (var i=0; i<this.items.length; i++) {
  340.             var item = this.items[i];
  341.  
  342.             item.img = new Image();
  343.             item.img.src = item.href;
  344.             item.img.alt = (Element.down(item, 'img')) ? Element.down(item, 'img').alt : item.innerHTML;
  345.             item.img.alt = item.img.alt.replace(/: click to enlarge/i, '');
  346.  
  347.             item.nav = this.getNav(item);
  348.  
  349.             item.img.shortsrc = item.img.src.substring(item.img.src.lastIndexOf('/')+1, item.img.src.length);
  350.  
  351.             this.setEvent(item, i);
  352.         }
  353.  
  354.     },
  355.  
  356.     getNav: function(item) {
  357.         var wrapper = Element.up(item, 'ul');
  358.         var siblings = wrapper.getElementsByClassName('overlaythumb');
  359.  
  360.         var items = [];
  361.         for (var i=0; i<siblings.length; i++) {
  362.             var cloned = siblings[i].cloneNode(true);
  363.             if (item == siblings[i]) Element.addClassName(cloned, 'active')
  364.              items.push(Builder.node('li', cloned));
  365.         }
  366.  
  367.         var list = Builder.node('ul', {'class':'w'+siblings.length}, items)
  368.         return list;
  369.     },
  370.  
  371.     setNav: function(item, i) {
  372.         this.overlaynav.innerHTML = '';
  373.  
  374.         // set up the nav
  375.         this.overlaynav.appendChild(item.nav);
  376.         var items = $$('.'+this.overlaynav.className+' .'+'overlaythumb');
  377.         items.each(function(item, index) {
  378.             item.observe('click', this.swapImage.bindAsEventListener(this, item, index, i));
  379.         }.bind(this));
  380.     },
  381.  
  382.     swapImage: function(evt, item, j, i) {
  383.         evt.stop();
  384.  
  385.         // swap the nav
  386.         var items = $$('.'+this.overlaynav.className+' .'+'overlaythumb');
  387.         for (var k=0; k<items.length; k++) {
  388.             if (items[k].href==item.href) {
  389.                 var clicked = items[k];
  390.                  Element.addClassName(clicked, 'active');
  391.             } else {
  392.                  Element.removeClassName(items[k], 'active')
  393.              }
  394.         }
  395.  
  396.  
  397.         // swap the image
  398.         this.overlayimg.src = clicked.href;
  399.         this.overlayimg.alt = Element.down(clicked).alt.replace(/: click to enlarge/i, '');
  400.     },
  401.  
  402.     prepPop: function(evt, item, i) {
  403.         // set the source for image in the overlay
  404.         this.overlayimg.src = item.img.src;
  405.         this.overlayimg.alt = item.img.alt;
  406.  
  407.         // set up the nav
  408.         this.setNav(item, i);
  409.  
  410.         // call the effect
  411.         this.pop(this.defaultWidth, this.setPopPosition().top, this.defaultHeight, this.setPopPosition().left, item, i);
  412.     }
  413.  
  414. });
  415.  
  416.  
  417.  
  418. AC.MovieOverlay = Class.create( AC.Overlay,
  419. {
  420.     movieController: false,
  421.  
  422.     overlayId: 'ACOverlayMovie',
  423.     overlayClasses: 'movie',
  424.     overlayShadowId: 'ACOverlayMovieShadow',
  425.     overlayShadowClasses: 'movieshadow',
  426.     overlayShadowSrc: 'http://images.apple.com/global/elements/overlay/overlay_movieshadow20070807.png',
  427.     
  428.     itemIdsByMovieUrl: {},
  429.     
  430.     initialize: function($super, items, options) {
  431.         $super(items, options);
  432.         document.observe(this.id+':beforePop', this.handleBeforePop.bind(this));
  433.         document.observe(this.id+':afterPop', this.handleAfterPop.bind(this));
  434.         document.observe(this.id+':beforeClose', this.handleBeforeClose.bind(this));
  435.     },
  436.  
  437.     buildContents: function() {
  438.         this.displayPanel = Builder.node('div', {'class':'overlaymovie'});
  439.         this.controllerPanel = Builder.node('div', {'class':'overlaycontroller'});
  440.         this.descriptionPanel = Builder.node('div');
  441.         var descriptionPanel = Builder.node('div', {'class':'overlaydescription'}, [
  442.             Builder.node('div', [this.descriptionPanel])
  443.         ]);
  444.  
  445.         this.overlayContents = [
  446.             this.closeBtn,
  447.             descriptionPanel,
  448.             this.displayPanel,
  449.             this.controllerPanel
  450.         ];
  451.     },
  452.  
  453.     setItemAttributes: function() {
  454.         for (var i=0; i<this.items.length; i++) {
  455.             var item = this.items[i];
  456.  
  457.             item.movieLink = Element.down(item, 'a.overlaymovielink');
  458.             item.movieUrl = item.movieLink.href;
  459.             this.itemIdsByMovieUrl[item.movieUrl] = i;
  460.             item.description = Element.down(item, '.overlaydescription');
  461.  
  462.             var posterFrame = Element.down(item, '.overlayposter');
  463.             if (posterFrame) item.posterFrameUrl = posterFrame.innerHTML.match(/src="(.*)"/)[1];
  464.  
  465.             this.setEvent(item, i);
  466.         }
  467.     },
  468.  
  469.     handleBeforePop: function() {
  470.         this.beforeClose();
  471.         this.afterClose();
  472.     },
  473.  
  474.     handleAfterPop: function(evt) {
  475.         var i = evt.memo.id;
  476.         var item = evt.memo.item;
  477.         // reset movie & static content
  478.         this.setMovie(item, i);
  479.         this.descriptionPanel.innerHTML = item.description.innerHTML;
  480.     },
  481.  
  482.     handleBeforeClose: function() {
  483.         // stop the movie and unplug the controller
  484.         if (this.movieController) {
  485.             this.movieController.Stop.bind(this.movieController);
  486.             this.movieController.detachFromMovie.bind(this.movieController);
  487.             this.movieController = false;
  488.         }
  489.  
  490.         // reset the containers
  491.         this.displayPanel.style.display = 'none';
  492.         this.displayPanel.innerHTML = '';
  493.         this.displayPanel.style.display = '';
  494.  
  495.         this.controllerPanel.innerHTML = '';
  496.  
  497.         this.descriptionPanel.innerHTML = '';
  498.     },
  499.  
  500.     prepPop: function(evt, item, i) {
  501.         // call the effect
  502.         this.pop(this.defaultWidth, this.setPopPosition().top, this.defaultHeight, this.setPopPosition().left, item, i);
  503.     },
  504.  
  505.     setMovie: function(item, i) {
  506.         this.movieOptions = {
  507.             width: (this.options.moviewidth) ? this.options.moviewidth : 480,
  508.             height: (this.options.movieheight) ? this.options.movieheight : 360,
  509.             controller: false,
  510.             showlogo: false,
  511.             background: '#ffffff',
  512.             cache: true
  513.         };
  514.  
  515.         // if we're Mobile or Opera, use the standard movie controller
  516.         if (AC.Detector.isMobile() || AC.Detector.isOpera()) this.movieOptions.controller = true;
  517.         // if we're Opera, add the height for the controller
  518.         if (AC.Detector.isOpera()) this.movieOptions.height += 16;
  519.  
  520.  
  521.         this.packageMovie(item);
  522.         this.connectMovieController();
  523.  
  524.         //this.movie = null; // for IE
  525.     },
  526.  
  527.     packageMovie: function(item) {
  528.         if (!AC.Detector.isQTInstalled()) {
  529.             this.displayPanel.innerHTML =
  530.                 '<div id="noqt">\
  531.                     <a href="http://www.apple.com/quicktime/download/"><img src="/main/images/overlay/movie_qt7required.gif" alt="QuickTime 7 Required" width="88" height="31" border="0" class="across"></a>\
  532.                     <p>Download <a href="http://www.apple.com/quicktime/download/">QuickTime</a>.</p>\
  533.                 </div>';
  534.         } else {
  535.             if (item.posterFrameUrl) this.movieOptions.posterFrame = item.posterFrameUrl;
  536.             this.movie = AC.Quicktime.packageMovie('overlaymovie', item.movieUrl, this.movieOptions);
  537.             this.displayPanel.appendChild(this.movie);
  538.         }
  539.     },
  540.  
  541.     connectMovieController: function() {
  542.         if (AC.Detector.isQTInstalled()) {
  543.             if (!this.movieOptions.controller) {
  544.                 this.movieController = new AC.QuicktimeController();
  545.                 this.movieController.render(this.controllerPanel);
  546.                 this.movieController.attachToMovie(this.movie, {
  547.                     onMoviePlayable: function() {
  548.                         this.movieController.monitorMovie();
  549.                     }.bind(this)});
  550.             }
  551.         }
  552.     }
  553. });
  554.  
  555.  
  556.  
  557. AC.MovieGalleryOverlay = Class.create( AC.MovieOverlay,
  558. {
  559.     overlayId: 'ACOverlayMovieGallery',
  560.     overlayClasses: 'movie tour',
  561.     overlayShadowId: 'ACOverlayMovieGalleryShadow',
  562.     overlayShadowClasses: 'movieshadow tourshadow',
  563.     overlayShadowSrc: '/main/images/overlay/shadow_moviegallery.png',
  564.     
  565.     initialize: function($super, items, options) {
  566.         $super(items, options);
  567.         document.observe('document:afterPop', this.handleAfterPop.bind(this));
  568.     },
  569.  
  570.     movieLinks: function(item, i) {
  571.         if (AC.Detector.isOpera()) {
  572.             this.previous = true;
  573.             this.next = true;
  574.         } else {
  575.             if (!this.next && !this.previous) {
  576.                 this.previous = Builder.node('a', {'class':'previous'}, 'Previous');
  577.                 this.next = Builder.node('a', {'class':'next'}, 'Next');
  578.                 var overlaynav = Builder.node('div', {'class':'overlaynav'}, [this.previous, this.next]);
  579.                 this.controllerPanel.parentNode.appendChild(overlaynav);
  580.             }
  581.             this.setMovieLinks(i);
  582.         }
  583.     },
  584.  
  585.     setMovieLinks: function(i) {
  586.         var onclick = function(item, i) {
  587.             this.swapMovie(item);
  588.             this.setMovieLinks(i);
  589.             return false;
  590.         }
  591.  
  592.         var pindex = (i==0) ? this.items.length-1 : i-1;
  593.         var previous = this.items[pindex];
  594.         this.previous.innerHTML = previous.title;
  595.         this.previous.href = previous.movieUrl;
  596.         this.previous.onclick = onclick.bind(this, previous, pindex);
  597.  
  598.         var nindex = (i==this.items.length-1) ? 0 : i+1;
  599.         var next = this.items[nindex];
  600.         this.next.innerHTML = next.title;
  601.         this.next.href = next.movieUrl;
  602.         this.next.onclick = onclick.bind(this, next, nindex);
  603.     },
  604.  
  605.     swapMovie: function(item) {
  606.         this.descriptionPanel.innerHTML = item.description.innerHTML;
  607.  
  608.         if (this.movieController) {
  609.             this.movieController.SetURL(item.movieUrl);
  610.         } else {
  611.             this.displayPanel.innerHTML = '';
  612.             this.packageMovie(item);
  613.         }
  614.     },
  615.  
  616.     handleAfterPop: function(evt) {
  617.         this.initializeMovie(evt.memo.id);
  618.     },
  619.     
  620.     initializeMovie: function(id) {
  621.         // reset movie & static content
  622.         var item = this.items[id]
  623.         this.setMovie(item, id);
  624.         this.descriptionPanel.innerHTML = item.description.innerHTML;
  625.         this.movieLinks(item, id);
  626.     }
  627. });
  628.  
  629.  
  630.  
  631. AC.MovieGallerySelectOverlay = Class.create( AC.MovieGalleryOverlay,
  632. {
  633.     overlayId: 'MovieGalleryOptionOverlay',
  634.     overlayShadowId: 'MovieGalleryOptionShadow',
  635.  
  636.     movieLinks: function(item, i) {
  637.         if (!this.select) {
  638.             this.select = Builder.node('select');
  639.  
  640.             var label = (this.options.overlaynavLabel) ? this.options.overlaynavLabel : 'Watch another video:';
  641.             label += ' ';
  642.             label = Builder.node('label', [label, this.select]);
  643.  
  644.             var overlaynav = Builder.node('div', {'class':'overlaynav'}, [label]);
  645.             this.controllerPanel.parentNode.appendChild(overlaynav);
  646.         }
  647.         this.setMovieLinks(i);
  648.     },
  649.  
  650.     setMovieSelect: function(id) {
  651.         this.select.innerHTML = '';
  652.         this.selectOptions = [];
  653.  
  654.         if (this.options.blankOption) {
  655.             var blankOption = Builder.node('option', { value:'' }, this.options.blankOptionTitle || '');
  656.             this.select.appendChild(blankOption);
  657.         }
  658.         for (var i=0; i<this.items.length; i++) {
  659.             var item = this.items[i];
  660.  
  661.             var option = Builder.node('option', { value:i }, item.title);
  662.             this.selectOptions.push(option);
  663.  
  664.             var optgroup = this.createOptGroup(item);
  665.             if (optgroup) {
  666.                 optgroup.appendChild(option);
  667.             } else {
  668.                 this.select.appendChild(option);
  669.             }
  670.         }
  671.         if (id && this.selectOptions[id]) this.selectOption(id);
  672.         else if (blankOption) blankOption.selected = true;
  673.     },
  674.     
  675.     selectOption: function(id) {
  676.         this.selectOptions[id].disabled = true;
  677.         this.selectOptions[id].selected = true;
  678.         this.selectOptions[id].className = 'selected';
  679.     },
  680.  
  681.     createOptGroup: function(item) {
  682.         var optgroup = false;
  683.  
  684.         if (item.up('ul')) {
  685.             var optgroup = item.up('ul').previous('.overlaymovieheader');
  686.             if (optgroup) {
  687.                 var optgroup = optgroup.innerHTML;
  688.                 var optgroupId = optgroup.replace(' ', '-').camelize();
  689.                 if ($(optgroupId)) {
  690.                     optgroup = $(optgroupId);
  691.                 } else {
  692.                     optgroup = Builder.node('optgroup', { id:optgroupId, label:optgroup });
  693.                     this.select.appendChild(optgroup);
  694.                 }
  695.             }
  696.         }
  697.  
  698.         return optgroup;
  699.     },
  700.  
  701.     setMovieLinks: function(id) {
  702.         this.setMovieSelect(id);
  703.  
  704.         this.select.onchange = this.onSelectChange.bind(this);
  705.     },
  706.     
  707.     onSelectChange: function() {
  708.         var boo = function() {
  709.             var id = this.select.value;
  710.             if (id) this.swapMovieAndSetLinks(id);
  711.             return false;
  712.         }
  713.         setTimeout(boo.bind(this), 10); // fixes IE 6 not changing properly, just needs a delay
  714.     },
  715.     
  716.     swapMovieAndSetLinks: function(id) {
  717.         this.swapMovie(this.items[id]);
  718.         this.setMovieLinks(id);
  719.     }
  720.  
  721. });
  722.  
  723.  
  724.  
  725. AC.MovieGallerySelectOverlayWithIndex = Class.create( AC.MovieGallerySelectOverlay,
  726. {
  727.     initialize: function($super, items, screen, options) {
  728.         $super(items, options);
  729.         this.options.blankOption = true;
  730.         
  731.         this.indexScreen = Builder.node('div', {'class':'overlayindex'});
  732.         this.controllerPanel.parentNode.appendChild(this.indexScreen);
  733.         
  734.         this.indexScreen.innerHTML = $(screen).innerHTML;
  735.         Element.select(this.indexScreen, 'a').each(function(a) {
  736.             var id = this.itemIdsByMovieUrl[a.href];
  737.             a.observe('click', function(evt) {
  738.                 evt.stop();
  739.  
  740.                 this.selectOption(id);
  741.                 this.indexScreen.hide();
  742.                 this.initializeMovie(id);
  743.             }.bindAsEventListener(this));
  744.         }.bind(this));
  745.         this.indexScreen.hide();
  746.         document.observe(this.id+':beforeClose', function() { this.indexScreen.hide(); }.bind(this));
  747.     },
  748.     
  749.     handleAfterPop: function() {
  750.         this.indexScreen.show();
  751.         this.movieLinks();
  752.     },
  753.     
  754.     onSelectChange: function() {
  755.         var boo = function() {
  756.             if (!this.select.value) {
  757.                 this.handleBeforeClose();
  758.                 this.handleAfterPop();
  759.                 this.indexScreen.show();
  760.             }
  761.             else {
  762.                 var id = this.select.value;
  763.                 if (this.indexScreen.visible()) {
  764.                     this.indexScreen.hide();
  765.                     this.initializeMovie(id);
  766.                 }
  767.                 else {
  768.                     this.swapMovieAndSetLinks(id);
  769.                 }
  770.             }
  771.             return false;
  772.         }
  773.         setTimeout(boo.bind(this), 10); // fixes IE 6 not changing properly, just needs a delay
  774.     }
  775. });
  776.  
  777.  
  778.  
  779. AC.HTMLOverlay = Class.create( AC.Overlay,
  780. {
  781.     overlayId: 'ACOverlayHTML',
  782.     overlayShadowId: 'ACOverlayHTMLShadow',
  783.     overlayShadowSrc: 'http://images.apple.com/global/elements/overlay/overlay_movieshadow20070807.png',
  784.     
  785.     initialize: function($super, items, options) {
  786.         $super(items, options);
  787.         document.observe(this.id+':afterPop', this.handleAfterPop.bind(this));
  788.         document.observe(this.id+':beforeClose', this.handleBeforeClose.bind(this));
  789.     },
  790.  
  791.     buildContents: function() {
  792.         this.descriptionPanel = Builder.node('div', {'class':'overlaydescription'});
  793.  
  794.         this.overlayContents = [
  795.             this.closeBtn,
  796.             this.descriptionPanel
  797.         ];
  798.     },
  799.  
  800.     setItemAttributes: function() {
  801.         for (var i=0; i<this.items.length; i++) {
  802.             var item = this.items[i];
  803.  
  804.             item.htmlLink = Element.down(item, 'a.overlayhtmllink');
  805.             item.description = Element.down(item, '.overlaydescription');
  806.  
  807.             this.setEvent(item, i);
  808.         }
  809.     },
  810.  
  811.     handleAfterPop: function(evt) {
  812.         var i = evt.memo.id;
  813.         var item = evt.memo.item;
  814.         // reset content
  815.         this.descriptionPanel.innerHTML = item.description.innerHTML;
  816.     },
  817.  
  818.     handleBeforeClose: function(evt) {
  819.         // reset the containers
  820.         this.descriptionPanel.innerHTML = '';
  821.     },
  822.  
  823.     prepPop: function(evt, item, i) {
  824.         // call the effect
  825.         this.pop(this.defaultWidth, this.setPopPosition().top, this.defaultHeight, this.setPopPosition().left, item, i);
  826.     }
  827. });
  828.