if (!PS) var PS = {};

/*
  Main Gallery - container for everything
  The outside world works with this
  It inits and deals with everything else
*/
PS.Gallery = Class.create(
{

  initialize: function(options) {
    this.options = {
        img_sig     : '.gallery-image'
      , add_borders : false
    };
    Object.extend(this.options, options || { });

    // create the other things we need
    this.disp = new PS.GalleryDisplay(this, this.options);
    this.loader = new PS.GalleryLoader(this, this.options);
    this.sellable_images = new Hash();
    
    this.load_from_class(this.options.img_sig);
    
    // create the url watcher to deal with the hashed urls
    this.showing_hash = 'dummy';
    this.watcher_locked = false;
    this.create_watcher();
  },
  
  create_watcher : function() {
    // every 100ms check the url to see if it has been changed
    this.watcher = new PeriodicalExecuter(this.check_url.bind(this), .1);
  },
  
  check_url : function() {
    // don't bother when the system is editing the hash
    if (this.watcher_locked)
      return;
      
    // nothing to do if the hash hasn't changed
    if (location.hash == this.showing_hash)
      return;

    // track the new hash
    this.showing_hash = location.hash;

		// tidy the hash
		var hash = location.hash.gsub(/#(ps-)?/, '');

    // deal with the thumbnail display case
    if (hash.length == 0)
      return this.disp.do_hide();

    // now find the image with the id on the url
    var image = this.loader.find_image_with_id(hash);

    // don't know about this image
    if (!image)
      return this.disp.do_hide();

    // clicked on a thumbnail from our gallery
    this.loader.display_image_with_id(image.id);
  },
  
  load_from_class: function(sel) {
    var images = new Array();

    // find all the images of the given class
    var img_links = $$(sel);
    
    // create new image objects for each
    img_links.each(function(link) {
      // take over the event handling
      Event.observe(link, 'click', this.thumb_clicked.bind(this));

      // create the new image object
      var gallery_image = new PS.GalleryImage(link.href, link.getMetaData().filename, link.id, link.title, this.options.add_borders);

      // and add to our array
      images.push(gallery_image);
    }.bind(this));
    
    // send them to our loader to look after
    this.loader.add_images(images);
  },
  
  thumb_clicked : function(e) {
    // stop the click from happening
    Event.stop(e);

    // update the hash
    this.set_hash($(e.target).up('a').id);

    // clicked on a thumbnail from our gallery
    this.loader.display_image_with_id($(e.target).up('a').id);
  },
  
  show_image : function(image) {
    // track it
    this.displaying = image;

    // and ask the display to show the image
    this.disp.display_content(image);

    // are there sales options to go along with this?
    if (this.can_purchase_image(image)) {
      // show the button
      this.disp.show_purchase_button();
    }
  },
  
  hide : function() {
    // update the url
    this.set_hash('gal');
    
    // actually hide the gallery
    this.disp.do_hide();
  },
  
  can_purchase_image : function(image) {
    // are there sales options to go along with this?
    return image.id && ((this.options.sales && this.options.sales.get(image.id)) || (this.options.sales_album && this.options.sales_album.size() > 0));
  },

  get_purchase_option : function(image) {
    // do we have a purchase option element built yet?
    if (!this.sellable_images.get(image.id)) {
      // nope - build it
			var sales_options = new Array();
      var sellable_image = this.options.sales.get(image.id);
			if (sellable_image != undefined)
				sales_options = sellable_image.pos;
      var po = new PS.GalleryPurchaseOptions(image.id, this.options.pp_account, this.options.pp_currency, this.options.pp_message, window.location.href, this.options.sales_album, sales_options, image.filename, image.caption);
      this.sellable_images.set(image.id, po);
    }
    
    return this.sellable_images.get(image.id);
  },
    
  show_prev : function() {
    // display the previous image
    var image = this.loader.display_prev();

    // update the hash
    this.set_hash(image.id);
  },

  show_next : function() {
    // display the next image
    var image = this.loader.display_next();
    
    // update the hash
    this.set_hash(image.id);
  },

  set_hash : function(hash) {
    // primative locking system
    this.watcher_locked = true;

    // switch the hash over
		if (hash != null) {
	    location.hash = '#ps-' + hash;
	    this.showing_hash = location.hash;
		}
    
    // unlock
    this.watcher_locked = false;
  },
  
  show_pricing : function() {
    // check that there are purchase options for the current image
    if (!this.displaying || !this.can_purchase_image(this.displaying))
      return false;

    // display the purchase options
    this.disp.show_purchase_options(this.get_purchase_option(this.displaying));
  }

});
if (!PS) var PS = {};

/*
  Display - handles the actually display of the gallery
  can display elements, resize them to fit, and adjust itself as the window changes
*/
PS.GalleryDisplay = Class.create(
{

  initialize : function(gallery, options) {
    this.options = { 
        i_margin_top    : 10
      , i_margin_right  : 10
      , i_margin_bottom : 100
      , i_margin_left   : 10
    };
    Object.extend(this.options, options || { });

		this.visible = false;
    this.gallery = gallery;

    this.utils = new PS.GalleryUtils();
    
    // construct the elements we need
    this.create_elements();
  },
  
  create_elements : function() {
    this.vp = new Element('div');
    this.vp.setStyle({zIndex:'1001', width:'10px', height:'10px', position:this.utils.is_ie6() ? 'absolute' : 'fixed', overflow:'hidden', top:'0', left:'0'});

    this.content = new Element('div');
    this.content.setStyle({width:'10px', height:'10px', position:'absolute', overflow:'hidden'});
    this.vp.insert(this.content);

    Event.observe(this.vp, 'click', this.hide_gallery.bind(this));

    // toolbar
    this.toolbar = new Element('div');
    this.toolbar.setStyle({position:'absolute', bottom:'0', left:'0', height:'80px', width:'10px'});
    this.vp.insert(this.toolbar);

    // back button to get out of the slideshow
    this.back = PS.GalleryElements.Button({offsetx:144, offsety:72, width:98, height:31});
    this.back.setStyle({position:'absolute', top:'20px', left:'100px'});
    this.toolbar.insert(this.back);

    // ability to purchase image
    this.purchase = PS.GalleryElements.Button({offsetx:242, width:98, height:31});
    this.purchase.setStyle({position:'absolute', top:'20px', right:'220px'});
    this.toolbar.insert(this.purchase);
    Event.observe(this.purchase, 'click', this.purchase_clicked.bind(this));

    // ability to share images
    this.share = PS.GalleryElements.Button({offsetx:144, width:98, height:31});
    this.share.setStyle({position:'absolute', top:'20px', right:'100px'});
    this.toolbar.insert(this.share);
    Event.observe(this.share, 'click', this.share_btn_click.bind(this));
    Event.observe(this.share, 'mouseover', this.share_over.bind(this));
    Event.observe(this.share, 'mouseout', this.share_out.bind(this));

    this.share_box = new Element('div');
    this.share_box.setStyle({width:322+'px', height:346+'px', background:'url(/img/boxes/share.png) no-repeat', position:'absolute', bottom:'65px', right:'68px'});
    this.share_box.hide();
    
    this.share_box.insert('<a target="_blank" rel="share_tw" href="#" style="display:block;width:280px;height:45px;position:absolute;top:88px;left:25px;"></a>');
    this.share_box.insert('<a target="_blank" rel="share_fb" href="#" style="display:block;width:280px;height:45px;position:absolute;top:138px;left:25px;"></a>');
    this.share_box.insert('<a target="_blank" rel="share_su" href="#" style="display:block;width:280px;height:45px;position:absolute;top:188px;left:25px;"></a>');
    this.share_box.insert('<a target="_blank" rel="share_dl" href="#" style="display:block;width:280px;height:45px;position:absolute;top:238px;left:25px;"></a>');
    
    this.share_box.select('a').each(function(elm){
      Event.observe(elm, 'click', this.share_update_link.bind(this));
    }.bind(this));
    
    Event.observe(this.share_box, 'mouseover', this.share_over.bind(this));
    Event.observe(this.share_box, 'mouseout', this.share_out.bind(this));
    this.toolbar.insert(this.share_box);

    // next / prev controls
    this.controls = new Element('div');
    this.controls.setStyle({marginLeft:'auto', marginRight:'auto', width:'200px', height:'80px'});
    this.toolbar.insert(this.controls);

    this.control_prev = PS.GalleryElements.Button({offsetx:72});
    this.control_prev.setStyle({'float':'left'});
    this.control_next = PS.GalleryElements.Button({offsetx:0});
    this.control_next.setStyle({'float':'right'});
    this.controls.insert(this.control_prev);
    this.controls.insert(this.control_next);

    Event.observe(this.control_prev, 'click', this.show_prev.bind(this));
    Event.observe(this.control_next, 'click', this.show_next.bind(this));

    // add the lot to the page (but hid it to start with)
    this.vp.hide();
    $(document.body).insert(this.vp);
    
    // the blanker can more of less look after itself
    this.blanker = new PS.GalleryElements.Blanker(this.options);
  },
  
  share_update_link : function(e) {
    var new_link = '';
		var image_id = this.gallery.displaying.id.gsub(/img-/, '');

    switch(e.target.rel) {
      case 'share_tw':
        new_link = '/share/twitter_image/' + image_id;
        break;
        
      case 'share_fb':
        new_link = '/share/fb_image/' + image_id;
        break;
        
      case 'share_su':
        new_link = '/share/su_image/' + image_id;
        break;
        
      case 'share_dl':
        new_link = '/share/dl_image/' + image_id;
        break;
    }
    
    // now update the link
    e.target.href = new_link;
    e.stopPropagation();
  },
  
  share_btn_click : function(e) {
    Event.stop(e);
  },

  share_over : function(e) {
    if (this.share_timer)
      clearTimeout(this.share_timer);
    this.share_timer = null;

    this.share_box.show();
  },

  share_out : function(e) {
    this.share_timer = setTimeout(this.kill_share.bind(this), 200);
  },
  
  kill_share : function() {
    this.share_box.hide();
  },
  
  display_content : function(content) {
    // remove the purchase button unless they ask for it
    this.purchase.hide();
    
    // already displaying something?
    if (!this.displaying)
      // init the display
      this.init_display();
    else if (this.current_content) {
      // need to remove the old content
      this.current_content.remove();
    }

    // now we can display the content
    this.blanker.show();
    this.vp.show();
    this.displaying = content;
    this.init_display();
    
    this.redraw();
  },
  
  show_purchase_button : function() {
    this.purchase.show();
  },

	is_visible : function() {
		return this.visible;
	},
  
  hide_gallery : function() {
    this.gallery.hide();
  },

  do_hide : function() {
    if (!this.visible)
      return;
    
    this.vp.hide();
    this.blanker.hide();
    this.remove_events();
    this.visible = false;
  },
  
  init_display : function() {
    // get the display ready
    this.attach_events();
    this.visible = true;
  },
  
  redraw : function() {
    // first figure out the position of the viewable area
    var view_area = this.utils.view_area();
    
    // reset the viewpane to match the window
    this.vp.setStyle({width:view_area.width+'px', height:view_area.height+'px'});
    // only need to rejig when absolute positioning used (ie6)
    if (this.utils.is_ie6())
      this.vp.setStyle({top:view_area.top+'px', left:view_area.left+'px'});
      
    // shuffle the controls
    this.toolbar.setStyle({width:view_area.width+'px'});
    
    // if there's not content being displayed there's not much else to do here
    if (!this.displaying)
      return;

    // now figure out the space for the content
    var content_area = {
        width  : view_area.width - (this.options.i_margin_left + this.options.i_margin_right)
      , height : view_area.height - (this.options.i_margin_top + this.options.i_margin_bottom)
    };
    this.content.setStyle({width:content_area.width+'px', height:content_area.height+'px', top:this.options.i_margin_top+'px', left:this.options.i_margin_left+'px'});

    // and add it to the page
    this.current_content = this.displaying.content();
    this.content.insert(this.current_content);

    // resize the content to fit into the space allocated
    this.displaying.redraw(content_area.width, content_area.height);
  },
    
  attach_events : function() {
    if (!this.bound) {
      this.bound = {
          redraw           : this.redraw.bind(this)
        , capture_keypress : this.capture_keypress.bind(this)
      };
    }
    
    Event.observe(window, 'resize', this.bound.redraw);
    Event.observe(window, 'scroll', this.bound.redraw);
    Event.observe(window, 'keyup', this.bound.capture_keypress);
  },

  remove_events : function() {
    // were the events ever attached
    if (!this.bound)
      return;
    
    Event.stopObserving(window, 'resize', this.bound.redraw);
    Event.stopObserving(window, 'scroll', this.bound.redraw);
    Event.stopObserving(window, 'keyup', this.bound.capture_keypress);
  },
  
  capture_keypress : function(e) {
    // was it the esc key?
    if (e.which && e.which == 27)
      this.hide_gallery();

    // prev?
    if (e.which && e.which == 37)
      this.show_prev(e);

    // next?
    if (e.which && e.which == 39)
      this.show_next(e);
  },
  
  show_prev : function(e) {
    Event.stop(e);
    
    this.gallery.show_prev();
  },
  
  show_next : function(e) {
    Event.stop(e);
    
    this.gallery.show_next();
  },
  
  purchase_clicked : function(e) {
    Event.stop(e);

    // let the gallery know that someone has clicked the purchase button
    this.gallery.show_pricing();
  },
  
  show_purchase_options : function(pos) {
    this.transition_to_content(pos);
  },
  
  transition_to_content : function(new_content) {
    // not showing content? - just show the new content
    if (!this.displaying)
      return this.display_content(new_content);
    
    // first we need to flip the old one away
    
    // figure out the current dimensions
    var ani_left = parseInt(this.content.down('img').getStyle('left'));
    var ani_width = this.content.down('img').getWidth();

    // animate the flipping out of the image
    this.flip_animator = new PS.Animator({duration:300, from:ani_width, to:0, orig_left:ani_left, orig_width:ani_width, new_content:new_content});
    this.flip_animator.animationStep = this.image_out.bind(this);
    this.flip_animator.whenfinished = this.image_animation_finished.bind(this);
    this.flip_animator.startAnimation();
  },
  
  image_out : function(t, animator) {
    this.content.down('img').setStyle({width:t+'px', left:animator.options.orig_left+Math.round((animator.options.orig_width - t)/2)+'px'});
  },
  
  image_animation_finished : function(animator) {
    // replace the image with our options
    this.content.update();
    this.displaying = animator.options.new_content;
    this.current_content = this.displaying.content();

    this.content.insert(this.current_content);
    this.current_content.setStyle({width:0});

    // animate the flipping in of the options
    this.flip_animator = new PS.Animator({duration:300, from:0, to:this.content.getWidth(), new_content:this.displaying, initial_width:this.content.getWidth(), initial_height:this.content.getHeight()});
    this.flip_animator.animationStep = this.options_in.bind(this);
    this.flip_animator.whenfinished = function(){ this.redraw(); }.bind(this);
    this.flip_animator.startAnimation();
  },
  
  options_in : function(t, animator) {
    this.current_content.setStyle({width:t+'px', height:animator.options.initial_height+'px' , left:Math.round((animator.options.initial_width-t)/2)+'px'});
  }
  
});
if (!PS) var PS = {};
if (!PS.GalleryElements) PS.GalleryElements = {};

/*
  Elements - interface elements
*/

PS.GalleryElements.Button = function(opt) {
  var options = {
      width   : 72
    , height  : 72
    , offsetx : 0
    , offsety : 0
    , id      : null
    , src     : '/img/buttons/gallery_icon_set.png'
  };
  Object.extend(options, opt || { });

  // create the button
  var button = new Element('div');
  button.setStyle({height:options.height+'px', width:options.width+'px', cursor:'pointer'});
  button.setStyle('background:url('+options.src+') no-repeat -'+options.offsetx+'px -'+options.offsety+'px');
  
  // observe etc
  Event.observe(button, 'mouseover', function(){ button.setStyle('background:url('+options.src+') no-repeat -'+options.offsetx+'px -'+(options.offsety+options.height)+'px'); } );
  Event.observe(button, 'mouseout',  function(){ button.setStyle('background:url('+options.src+') no-repeat -'+options.offsetx+'px -'+(options.offsety)+'px'); } );
  
  return button;
};


PS.GalleryElements.Blanker = Class.create(
{

  initialize : function(options) {
    this.options = {
        opacity : .9
    };
    Object.extend(this.options, options || { });
    
    this.utils = new PS.GalleryUtils();
    this.visible = false;

    // create the visual element
    this.create_elements();
  },
  
  create_elements : function() {
    this.blanker = new Element('div');
    this.blanker.setStyle({backgroundColor:'#000', width:'1px', height:'1px', position:this.utils.is_ie6() ? 'absolute' : 'fixed', top:'0px', left:'0px'});
    this.blanker.setOpacity(this.options.opacity);
    this.blanker.hide();
    
    // add it to the page
    $(document.body).insert(this.blanker);
  },
  
  show : function() {
    if (this.visible)
      return;
    
    this.visible = true;
    
    this.attach_events();
    this.redraw();
    this.blanker.show();
  },

  hide : function() {
    this.visible = false;
    this.remove_events();
    this.blanker.hide();
  },
  
  redraw : function() {
    // first figure out the position of the viewable area
    var view_area = this.utils.view_area();
    
    // reset the viewpane to match the window
    this.blanker.setStyle({zIndex:'1000', width:view_area.width+'px', height:view_area.height+'px'});

    // only need to rejig when absolute positioning used (ie6)
    if (this.utils.is_ie6())
      this.blanker.setStyle({top:view_area.top+'px', left:view_area.left+'px'});
  },
  
  attach_events : function() {
    if (!this.bound_redraw)
      this.bound_redraw = this.redraw.bind(this);
    
    Event.observe(window, 'resize', this.bound_redraw);
    Event.observe(window, 'scroll', this.bound_redraw);
  },

  remove_events : function() {
    if (!this.bound_redraw)
      return;
    
    Event.stopObserving(document, 'resize', this.bound_redraw);
    Event.stopObserving(document, 'scroll', this.bound_redraw);
  }

});




PS.GalleryElements.Controls = Class.create(
{
  
  initialize : function(gallery, options) {
    this.options = {
        opacity : .8
    };
    Object.extend(this.options, options || { });
    
    this.gallery = gallery;
    
    this.utils = new PS.GalleryUtils();
    this.visible = false;

    // create the visual element
    this.create_elements();
  },
  
  create_elements : function() {
    // build the elements
    this.controls = new Element('div');
    this.ctl_prev = new Element('div');
    this.ctl_next = new Element('div');
    this.controls.insert(this.ctl_prev);
    this.controls.insert(this.ctl_next);
    this.controls.hide();
    
    // and add everything to the document
    $(document.body).insert(this.controls);
    
    // attach the callbacks to the controls
    Event.observe(this.ctl_prev, 'click', this.show_prev.bind(this));
    Event.observe(this.ctl_next, 'click', this.show_next.bind(this));
  },
  
  show_prev : function() {
    this.galery.show_prev();
  },
  
  show_next : function() {
    this.galery.show_next();
  }
  
});
if (!PS) var PS = {};

/*
  Image - a particular image in the gallery
  knows about the state of an image and whether it's ready to be displayed
  can deal with resizing itself to fit into a particular sized box
*/
PS.GalleryImage = Class.create(
{

  initialize : function(src, filename, id, caption, add_borders) {
    this.src = src;
    this.filename = filename;
    this.id = id;
    this.caption = caption ? caption : '';
    this.add_borders = (add_borders == 'undefined') ? false : add_borders;
    this.to_display = null;
    
    // deal with IE stack overflow bug 
    if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent))
      this.src = this.src + '?r=' + Math.random().toString();
    
    // load state
    this.loading = false;
    this.load_finished = false;
    this.borked = false;
  },

  preload : function(callback) {
    // don't bother if we're already doing it / have done it
    if (this.loading && this.load_finished)
      return;

    // update state
    this.loading = true;

    // do this after the image is loaded / found to be broken
    this.callback = callback;

		// bind the local functions (otherwise we get a memory leak)
		this.bound_loaded = this.image_loaded.bind(this);
		this.bound_errored = this.image_loaded.bind(this);
    
    // create the new image
    this.image = new Image();
		Event.observe(this.image, 'load', this.bound_loaded);
		Event.observe(this.image, 'error', this.bound_errored);
    this.image.src = this.src;
  },
  
	stop_observing : function() {
		Event.stopObserving(this.image, 'load', this.bound_loaded);
		Event.stopObserving(this.image, 'error', this.bound_errored);
	},

  image_loaded : function(e) {
    // update the state
    this.load_finished = true;
    this.borked = false;
    this.loading = false;

		this.stop_observing();

    // store the dimensions
    this.original_width = this.image.width;
    this.original_height = this.image.height;
    this.ratio = this.original_width / this.original_height;
    $(this.image);
    this.to_display = new Element('div');
    this.to_display.insert(this.image);
    if (this.caption.length > 0) {
      this.cap_elm = new Element('p').update(this.caption.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"));
      this.to_display.insert(this.cap_elm);
    }

		// stop people from right-clicking to save the images
		this.to_display.oncontextmenu = function(){ return false; };

    // fire the callback
    if (this.callback)
      this.callback(this);
  },
  
  image_errored : function(e) {
    // update the state
    this.load_finished = true;
    this.borked = true;
    this.loading = false;

		this.stop_observing();

    // fire the callback
    if (this.callback)
      this.callback(this);
  },

  
  content : function() {
    // to be inserted into the gallery
    return this.to_display;
  },

  redraw : function(width, height) {
		// is there a caption?
    if (this.cap_elm) {
      // add it to the doc so we can find its dimensions
      $(document.body).insert(this.cap_elm);
      var cap_width = this.cap_elm.getWidth();
      var cap_height = this.cap_elm.getHeight();
      
      // this is height that's not available
      height -= (cap_height + 10);
    }

    // fit the content into the space given
    var ratio = width / height;
    if (this.ratio < ratio) {
      // fit to height
      img_height = height;
      img_width = Math.round(height*this.ratio);
    } else {
      // fit to width
      img_width = width;
      img_height = Math.round(width.toString()/this.ratio);
    }
    
    // should we put a border on?
    var border_size = 0;
    if (this.add_borders) {
      // make a bit of space for the borders
      border_size = 8;
      // and add it to the image
      this.image.setStyle({border:'4px solid #fff'});
    }

    // make sure we don't scale images up
    if (img_width > this.original_width) {
      img_width = this.original_width;
      img_height = this.original_height;
    }

    // center the image
    var img_top = 0;
    var img_left = 0;
    if (img_height < height)
      img_top += Math.round((height - img_height)/2);
    if (img_width < width)
      img_left += Math.round((width - img_width)/2);

    // and add it to the page
    this.image.setStyle({position:'absolute', height:(img_height-border_size)+'px', width:(img_width-border_size)+'px', top:img_top+'px', left:img_left+'px'});

    // now put the caption below it (if there is one)
    if (this.cap_elm) {
      // push it down to beneath the image
      this.cap_elm.setStyle({textAlign:'center', position:'absolute', top:(img_height+img_top+10)+'px', width:width+'px'});

      // and add it in
      this.to_display.insert(this.cap_elm);
    }
  }
  
});
if (!PS) var PS = {};

/*
  PurchaseOptions - a set of purchase options for a particular image
*/
PS.GalleryPurchaseOptions = Class.create(
{

  initialize : function(id, account, currency, message, link_to, album_purchase_options, purchase_options, filename, caption) {
    // store everything
    this.id = id;
    this.filename = filename;
    this.caption = caption;
    this.account = account;
    this.link_to = link_to;
    this.currency = currency;
    this.message = message;
    this.purchase_options = purchase_options;
    this.album_purchase_options = album_purchase_options;

    // create the html
    this.create_elements();
  },
  
  create_elements : function() {
    // create the shell
    this.to_display = new Element('div');
    this.to_display.setStyle({width:'0px', position:'absolute', backgroundColor:'#333', overFlow:'hidden', padding:'20px'});
    
    this.to_display.update('<h2>Available For Purchase In These Formats</h2>');
    this.to_display.down('h2').setStyle("margin-bottom:30px; padding-bottom:10px; border-bottom:1px solid #fff;text-align:center;font-size:16px;");

    // and add everything else
    var cart_html = '';
		cart_html += this.create_cart_rows(this.album_purchase_options);
		cart_html += this.create_cart_rows(this.purchase_options);
    
    this.to_display.insert(new Element('div').update('<table class="ps-gal-purchase-options"><tr><th>Media</th><th>Size</th><th>Price</th><th></th></tr>' + cart_html + '</table>'));

    // hack in the styles
    this.to_display.down('table').setStyle("width:100%;");
    this.to_display.select('th').each(function(elm){
      elm.setStyle("text-align:left;");
    });
    this.to_display.select('th').each(function(elm){
      elm.setStyle("text-align:left;");
    });
    this.to_display.select('td').each(function(elm){
      elm.setStyle("line-height:31px;height:31px;vertical-align:top;");
    });

    this.to_display.insert('<div style="position:absolute;bottom:10px;left:10px;width:500px;"><em>' + this.message + '</em></div>');
    this.to_display.insert('<div style="position:absolute;bottom:10px;right:10px;"><p><em>prices shown are in ' + this.currency + '</em></p></div>');
    
    // stop buy now clicks from closing the purchase page
    Event.observe(this.to_display, 'click', function(e){ e.stopPropagation(); });
  },

	create_cart_rows : function(options) {
		var cart_html = '';
		
    options.each(function(elm){
      cart_html += '<tr><td>'+elm.media+'</td><td>'+elm.size+'</td><td>'+parseFloat(elm.price).toFixed(2)+'</td><td class="ps-buy-link">' + 
      this.checkout_button(
        {
            size         : elm.size
          , media        : elm.media
          , price        : parseFloat(elm.price).toFixed(2)
          , currency     : this.currency
          , merchant_id  : this.account
          , url_success  : ''
          , url_cancel   : ''
          , url_notify   : ''
        })
      + '</td></tr>';
    }.bind(this));

		return cart_html;
	},
  
  content : function() {
    // to be inserted into the gallery
    return this.to_display;
  },

  redraw : function(width, height) {
    // update the dimensions
    this.to_display.setStyle({position:'absolute', height:height-40+'px', width:width-40+'px', overflow:'auto'});
  },
  
  checkout_button : function(opt) {
    // create the template
    if (!this.tem_buynow)
      this.tem_buynow = new Template('\
      <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">\
      <input type="hidden" name="cmd" value="_xclick">\
      <input type="hidden" name="business" value="#{merchant_id}">\
      <input type="hidden" name="lc" value="GB">\
      <input type="hidden" name="item_name" value="#{description}">\
      <input type="hidden" name="custom" value="#{custom}">\
      <input type="hidden" name="item_number" value="#{id}">\
      <input type="hidden" name="amount" value="#{price}">\
      <input type="hidden" name="currency_code" value="#{currency}">\
      <input type="hidden" name="button_subtype" value="products">\
      <input type="hidden" name="cn" value="Add special instructions to the seller">\
      <input type="hidden" name="no_shipping" value="2">\
      <input type="hidden" name="rm" value="1">\
      <input type="hidden" name="return" value="#{url_return}">\
      <input type="hidden" name="cancel_return" value="#{url_cancel}">\
      <input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynowCC_LG.gif:NonHosted">\
      <input type="hidden" name="notify_url" value="#{url_notify}">\
      <input type="image" src="https://www.paypal.com/en_GB/i/btn/btn_buynow_SM.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online." style="margin-top:5px;" />\
      <img alt="" border="0" src="https://www.paypal.com/en_GB/i/scr/pixel.gif" width="1" height="1">\
      </form>\
    ');
    
		// build the description
    opt.description = 'Image:' + this.id + ' Filename:' + this.filename.gsub(/[^-_\. a-zA-Z0-9]/, '');
		opt.description += ' Media:' + opt.media + ' Size:' + opt.size;
		if (this.caption.length)
 			opt.description += ' Caption:' + this.caption.gsub(/[^-_\. a-zA-Z0-9]/, '');
		// and link to this image
		opt.custom = 'Link: ' + this.link_to;
		
		// piece it all together
    return this.tem_buynow.evaluate(opt);
  }
  
});
if (!PS) var PS = {};

/*
  Loader - loads the images
  Shell for the images in a gallery
  mostly it deals with pre-loading in the correct order for optimal viewing
  but also with notifying when the next desired image is available
*/
PS.GalleryLoader = Class.create(
{

  initialize : function(gallery, options) {
    this.gallery = gallery;

    // default options
    this.options = {
        broken_path  : '/img/icons/broken.png'
      , missing_path : '/img/icons/missing.gif'
    };
    Object.extend(this.options, options || { });
    
    // create the placeholder images for when the other images aren't ready (or are broken)
    this.missing = new PS.GalleryImage(this.options.missing_path, 'The image is still being loaded.');
    this.missing.preload();
    this.broken = new PS.GalleryImage(this.options.broken_path, null, 'There appears to be something wrong with this image.');
    this.broken.preload();

    // the images themselves and which is being displayed at the moment
    this.images = new Array();
    this.current_id = null;
    this.current_pos = null;

    // preloader things
    this.processor_up = true;
    this.processor_next = 0;
    this.processed_count = 0;
  },
  
  add_images : function(images) {
    // add to our list of images
    images.inject(this.images, function(array, value) { 
      array.push(value); 
      return array; 
    }); 
    
    // start the preloading the new ones
    this.prepare_next_image();
  },
  
  prepare_next_image : function() {
    // are there images left to process?
    if (this.images.size() == this.processed_count)
      return;
    
    // now we know which to process next - check to see if it's been done already
    if (this.images[this.processor_next].load_finished) {
      // move to the next / prev image
      if (this.processor_up)
        this.processor_next++;
      else
        this.processor_next--;

      // reset to within bounds
      if (this.processor_next < 0)
        this.processor_next = this.images.size() - 1;
      if (this.processor_next > this.images.size() - 1)
        this.processor_next = 0;

      // already loaded - move on to the next one
      this.prepare_next_image();
    }
    else {
      // start it loading
      this.images[this.processor_next].preload(this.image_load_finished.bind(this));
    }
  },
  
  image_load_finished : function(image) {
    // mark another one as ready
    this.processed_count++;
      
    // images callback to here when they have finished loading
    
    // were we waiting to display this image?
    if (this.current_id && this.current_id == image.id) {
      // yup - best display it
      this.display_image(image);
    }
    
    // process the next one
    this.prepare_next_image();
  },


  display_image : function(image) {
    // update the cursors so we know where we are
    this.current_id = image.id;
    this.current_pos = this.images.indexOf(image);
    
    // processor should run from the current image
    this.processor_next = this.current_pos;

    // display either missing, broken or the actual image
    if (!image.load_finished)
      this.gallery.show_image(this.missing);
    else if (image.borked)
      this.gallery.show_image(this.broken);
    else
      this.gallery.show_image(image);
  },


  find_image_with_id : function(id) {
    return this.images.find(function(image){ return image.id == id; }); 
  },
  
  display_image_with_id : function(id) {
    // try to find the image with the given id in our list
    var image = this.find_image_with_id(id);
    
    if (image) {
      // reset the loader so that it preloads forwards from the given image
      this.processor_up = true;
      
      this.display_image(image);
    }
  },
  
  display_prev : function() {
    if (this.current_pos == null)
      return;
    
    // move to the previous image
    this.current_pos--;
    
    // reset to within the bounds
    if (this.current_pos < 0)
      this.current_pos = this.images.size() - 1;

    // also reset the loader so that it preloads backwards from the given image
    this.processor_up = false;

    // display it
    this.display_image_at_position(this.current_pos);
    
    // return it (even though it may not be ready)
    return this.images[this.current_pos];
  },
  
  display_next : function() {
    if (this.current_pos == null)
      return;

    // move to the previous image
    this.current_pos++;
    
    // reset to within the bounds
    if (this.current_pos > this.images.size() - 1)
      this.current_pos = 0;
    // also reset the loader so that it preloads from the given image
    this.processor_up = true;
    
    // display it
    this.display_image_at_position(this.current_pos);

    // return it (even though it may not be ready)
    return this.images[this.current_pos];
  },

  display_image_at_position : function(pos) {
    if (pos < 0 || pos > this.images.size() - 1)
      return;
    
    var image = this.images[pos];
    this.display_image(image);
  }

});
if (!PS) var PS = {};

/*
  Utils - utility functions that the various objects need
*/
PS.GalleryUtils = Class.create(
{

  initialize : function(){
    
  },

  view_area : function() {
    return {
        height : this.inner_height()
      , width  : this.inner_width()
      , left   : document.viewport.getScrollOffsets()[0]
      , top    : document.viewport.getScrollOffsets()[1]
    };
  },

  inner_height : function() {
    // figure out the viewable height
    var innerHeight = 0;
    if (document.viewport.getHeight()) {
      innerHeight = document.viewport.getHeight();
    } else if (window.innerHeight) {
      innerHeight = window.innerHeight;
    } else if (document.documentElement.clientHeight && document.documentElement.clientHeight > 0) {
      innerHeight = document.documentElement.clientHeight;
    } else if (document.body.clientHeight) {
      innerHeight = document.body.clientHeight;
    }
    return innerHeight;
  },

  inner_width : function() {
    // figure out the viewable width
    var innerWidth = 0;
    if (document.viewport.getWidth()) {
      innerWidth = document.viewport.getWidth();
    } else if (window.innerWidth) {
      innerWidth = window.innerWidth;
    } else if (document.documentElement.clientWidth && document.documentElement.clientWidth > 0) {
      innerWidth = document.documentElement.clientWidth;
    } else if (document.body.clientWidth) {
      innerWidth = document.body.clientWidth;
    }
    return innerWidth;
  },
  
  is_ie6 : function() {
    // is this ie6?
    return Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 6;
  }

});
if (!PS) var PS = {};

/*
  PS.Animator - class to perform time based animations
*/ 


PS.Animator = Class.create(
{

  initialize : function(options) {
    this.options = { 
    	  duration : 400
    	, from     : 0
    	, to       : 0
    };
    Object.extend(this.options, options || { });

  	this.starttime = 0;
  	this.timer = null;
  	this.now = 0;
    // space for users of this objec to put their own info
  	this.externals = {};

  	// element being animated
  	this.element = null;

  	// function to call the at each step of animation
  	this.animationStep = null;
  	
  	// function to call when animation finished
  	this.whenfinished = null;
  },

	animate : function() {
		var T;
		var ease;
		var time = (new Date).getTime();
		var finishedanimation = false;

		T = this.limit_3(time - this.starttime, 0, this.options.duration);

		if (T >= this.options.duration) {
			clearInterval(this.timer);
			this.timer = null;
			this.now = this.options.to;
			finishedanimation = true;
		} else {
			ease = 0.5 - (0.5 * Math.cos(Math.PI * T / this.options.duration));
			this.now = this.computeNextFloat(this.options.from, this.options.to, ease);
		}

		// user defined function
		this.animationStep(this.now, this);

		if (finishedanimation) {
			// call the user's post animation function
			if (this.whenfinished != null) {
				setTimeout(function() { this.whenfinished(this) }.bind(this), 0);
			}
		}
	},

	startAnimation : function() {
		var starttime = (new Date).getTime() - 13;
		this.starttime = starttime;

		// run the animation
		this.timer = setInterval(this.animate.bind(this), 13);
		this.animate();
	},

	pauseAnimation : function() {
		// stop the clock!
		if (this.timer != null) {
			clearInterval(this.timer);
			this.timer = null;
		}
	},

	limit_3 : function(a, b, c) {
		return a < b ? b : (a > c ? c : a);	
	},

	computeNextFloat : function(from, to, ease) {
		return from + (to - from) * ease;
	}
});
if (!PS) var PS = {};

/*
  Lazy Loader - only load thumbnails when required
*/
PS.LazyLoader = Class.create(
{

  initialize: function() {
		// primitive locking mechanism
		this.thumb_scroll_locked = false;
		
		// first index the thumbnails
		this.indexThumbnails();
		
		// and attach the watchers
		this.attachEvents();
		
		// and run once
		this.drawThumbs();
  },


	indexThumbnails : function() {
		// we need to know roughly where all the thumbnails are on the page
		this.thumb_positions = new Hash();
		
	  $$('.ps-ll').each(function(elm){
	    var block_pos = Math.round(elm.cumulativeOffset()[1]/100);
	    if (!this.thumb_positions.get(block_pos))
	      this.thumb_positions.set(block_pos, new Array());

	    this.thumb_positions.get(block_pos).push(elm);
	  }.bind(this));
	},

	attachEvents : function() {
		if (!this.bound_drawThumbs)
			this.bound_drawThumbs = this.drawThumbs.bind(this);

		// no need to do this if there are no thumbnails to load
		if (this.thumb_positions.size() == 0)
			return;
		
		// run when the viewport changes at all
		Event.observe(window, 'scroll', this.bound_drawThumbs);
		Event.observe(window, 'resize', this.bound_drawThumbs);
	},
	
	detachEvents : function() {
		// remove the events when we don't need them anymore
		Event.stopObserving(window, 'scroll', this.bound_drawThumbs);
		Event.stopObserving(window, 'resize', this.bound_drawThumbs);
	},

	drawThumbs : function() {
	  // some primative locking
	  if (this.thumb_scroll_locked)
	    return;
	  this.thumb_scroll_locked = true;

	  // where are we on the page?
	  var y = document.viewport.getScrollOffsets()[1];
	  var h = document.viewport.getDimensions().height;

	  // convert to block position (+- a little)
	  var first = Math.max(Math.round(y/100) - 4, 0);
	  var last = Math.round((y + h)/100) + 4;

	  $R(first, last).each(function(pos){
	    // grab the block (if it exists)
	    var blk = this.thumb_positions.unset(pos);

	    if (blk) {
	      setTimeout(function(){
	        blk.each(function(elm){
						this.replaceElement(elm);
	        }.bind(this));
	      }.bind(this), 1);
	    }
	  }.bind(this));

	  var final_y = document.viewport.getScrollOffsets()[1];
	  var final_height = document.viewport.getDimensions().height;

		// if we've loaded everything then stop watching
		if (this.thumb_positions.size() == 0)
			this.detachEvents();

	  // unlock
	  this.thumb_scroll_locked = false;

	  // run again if it's moved since we started
	  if (y != final_y || h != final_height)
	    this.drawThumbs();
	},
	
	replaceElement : function(elm) {
		// replace the box with the image (do it like this so we can delay the replacement till after image load)
		var img = new Image();
		img.onload = function(){ elm.replace(img); };
		img.src = elm.down('span').innerHTML;
	}
	
});
if (!PS) var PS = {};

/*
  Cover Loader - loads the thumbnails for the main page
*/
PS.CoverLoader = Class.create(
{
  
  initialize: function(thumbs) {
    // set of the other covers (arrays hashed by album_id)
    this.thumbs = thumbs;
    this.images = new Hash();
    
		// attach events to the albums
		this.attachEvents();

		// start loading the covers
    this.loadCovers();
    
    this.last_image = '';
  },
  
  attachEvents : function() {
    $$('.album-thumb').each(function(thmb){
      var id = thmb.id;
      this.images.set(id, { master : thmb.getStyle('backgroundImage'), other : new Array() });

      Event.observe(thmb, 'mousemove', this.overThumb.bind(this));
      Event.observe(thmb, 'mouseout', this.outThumb.bind(this));
    }.bind(this));
  },
  
  loadCovers : function() {
    // load each cover
    this.thumbs.each(function(pair){
      var album_id = pair.key;
      pair.value.each(function(src){
        var img = new Image();
        // once loaded it'll add itself to the correct array in the images index
        img.onload = function(){ this.pushCover(img, 'album-' + album_id.toString()); }.bind(this);
        img.src = src;
      }.bind(this));
    }.bind(this));
  },
  
  pushCover : function(img, album_id) {
    // add the image to the correct array
    // todo: deal with ordering (sort it the same as the thumbs array)
    this.images.get(album_id).other.push(img);
  },
  
  overThumb : function(e) {
    var id = e.target.id;
    var width = 160;

		// no thumbs? nothing to do
		if (this.images.get(id).other.size() == 0)
			return false;

    // figure out how many other images are in the album
    var img_count = this.images.get(id).other.size();

    // no need to continue if there are no images
    if (img_count == 0)
      return false;

    // and how big each segment should be
    var segment_size = width / img_count;

    // which segment are they in?
    var offset = e.offsetX;
    if (!offset)
      offset = e.layerX;

		// deal with strange ie6 offset issue
		if (offset == undefined)
			return;

   	var segment = Math.floor(offset/segment_size);

		// don't bother doing anything if it's the same image
		if (this.last_image == this.images.get(id).other[segment].src)
			return;

		this.last_image = this.images.get(id).other[segment].src;

    // display the appropriate image
    e.target.setStyle({ backgroundImage: "url('"+this.images.get(id).other[segment].src+"')" });
  },
  
  outThumb : function(e) {
    var id = e.target.id;
    // revert to the album image
    e.target.setStyle({ backgroundImage: this.images.get(id).master });
		this.last_image = '';
  }

});

// include the ability to extract metadata from elements (and add it in too)
Element.addMethods({
  getMetaData: function(element){
    element = $(element);
    var found = element.className.match(/\{.*\}/);
    if (found) {
      try {
        return eval("(" + found[0] + ")");
      } catch(e) {
        return {};
      }
    } else {
      return {};
    }
  },

  setMetaData: function(element, md) {
    element = $(element);
    // strip out any existing metadata (sorry!)
    var found = element.className.match(/\{.*\}/);
    if (found) {
      element.removeClassName(found);
    }
    // add in the new stuff
    element.addClassName(Object.toJSON(md));
  }
});

// run on system start
Event.observe(document, 'dom:loaded', function(){
  // stuff to deal with the help tips
  if (PS && PS.HelpTips)
    window.helptips = new PS.HelpTips();

  // and user message boxes
  if (PS && PS.UserMessages)
    window.usermessages = new PS.UserMessages();

	// don't allow double click on certain submit buttons
	$$('.click-once').each(function(elm){
		Event.observe(elm, 'click', function() {
			if (elm.ps_sudo_disabled)
				elm.disabled = true;
			elm.ps_sudo_disabled = true;
		});
	});
});
/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();