/*
Script: Clientcide.js
  The Clientcide namespace.

License:
  http://www.clientcide.com/wiki/cnet-libraries#license
*/
var Clientcide = {
  version: '2.0.3',
  setAssetLocation: function(baseHref) {
    var clean = function(str){
      return str.replace(/\/\//g, '/');
    };
    if (window.StickyWin && StickyWin.UI) {
      StickyWin.UI.implement({
        options: {
          baseHref: clean(baseHref + '/stickyWinHTML/')
        }
      });
      if (StickyWin.alert) {
        var CGFsimpleErrorPopup = StickyWin.alert.bind(window);
        StickyWin.alert = function(msghdr, msg, base) {
            return CGFsimpleErrorPopup(msghdr, msg, base||clean(baseHref + "/simple.error.popup"));
        };
      }
      if (StickyWin.UI.Pointy) {
        StickyWin.UI.Pointy.implement({
          options: {
            baseHref: clean(baseHref + '/PointyTip/')
          }
        });
      }
    }
    if (window.TagMaker) {
      TagMaker.implement({
          options: {
              baseHref: clean(baseHref + '/tips/')
          }
      });
    }
    if (window.ProductPicker) {
      ProductPicker.implement({
          options:{
              baseHref: clean(baseHref + '/Picker')
          }
      });
    }

    if (window.Autocompleter) {
      Autocompleter.Base.implement({
          options: {
            baseHref: clean(baseHref + '/autocompleter/')
          }
      });
    }

    if (window.Lightbox) {
      Lightbox.implement({
          options: {
              assetBaseUrl: clean(baseHref + '/slimbox/')
          }
      });
    }

    if (window.Waiter) {
      Waiter.implement({
        options: {
          baseHref: clean(baseHref + '/waiter/')
        }
      });
    }
  },
  preLoadCss: function(){
    if (window.StickyWin && StickyWin.ui) StickyWin.ui();
    if (window.StickyWin && StickyWin.pointy) StickyWin.pointy();
    Clientcide.preloaded = true;
    return true;
  },
  preloaded: false
};
(function(){
  if (!window.addEvent) return;
  var preload = function(){
    if (window.dbug) dbug.log('preloading clientcide css');
    if (!Clientcide.preloaded) Clientcide.preLoadCss();
  };
  window.addEvent('domready', preload);
  window.addEvent('load', preload);
})();
setCNETAssetBaseHref = Clientcide.setAssetLocation;

/*
Script: ToElement.js
  Defines the toElement method for a class.

License:
  http://www.clientcide.com/wiki/cnet-libraries#license
*/
Class.ToElement = new Class({
  toElement: function(){
    return this.element;
  }
});
var ToElement = Class.ToElement;

/*
Script: modalizer.js
  Defines Modalizer: functionality to overlay the window contents with a semi-transparent layer that prevents interaction with page content until it is removed

License:
  http://www.clientcide.com/wiki/cnet-libraries#license
*/
var Modalizer = new Class({
  defaultModalStyle: {
    display:'block',
    position:'fixed',
    top:0,
    left:0,
    'z-index':5000,
    'background-color':'#333',
    opacity:0.8
  },
  setModalOptions: function(options){
    this.modalOptions = $merge({
      width:(window.getScrollSize().x),
      height:(window.getScrollSize().y),
      elementsToHide: 'select, embed' + (Browser.Engine.trident ? '': ', object'),
      hideOnClick: true,
      modalStyle: {},
      updateOnResize: true,
      layerId: 'modalOverlay',
      onModalHide: $empty,
      onModalShow: $empty
    }, this.modalOptions, options);
    return this;
  },
  layer: function(){
    if (!this.modalOptions.layerId) this.setModalOptions();
    return $(this.modalOptions.layerId) || new Element('div', {id: this.modalOptions.layerId}).inject(document.body);
  },
  resize: function(){
    if (this.layer()) {
      this.layer().setStyles({
        width:(window.getScrollSize().x),
        height:(window.getScrollSize().y)
      });
    }
  },
  setModalStyle: function (styleObject){
    this.modalOptions.modalStyle = styleObject;
    this.modalStyle = $merge(this.defaultModalStyle, {
      width:this.modalOptions.width,
      height:this.modalOptions.height
    }, styleObject);
    if (this.layer()) this.layer().setStyles(this.modalStyle);
    return(this.modalStyle);
  },
  modalShow: function(options){
    this.setModalOptions(options);
    this.layer().setStyles(this.setModalStyle(this.modalOptions.modalStyle));
    if (Browser.Engine.trident4) this.layer().setStyle('position','absolute');
    this.layer().removeEvents('click').addEvent('click', function(){
      this.modalHide(this.modalOptions.hideOnClick);
    }.bind(this));
    this.bound = this.bound||{};
    if (!this.bound.resize && this.modalOptions.updateOnResize) {
      this.bound.resize = this.resize.bind(this);
      window.addEvent('resize', this.bound.resize);
    }
    if ($type(this.modalOptions.onModalShow)  == "function") this.modalOptions.onModalShow();
    this.togglePopThroughElements(0);
    this.layer().setStyle('display','block');
    return this;
  },
  modalHide: function(override, force){
    if (override === false) return false; //this is internal, you don't need to pass in an argument
    this.togglePopThroughElements(1);
    if ($type(this.modalOptions.onModalHide) == "function") this.modalOptions.onModalHide();
    this.layer().setStyle('display','none');
    if (this.modalOptions.updateOnResize) {
      this.bound = this.bound||{};
      if (!this.bound.resize) this.bound.resize = this.resize.bind(this);
      window.removeEvent('resize', this.bound.resize);
    }
    return this;
  },
  togglePopThroughElements: function(opacity){
    if (Browser.Engine.trident4 || (Browser.Engine.gecko && Browser.Platform.mac)) {
      $$(this.modalOptions.elementsToHide).each(function(sel){
        sel.setStyle('opacity', opacity);
      });
    }
  }
});

/*
Script: Waiter.js

Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.

License:
  http://www.clientcide.com/wiki/cnet-libraries#license
*/
var Waiter = new Class({
  Implements: [Options, Events, Chain],
  options: {
    baseHref: 'http://www.cnet.com/html/rb/assets/global/waiter/',
    containerProps: {
      styles: {
        position: 'absolute',
        'text-align': 'center'
      },
      'class':'waiterContainer'
    },
    containerPosition: {},
    msg: false,
    msgProps: {
      styles: {
        'text-align': 'center',
        fontWeight: 'bold'
      },
      'class':'waiterMsg'
    },
    img: {
      src: 'waiter.gif',
      styles: {
        width: 24,
        height: 24
      },
      'class':'waiterImg'
    },
    layer:{
      styles: {
        width: 0,
        height: 0,
        position: 'absolute',
        zIndex: 999,
        display: 'none',
        opacity: 0.9,
        background: '#fff'
      },
      'class': 'waitingDiv'
    },
    useIframeShim: true,
    fxOptions: {},
    injectWhere: null
//  iframeShimOptions: {},
//  onShow: $empty
//  onHide: $empty
  },
  initialize: function(target, options){
    this.target = $(target)||$(document.body);
    this.setOptions(options);
    this.waiterContainer = new Element('div', this.options.containerProps);
    if (this.options.msg) {
      this.msgContainer = new Element('div', this.options.msgProps);
      this.waiterContainer.adopt(this.msgContainer);
      if (!$(this.options.msg)) this.msg = new Element('p').appendText(this.options.msg);
      else this.msg = $(this.options.msg);
      this.msgContainer.adopt(this.msg);
    }
    if (this.options.img) this.waiterImg = $(this.options.img.id) || new Element('img').inject(this.waiterContainer);
    this.waiterOverlay = $(this.options.layer.id) || new Element('div').adopt(this.waiterContainer);
    this.waiterOverlay.set(this.options.layer);
    this.place(target);
    try {
      if (this.options.useIframeShim) this.shim = new IframeShim(this.waiterOverlay, this.options.iframeShimOptions);
    } catch(e) {
      dbug.log("Waiter attempting to use IframeShim but failed; did you include IframeShim? Error: ", e);
      this.options.useIframeShim = false;
    }
    this.waiterFx = this.waiterFx || new Fx.Elements($$(this.waiterContainer, this.waiterOverlay), this.options.fxOptions);
  },
  place: function(target, where){
    var where = where || this.options.injectWhere || target == document.body ? 'inside' : 'after';
    this.waiterOverlay.inject(target, where);
  },
  toggle: function(element, show) {
    //the element or the default
    element = $(element) || $(this.active) || $(this.target);
    this.place(element);
    if (!$(element)) return this;
    if (this.active && element != this.active) return this.stop(this.start.bind(this, element));
    //if it's not active or show is explicit
    //or show is not explicitly set to false
    //start the effect
    if ((!this.active || show) && show !== false) this.start(element);
    //else if it's active and show isn't explicitly set to true
    //stop the effect
    else if (this.active && !show) this.stop();
    return this;
  },
  reset: function(){
    this.waiterFx.cancel().set({
      0: { opacity:[0]},
      1: { opacity:[0]}
    });
  },
  start: function(element){
    this.reset();
    element = $(element) || $(this.target);
    this.place(element);
    if (this.options.img) {
      this.waiterImg.set($merge(this.options.img, {
        src: this.options.baseHref + this.options.img.src
      }));
    }

    var start = function() {
      var dim = element.getComputedSize();
      this.active = element;
      this.waiterOverlay.setStyles({
        width: this.options.layer.width||dim.totalWidth,
        height: this.options.layer.height||dim.totalHeight,
        display: 'block'
      }).position({
        relativeTo: element,
        position: 'upperLeft'
      });
      this.waiterContainer.position($merge({
        relativeTo: this.waiterOverlay
      }, this.options.containerPosition));
      if (this.options.useIframeShim) this.shim.show();
      this.waiterFx.start({
        0: { opacity:[1] },
        1: { opacity:[this.options.layer.styles.opacity]}
      }).chain(function(){
        if (this.active == element) this.fireEvent('onShow', element);
        this.callChain();
      }.bind(this));
    }.bind(this);

    if (this.active && this.active != element) this.stop(start);
    else start();

    return this;
  },
  stop: function(callback){
    if (!this.active) {
      if ($type(callback) == "function") callback.attempt();
      return this;
    }
    this.waiterFx.cancel();
    this.waiterFx.clearChain();
    //fade the waiter out
    this.waiterFx.start({
      0: { opacity:[0]},
      1: { opacity:[0]}
    }).chain(function(){
      this.active = null;
      this.waiterOverlay.hide();
      if (this.options.useIframeShim) this.shim.hide();
      this.fireEvent('onHide', this.active);
      this.callChain();
      this.clearChain();
      if ($type(callback) == "function") callback.attempt();
    }.bind(this));
    return this;
  }
});

if (typeof Request != "undefined" && Request.HTML) {
  Request.HTML = Class.refactor(Request.HTML, {
    options: {
      useWaiter: false,
      waiterOptions: {},
      waiterTarget: false
    },
    initialize: function(options){
      this._send = this.send;
      this.send = function(options){
        if (this.waiter) this.waiter.start().chain(this._send.bind(this, options));
        else this._send(options);
        return this;
      };
      this.previous(options);
      if (this.options.useWaiter && ($(this.options.update) || $(this.options.waiterTarget))) {
        this.waiter = new Waiter(this.options.waiterTarget || this.options.update, this.options.waiterOptions);
        ['onComplete', 'onException', 'onCancel'].each(function(event){
          this.addEvent(event, this.waiter.stop.bind(this.waiter));
        }, this);
      }
    }
  });
}

Element.Properties.waiter = {

  set: function(options){
    var waiter = this.retrieve('waiter');
    return this.eliminate('waiter').store('waiter:options', options);
  },

  get: function(options){
    if (options || !this.retrieve('waiter')){
      if (options || !this.retrieve('waiter:options')) this.set('waiter', options);
      this.store('waiter', new Waiter(this, this.retrieve('waiter:options')));
    }
    return this.retrieve('waiter');
  }

};

Element.implement({

  wait: function(options){
    this.get('waiter', options).start();
    return this;
  },

  release: function(){
    var opt = Array.link(arguments, {options: Object.type, callback: Function.type});
    this.get('waiter', opt.options).stop(opt.callback);
    return this;
  }

});

/*
Script: TabSwapper.js

Handles the scripting for a common UI layout; the tabbed box.

License:
  http://www.clientcide.com/wiki/cnet-libraries#license
*/
var TabSwapper = new Class({
  Implements: [Options, Events],
  options: {
    selectedClass: 'tabSelected',
    mouseoverClass: 'tabOver',
    deselectedClass: '',
    rearrangeDOM: true,
    initPanel: 0,
    smooth: false,
    smoothSize: false,
    maxSize: null,
    effectOptions: {
      duration: 500
    },
    cookieName: null,
    cookieDays: 999
//  onActive: $empty,
//  onActiveAfterFx: $empty,
//  onBackground: $empty
  },
  tabs: [],
  sections: [],
  clickers: [],
  sectionFx: [],
  initialize: function(options){
    this.setOptions(options);
    var prev = this.setup();
    if (prev) return prev;
    if (this.options.cookieName && this.recall()) this.show(this.recall().toInt());
    else this.show(this.options.initPanel);
  },
  setup: function(){
    var opt = this.options;
    sections = $$(opt.sections);
    tabs = $$(opt.tabs);
    if (tabs[0] && tabs[0].retrieve('tabSwapper')) return tabs[0].retrieve('tabSwapper');
    clickers = $$(opt.clickers);
    tabs.each(function(tab, index){
      this.addTab(tab, sections[index], clickers[index], index);
    }, this);
  },
  addTab: function(tab, section, clicker, index){
    tab = $(tab); clicker = $(clicker); section = $(section);
    //if the tab is already in the interface, just move it
    if (this.tabs.indexOf(tab) >= 0 && tab.retrieve('tabbered')
       && this.tabs.indexOf(tab) != index && this.options.rearrangeDOM) {
      this.moveTab(this.tabs.indexOf(tab), index);
      return this;
    }
    //if the index isn't specified, put the tab at the end
    if (!$defined(index)) index = this.tabs.length;
    //if this isn't the first item, and there's a tab
    //already in the interface at the index 1 less than this
    //insert this after that one
    if (index > 0 && this.tabs[index-1] && this.options.rearrangeDOM) {
      tab.inject(this.tabs[index-1], 'after');
      section.inject(this.tabs[index-1].retrieve('section'), 'after');
    }
    this.tabs.splice(index, 0, tab);
    clicker = clicker || tab;

    tab.addEvents({
      mouseout: function(){
        tab.removeClass(this.options.mouseoverClass);
      }.bind(this),
      mouseover: function(){
        tab.addClass(this.options.mouseoverClass);
      }.bind(this)
    });

    clicker.addEvent('click', function(e){
      e.preventDefault();
      this.show(index);
    }.bind(this));

    tab.store('tabbered', true);
    tab.store('section', section);
    tab.store('clicker', clicker);
    this.hideSection(index);
    return this;
  },
  removeTab: function(index){
    var now = this.tabs[this.now];
    if (this.now == index){
      if (index > 0) this.show(index - 1);
      else if (index < this.tabs.length) this.show(index + 1);
    }
    this.now = this.tabs.indexOf(now);
    return this;
  },
  moveTab: function(from, to){
    var tab = this.tabs[from];
    var clicker = tab.retrieve('clicker');
    var section = tab.retrieve('section');

    var toTab = this.tabs[to];
    var toClicker = toTab.retrieve('clicker');
    var toSection = toTab.retrieve('section');

    this.tabs.erase(tab).splice(to, 0, tab);

    tab.inject(toTab, 'before');
    clicker.inject(toClicker, 'before');
    section.inject(toSection, 'before');
    return this;
  },
  show: function(i){
    if (!$chk(this.now)) {
      this.tabs.each(function(tab, idx){
        if (i != idx)
          this.hideSection(idx)
      }, this);
    }
    this.showSection(i).save(i);
    return this;
  },
  save: function(index){
    if (this.options.cookieName)
      Cookie.write(this.options.cookieName, index, {duration:this.options.cookieDays});
    return this;
  },
  recall: function(){
    return (this.options.cookieName)?$pick(Cookie.read(this.options.cookieName), false): false;
  },
  hideSection: function(idx) {
    var tab = this.tabs[idx];
    if (!tab) return this;
    var sect = tab.retrieve('section');
    if (!sect) return this;
    if (sect.getStyle('display') != 'none') {
      this.lastHeight = sect.getSize().y;
      sect.setStyle('display', 'none');
      tab.swapClass(this.options.selectedClass, this.options.deselectedClass);
      this.fireEvent('onBackground', [idx, sect, tab]);
    }
    return this;
  },
  showSection: function(idx) {
    var tab = this.tabs[idx];
    if (!tab) return this;
    var sect = tab.retrieve('section');
    if (!sect) return this;
    var smoothOk = this.options.smooth && (!Browser.Engine.trident4
                    || (Browser.Engine.trident4 && !Browser.Engine.trident4));
    if (this.now != idx) {
      if (!tab.retrieve('tabFx'))
        tab.store('tabFx', new Fx.Morph(sect, this.options.effectOptions));
      var start = {
        display:'block',
        overflow: 'hidden'
      };
      if (smoothOk) start.opacity = 0;
      var effect = false;
      if (smoothOk) {
        effect = {opacity: 1};
      } else if (sect.getStyle('opacity').toInt() < 1) {
        sect.setStyle('opacity', 1);
        if (!this.options.smoothSize)
          this.fireEvent('onActiveAfterFx', [idx, sect, tab]);
      }
      if (this.options.smoothSize) {
        var size = sect.getDimensions().height;
        if ($chk(this.options.maxSize) && this.options.maxSize < size)
          size = this.options.maxSize;
        if (!effect) effect = {};
        effect.height = size;
      }
      if ($chk(this.now)) this.hideSection(this.now);
      if (this.options.smoothSize && this.lastHeight) start.height = this.lastHeight;
      sect.setStyles(start);
      if (effect) {
        tab.retrieve('tabFx').start(effect).chain(function(){
          this.fireEvent('onActiveAfterFx', [idx, sect, tab]);
          sect.setStyle("height", "auto");
        }.bind(this));
      }
      this.now = idx;
      this.fireEvent('onActive', [idx, sect, tab]);
    }
    tab.swapClass(this.options.deselectedClass, this.options.selectedClass);
    return this;
  }
});


/*
Script: InputFocus.js
  Adds a focused css class to inputs when they have focus.

License:
  http://www.clientcide.com/wiki/cnet-libraries#license
*/
var InputFocus = new Class({
  Implements: [Options, Class.Occlude, Class.ToElement],
  Binds: ['focus', 'blur'],
  options: {
    focusedClass: 'focused',
    hideOutline: false
  },
  initialize: function(input, options) {
    this.element = $(input);
    if (this.occlude('focuser')) return this.occluded;
    this.setOptions(options);
    this.element.addEvents({
      focus: this.focus,
      blur: this.blur
    });
  },
  focus: function(){
    if (this.options.hideOutline) {
      (function(){
        if (Browser.Engine.trident) $(this).set('hideFocus', true);
        else $(this).setStyle('outline', '0');
      }).delay(500, this);
    }
    $(this).addClass(this.options.focusedClass);
  },
  blur: function(){
    $(this).removeClass(this.options.focusedClass);
  }
});

/*
Script: Lightbox.js
  A lightbox clone for MooTools.

* Christophe Beyls (http://www.digitalia.be); MIT-style license.
* Inspired by the original Lightbox v2 by Lokesh Dhakar: http://www.huddletogether.com/projects/lightbox2/.
* Refactored by Aaron Newton

*/
var Lightbox = new Class({
  Implements: [Options, Events, Modalizer],
  Binds: ['click', 'keyboardListener', 'addHtmlElements'],
  options: {
//    anchors: null,
    resizeDuration: 400,
//    resizeTransition: false,  // default transition
    initialWidth: 250,
    initialHeight: 250,
    zIndex: 5000,
    animateCaption: true,
    showCounter: true,
    autoScanLinks: true,
    relString: 'lightbox',
    useDefaultCss: true,
    assetBaseUrl: 'http://www.cnet.com/html/rb/assets/global/slimbox/',
    overlayStyles: {
      opacity: 0.8
    }
//    onImageShow: $empty,
//    onDisplay: $empty,
//    onHide: $empty
  },

  initialize: function(){
    var args = Array.link(arguments, {options: Object.type, links: Array.type});
    this.setOptions(args.options);
    var anchors = args.links || this.options.anchors;
    if (this.options.autoScanLinks && !anchors) anchors = $$('a[rel^='+this.options.relString+']');
    if (!$$(anchors).length) return; //no links!
    this.addAnchors(anchors);
    if (this.options.useDefaultCss) this.addCss();
    window.addEvent('domready', this.addHtmlElements.bind(this));
  },

  anchors: [],

  addAnchors: function(anchors){
    $$(anchors).each(function(el){
      if (!el.retrieve('lightbox')) {
        el.store('lightbox', this);
        this.attach(el);
      }
    }.bind(this));
  },

  attach: function(el) {
    el.addEvent('click', this.click.pass(el, this));
    this.anchors.include(el);
  },

  addHtmlElements: function(){
    this.container = new Element('div', {
      'class':'lbContainer'
    }).inject(document.body);
    this.setModalOptions({
      onModalHide: this.close.bind(this)
    });
    this.overlay = this.layer().addClass('lbOverlay');
    this.setModalStyle($merge(this.options.overlayStyles, {
        opacity: 0
      })
    );
    this.popup = new Element('div', {
      'class':'lbPopup'
    }).inject(this.container);
    this.overlay.inject(this.popup);
    this.center = new Element('div', {
      styles: {
        width: this.options.initialWidth,
        height: this.options.initialHeight,
        marginLeft: (-(this.options.initialWidth/2)),
        display: 'none',
        zIndex:this.options.zIndex+1
      }
    }).inject(this.popup).addClass('lbCenter');
    this.image = new Element('div', {
      'class': 'lbImage'
    }).inject(this.center);

    this.prevLink = new Element('a', {
      'class': 'lbPrevLink',
      href: 'javascript:void(0);',
      styles: {'display': 'none'}
    }).inject(this.image);
    this.nextLink = this.prevLink.clone().removeClass('lbPrevLink').addClass('lbNextLink').inject(this.image);
    this.prevLink.addEvent('click', this.previous.bind(this));
    this.nextLink.addEvent('click', this.next.bind(this));

    this.bottomContainer = new Element('div', {
      'class': 'lbBottomContainer',
      styles: {
        display: 'none',
        zIndex:this.options.zIndex+1
    }}).inject(this.popup);
    this.bottom = new Element('div', {'class': 'lbBottom'}).inject(this.bottomContainer);
    new Element('a', {
      'class': 'lbCloseLink',
      href: 'javascript:void(0);'
    }).inject(this.bottom).addEvent('click', this.close.bind(this));
    this.overlay.addEvent('click', this.close.bind(this));
    this.caption = new Element('div', {'class': 'lbCaption'}).inject(this.bottom);
    this.number = new Element('div', {'class': 'lbNumber'}).inject(this.bottom);
    new Element('div', {'styles': {'clear': 'both'}}).inject(this.bottom);
    var nextEffect = this.nextEffect.bind(this);
    this.fx = {
      overlay: new Fx.Tween(this.overlay, {property: 'opacity', duration: 500}).set(0),
      resize: new Fx.Morph(this.center, $extend({
        duration: this.options.resizeDuration,
        onComplete: nextEffect},
        this.options.resizeTransition ? {transition: this.options.resizeTransition} : {})),
      image: new Fx.Tween(this.image, {property: 'opacity', duration: 500, onComplete: nextEffect}),
      bottom: new Fx.Tween(this.bottom, {property: 'margin-top', duration: 400, onComplete: nextEffect})
    };

    this.preloadPrev = new Element('img');
    this.preloadNext = new Element('img');
  },

  addCss: function(){
    window.addEvent('domready', function(){
      if ($('LightboxCss')) return;
      new Element('link', {
        rel: 'stylesheet',
        media: 'screen',
        type: 'text/css',
        href: this.options.assetBaseUrl + 'slimbox.css',
        id: 'LightboxCss'
      }).inject(document.head);
    }.bind(this));
  },

  click: function(el){
    link = $(el);
    var rel = link.get('rel')||this.options.relString;
    if (rel == this.options.relString) return this.show(link.get('href'), link.get('title'));

    var j, imageNum, images = [];
    this.anchors.each(function(el){
      if (el.get('rel') == link.get('rel')){
        for (j = 0; j < images.length; j++) if (images[j][0] == el.get('href')) break;
        if (j == images.length){
          images.push([el.get('href'), el.get('title')]);
          if (el.get('href') == link.get('href')) imageNum = j;
        }
      }
    }, this);
    return this.open(images, imageNum);
  },

  show: function(url, title){
    return this.open([[url, title]], 0);
  },

  open: function(images, imageNum){
    this.fireEvent('onDisplay');
    this.images = images;
    this.setup(true);
    this.top = (window.getScroll().y + (window.getSize().y / 15)).toInt();
    this.center.setStyles({
      top: this.top,
      display: ''
    });
    this.modalShow();
    this.fx.overlay.start(0, this.options.overlayStyles.opacity);
    return this.changeImage(imageNum);
  },

  setup: function(open){
    var elements = $$('iframe');
    elements.extend($$(Browser.Engine.trident ? 'select' : 'embed, object'));
    elements.reverse().each(function(el){
      if (open) el.store('lbBackupStyle', el.getStyle('visibility') || 'visible');
      var vis = (open ? 'hidden' : el.retrieve('lbBackupStyle') || 'visible');
      el.setStyle('visibility', vis);
    });
    var fn = open ? 'addEvent' : 'removeEvent';
    document[fn]('keydown', this.keyboardListener);
    this.step = 0;
  },

  keyboardListener: function(event){
    switch (event.code){
      case 27: case 88: case 67: this.close(); break;
      case 37: case 80: this.previous(); break;
      case 39: case 78: this.next();
    }
  },

  previous: function(){
    return this.changeImage(this.activeImage-1);
  },

  next: function(){
    return this.changeImage(this.activeImage+1);
  },

  changeImage: function(imageNum){
    this.fireEvent('onImageShow', [imageNum, this.images[imageNum]]);
    if (this.step || (imageNum < 0) || (imageNum >= this.images.length)) return false;
    this.step = 1;
    this.activeImage = imageNum;

    this.center.setStyle('backgroundColor', '');
    this.bottomContainer.setStyle('display', 'none');
    this.prevLink.setStyle('display', 'none');
    this.nextLink.setStyle('display', 'none');
    this.fx.image.set(0);
    this.center.addClass('lbLoading');
    this.preload = new Element('img', {
      events: {
        load: function(){
          this.nextEffect.delay(100, this)
        }.bind(this)
      }
    });
    this.preload.set('src', this.images[imageNum][0]);
    return false;
  },

  nextEffect: function(){
    switch (this.step++){
    case 1:
      this.image.setStyle('backgroundImage', 'url('+this.images[this.activeImage][0]+')');
      this.image.setStyle('width', this.preload.width);
      this.bottom.setStyle('width',this.preload.width);
      this.image.setStyle('height', this.preload.height);
      this.prevLink.setStyle('height', this.preload.height);
      this.nextLink.setStyle('height', this.preload.height);

      this.caption.set('html',this.images[this.activeImage][1] || '');
      this.number.set('html',(!this.options.showCounter || (this.images.length == 1)) ? '' : 'Image '+(this.activeImage+1)+' of '+this.images.length);

      if (this.activeImage) $(this.preloadPrev).set('src', this.images[this.activeImage-1][0]);
      if (this.activeImage != (this.images.length - 1))
        $(this.preloadNext).set('src',  this.images[this.activeImage+1][0]);
      if (this.center.clientHeight != this.image.offsetHeight){
        this.fx.resize.start({height: this.image.offsetHeight});
        break;
      }
      this.step++;
    case 2:
      if (this.center.clientWidth != this.image.offsetWidth){
        this.fx.resize.start({width: this.image.offsetWidth, marginLeft: -this.image.offsetWidth/2});
        break;
      }
      this.step++;
    case 3:
      this.bottomContainer.setStyles({
        top: (this.top + this.center.getSize().y),
        height: 0,
        marginLeft: this.center.getStyle('margin-left'),
        display: ''
      });
      this.fx.image.start(1);
      break;
    case 4:
      this.center.style.backgroundColor = '#000';
      if (this.options.animateCaption){
        this.fx.bottom.set(-this.bottom.offsetHeight);
        this.bottomContainer.setStyle('height', '');
        this.fx.bottom.start(0);
        break;
      }
      this.bottomContainer.style.height = '';
    case 5:
      if (this.activeImage) this.prevLink.setStyle('display', '');
      if (this.activeImage != (this.images.length - 1)) this.nextLink.setStyle('display', '');
      this.step = 0;
    }
  },

  close: function(){
    this.fireEvent('onHide');
    if (this.step < 0) return;
    this.step = -1;
    if (this.preload) this.preload.destroy();
    for (var f in this.fx) this.fx[f].cancel();
    this.center.setStyle('display', 'none');
    this.bottomContainer.setStyle('display', 'none');
    this.fx.overlay.chain(this.setup.pass(false, this)).start(0);
    return;
  }
});
window.addEvent('domready', function(){if ($(document.body).get('html').match(/rel=?.lightbox/i)) new Lightbox()});