/*
  Modified by Miles Romney.  All changed lines marked.
*/

/*
 * Facebox (for jQuery)
 * version: 1.2 (05/05/2008)
 * @requires jQuery v1.2 or later
 *
 * Examples at http://famspam.com/facebox/
 *
 * Licensed under the MIT:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ]
 *
 * Usage:
 *  
 *  jQuery(document).ready(function() {
 *    jQuery('a[rel*=facebox]').facebox() 
 *  })
 *
 *  <a href="#terms" rel="facebox">Terms</a>
 *    Loads the #terms div in the box
 *
 *  <a href="terms.html" rel="facebox">Terms</a>
 *    Loads the terms.html page in the box
 *
 *  <a href="terms.png" rel="facebox">Terms</a>
 *    Loads the terms.png image in the box
 *
 *
 *  You can also use it programmatically:
 * 
 *    jQuery.facebox('some html')
 *
 *  The above will open a facebox with "some html" as the content.
 *    
 *    jQuery.facebox(function($j) { 
 *      $j.get('blah.html', function(data) { $j.facebox(data) })
 *    })
 *
 *  The above will show a loading screen before the passed function is called,
 *  allowing for a better ajaxy experience.
 *
 *  The facebox function can also display an ajax page or image:
 *  
 *    jQuery.facebox({ ajax: 'remote.html' })
 *    jQuery.facebox({ image: 'dude.jpg' })
 *
 *  Want to close the facebox?  Trigger the 'close.facebox' document event:
 *
 *    jQuery(document).trigger('close.facebox')
 *
 *  Facebox also has a bunch of other hooks:
 *
 *    loading.facebox
 *    beforeReveal.facebox
 *    reveal.facebox (aliased as 'afterReveal.facebox')
 *    init.facebox
 *
 *  Simply bind a function to any of these hooks:
 *
 *   $j(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })
 *
 */
(function($j) {
  $j.facebox = function(data, klass) {
    $j.facebox.loading()

    if (data.ajax) fillFaceboxFromAjax(data.ajax)
    else if (data.image) fillFaceboxFromImage(data.image)
    else if (data.div) fillFaceboxFromHref(data.div)
    else if ($j.isFunction(data)) data.call($j)
    else $j.facebox.reveal(data, klass)
  }

  /*
   * Public, $j.facebox methods
   */

// facboxHtml below modified by Miles Romney, removed "footer" element
  $j.extend($j.facebox, {
    settings: {
      opacity      : 0,
      overlay      : true,
      loadingImage : '/images/facebox/loading.gif',
      closeImage   : '/images/facebox/closelabel.gif',
      imageTypes   : [ 'png', 'jpg', 'jpeg', 'gif' ],
      faceboxHtml  : '\
    <div id="facebox" style="display:none;"> \
      <div class="popup"> \
        <table> \
          <tbody> \
            <tr> \
              <td class="tl"/><td class="b"/><td class="tr"/> \
            </tr> \
            <tr> \
              <td class="b"/> \
              <td class="body"> \
                <div class="content"> \
                </div> \
              </td> \
              <td class="b"/> \
            </tr> \
            <tr> \
              <td class="bl"/><td class="b"/><td class="br"/> \
            </tr> \
          </tbody> \
        </table> \
      </div> \
    </div>'
    },

    loading: function() {
      init()
      if ($j('#facebox .loading').length == 1) return true
      showOverlay()

      $j('#facebox .content').empty()
      /* 
        Line changed by Miles Romney, .hide().fadeIn() added to delay display of loading icon
        until after the overlay finishes display.  The alias is too hard to work out otherwise.
      */
      $j('#facebox .body').children().hide().end().
        append('<div class="loading"><img src="'+$j.facebox.settings.loadingImage+'"/></div>').hide().fadeIn(200)

      $j('#facebox').css({
        top:	$j.facebox.getPageScroll()[1] + ($j.facebox.getPageHeight() / 10),
        left:	385.5
      }).show()

      $j(document).bind('keydown.facebox', function(e) {
        if (e.keyCode == 27) $j.facebox.close()
        return true
      })
      $j(document).trigger('loading.facebox')
    },

    reveal: function(data, klass) {
      $j(document).trigger('beforeReveal.facebox')
      if (klass) $j('#facebox .content').addClass(klass)
      $j('#facebox .content').append(data)
      $j('#facebox .loading').remove()
      $j('#facebox .body').children().fadeIn('normal')
      $j('#facebox').css('left', $j.facebox.getLeft())
	    $j('#facebox').css('top', $j.facebox.getTop())
      $j(document).trigger('reveal.facebox').trigger('afterReveal.facebox')
    },

    /* Nelson - 7/8/2009 */
    replace: function(data, klass) {
      //$j('#facebox').fadeOut();
      $j('#facebox .content').html($j(data).clone().show());
      if (klass) $j('#facebox .content').addClass(klass);
      $j('#facebox .body').children().fadeIn('normal');
      $j('#facebox').css('left', $j.facebox.getLeft())
	    $j('#facebox').css('top', $j.facebox.getTop())
    },

    close: function() {
      $j(document).trigger('close.facebox')
      return false
    }
  })

  /*
   * Public, $j.fn methods
   */

  $j.fn.facebox = function(settings) {
    init(settings)
    
    /*
      Line added by Miles Romney.  This prevents multiple activation calls from 
      redundantly (it's ugly) initialising FB anchor tags.
    */
    $j(this).attr('rel', 'fb_activated')

    function clickHandler() {
      $j.facebox.loading(true)

      // support for rel="facebox.inline_popup" syntax, to add a class
      // also supports deprecated "facebox[.inline_popup]" syntax
      var klass = this.rel.match(/facebox\[?\.(\w+)\]?/)
      if (klass) klass = klass[1]

      fillFaceboxFromHref(this.href, klass)
      return false
    }

    return this.click(clickHandler)
  }

  /*
   * Private methods
   */

  // called one time to setup facebox on this page
  function init(settings) {
    if ($j.facebox.settings.inited) return true
    else $j.facebox.settings.inited = true

    $j(document).trigger('init.facebox')
    makeCompatible()

    var imageTypes = $j.facebox.settings.imageTypes.join('|')
    $j.facebox.settings.imageTypesRegexp = new RegExp('\.' + imageTypes + '$j', 'i')

    if (settings) $j.extend($j.facebox.settings, settings)
    $j('body').append($j.facebox.settings.faceboxHtml)

    var preload = [ new Image(), new Image() ]
    preload[0].src = $j.facebox.settings.closeImage
    preload[1].src = $j.facebox.settings.loadingImage

    $j('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() {
      preload.push(new Image())
      preload.slice(-1).src = $j(this).css('background-image').replace(/url\((.+)\)/, '$j1')
    })

    $j('#facebox .close').click($j.facebox.close)
    $j('#facebox .close_image').attr('src', $j.facebox.settings.closeImage)
  }
  
  // getPageScroll() by quirksmode.com
  $j.facebox.getPageScroll = function() {
    var xScroll, yScroll;
    if (self.pageYOffset) {
      yScroll = self.pageYOffset;
      xScroll = self.pageXOffset;
    } else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict
      yScroll = document.documentElement.scrollTop;
      xScroll = document.documentElement.scrollLeft;
    } else if (document.body) {// all other Explorers
      yScroll = document.body.scrollTop;
      xScroll = document.body.scrollLeft;	
    }
    return new Array(xScroll,yScroll) 
  }

	$j.facebox.getTop = function() {
		var min_pad = 0;
		var h_diff = ($j.facebox.getPageHeight() / 2 )- ($j('#facebox table:first').height() / 2)
		
		return $j.facebox.getPageScroll()[1] + (min_pad < h_diff ? h_diff : min_pad);
	}
	$j.facebox.getLeft = function() {
		return $j(window).width() / 2 - ($j('#facebox table').width() / 2);
	}

  // Adapted from getPageSize() by quirksmode.com
  $j.facebox.getPageHeight = function() {
    var windowHeight
    if (self.innerHeight) {	// all except Explorer
      windowHeight = self.innerHeight;
    } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
      windowHeight = document.documentElement.clientHeight;
    } else if (document.body) { // other Explorers
      windowHeight = document.body.clientHeight;
    }	
    return windowHeight
  }

  // Backwards compatibility
  function makeCompatible() {
    var $s = $j.facebox.settings

    $s.loadingImage = $s.loading_image || $s.loadingImage
    $s.closeImage = $s.close_image || $s.closeImage
    $s.imageTypes = $s.image_types || $s.imageTypes
    $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml
  }

  // Figures out what you want to display and displays it
  // formats are:
  //     div: #id
  //   image: blah.extension
  //    ajax: anything else
  function fillFaceboxFromHref(href, klass) {
    // div
    if (href.match(/#/)) {
      var url    = window.location.href.split('#')[0]
      var target = href.replace(url,'')
      $j.facebox.reveal($j(target).clone().show(), klass)

    // image
    } else if (href.match($j.facebox.settings.imageTypesRegexp)) {
      fillFaceboxFromImage(href, klass)
    // ajax
    } else {
      fillFaceboxFromAjax(href, klass)
    }
  }

  function fillFaceboxFromImage(href, klass) {
    var image = new Image()
    image.onload = function() {
      $j.facebox.reveal('<div class="image"><img src="' + image.src + '" /></div>', klass)
    }
    image.src = href
  }

  function fillFaceboxFromAjax(href, klass) {
    $j.get(href, function(data) { $j.facebox.reveal(data, klass) })
  }

  function skipOverlay() {
    return $j.facebox.settings.overlay == false || $j.facebox.settings.opacity === null 
  }

  function showOverlay() {
    if (skipOverlay()) return

    if ($j('facebox_overlay').length == 0) 
      $j("body").append('<div id="facebox_overlay" class="facebox_hide"></div>')

    $j('#facebox_overlay').hide().addClass("facebox_overlayBG")
      .css('opacity', $j.facebox.settings.opacity)
      .click(function() { $j(document).trigger('close.facebox') })
      .fadeIn(200)
    return false
  }

  function hideOverlay() {
    if (skipOverlay()) return

    $j('#facebox_overlay').fadeOut(200, function(){
      $j("#facebox_overlay").removeClass("facebox_overlayBG")
      $j("#facebox_overlay").addClass("facebox_hide") 
      $j("#facebox_overlay").remove()
    })
    
    return false
  }

  /*
   * Bindings
   */

  $j(document).bind('close.facebox', function() {
    $j(document).unbind('keydown.facebox')
    $j('#facebox').fadeOut(function() {
		$j('#facebox .content').html('');	// added by Chris Sturgill for form validation purposes
      $j('#facebox .content').removeClass().addClass('content')
      hideOverlay()
      $j('#facebox .loading').remove()
    })
  })

})(jQuery);


/************************ Utilities *****************************************/

// global storage to store temp stuff
var RE = {};

function observe_event(value) {
    if (value == "event") {
        $("dl_audio").hide();
        $("dl_image").hide();
        $("dl_related_content").hide();
        $("dl_location").show();
        $("dl_date").show();
    } else {
        $("dl_audio").show();
        $("dl_image").show();
        $("dl_related_content").show();
        $("dl_location").hide();
        $("dl_date").hide();
    }
}

function add_slim_player(file, div) {
  var so = new SWFObject('/swfs/radio_slimline.swf', 'radio', '171', '18', '9', '#000000');
  so.addParam('allowScriptAccess', 'always');
  so.addParam('quality', 'high');
  so.addParam('scale', 'noscale');
  so.addParam('salign', 'tl');
  so.addParam('menu', 'true');
  so.addParam('wmode', 'transparent');
  so.addParam('swLiveConnect', 'true');
  so.addVariable('ptf', file)
  so.addVariable('autoplay', 'false');
  so.write(div);
}

Array.prototype.in_array = function ( obj ) {
	for ( var x = 0 ; x <= this.length ; x++ ) {
		if ( this[x] == obj ) return true;
	}
	return false;
}

/*
	Sets up all anchor tags with "rel=facebox" as facebox triggers.
*/
function activateFacebox(from_elem) {
  if(from_elem == undefined)
    { from_elem = '' }
  else
    { from_elem += ' ' }

  $j(document).ready(function($) {
    $j(from_elem + 'a[rel*=facebox]').facebox({
	    opacity : 0.7,
      loadingImage : '/images/facebox/loading.gif'
    });
  })
}

/*
	Loads data from a remote URL into facebox.
*/
function faceboxRemote(url) {
	$j.facebox(function($) {
	  $j.get(url, function(data) {
	    $j.facebox(data);
	  })
	})
}

/*
  Close Facebox pop-up.
*/
function closeFacebox() {
  $j.facebox.close();
}

/*
	This method executes after every prototype Ajax request.
*/
function afterAjax() {
	// Activate our facbox links
	activateFacebox();
  enable_facebox_form();
	add_candystripe();
}

/*
  Submit any form from anywhere ;  prevent multiple form submissions.
*/
var last_submitted_form = '';

function submit_form(element_id) {
  if(last_submitted_form != element_id) {
    if(element_id.indexOf('#facebox') > -1) {
      disable_facebox_form();
    }
   // last_submitted_form = element_id;
    element = $j(element_id);
    element.attr('onsubmit') ? element.trigger('onsubmit') : element.trigger('submit');
  }
}

function disable_facebox_form(){
    var el = $j('#facebox #submit_button a')[0];
    var old_onclick = get_function_body(el.onclick.toString());

    el.disabled = true;
    el.onclick = new Function('return false;' + old_onclick);
}
function enable_facebox_form(){
    var el = $j('#facebox #submit_button a')[0];
    if (el != undefined) {
      var old_onclick = get_function_body(el.onclick.toString());
      var new_onclick = old_onclick.replace(/^\s+return false;/, '');

      el.disabled = false;
      el.onclick = new Function(new_onclick);
    }
}
function get_function_body(str) {
  str=str.replace(/[^{]+{/,"");
  str=str.substring(0,str.length-1);
  str = str.replace(/\n/gi,"");
  if(!str.match(/\(.*\)/gi))str += ")";
  return str;
}

/****************************************************************************/

/************************ Document Prep *************************************/

var new_message_description_interval;
var $j = jQuery.noConflict();
var bypass_verification = false;		//	flag to keep track if action was submitted by a form -> used in verifying when leaving a page

$j(document).ready(function() {
	activateFacebox();
	add_candystripe();
	var w = $j('#header .my_links .my_re a #pm-unread-count').width() * -1;
	$j('#header .my_links .my_re a #pm-unread-count').css('left', w - 1);
	$j('#pm-unread-count').mouseover(function() {
		new_message_description_interval = setTimeout("$j('#new-message-description').show();", 1000);
	});
	$j('#pm-unread-count').mouseout(function() {
		clearTimeout(new_message_description_interval);
		$j('#new-message-description').hide();
	});
	$j('form').submit(function() {
		bypass_verification = true;
	});
	activate_person_contextual();
})

/****************************************************************************/

/************************ Specialised ***************************************/

function checkToClearField(field) {
    if (field.value == field.defaultValue) {
        field.value = '';
    }
}

function checkToClearFieldForPsw(field) {
    if (field.value == field.defaultValue) {
        field.value = '';
        field.type  = "password";
    }
}

function checkToResetField(field) {
	if (field.value == '') {
		field.value = field.defaultValue;
	}
}

function checkToResetFieldForPsw(field) {
	if (field.value == '') {
		field.value = field.defaultValue;
        field.type  = "text";
	}
}

function init_staff_details(){
  var form_val_name = $j('.form_val_name');
  var form_val_title = $j('.form_val_title');


  form_val_name.each(function(el){
    if(this.value == ""){
      this.value = "Name:";
    }
  });
  form_val_title.each(function(el){
    if(this.value == ""){
      this.value = "Title:";
    }
  });


  form_val_name.focus(function(){
    if(this.value == "Name:"){
      this.value = "";
    }
  });
  form_val_name.blur(function(){
    if(this.value == ""){
      this.value = "Name:";
    }
  });

  form_val_title.focus(function(){
    if(this.value == "Title:"){
      this.value = "";
    }
  });
  form_val_title.blur(function(){
    if(this.value == ""){
      this.value = "Title:";
    }
  });
}

function reset_member_form(group){
  $j('#'+group+'_form .form_val_name')[0].value  = 'Name:';
  $j('#'+group+'_form .form_val_title')[0].value = 'Title:';
}

function reorder_believers(el, ph, page){
  var order = el.options[el.options.selectedIndex].value;

  $j.get('/philanthropy/sort_believers',{order: order, id: ph, page: page}, function(data){
    $j('#believers_content').html(data);
  });
}

function connect_remote_pane(container){
    $j(container).click(function(ev){
      var el = ev.target;
      if((el.tagName == 'A') && (el.getAttribute('rel') != 'noajax') && (el.getAttribute('rel') != 'facebox')){
        ev.stopPropagation();
        $j(container).load(el.getAttribute('href'));
        return false;
      }
    });
}

function connect_remote_pane_sub(container, links) {
	$j(links).click(function(ev){
      ev.stopPropagation();
      var el = ev.target;
      var href = ''

      if(el.tagName == 'A' && el.getAttribute('rel') != 'noajax'){
        href = el.getAttribute('href');
        //console.log(href);
        $j(container).load(href);
        return false;
      }
    });
}

//wraper method to create a confirm dialog
(function($j){
  $j.confirmDialog = {}
  $j.fn.confirmDialog = function(el){
    $j.confirmDialog.container = el || '#confirm_dialog_modal_container';

    this.each(function(){
      var el = $j(this);
      el.click(function(ev){
        ev.preventDefault();
        $j.confirmDialog.show_modal();
        $j(document).bind('close.facebox', $j.confirmDialog.attach_to_facebox);

        $j.confirmDialog.attach_modal_events(this.href);
        //console.log(this.href);
      });
    });

    return this;
  };

  $j.confirmDialog.show_modal = function(){
    jQuery.facebox($j($j.confirmDialog.container).html());
  };

  $j.confirmDialog.attach_modal_events = function(href){
    //connect where facebox listen
    $j('#facebox').bind('click', href, $j.confirmDialog.click_handler);
  };

  $j.confirmDialog.click_handler = function(ev){
    ev.preventDefault();
    var str = ev.target.id;
    if(str == 'confirm_modal_cancel'){$j(document).trigger('close.facebox')}
    if(str == 'confirm_modal_submit'){
      $j.ajax({dataType:'script', url:ev.data});
      $j(document).trigger('close.facebox')
      //console.log('submit ' + ev.data);
    }
  };

  $j.confirmDialog.attach_to_facebox = function(){
    $j('#facebox').unbind('click', $j.confirmDialog.click_handler);
    $j(document).unbind('close.facebox', $j.confirmDialog.attach_to_facebox);
  };

})(jQuery);


//wraper method to create a confirm dialog
(function($j){
  $j.confirmDialogBoard = {}
  $j.fn.confirmDialogBoard = function(){
    this.each(function(){
      var el = $j(this);
      el.click(function(ev){
        ev.preventDefault();
        $j.confirmDialogBoard.show_modal();
        $j(document).bind('close.facebox', $j.confirmDialogBoard.attach_to_facebox);

        $j.confirmDialogBoard.attach_modal_events(this.href);
        //console.log(this.href);
      });
    });

    return this;
  };

  $j.confirmDialogBoard.show_modal = function(){
    jQuery.facebox($j('#confirm_dialog_modal_board_container').html());
  };

  $j.confirmDialogBoard.attach_modal_events = function(href){
    //connect where facebox listen
    $j('#facebox').bind('click', href, $j.confirmDialogBoard.click_handler);
  };

  $j.confirmDialogBoard.click_handler = function(ev){
    ev.preventDefault();
    var str = ev.target.id;
    if(str == 'confirm_modal_cancel'){$j(document).trigger('close.facebox')}
    if(str == 'confirm_modal_submit'){
      $j.ajax({dataType:'script', url:ev.data});
      $j(document).trigger('close.facebox')
      //console.log('submit ' + ev.data);
    }
  };

  $j.confirmDialogBoard.attach_to_facebox = function(){
    $j('#facebox').unbind('click', $j.confirmDialogBoard.click_handler);
    $j(document).unbind('close.facebox', $j.confirmDialogBoard.attach_to_facebox);
  };

})(jQuery);

/*


*/

//wraper method to create a confirm dialog for media uploader
(function($j){
  $j.fn.confirmDialogMedia = function(el){
    var nel = el ? {container: el} : undefined;
    var defaults = {container: '#confirm_media_dialog_modal_container'}

    var opts = $j.extend(defaults, nel);

    this.each(function(){
      var el = $j(this);
      el.click(function(ev){
        ev.preventDefault();
        $j.fn.confirmDialogMedia.show_modal(opts.container);
        $j(document).bind('close.facebox', $j.fn.confirmDialogMedia.attach_to_facebox);

        $j.fn.confirmDialogMedia.attach_modal_events(this.href);
        //console.log(this.href);
      });
    });

    return this;
  };

  $j.fn.confirmDialogMedia.defaults = {container: '#confirm_media_dialog_modal_container'}

  $j.fn.confirmDialogMedia.show_modal = function(container){
    jQuery.facebox($j(container).html());
  };

  $j.fn.confirmDialogMedia.attach_modal_events = function(href){
    //connect where facebox listen
    $j('#facebox').bind('click', href, $j.fn.confirmDialogMedia.click_handler);
  };

  $j.fn.confirmDialogMedia.click_handler = function(ev){
    ev.preventDefault();
    var str = ev.target.id;
    if(str == 'confirm_modal_cancel'){$j(document).trigger('close.facebox')}
    if(str == 'confirm_modal_submit'){
      $j.ajax({dataType:'script', url:ev.data});
      $j(document).trigger('close.facebox');
      //console.log('submit ' + ev.data);
    }
  };

  $j.fn.confirmDialogMedia.attach_to_facebox = function(){
    $j('#facebox').unbind('click', $j.fn.confirmDialogMedia.click_handler);
    $j(document).unbind('close.facebox', $j.fn.confirmDialogMedia.attach_to_facebox);
  };

})(jQuery);

/*


*/

function repaint_rows(){
  $j('#hours_links .hr:even').addClass('highlight');
  $j('#hours_links .hr:odd').removeClass('highlight');
}

function add_hour_row(){
  create_hour_row();
  recalc_form();

    function create_hour_row(){
      var size = $j('#hours_links .hr').size();
      var das  = $j('#day_name')[0].options[$j('#day_name')[0].options.selectedIndex].innerHTML;
      var da   = $j('#day_name').val();
      var st   = $j('#start_time').val();
      var et   = $j('#end_time').val();

      var div = document.createElement('div');
      div.addClassName('hr');

      var input_da = document.createElement('input');
      input_da.name = 'whours[' + size + '][da]';
      input_da.type = 'hidden';
      input_da.value = da;

      var input_st = document.createElement('input');
      input_st.name = 'whours[' + size + '][st]';
      input_st.type = 'hidden';
      input_st.value = st;

      var input_et = document.createElement('input');
      input_et.name = 'whours[' + size + '][et]';
      input_et.type = 'hidden';
      input_et.value = et;

      var divhr = document.createElement('div');
      var anchor = document.createElement('a');
      anchor.href = '#';
      anchor.className = 'remove_hour';
      //anchor.rel = 'facebox'
      anchor.appendChild(document.createTextNode('Remove'));

      divhr.appendChild(anchor);
      div.appendChild(input_da);
      div.appendChild(input_st);
      div.appendChild(input_et);
      div.appendChild(divhr);

      $j('#hours_links').append(div);
      $j(divhr).prepend('<span>' + das + '</span><span class="center_row">' + st + '</span><span class="center_row">till</span><span class="center_row">' + et + '</span>');
    }

    function recalc_form(){
      var count = 0;
      var re = /whours\[(\d+)\]/;
      $j('#hours_links .hr').each(function(){
        $j(this).find('input').each(function(){
          this.name = this.name.replace(re, "whours["+ count +"]");
        });
        count++;
      });
    }
}

/*
  Set a form value with the Flash meters.
*/
function setMeterValue(attribute, value) {
  $('review_' + attribute + '_rating').value = value;
}

// Family members on personal profile pane
(function($j){

  $j.fn.family_members = function(){
    //add or remove family members
    this.click(function(ev){
      var el = ev.target;
      if($j(el).hasClass('add_fm')){
        add_row();
      }
      else if(el.className == 'rm_fm'){
        el.parentNode.remove();
      }
      return false;
    });

    //pluralize or singularize strings on selects on count change
    this.change(function(ev){
      var el = ev.target;
      if(el.className == 'fm_fc'){
        check_selects(el);
      }
    });

    //initial selects text
    this.find('div .fm_fc').each(function(){
      check_selects(this);
    });
  }

  function remove_blanks(form){
    $j(form).submit(function(){
      var divs = $j('#fm_options').children('div');
      divs.each(function(){
        if($j($j(this).children('select:first')[0]).val() == '' ||
           $j($j(this).children('select:last')[0]).val() == '' ){
           this.remove();
        }
      });

    });
  }

  function add_row(){
    var html = "<div>";
 html += '<select name="family_member[][ftype]" class="fm_ft">' +
            '<option value=""></option>' +
            '<option value="Son">Son</option>' +
            '<option value="Daughter">Daughter</option>' +
            '<option value="Niece">Niece</option>' +
            '<option value="Nephew">Nephew</option>' +
            '<option value="Godson">Godson</option>' +
            '<option value="Goddaughter">Goddaughter</option>' +
            '</select>';
 html += '<select name="family_member[][count]" class="fm_fc">' +
            '<option value=""></option><option value="1">1</option>' +
            '<option value="2">2</option><option value="3">3</option>' +
            '<option value="4">4</option><option value="5">5</option>' +
            '<option value="6">6</option><option value="7">7</option>' +
            '<option value="8">8</option><option value="9">9</option>' +
            '<option value="10">10</option></select>';



    html += '&nbsp;&nbsp;<a href="#" class="rm_fm">Remove</a>';
    html += '</div>';

    $j('#fm_options').append(html);
  }

  function check_selects(el){
    var val = $j(el).val();
    var options = $j(el).siblings('select:last')[0].options

    if(val == ''){
      options[0].selected = 'selected';
    }
    else if(parseInt(val) > 1){
      $j(options).each(function(){
        if(this.innerHTML != ''){
          this.innerHTML = plural(this.innerHTML);
        }
      });
    }
    else if(val == '1'){
      $j(options).each(function(){
        if(this.innerHTML != ''){
          this.innerHTML = singular(this.innerHTML);
        }
      });
    }
  }

})(jQuery);

// Pets on personal profile pane
(function($j){

  $j.fn.pets = function(){
    //add or remove family members
    this.click(function(ev){
      var el = ev.target;
      if($j(el).hasClass('add_pt')){
        add_row();
      }
      else if(el.className == 'rm_pt'){
        el.parentNode.remove();
      }
      return false;
    });

    //pluralize or singularize strings on selects on count change
    this.change(function(ev){
      var el = ev.target;
      if(el.className == 'pt_fc'){
        check_selects(el);
      }
    });

    //initial selects text
    this.find('div .pt_fc').each(function(){
      check_selects(this);
    });
  }

  function remove_blanks(form){
    $j(form).submit(function(){
      var divs = $j('#pt_options').children('div');
      divs.each(function(){
        if($j($j(this).children('select:first')[0]).val() == '' ||
           $j($j(this).children('select:last')[0]).val() == '' ){
           this.remove();
        }
      });
    });
  }

  function add_row(){
    var html = "<div>";
    html += '<select name="pet[][ftype]" class="pt_ft">' +
            '<option value""></option>' +
            '<option value="Bird">Bird</option>' +
            '<option value="Cat">Cat</option>' +
            '<option value="Dog">Dog</option>' +
            '<option value="Ferret">Ferret</option>' +
            '<option value="Fish">Fish</option>' +
            '<option value="Frog">Frog</option>' +
            '<option value="Gecko">Gecko</option>' +
            '<option value="Guinea Pig">Guinea Pig</option>' +
            '<option value="Hamster">Hamster</option>' +
            '<option value="Horse">Horse</option>' +
            '<option value="Iguana">Iguana</option>' +
            '<option value="Lizard">Lizard</option>' +
            '<option value="Mouse">Mouse</option>' +
            '<option value="Rabbit">Rabbit</option>' +
            '<option value="Rat">Rat</option>' +
            '<option value="Snake">Snake</option>' +
            '<option value="Turtle">Turtle</option>' +
            '</select>';
 html += '<select name="pet[][count]" class="pt_fc">' +
            '<option value=""></option><option value="1">1</option>' +
            '<option value="2">2</option><option value="3">3</option>' +
            '<option value="4">4</option><option value="5">5</option>' +
            '<option value="6">6</option><option value="7">7</option>' +
            '<option value="8">8</option><option value="9">9</option>' +
            '<option value="10">10</option></select>';

    html += '&nbsp;&nbsp;<a href="#" class="rm_pt">Remove</a>';
    html += '</div>';

    $j('#pt_options').append(html);
  }

  function check_selects(el){
    var val = $j(el).val();
    var options = $j(el).siblings('select:last')[0].options

    if(val == ''){
      options[0].selected = 'selected';
    }
    else if(parseInt(val) > 1){
      $j(options).each(function(){
        if(this.innerHTML != ''){
          this.innerHTML = plural(this.innerHTML);
        }
      });
    }
    else if(val == '1'){
      $j(options).each(function(){
        if(this.innerHTML != ''){
          this.innerHTML = singular(this.innerHTML);
        }
      });
    }
  }

})(jQuery);

/****************************************************************************/
/* http://www.thinksharp.org/javascript-rails-like-pluralize-function */

Inflector = {
    /*
     * The order of all these lists has been reversed from the way
     * ActiveSupport had them to keep the correct priority.
     */
    Inflections: {
        plural: [
            [/(quiz)$/i,               "$1zes"  ],
            [/^(ox)$/i,                "$1en"   ],
            [/([m|l])ouse$/i,          "$1ice"  ],
            [/(matr|vert|ind)ix|ex$/i, "$1ices" ],
            [/(x|ch|ss|sh)$/i,         "$1es"   ],
            [/([^aeiouy]|qu)y$/i,      "$1ies"  ],
            [/(hive)$/i,               "$1s"    ],
            [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
            [/sis$/i,                  "ses"    ],
            [/([ti])um$/i,             "$1a"    ],
            [/(buffal|tomat)o$/i,      "$1oes"  ],
            [/(bu)s$/i,                "$1ses"  ],
            [/(alias|status)$/i,       "$1es"   ],
            [/(octop|vir)us$/i,        "$1i"    ],
            [/(ax|test)is$/i,          "$1es"   ],
            [/s$/i,                    "s"      ],
            [/$/,                      "s"      ]
        ],
        singular: [
            [/(quiz)zes$/i,                                                    "$1"     ],
            [/(matr)ices$/i,                                                   "$1ix"   ],
            [/(vert|ind)ices$/i,                                               "$1ex"   ],
            [/^(ox)en/i,                                                       "$1"     ],
            [/(alias|status)es$/i,                                             "$1"     ],
            [/(octop|vir)i$/i,                                                 "$1us"   ],
            [/(cris|ax|test)es$/i,                                             "$1is"   ],
            [/(shoe)s$/i,                                                      "$1"     ],
            [/(o)es$/i,                                                        "$1"     ],
            [/(bus)es$/i,                                                      "$1"     ],
            [/([m|l])ice$/i,                                                   "$1ouse" ],
            [/(x|ch|ss|sh)es$/i,                                               "$1"     ],
            [/(m)ovies$/i,                                                     "$1ovie" ],
            [/(s)eries$/i,                                                     "$1eries"],
            [/([^aeiouy]|qu)ies$/i,                                            "$1y"    ],
            [/([lr])ves$/i,                                                    "$1f"    ],
            [/(tive)s$/i,                                                      "$1"     ],
            [/(hive)s$/i,                                                      "$1"     ],
            [/([^f])ves$/i,                                                    "$1fe"   ],
            [/(^analy)ses$/i,                                                  "$1sis"  ],
            [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis"],
            [/([ti])a$/i,                                                      "$1um"   ],
            [/(n)ews$/i,                                                       "$1ews"  ],
            [/s$/i,                                                            ""       ]
        ],
        irregular: [
            ['move',   'moves'   ],
            ['sex',    'sexes'   ],
            ['child',  'children'],
            ['man',    'men'     ],
            ['person', 'people'  ]
        ],
        uncountable: [
            "sheep",
            "fish",
            "series",
            "species",
            "money",
            "rice",
            "information",
            "equipment"
        ]
    },
    ordinalize: function(number) {
        if (11 <= parseInt(number) % 100 && parseInt(number) % 100 <= 13) {
            return number + "th";
        } else {
            switch (parseInt(number) % 10) {
                case  1: return number + "st";
                case  2: return number + "nd";
                case  3: return number + "rd";
                default: return number + "th";
            }
        }
    },
    pluralize: function(word) {
        for (var i = 0; i < Inflector.Inflections.uncountable.length; i++) {
            var uncountable = Inflector.Inflections.uncountable[i];
            if (word.toLowerCase() == uncountable) {
                return uncountable;
            }
        }
        for (var i = 0; i < Inflector.Inflections.irregular.length; i++) {
            var singular = Inflector.Inflections.irregular[i][0];
            var plural   = Inflector.Inflections.irregular[i][1];
            if ((word.toLowerCase() == singular) || (word == plural)) {
                return plural;
            }
        }
        for (var i = 0; i < Inflector.Inflections.plural.length; i++) {
            var regex          = Inflector.Inflections.plural[i][0];
            var replace_string = Inflector.Inflections.plural[i][1];
            if (regex.test(word)) {
                return word.replace(regex, replace_string);
            }
        }
    },
    singularize: function(word) {
        for (var i = 0; i < Inflector.Inflections.uncountable.length; i++) {
            var uncountable = Inflector.Inflections.uncountable[i];
            if (word.toLowerCase() == uncountable) {
                return uncountable;
            }
        }
        for (var i = 0; i < Inflector.Inflections.irregular.length; i++) {
            var singular = Inflector.Inflections.irregular[i][0];
            var plural   = Inflector.Inflections.irregular[i][1];
            if ((word.toLowerCase() == singular) || (word == plural)) {
                return plural;
            }
        }
        for (var i = 0; i < Inflector.Inflections.singular.length; i++) {
            var regex          = Inflector.Inflections.singular[i][0];
            var replace_string = Inflector.Inflections.singular[i][1];
            if (regex.test(word)) {
                return word.replace(regex, replace_string);
            }
        }
    }
}

function ordinalize(number) {
    return Inflector.ordinalize(number);
}

/*
 * Javascript doesn't have optional parameters or overloading so I had to use
 * the ever popular pseudo options hash object technique.
 * required properties:
 *     count    Number of objects to pluralize
 *     singular Singular noun for the objects
 * optional property:
 *     plural   Plural (probably irregular) noun for the objects
 * examples:
 *      pluralize({ count: total_count, singular: "Issue" })
 *      pluralize({ count: total_count, singular: "Goose", plural: "Geese" })
 */
function pluralize(options) {
    return options.count + " " + (1 == parseInt(options.count) ?
        options.singular :
        options.plural || Inflector.pluralize(options.singular));
}

function plural(word){
  var ret = Inflector.pluralize(word);
  return (ret != undefined) ? ret : word
}

function singular(word){
  var ret = Inflector.singularize(word);
  return (ret != undefined) ? ret : word
}

/*
 * Function to upload a file in an pseudo-ajaxy-way.
 *
 * Requires a single parameter: the form DOM element containing
 * the file to be uploaded.  The form is submitted to the dynamic
 * iframe, so set the target script in the form element.
 */
function uploadFile(el_form, upload_progress, kind) {
    var f = $j(el_form);
	var iframe_name = 'iframe' + parseInt(new Date().getTime().toString().substring(0, 10));
    //Generate id for upload progress tracking
    var uuid = "";
    for (i = 0; i < 32; i++){
        uuid += Math.floor(Math.random() * 16).toString(16);
    }
    //Attach the id in the form
    f.attr("action", f.attr("action") + "?X-Progress-ID=" + uuid +"&kind="+ kind);
    var container = ''
    if($j('#container').size() > 0){
        container = $j('#container');
    }else{
        container = $j('#audio_container')
    }
	container.append('<iframe id="' + iframe_name + '" name="' + iframe_name + '" style="height: 0; width: 0; display: none;" />');

	f[0].target = iframe_name;
    f[0].submit();

   if(upload_progress){
		$j('#media_uploader .title:first').html('Uploading');
        $j('#reply_image_form').hide();
        //$j('#cta_image').hide();
        $j('#progress_bar_pane').show();
        timer = window.setInterval(function(){update_progres_bar(uuid, kind)}, 2000);
    } else {
        $j('#facebox .content:first').load('/shared/display_upload_progress?kind='+ kind);
    }

}
// Atention: don't hack this, to disable the uploadprogress go to environment.rb
// UPLOAD_PROGRESS = false to bypass this functionality, revert back to true for the server
//
function update_progres_bar(uuid, kind){
	//	uncomment out following lines for dev
//	 window.clearTimeout(timer);
//	 $j('#facebox .content:first').load('/shared/display_upload_progress?kind='+ kind);
//	 return;

	$j.ajax({
		type: 'GET',
		url: '/progress',
		dataType: 'json',
		beforeSend: function(xhr) {
			xhr.setRequestHeader("X-Progress-ID", uuid);
		},
		success: function(upload) {
			// change the width in the inner progressbar
			if (upload.state == 'uploading') {
				bar = $j('#upload_bar');
				w = Math.floor((upload.received / upload.size)*100);
				bar.width(w + '%');
			}
			//done, stop the interval
			if (upload.state == 'done') {
				window.clearTimeout(timer);
                //$j.facebox.close();
                $j('#facebox .content:first').load('/shared/display_upload_progress?kind='+ kind);
			}
		}
	});
}

/****************************************************************************/


function check_kudos_pane(){
  if($j('#kudos_pane').length == 0){
    $j('#sidebar').append('<div id="kudos_pane"></div>')
  }
}

function exec_link(l) {
    $j(l).trigger("click");
}

var in_nav_menu = true;

function change_sub_nav(new_nav) {
    $j("#sub_nav_div > ul").css("display", "none");
    if (new_nav) {
        $j(new_nav).show();
    }
    in_nav_menu = true;
}

function set_current_nav(event, controller) {
    if (in_nav_menu && (event.pageY < 176 || event.pageY > 216)) {
        change_sub_nav("#"+ controller +"_sub_nav");
        in_nav_menu = false;
    }
}

function ta_ml(el, len) {
	if (el.value.length > len) {
		el.value = el.value.substring(0, len);
	}
}

function update_facebox(href) {
	$j('#facebox').css('visibility', 'hidden');
	$j('#facebox .content:first').html(' ');
	$j('#facebox .content:first').load(href, function() {
		setTimeout("$j('#facebox').css('left', $j.facebox.getLeft()); $j('#facebox').css('top', $j.facebox.getTop()); $j('#facebox').css('visibility', 'visible');", 500);
	});
}

function create_flash_media(){
    var regex = /(\d+)_(audio|video)_/;

    $j('.vplayer').each(function(){
      var el = $j(this);
      var insertion_point = el.parents('.video_thumbnail').get(0);
      var media_file = el.attr('id');
      var meta = regex.exec(media_file);
      var media_type = meta[2];
      var time = calc_flash_time(meta[1]);

      insert_flash(insertion_point, media_type, media_file, time);
    });
}
function insert_flash(el, media_type, media, time){
    var so = new SWFObject("/swfs/player_"+ media_type +".swf", "player_video", "338", "278", "8", "#000000");
    so.addParam("allowScriptAccess", "always");
    so.addParam("quality", "high");
    so.addParam("scale", "noscale");
    so.addParam("salign", "tl");
    so.addParam("menu", "true");
    so.addParam("wmode", "transparent");
    so.addParam("swLiveConnect", "true");
    so.addVariable("autoplay", "false");
    so.addVariable("ptf", media);
    so.addVariable("lM", time[0]);
    so.addVariable("lS", time[1]);
    so.write(el);
}

function calc_flash_time(time){
    //Use string to not mess with float math
    var ntime = ((time / 60) + '').split('.');
    minutes = ntime[0];
    //add the floating point to get the seconds
    seconds = ('.' + ntime[1]) * 60;

    return [minutes, seconds];
}
//	check to see if all the elements of a submitted array are checked (to update parent check all elements)
//	p = array of parent check all element ids
//	c = array of child elements to check
function check_2_check_all(p, c) {
	var all_checked = true
	for (i = 0; i < c.length; i++) {
		if (!$(c[i]).checked) {
			all_checked = false;
			break;
		}
	}
	for (i = 0; i < p.length; i++) {
		$(p[i]).checked = all_checked;
	}
}
function process_check_all(c, d) {
	for (i = 0; i < c.length; i++) {
		$(c[i]).checked = d;
	}
}
function change_trove_picture(az_link, reset_image) {
  var url = 'url=' + az_link + '&reset=' + (reset_image ? 'true' : 'false')
	$j.ajax({dataType:'script', url: '/people/trove_load_default_pic', data: url});
}

/* Believer of, box */
function toggle_extra_info(cause_id, type) {
  var t1 = (type == 'patrons') ? 'patrons' : 'believers';
  var t2 = (type == 'patrons') ? 'patron_cat' : 'cause';
    $j('#' + t2 + '_'+ cause_id +" > .extra-info").toggle();
    $j('#view_all_' + t1 + '_'+ cause_id).toggle();
    $j('#close_' + t1 + '_' + cause_id).toggle();
}

function view_all_believers(cause_id) {

    toggle_extra_info(cause_id);

}

function close_believers(cause_id) {
    $j("#close_believers_"+ cause_id).hide();
    toggle_extra_info(cause_id);
    $j("#view_all_believers_"+ cause_id).show();
}

function verify_passion_submission(f) {
	$j('#' + f + '_error').html('');
	var error = false;

	if ($(f).elements['conversation_reply[content]'].value == '') {
		error = 'Enter a message in the field before submitting.';
		$(f).elements['conversation_reply[content]'].focus();
	}
	if (error) {
		last_submitted_form = '';
		$j('#' + f + '_error').html(error);
		return false;
	}
	$(f).submit();
}

function show_uk_in_common() {
	var c;
	for (i = 0; i < uk_terms.length; i++) {
		c = uk_terms_in_common.in_array(uk_terms[i]) ? 'text' : 'hide'
		$j('#uk_term_' + uk_terms[i]).addClass(c);
	}
	$j('#uk_in_common_stamp .connections').show();
}
function hide_uk_in_common() {
	var c;
	for (i = 0; i < uk_terms.length; i++) {
		c = uk_terms_in_common.in_array(uk_terms[i]) ? 'text' : 'hide'
		$j('#uk_term_' + uk_terms[i]).removeClass(c);
	}
	$j('#uk_in_common_stamp .connections').hide();
}

function toggle_advanced(that) {
  $j("#search_fields").slideToggle();
  var el = $j('#advanced_search');
  if (el.val() == 'true') {
    el.val('false');
    $j(that).html('advanced search');
  } else {
    el.val('true');
    $j(that).html('basic search');
  }
  return false;
}

// This is essentially the same as above, with text chagnes and handle of 'GO' button
function toggle_advanced_new(that) {
  $j("#search_fields").slideToggle();
  var el = $j('#advanced_search');
  if (el.val() == 'true') {
    el.val('false');
    $j(that).html('try advanced search');
    $('go_button_first').show();
  } else {
    el.val('true');
    $j(that).html('try basic search');
    $('go_button_first').hide();
  }
  return false;
}

var current_uk_tag_id;
var uk_rollover_interval;
var uk_tag_options_link;
var uk_hide_owner_box_interval;

function uk_rollover(link, tag_id, tag_name) {
	uk_tag_options_link = link;
	eval("uk_rollover_interval = setTimeout(\"uk_show_tag_options(uk_tag_options_link, '" + tag_id + "', '" + tag_name + "')\", 2000);");
}
function uk_rolloff() {
	clearInterval(uk_rollover_interval);
}
function uk_show_tag_options(link, tag_id, tag_name) {
	link.focus();
	link.blur();
	uk_rolloff();
	current_uk_tag_id = tag_id;
	var o_box = '#uniquity_key .cloud #owner_link_action'
	var width = $j('#' + link.id).width();
	var height = $j('#' + link.id).height();
	var line_height = $j('#' + link.id).css('line-height');
	line_height = line_height.split('px');
	line_height = parseInt(line_height[0]) + 5;
	var pos = $(link.id).positionedOffset();
	var tagged = reload_with_tagged ? '?tag=' + reload_with_tagged : '';
	if (reload_with_tagged) {
		if (reload_with_tagged == tag_id) {
			$j(o_box).addClass('single');
		}
		else {
			$j(o_box).removeClass('single');
		}
	}
	$j(o_box).css('top', pos.top - 50 + 'px');

	var left = (height > line_height)  ? pos.left - 65 : pos.left + (width / 2) - 75;
	$j(o_box).css('left', left);
	$j(o_box + ' .learn a').attr('href', link.href);
	$j(o_box + ' .remove a').attr('href', '/tags/remove_uk_tag_modal/' + tag_id + tagged);
	$j(o_box).show();
}
function uk_hide_owner_box(id) {
	if (id != current_uk_tag_id) {
		uk_hide_owner_box_interval = setTimeout("$j('#uniquity_key .cloud #owner_link_action').hide();", 100);
	}
	else {
		clearInterval(uk_hide_owner_box_interval);
	}
}

/**
 * Radio Sidebar
 **/
function launchRadio(episode_number, type) {
   type = typeof(type) != 'undefined' ? type : '';
   window.open("/radio/episode/"+ episode_number + '/' + type, "re_radio", "width=850,height=610,status=0,resizable=0,menubar=0,scrollbars=0,titlebar=0,toolbar=0,location=0");
}
function resizeRadio(size) {
	var width = (size == 'maximize') ? 850 : 400;
	window('re_radio').resizeTo(width);
}

function toggle_view(toshow,tohide) {
  $j(tohide).hide();
  $j(toshow).show();
}


var current_patron_id;
var patron_rollover_interval;
var patron_tag_options_link;
var patron_hide_owner_box_interval;

function patron_rollover(link, user_id, review_id) {
	patron_tag_options_link = link;
	eval("patron_rollover_interval = setTimeout(\"patron_show_tag_options(patron_tag_options_link, '" + user_id + "', '" + review_id + "')\", 2000);");
}
function patron_rolloff() {
	clearInterval(patron_rollover_interval);
}
function sortable_hover(obj) {
	obj.addClass('hover');
	var o_box = '#owner_link_action';
	var pos = $(obj.parent().attr('id')).positionedOffset();
	$j(o_box).css('left', pos.left - 86);
	$j(o_box).css('top', pos.top - 49);
	$j(o_box).show();
}
function sortable_rolloff(obj) {
	clearTimeout(sortable_hover_interval);
	sortable_hover_object = '';
	$j('#owner_link_action').hide();
	obj.removeClass('hover');
}
function sortable_active(obj) {
	clearTimeout(sortable_hover_interval);
	$j('#owner_link_action').hide();
	$j('#sortable .sort .handle').removeClass('hover');
	$j('#sortable .sort .handle').removeClass('active');
	obj.addClass('active');
}
function sortable_inactive() {
	$j('#sortable .sort .handle').removeClass('active');
}
function patron_show_tag_options(link, user_id, review_id) {
	link.focus();
	link.blur();
	patron_rolloff();
	current_patron_id = user_id;
	var o_box = '#owner_link_action'
	var width = $j('#' + link.id).width();
	var height = $j('#' + link.id).height();
	var line_height = 45;
	var pos = $(link.id).positionedOffset();

	if (!review_id || review_id == 'false') {
		$j(o_box).addClass('single');
	}
	else {
		$j(o_box).removeClass('single');
	}

	$j(o_box).css('top', pos.top - 80 + 'px');

	var left = (height > line_height)  ? pos.left - 65 : pos.left + (width / 2) - 75;
	$j(o_box).css('left', left);
	$j(o_box + ' .user a').attr('href', link.href);
	$j(o_box + ' .review a').attr('href', '?selected_review=' + review_id);
	$j(o_box).show();
}
function patron_hide_owner_box(id) {
	if (id != current_patron_id) {
		patron_hide_owner_box_interval = setTimeout("$j('#owner_link_action').hide();", 100);
	}
	else {
		clearInterval(patron_hide_owner_box_interval);
	}
}

function toggle_publish_review(business,rev) {
  new Ajax.Request('/pursuits/toggle_review/'+business+'?review='+rev, {asynchronous:false});
  var review = $j('#review-'+rev);
  var featured = $j('#featured-'+rev);
  if ((!review.attr('checked')) && (featured.attr('checked'))) {
    new Ajax.Request('/pursuits/featured_review/'+business+'?review='+rev, {asynchronous:false});
    featured.attr('checked',false);
  }
  return false;
}
function toggle_featured_review(business,rev) {
  new Ajax.Request('/pursuits/featured_review/'+business+'?review='+rev, {asynchronous:false});
  var review = $j('#review-'+rev);
  var featured = $j('#featured-'+rev);
  if (!review.attr('checked')) {
    new Ajax.Request('/pursuits/toggle_review/'+business+'?review='+rev, {asynchronous:false});
    review.attr('checked',true);
  }
  return false;
}

function load_invite_uk_player() {
  var so = new SWFObject('/swfs/invite_tag_cloud.swf', 'radio', '170', '92', '9', '#000000');
  so.addParam('allowScriptAccess', 'always');
  so.addParam('quality', 'high');
  so.addParam('scale', 'noscale');
  so.addParam('salign', 'tl');
  so.addParam('menu', 'true');
  so.addParam('wmode', 'transparent');
  so.addParam('swLiveConnect', 'true');
  so.addVariable('autoplay', 'false');
  so.write('invite_uk_player');
}
function date_select_prompt(prompt, class_name) {
	class_name = typeof(class_name) != 'undefined' ? class_name : 'date_select';
	$j('.' + class_name).addOption('', prompt);
}

function episode_segment_mail(this_var){
  var el = $j(this_var);
  el.parents('form').trigger('onsubmit');
}
function resend_invite_email(id) {
	$j('#user_' + id + '_invite_status').html('<span class="resent">Sending...</span>');
}
function toggle_state(val) {
	if (val == 'United States') {
		$j('input.state').hide();
		$j('input.state').val('');
		$j('input.state').attr('disabled', true);
		$j('select.state').show();
		$j('select.state').attr('disabled', false);
	}
	else {
		$j('select.state').hide();
		$j('select.state').attr('selectedIndex', 0);
		$j('select.state').attr('disabled', true);
		$j('input.state').show();
		$j('input.state').attr('disabled', false);
	}
}
function add_candystripe() {
	$j('a[rel*=candystripe_narrow]').each(function(){
		$j(this).attr('rel', 'stripe_activated');

		var h = $j(this).parent().html();
		var l = $j(this).parent().find('a:first').html();
		$j(this).parent().html(h + '<div class="candystripe" style="display: none;"><div><img src="/images/candystripe_narrow.gif" /></div></div><div class="candy-hide" style="display: none;"><a href="#" onclick="return false;">' + l + '</a></div>');
	});
	$j('a[rel*=candystripe]').each(function(){
		$j(this).attr('rel', 'stripe_activated');

		var h = $j(this).parent().html();
		var l = $j(this).parent().find('a:first').html();
		$j(this).parent().html(h + '<div class="candystripe" style="display: none;"><div><img src="/images/candystripe.gif" /></div></div><div class="candy-hide" style="display: none;"><a href="#" onclick="return false;">' + l + '</a></div>');
//		$j(this).parent().html(h);
	});
}
function show_candystripe(t) {
	var e = $j(t).hide();
  var p = e.parent();
	p.find('.candy-hide').show();
	p.find('.candystripe').show();
	bypass_verification = true;
}
function hide_candystripe(t) {
	t.hide();
  var p = t.parent();
	p.find('.candy-hide').hide();
	p.find('a').removeClass('hide-me');
	p.find('a').show();
	bypass_verification = false;
}
function wallpaper_change(w) {
	var sel = $j('#wallpaper_selector .option .' + w);
	$j('#wallpaper_selector .option a').blur();
	$j('#wallpaper_selector .option').removeClass('selected');
	sel.parent().addClass('selected');
	$j('body').attr('class', 'b' + sel.attr('class'));
	$j.post('/account/update_wallpaper', { wallpaper : w });
}
jQuery.preloadImages = function() {
	var img;
	for(var i = 0; i<arguments.length; i++) {
		$j(document.createElement('img')).bind('load', function() {
			this.src = arguments[i];
		}).trigger('load');
//		img = $j("<img />").attr("src", arguments[i]).load(function(){
//			alert(img.width());
//		});
	}
}

function remove_unread(msgid) {
  $j(msgid + ' td:first').html('');
}

window.onbeforeunload = function() {
        /* Temporarily disable */
/**/
	var has_changes = false;
	var from;	//	for dev
	if (!bypass_verification) {	//	if a form was submitted, don't check for changes
		$j('[rel*=verify]').each(function() {
			if (!has_changes) {
				var t = $j(this).attr('type');
				if (t == 'checkbox' || t == 'radio') {
					if ($j(this).is(':checked') != $j(this).get(0).defaultChecked) {
						has_changes = true;
						from = 'box';
					}
				}
				else if ($j(this).is('select')) {
					if (!$j(this).find('option[selected]').get(0).defaultSelected) {
						for(i = 0, has_default = false; i < $j(this).find('option').size(); i++) {
							if ($j(this).find('option').get(i).defaultSelected) {
								has_default = true;
							}
						}

						if (has_default || $j(this).attr('selectedIndex') > 0) {
							has_changes = true;
							from = 'sel';
						}
					}
				}
				else if (t == 'text' || $j(this).is('textarea')) {
					var def_value = $j(this).attr('defaultValue');
					def_value = typeof(def_value) == 'undefined' ? '' : def_value;
					if ($j(this).hasClass('hinted') && def_value == '') {
						def_value = $j(this).attr('title');
					}
					if ($j(this).val() != def_value) {
						has_changes = true;
						from = 'text';
					}
				}
			}
		});
	}
	if (has_changes) {
		return 'You have added content on this page without submitting it.'
	}
/**/
}

$j.fn.re_button = function (label, link, submit) {
  var t = $j("<div id='"+label+"' class='re_button'> \
      <div class='left_carrot carrot'>&nbsp;</div> \
      <div id='to_replace' /> \
      <div class='right_carrot carrot'>&nbsp;</div> \
      <div class='nocount' style='clear:both;height:1px:width:0px' /> \
    </div>");
  var lnk = $j(link).clone();
  // AlfterAjax event execute before us :(
  if (lnk.attr('rel') == 'fb_activated') {
    lnk.attr('rel', 'facebox')
  };
  t.find('#to_replace').replaceWith(lnk.show());
  t.appendTo($j(this));
  var totwidth=0;
  $j(this).find('.re_button').children().not('.nocount').each( function(x,e) {
      totwidth += $j(e).outerWidth();
  });
  $j(this).css('width',totwidth+1);
  activateFacebox('#'+label);
}


$j.fn.overlay = function(label) {
  //var d = $j('#overlay').clone().attr('id',label).appendTo($j('body'));
  //var pos = $j(this).offset();
  //$j(label).css({'left':(pos.left)+'px', 'top':pos.top + 'px'}).show();
  $j(label).show();
}


/* support functions for curatsus */

function cancel_comment(oid) {
  $j('#curatsu-comment-buttons-'+oid).slideToggle();
  var area = $j('#curatsu-comment-form-'+oid+' textarea');
  area.animate({height: "16px"});
  area.queue(function() {
    area.remove();
    $j('<p class="leyend">Contribute your comment... <i>respondez vous, ici!</i></p>').prependTo('#curatsu-comment-form-'+oid+' form');
    $j(this).dequeue();
  });
  setTimeout('bind_leyend('+oid+')', 1000);
}

function bind_leyend(oid) {
  $j('#curatsu-comment-form-'+oid+' p.leyend').click(function() {
    $j(this).remove();
    var area = $j('<textarea name="comment"></textarea>');
    area.prependTo($j('#curatsu-comment-form-'+oid+' form')).animate({height: "30px"});
    $j('#curatsu-comment-buttons-'+oid).slideToggle();
    setTimeout('setareafocus('+oid+')',500);
  });
}

function bind_tags_leyend(oid) {
  $j('#curatsu-tag-form-inner-'+oid+' p.leyend').click(function() {
    $j(this).hide();
    $j('#curatsu-tag-form-inner-'+oid+' .fblist').show().animate({height: "30px"}, function() {$j(this).css('height','');});
    $j('#curatsu-tag-buttons-'+oid).slideDown();
    $j('#curatsu-tag-form-inner-'+oid+' .fblist .maininput').focus();
  });
}

function setareafocus(oid) {
  $j('#curatsu-comment-form-'+oid+' form textarea').focus();
}

function submit_comment(that) {
  show_candystripe(that);
  $j(that).parents('form').trigger('onsubmit');
  return false;
}

function submit_tags(that, id) {
  eval('fblist_for_curatsu_tag_'+id+'.manualAdd();');
  show_candystripe(that);
  $j(that).parents('form').trigger('onsubmit');
  return false;
}

function cancel_tag(id) {
  $j('#curatsu-tag-buttons-'+id).slideUp();
  var fb = $j('#curatsu-tag-form-inner-'+id+' .fblist');
  $j('#curatsu-tag-form-inner-'+id+' .fblist .bit-box').remove();
  eval('fblist_for_curatsu_tag_'+id+'.reset();');
  fb.filter('.bit-box').remove();
  fb.queue( function() {
    fb.hide();
    $j('#curatsu-tag-form-inner-'+id+' p.leyend').show();
    $j(this).dequeue();
  });
  $j('#curatsu-tag-inner-'+id).slideUp();
  $j('#ci-tag-link-'+id).show();
}

function show_tag(id) {
  $j('#curatsu-tag-inner-'+id).slideDown();
  $j('#curatsu-tag-form-inner-'+id+' p.leyend').show();
  $j('#curatsu-tag-form-inner-'+id+' .fblist').hide();
  $j('#curatsu-tag-buttons-'+id).slideDown();
  $j('#curatsu-tag-form-inner-'+id+' .fblist .maininput').focus();
  $j('#ci-tag-link-'+id).hide();
}

function toggle_curatsu_blurb(id) {
  var cc = $j('#mnn_nav #curatsu-blurb-'+id);
  cc.find('a').toggleClass('selected');
  cc.find('a span').toggleClass('selected');
}

function remove_comment(sele) {
  var comm = $j(sele);
  var hr = comm.next();
  if (hr.length > 0) {
    if (hr.hasClass('single_hr')) {
      hr.remove();
    }
  } else {
    hr = comm.prev();
    if (hr.hasClass('single_hr')) {
      hr.remove();
    }
  }
  comm.remove();
}

function toggleResonate(that, kind, oid) {
  var p = $j(that).parent();
  p.find('a').hide();
  p.find('img').show();
  jQuery.get('/curatsu/toggle_resonate', {kind: kind, oid: oid}, function(data) {
    $j("#curatsu-inline-resonates-"+oid).html(data);
  });
}

function cdpStyleAdjustments(type) {
  $j('#curatsu-inline-resonates').css("margin-top", "-11px");
  $j('#curatsu-comment-count').css("margin", "-11px 0 -5px 0");
  $j('#resonate-bottom').css("height", "5px");
  $j('#ci-tag-bottom').css("height", "5px");
  if (type == 'Gallery') {
    $j('ul.holder').css("width", "542px");
    $j('#fblist_options').addClass("gcdplist");
  } else {
    $j('ul.holder').css("width", "506px");
    $j('#fblist_options').addClass("cdplist");
  }
}

function curatsu_blurb_counter(id, cnt) {
  $j('#curatsu-blurb-'+id+' a span').text(cnt === 0 ? '+' : cnt);
  $j('#notice_'+id+' #curatsu-blurb-'+id+' a span').text(cnt === 0 ? '+' : cnt);
  $j('#gallery_large #curatsu-blurb-'+id+' a span').text(cnt === 0 ? '+' : cnt);
  $j('#rs-curatsu-blurb-'+id+' a span').text(cnt === 0 ? '+' : cnt);
}

function curatsu_comment_counter(id, cnt) {
  var v = $j('#curatsu-comment-count-'+id);
  if (cnt === 0) {
    v.text('COMMENTS:');
  } else if (cnt === 1) {
    v.text('1 COMMENT:');
  } else {
    v.text(cnt+' COMMENTS:');
  }
}

function person_contextual_show(link, page, id) {
	$j('.person_contextual').hide();
	var pos = $(link.id).positionedOffset();
	var right = pos.left + 38;
	$j('#pc-' + page + '-' + id).css('top', pos.top - 125 + 'px');
	$j('#pc-' + page + '-' + id).show();
	var width = $j('#pc-' + page + '-' + id).width();
	$j('#pc-' + page + '-' + id).css('left', right - width + 'px');
}
function activate_person_contextual() {
	$j('.person_contextual').mouseover(function() {
		clearTimeout(clear_person_contextual);
	});
	$j('.person_contextual').mouseout(function() {
		clear_person_contextual = setTimeout("$j('.person_contextual').hide();", 100);
	});
}
function public_rollover() {
	$j('a[rel*=public-rollover]').each(function(){
		$j(this).mouseover(function(){
			var id = $j(this).attr('id');
			var pos = $(id).positionedOffset();
			var w = $j(this).width();
			var l = pos.left + w/2 - 93;
			$j('#public-rollover').css('top', pos.top - 134 + 'px');
			$j('#public-rollover').css('left', l + 'px');
			$j('#public-rollover td').html($j('#' + id + '-text').html());
			$j('#public-rollover').show();
		});
		$j(this).mouseout(function(){
			$j('#public-rollover').hide();
		});
	});
}
function load_account_settings(tab) {
	$j.get('/account/settings', {load: tab}, function(data) {
		$j('#account_settings #details .show').html(data);
		$j('#account_settings #tabbed_nav li').removeClass('ui-tabs-selected');
		$j('#account_settings #tabbed_nav li#tab-' + tab).addClass('ui-tabs-selected');
		activateFacebox();
		add_candystripe();
	});
}

var _facebook_list_min_chars = 4;

function toggle_status_view(show) {
	switch(show) {
		case 'mini':
			hide = 'full';
			status_class = 'double_line';
			break;
		case 'full':
			hide = 'mini';
			status_class = 'full';
			break;
	}
	hide = (show == 'mini') ? 'full' : 'mini';
	$j('.' + hide + '-status').addClass('hidden');
	$j('.' + show + '-status').removeClass('hidden');
	$j('#profile_status_bar').attr('class', status_class);
}

function toggle_truncate(ele) {
  var e = $j(ele);
  e.find('.full-status').toggleClass('hidden');
  e.find('.mini-status').toggleClass('hidden');
  return false;
}

function toggle_status_chars(is_checked) {
	$j('#chars-remaining').attr('class', is_checked ? '' : 'invisible');
	change_status_submit();
}
function change_status_submit() {
	var button = $j('#form_edit_profile_status #submit_button');
	if ($j('#twitter_share').is(':checked') && bitly_string($j('#profile_status').val()).length > 140) {
		if (!button.hasClass('submit-disabled')) {
			button.addClass('submit-disabled');
		}
	}
	else {
		button.removeClass('submit-disabled');
	}
}

function bitlyPopupIn(evt) {
  var o = $j(evt.target);
  var pos = o.position();
  if (o.parents('#sidebar').length > 0) {
    var p = o.find('.bitly-popup-right');
    p.css({left: pos.left-p.width()+110, top: pos.top-30}).
      show();
  } else {
    o.find('.bitly-popup').
      css({left: pos.left-50, top: pos.top-30}).
      show();
  }
}

function bitlyPopupOut(evt) {
  var o = $j(evt.target);
  if (o.parents('#sidebar').length > 0) {
    o.find('.bitly-popup-right').hide();
  } else {
    o.find('.bitly-popup').hide();
  }
}

function enable_bitlypopus() {
  $j('#sidebar .bitly-popup').each(function(i,e) {
    $j(e).removeClass('bitly-popup').addClass('bitly-popup-right').css('width',($j(e).width()+30)+'px');
  });
  $j('a[rel="shorten_url"]').hover(bitlyPopupIn, bitlyPopupOut);
}


function reload_recent_photo() {
	$j.get('/page/reload_recent_photo', {}, function(data) {
		$j('#recent-photo-pane .content img').fadeOut(500, function() {
			$j('#recent-photo-pane .content img').attr('src', data);
			$j('#recent-photo-pane .content img').fadeIn(500, function() {
				setTimeout('reload_recent_photo();', 10000);
			});
		});
	});
}

function reload_recent_photo() {
  var photos = $j('#recent-photo-pane .img');
  var active = photos.filter(':visible');
  if (active.length == 0) {
    $j(photos.get(0)).fadeIn('slow');
  } else {
    $j(active).fadeOut('slow');
    var n = $j(active).next();
    $j(active).queue(function() {
      if (n.length == 0) {
        $j(photos.get(0)).fadeIn('slow');
      } else {
        n.fadeIn('slow');
      }
      $j(this).dequeue();
    });
  }
}

function bitly_string(str) {
	var arr;
	var bit = 'http://bit.ly/xxxxxx';
	arr = str.split(' ');
	for (i=0; i<arr.length; i++) {
		if (arr[i].substring(0, 4).toLowerCase() == 'www.' || arr[i].substring(0, 7).toLowerCase() == 'http://' || arr[i].substring(0, 8).toLowerCase() == 'https://') {
			arr[i] = bit;
		}
	}
	str = arr.join(' ');
	return str;
}

function get_size(kind) {
  return ( $j('.size_small').hasClass('opaque') ? ($j('.size_medium').hasClass('opaque') ? 'large' : 'medium') : 'small' );
}
function get_view(kind) {
  if (kind == 'gallery') {
    ret = $j('#'+kind+'_select').text();
  } else {
    ret = $j('#'+kind+'_select').val();
  }
  return ret;
}
function get_sort(kind) {
  return $j('#'+kind+'_sort').val();
}
function get_search(kind) {
  return $j('#'+kind+'_search_term').html();
}
function reset_page() {
  $j('#current_page').text(1);
}

function set_gallery_size(size, uid) {
  var sortby = get_sort('gallery');
  var term = get_search('gallery');
  var view = get_view('gallery');
  $j('#gallery_size_loading').show();
  $j.get('/gallery/reshow_gallery', {size: size, sortby: sortby, id: uid, term: term, view: view}, function(data) {
		$j('#gallery_images').html(data);
    (size == 'small') ? $j('.size_small').removeClass('opaque') : $j('.size_small').addClass('opaque');
    (size == 'medium') ? $j('.size_medium').removeClass('opaque') : $j('.size_medium').addClass('opaque');
    (size == 'large') ? $j('.size_large').removeClass('opaque') : $j('.size_large').addClass('opaque');
    $j('#gallery_size_loading').hide();
    reset_page();
    enable_bitlypopus();
  });
}

function set_gallery_sort(uid) {
  var size = get_size('gallery');
  var sortby = get_sort('gallery');
  var term = get_search('gallery');
  var view = get_view('gallery');
  $j.get('/gallery/reshow_gallery', {size: size, sortby: sortby, id: uid, term: term, view: view}, function(data) {
		$j('#gallery_images').html(data);
    $j('#gallery_sort_loading').hide();
    reset_page();
    rewire_events();
  });
}

function set_gallery_view(view, uid, title) {
  var size = get_size('gallery');
  var sortby = get_sort('gallery');
  var term = '';
  $j.get('/gallery/reshow_gallery', {size: size, sortby: sortby, id: uid, term: term, view: view}, function(data) {
    $j('#gallery_search_term').html('');
    $j('#gallery_search_results').hide();
		$j('#gallery_images').html(data);
    $j('#gallery_select').text(view);
    $j('#gallery_title').text(title);
    $j.get('/gallery/update_breadcrumb', {id: uid, view: view}, function(data) {
  		$j('#gallery_breadcrumb').html(data);
    });
    reset_page();
    rewire_events();
  });
}

function set_gallery(uid, size, sortby) {
  var term = get_search('gallery');
  var view = get_view('gallery');
  $j.get('/gallery/reshow_gallery', {size: size, sortby: sortby, id: uid, term: term, view: view}, function(data) {
		$j('#gallery_images').html(data);
    reset_page();
    rewire_events();
  });
}

function gallery_search(uid) {
  var term = $j('#gallery_search_box').val();
  var size = get_size('gallery');
  var sortby = get_sort('gallery');
  var view = get_view('gallery');
  show_candystripe('#gallery_search_go');
  RE.gallery_search = 'in_progress';
  $j.get('/gallery/reshow_gallery', {size: size, sortby: sortby, id: uid, term: term, view: view}, function(data) {
    if (data == 'No Data') {
      $j('#gallery_search_term').html('');
      $j('#gallery_search_results').hide();
      $j('#gallery_images').html('No search results found.');
    } else {
      if (data.substring(0,3) == 'try') {
        eval(data);
        rewire_events();
      } else {
        $j('#gallery_search_term').html(term);
        $j('#gallery_search_results').show();
    		$j('#gallery_images').html(data);
        rewire_events();
      }
    }
    $j('#gallery_search_box').val('Search:');
    reset_page();
    hide_candystripe($j('#gallery_selection .search_btn .candystripe'));
    RE.gallery_search = 'finished';
  });
}

function gallery_clear_search(uid) {
  var term = '';
  var size = get_size('gallery');
  var sortby = get_sort('gallery');
  var view = get_view('gallery');
  $j.get('/gallery/reshow_gallery', {size: size, sortby: sortby, id: uid, term: term, view: view}, function(data) {
    $j('#gallery_search_term').html('');
    $j('#gallery_search_results').hide();
		$j('#gallery_images').html(data);
    reset_page();
    rewire_events();
  });
}

function show_gallery_overlay(gid, first, last) {
  $j('#gallery_ol_loading_'+gid).show();
  $j('#go_outer_container').show();
  $j.get('/gallery/overlay_image', {id: gid, first_pic: first, last_pic: last}, function(data) {
    $j('#go_outer_container').css('left', window.pageXOffset+100);
    $j('#go_outer_container').css('top', window.pageYOffset-200);
	  $j('#go_inner_container').html(data);
    $j('#gallery_ol_loading_'+gid).hide();
  });
}

function hide_gallery_overlay() {
  $j('#go_outer_container').hide();
  $j('#go_inner_container').html('');
}

function next_overlay(gid, direction) {
  $j.get('/gallery/next_overlay_image', {id: gid, direction: direction}, function(data) {
	  $j('#go_inner_container').html(data);
    $j('.prev_photo_loading').hide()
    $j('.next_photo_loading').hide()
    $j('.prev_photo').show()
    $j('.next_photo').show()
  });
}

function set_product_view() {
  var sortby = get_sort('product');
  var term = '';
  var view = get_view('product');
  $j.get('/products/reshow_products', {sortby: sortby, term: term, view: view}, function(data) {
    $j('#product_search_term').html('');
    $j('#category_select').val('all');
    $j('#product_search_results').hide();
		$j('#product_images').html(data);
    $j('#product_select_loading').hide();
    reset_page();
    rewire_events();
  });
}

function set_category_view() {
  var sortby = get_sort('product');
  var term = get_search('product');
  var view = get_view('product');
  var category = $j('#category_select').val();

  $j.get('/products/reshow_products', {sortby: sortby, term: term, view: view, category: category}, function(data) {
		$j('#product_images').html(data);
    $j('#product_category_loading').hide();
    reset_page();
    rewire_events();
  });
}

function set_product_sort() {
  var sortby = get_sort('product');
  var term = get_search('product');
  var view = get_view('product');
  $j.get('/products/reshow_products', {sortby: sortby, term: term, view: view}, function(data) {
		$j('#product_images').html(data);
    $j('#product_category_loading').hide();
    reset_page();
    rewire_events();
  });
}

function product_search() {
  var term = $j('#product_search_box').val();
  var sortby = get_sort('product');
  var view = get_view('product');
  RE.product_search = 'in_progress';
  show_candystripe('#product_search_go');
  $j.get('/products/reshow_products', {sortby: sortby, term: term, view: view}, function(data) {
    if (data == 'No Data') {
      $j('#product_search_term').html('');
      $j('#product_search_results').hide();
      $j('#product_images').html('No search results found.');
    } else {
      if (data.substring(0,3) == 'try') {
        eval(data);
        rewire_events();
      } else {
        $j('#product_search_term').html(term);
        $j('#product_search_results').show();
    		$j('#product_images').html(data);
        rewire_events();
        $j('#product-view-options').hide();
      }
    }
    //$j('#product_search_box').val('Search:');
    reset_page();
    hide_candystripe($j('#product_selection .search_btn .candystripe'));
    RE.product_search = 'finished';
  });
}

function product_clear_search() {
  var term = '';
  var sortby = get_sort('product');
  var view = get_view('product');
  $j.get('/products/reshow_products', {sortby: sortby, term: term, view: view}, function(data) {
    $j('#product_search_term').html('');
    $j('#product_search_results').hide();
		$j('#product_images').html(data);
    reset_page();
    rewire_events();
    $j('#product-view-options').show();
  });
}

function show_hover_card_focus(evt) {
  if (!RE.select_from_gallery_selected) {
    var $e = $j(evt.target);
    var $o = $j('.hc_id_'+$e.attr('oid'));
    if (!$o.hasClass('hovering')) {
      $j('#select_from_gallery').find('span.hovering').hide().removeClass('hovering');
      $j('#select_from_gallery .badge').css('z-index','5');
      $o.show().addClass('hovering');
      $e.prev().css('z-index','15');
      var hc = $o.find('div.hover_content').height() + 35;
      $o.find('div.hover_border').css('height', hc+'px');
    }
  }
}
// do not remove
function show_hover_card_blur(evt) {}

function init_product_hover_card(gal_type) {
  $j('.user_product .badge').hover(
    //over
    function(ev){
      var badge = $j(ev.target)
          panel = badge.next(),
          img = panel.find('.image');

      badge.css('z-index','105')
      panel.css('z-index','102').show();
      badge.parent().find('.curatsu-blurb').css('z-index', '150');
      if(gal_type == 'small'){
        badge.parent().find('.curatsu_blurb').css('margin-right', '-91px')
                                             .css('margin-top', '158px');
      }
    },
    //out
    function(ev){}
  );
  $j('.user_product').hover(
    //over
    function(){},
    //out
    function(ev){
      var el = $j(this),
          badge = el.find('.badge');
          panel = el.find('.productized_panel');
          img = panel.find('.image');

      img.css('border', '1px solid #ccc');
      panel.hide().css('z-index','98');
      badge.css('z-index', '100');
      badge.parent().find('.curatsu-blurb').css('z-index', '90');
      if(gal_type == 'small'){
        badge.parent().find('.curatsu_blurb').css('margin-right', '-3px')
                                             .css('margin-top', '-12px');
      }
    }
  );
}

function select_from_gallery_search() {
  var term = $j('#sfg_search_box').val();
  var sortby = get_sort('select_from_gallery');
  show_candystripe('#sfg_search_go');
  $j.get('/products/reshow_select_from_gallery', {sortby: sortby, term: term}, function(data) {
    if (data == 'No Data') {
      $j('#select_from_gallery_search_term').html('');
      $j('#select_from_gallery_search_results').hide();
      $j('#remote_content_images').html('No search results found.');
    } else {
      if (data.substring(0,3) == 'try') {
        eval(data);
      } else {
        $j('#select_from_gallery_search_term').html(term);
        $j('#select_from_gallery_search_results').show();
    		$j('#remote_content_images').html(data);
      }
    }
    $j('#sfg_search_box').val('Search:');
    hide_candystripe($j('#select_from_gallery_selection .search_btn .candystripe'));
  });
}

function select_from_gallery_clear_search() {
  var term = '';
  var sortby = get_sort('select_from_gallery');
  $j.get('/products/reshow_select_from_gallery', {sortby: sortby, term: term}, function(data) {
    $j('#select_from_gallery_search_term').html('');
    $j('#select_from_gallery_search_results').hide();
		$j('#remote_content_images').html(data);
  });
}

function select_from_gallery_sort() {
  var sortby = get_sort('select_from_gallery');
  var term = get_search('select_from_gallery');
  $j.get('/products/reshow_select_from_gallery', {sortby: sortby, term: term}, function(data) {
		$j('#remote_content_images').html(data);
    $j('#select_from_gallery_sort_loading').hide();
    reset_page();
  });
}

/*
 * Define a helper funcion to use for event deletation.
 * the function take a data object where the keys are the css class selectors
 * and the values are the functions to execute for that selectors
 *
 * $j('container_element).click(
 *    $j.delegate( {'css_class': function_to_execute(e) {console.log(e)} });
 * );
 *
*/
$j.delegate = function(rules) {
  return function(e) {
    for (var selector in rules) {
      if ($j(e.target).hasClass(selector)) {
        return rules[selector].apply(this, $j.makeArray(arguments));
      }
    }
  }
}

function show_inline_curatsu(e) {
  var t = $j(e.target);
  var oid = t.attr('rel');
  var kv = oid.split('-');
  var current = (kv[2] == 'notab') ? $j('#notab-inlinefeed-'+kv[0]+'-'+kv[1]) : $j('#'+RE.myworld_active_tab+'-inlinefeed-'+oid);
  var tab = (kv[2] == 'notab') ? kv[2] : RE.myworld_active_tab;
  if (current.is(':visible')) {
    current.slideToggle('normal', function(){current.html('<div id="horiz_loader" style="text-align:right;"><img src = "/images/horiz_loader.gif"></div>')});
    toggle_curatsu_blurb(oid);
  } else {
    toggle_curatsu_blurb(oid);
    $j(RE.inline_curatsu_container+' .curatsu-inline-feedback:visible').slideToggle('normal', function(){$j(this).html('<div id="horiz_loader" style="text-align:right;"><img src = "/images/horiz_loader.gif"></div>')});
    current.slideToggle();
    $j.get('/curatsu/show_inline_feedback', {kind: kv[0], oid: kv[1], tab: tab}, undefined, 'script');
  }
}

function show_image_strip_overlay(id) {
  $j('.so_id_'+id).show();
}

function hide_image_strip_overlay(id) {
  $j('.so_id_'+id).hide();
}

function get_remote_content(url, data, success_act, error_act) {
  $j.ajax( {
    async: true,
    data: data,
    type: 'GET',
    url: url,
    error: error_act,
    success: success_act
  });
  return false;
}
//
// helper to post a generic form by ajax
//
// ex: form_for :object, :url => {:action => 'post_actiob'}, :html => {:onsubmit => jq_post_form(this)}
//
function jq_post_form(that, datatype, success_act, error_act) {
  $j.ajax( {
    async: true,
    data: $j.param($j(that).serializeArray()),
    dataType: datatype || 'text',
    type: 'POST',
    url: that.action,
    error: error_act,
    success: success_act
  });
  return false;
}


function bind_tag_interface(prefix, oid) {
  $j('#'+prefix+'_container_'+oid+' p.leyend').click(function() {
    $j(this).hide();
    $j('#'+prefix+'_container_'+oid+' .fblist').
      css('height','1').
      animate({height: "30px"}, function() {
        var $t = $j(this);
        $t.css('height','');
        $t.find('.maininput').focus();
      });
      $j('#'+prefix+'_container_'+oid).find('.form_btns').slideDown();
  });
}

// clear fblist inner state and current selections
function tag_interface_clear_fblist(ele) {
  $j('#'+ele).find('.fblist .bit-box').remove();
  eval('fblist_for_'+$j('#'+ele).find(".fblist input:not([class])").attr('id')+'.reset();');
}

// slide up all fblist visual elements
function tag_interface_close_fblist(ele) {
  var $e = $j('#'+ele);
  $e.find('.form_btns').slideUp();
  $e.find('.fblist').slideUp(function() {
    $e.find('.fblist .bit-box').remove();
    $e.find(' p.leyend').show();
    tag_interface_clear_fblist(ele);
    })
}
// default action to send form tags
function tag_interface_submit_fblist(that, ele) {
  show_candystripe(that);
  jq_post_form($j(that).parents('form').get(0), 'script',
    function() {
      hide_candystripe($j(that).find('.candystripe'));
      tag_interface_close_fblist(ele);
    },
    function() {
      hide_candystripe($j(that).parent().find('.candystripe'));
    }
  );
}


function tag_interface_cancel(callback, ele, show_fblist) {
   callback(ele, show_fblist);
}

function tag_interface_submit(callback, that, ele) {
  callback(that, ele);
}

// Show a facebox based on a div#name
function show_generic_fb_notice(name) {
  $j.facebox({div:name});
}

// helper function to remove tag from the curatsu's inline_feedback interface
function remove_inline_tag(that,curatsuid, word, link) {
  $j.ajax( {
    async: true,
    data: word ? {curatsuid: curatsuid, word: word} : {curatsu_id: curatsu_id, link: link},
    dataType: 'text',
    type: 'POST',
    url: '/curatsu/remove_tag'
  });
  var h = $j(that).parents('div.inlinefeedback-section');
  $j(that).parent().remove();
  var z = h.html().replace('>, <','>&nbsp;<');
  h.html(z);
}



/* =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ */
// Code use on photo productize
var market = {
  mode: undefined,
  empty_text: '$150.00, Negotiable, Free, etc.',

  setauto: function(ele) {
    var $e = ele.find('input.update_links');
    $e.autocomplete('/products/product_autocomplete_search', {
        multipleSeparator: ',',
        minChars:3,
        width: $e.width()+4,
        scrollHeight: 240,
        autoSubmit: false,
        selectFirst: false,
        formatItem: function(data, max, value, term) {return data[1];},
        formatResult: function(value,row) {return value[1]},
        onSelected: function(data) {
          ele.find('input.website').val(data.data[2]);
          ele.find('input.merchant_id').val(data.data[0]);
        },
        highlight: function(value,term) {return value;}
    });
  },

  change_product_type: function(ele) {
    if ($j('input[name="product[consignment]"]:checked').val() === "0") {
      $j('#consigment_price').hide();
      var mc = $j('#merchants-container');
      var m = $j('#merchant-blank .merchant').clone().appendTo(mc).addClass('lastmerchant');
      mc.show();
      $j('#dproduct_name').css('width', '100%');
      $j('#dproduct_price').show();
      this.setauto(m);
    } else {
      $j('#merchants-container').hide().html('');
      $j('#product_consigment').val(this.empty_text);
      $j('#consigment_price').show();
      $j('#dproduct_name').css('width', '250px');
      $j('#dproduct_price').hide();
    }
  },

  consignment_blur: function(evt) {
    if ($j('input[name="product[consignment]"]:checked').val() === "1") {
      var t = $j.trim($j(evt.target).val());
      if ((t === "") || (t === this.empty_text)) {
        $j(evt.target).addClass('shadow_text').val(this.empty_text);
      }
    }
  },

  consignment_focus: function(evt) {
    if ($j('input[name="product[consignment]"]:checked').val() === "1") {
      if ($j(evt.target).val() === this.empty_text) {
        $j(evt.target).val('').removeClass('shadow_text');
      }
    }
  },

  add_merchant: function(e) {
    var m = $j('#merchant-blank .merchant').clone();
    var $o = $j(e.target);
    var $mc = $j('#merchants-container .merchant').length;

    if ($o.html() == 'Cancel') {
      $o.parents('div.merchant').remove();
      if ($mc == 1) {
        $j('#merchants-container .merchant a.add_merchant').show();
      }
      var $l = $j('#merchants-container .merchant:last');
      $l.addClass('lastmerchant');
      $l.find('a.add_merchant').html('Add Another Merchant').show();
    } else {
      m.find('a.add_merchant').html('Cancel');
      m.appendTo($j('#merchants-container'));
      m.find('a.add_merchant').show();
      m.addClass('lastmerchant');
      m.find('label.merchant_name').html('Merchant Name (#'+$mc+'):');
      m.find('label.merchant_link').html('Merchant Link (#'+$mc+'):');
      m.show();
      $o.hide();
      market.setauto(m);
    }
    market.renumber_merchants();
  },

  show_merchant_link: function() {
    var $l = $j('#merchants-container .merchant:last');
    $l.addClass('lastmerchant');
    $l.find('a.add_merchant').html('Add Another Merchant').show();
  },

  prepare_remove_merchant: function(evt) {
    var p = $j(evt.target).parents('div.merchant').attr('rel');
    $j('#show_remove_merchant_modal_container input#bag0').val(p);
    show_generic_fb_notice('#show_remove_merchant_modal_container');
  },

  remove_merchant: function(evt) {
    var p = $j('#show_remove_merchant_modal_container input#bag0').val();
    $j('#merchant_form_'+p).remove();
    market.renumber_merchants();
    closeFacebox();
  },

  renumber_merchants: function() {
    $j('#merchants-container .merchant').each(function(idx,ele) {
      if (idx === 0) {
        $j(ele).find('label.merchant_name').html('Merchant Name:');
        $j(ele).find('label.merchant_link').html('Merchant Link:');
      } else {
        var l = $j(ele).find('label.merchant_name');
        l.html(l.html().replace(/\(#\d+\)/,"(#"+(idx+1)+")"));
        l = $j(ele).find('label.merchant_link');
        l.html(l.html().replace(/\(#\d+\)/,"(#"+(idx+1)+")"));
      }
    });
  },

  update_links: function(e) {
    var $o = $j(e.target);
    if ($j('#merchants-container .merchant').length == 1) {
      if ($j.trim($o.val()).length > 0) {
        $o.parent().find('a.add_merchant').html('Add Another Merchant').show();
      } else {
        $o.parent().find('a.add_merchant').hide();
      }
    } else {
        if ($j.trim($o.val()).length > 0) {
          if ($o.parents('div.merchant').hasClass('lastmerchant')) {
            $o.parent().find('a.add_merchant').html('Add Another Merchant').show();
          } else {
            $o.parent().find('a.add_merchant').hide();
          }
        } else {
          $o.parent().find('a.add_merchant').html('Cancel').show();
        }
    }
    if ($o.val().match(/amazon/i)) {
      $o.parent().parent().find('a.amazon_link').show();
    } else {
      $o.parent().parent().find('a.amazon_link').hide();
    }
  },

  update_amazon_link: function(e) {
    var $o = $j(e.target);
    if ($o.val().match(/amazon/i)) {
      $o.parent().parent().find('a.amazon_link').show();
    } else {
      $o.parent().parent().find('a.amazon_link').hide();
    }
  },

  has_magic: function(evt) {
    if (evt.target.checked) {
      $j('#has_magic input[class="jewel"]').each(function(i,e) { e.checked = true });
    }
  },

  send_form: function(that) {
    $j('#goods_error').html('');
    show_candystripe('#edit_product_form #submit_button a:first');
    jq_post_form(that, 'text',
      function(data) {
        eval(data);
        //hide_candystripe($j('#edit_product_form #submit_button .candystripe'));
      },
      function(xhr) {
        hide_candystripe($j('#edit_product_form #submit_button .candystripe'));
        var e = eval("("+xhr.responseText+")");
        $j.each(e, function(i, val) {
          $j('#goods_error').append(val+"<br />");
          });
      }
    );
  },

  update_form: function(that) {
    $j('#goods_error').html('');
    show_candystripe('#edit_product_form #submit_button a:first');
    jq_post_form(that, 'text',
      function(data) {
        eval(data);
        //hide_candystripe($j('#edit_product_form #submit_button .candystripe'));
      },
      function(xhr) {
        hide_candystripe($j('#edit_product_form #submit_button .candystripe'));
        var e = eval("("+xhr.responseText+")");
        $j.each(e, function(i, val) {
          $j('#goods_error').append(val+"<br />");
          });
      }
    );
  },
  remove_product_photo: function(that, url) {
    var p = $j("#product_photo_link");
    show_candystripe(that);
    $j.get(url, function(data) {
        eval(data);
        hide_candystripe($j(that).parent().find('.candystripe'));
        closeFacebox();
    });
    return false;
  },
  remove_product: function(that, url) {
    $j.get(url, function(data) {
        eval(data);
        closeFacebox();
    });
    return false;
  },
  close_productize: function(evt) {
    closeFacebox();
    return;
    /*
    if (market.mode == 'amazon') {
      closeFacebox();
    } else {
      $j('#convertto').slideUp(function() {
        $j(this).html('');
        $j('#convertto-container .head').show();
      });
    }
    */
  }

}
/* =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ */
/* end Market */


/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;

/**************************************************
 * dom-drag.js
 * 09.25.2001
 * www.youngpup.net
 * Script featured on Dynamic Drive (http://www.dynamicdrive.com) 12.08.2005
 **************************************************
 * 10.28.2001 - fixed minor bug where events
 * sometimes fired off the handle, not the root.
 **************************************************/

var Drag = {

	obj : null,

	init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
	{
		o.onmousedown	= Drag.start;

		o.hmode			= bSwapHorzRef ? false : true ;
		o.vmode			= bSwapVertRef ? false : true ;

		o.root = oRoot && oRoot != null ? oRoot : o ;

		if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
		if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
		if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
		if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";

		o.minX	= typeof minX != 'undefined' ? minX : null;
		o.minY	= typeof minY != 'undefined' ? minY : null;
		o.maxX	= typeof maxX != 'undefined' ? maxX : null;
		o.maxY	= typeof maxY != 'undefined' ? maxY : null;

		o.xMapper = fXMapper ? fXMapper : null;
		o.yMapper = fYMapper ? fYMapper : null;

		o.root.onDragStart	= new Function();
		o.root.onDragEnd	= new Function();
		o.root.onDrag		= new Function();
	},

	start : function(e)
	{
		var o = Drag.obj = this;
		e = Drag.fixE(e);
		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
		o.root.onDragStart(x, y);

		o.lastMouseX	= e.clientX;
		o.lastMouseY	= e.clientY;

		if (o.hmode) {
			if (o.minX != null)	o.minMouseX	= e.clientX - x + o.minX;
			if (o.maxX != null)	o.maxMouseX	= o.minMouseX + o.maxX - o.minX;
		} else {
			if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
			if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
		}

		if (o.vmode) {
			if (o.minY != null)	o.minMouseY	= e.clientY - y + o.minY;
			if (o.maxY != null)	o.maxMouseY	= o.minMouseY + o.maxY - o.minY;
		} else {
			if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
			if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
		}

		document.onmousemove	= Drag.drag;
		document.onmouseup		= Drag.end;

		return false;
	},

	drag : function(e)
	{
		e = Drag.fixE(e);
		var o = Drag.obj;

		var ey	= e.clientY;
		var ex	= e.clientX;
		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
		var nx, ny;

		if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
		if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
		if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
		if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);

		nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
		ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

		if (o.xMapper)		nx = o.xMapper(y)
		else if (o.yMapper)	ny = o.yMapper(x)

		Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
		Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
		Drag.obj.lastMouseX	= ex;
		Drag.obj.lastMouseY	= ey;

		Drag.obj.root.onDrag(nx, ny);
		return false;
	},

	end : function()
	{
		document.onmousemove = null;
		document.onmouseup   = null;
		Drag.obj.root.onDragEnd(	parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
									parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
		Drag.obj = null;
	},

	fixE : function(e)
	{
		if (typeof e == 'undefined') e = window.event;
		if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
		if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
		return e;
	}
};

// kropper.js: this file holds the javascript for the Kropper image cropping UI.
// This file depends on prototype.js and dom-drag.js.

// This method sets up the initial position of the image to be cropped
// and the callback methods to be called when the image is dragged,
// the zoom slider handle is dragged, and the reset button is clicked.
// max_zoom determines the maximum zoom-in level for the zoom slider.
function setup_image_cropper(max_zoom, allow_small)
{
	// make sure the image cropper is on the page...
	if ($("image_cropper"))
	{
		// derive the dimensions of the crop canvas, crop stencil, 
		// and slider range from the browser -- this lets us set the dimensions
		// of the crop canvas, crop stencil, and zoom slider with CSS.
		// IE6 weirdness note: I had to remove the underscores from 
		// the cropcanvas and cropstencil variable names
		// due to a strange error in IE6. Go figure...

		// the crop canvas
		crop_canvas_obj = $("crop_canvas");
		cropcanvas = new Object();
		cropcanvas.w = crop_canvas_obj.clientWidth;
		cropcanvas.h = crop_canvas_obj.clientHeight;
		// the crop stencil
		crop_stencil_obj = $("crop_stencil");
		cropstencil = new Object();
		cropstencil.l = crop_stencil_obj.offsetLeft;
		cropstencil.t = crop_stencil_obj.offsetTop;
		cropstencil.w = crop_stencil_obj.offsetWidth;  // NOTE: includes the width of any CSS border!
		cropstencil.h = crop_stencil_obj.offsetHeight; // NOTE: includes the width of any CSS border!
		cropstencil.aspect_ratio = cropstencil.h / cropstencil.w;
		// the zoom slider
		zoom_slider_obj = $("zoom_slider");
		zoom_slider_handle_obj = $("zoom_slider_handle");
		slider_range = zoom_slider_obj.offsetWidth - zoom_slider_handle_obj.offsetWidth;
		min_zoom = 1; // we want the crop stencil to stay inside the image boundaries, so we can't allow zooming out. This will stop that.

		// set the initial image position, slider position, and form fields.
		form_params = new Object();
		form_params.crop_left = $("crop_left");
		form_params.crop_top = $("crop_top");
		form_params.crop_width = $("crop_width");
		form_params.crop_height = $("crop_height");
		form_params.stencil_width = $("stencil_width");   
		form_params.stencil_height = $("stencil_height");
		form_params.enlarge_on_save = $("enlarge_on_save");
		form_params.img_w = $("img_w");
		form_params.img_h = $("img_h");
		
		// set up the initial size of the uncropped image
		uncropped_image_obj = $("uncropped_image");
		img = new Object();
		img.w = uncropped_image_obj.width;
		img.h = uncropped_image_obj.height;
		form_params.img_w.value = img.w;
		form_params.img_h.value = img.h;
		img.aspect_ratio = (img.h / img.w);
		img.display = new Object(); // this will store the image's current display rect
		if (allow_small === true && img.w < cropstencil.w && img.h < cropstencil.h) {
			img.display.orig_w = img.w;
			img.display.orig_h = img.h;
		}
		else {
			form_params.enlarge_on_save.value = 0;	//	image is bigger than scale, don't resize on save
			if (img.aspect_ratio > cropstencil.aspect_ratio)
			{
				// the image is taller than the crop stencil, so set its width
				// to the width of the crop stencil and let the height overflow
				img.display.orig_w = cropstencil.w;
				img.display.orig_h = cropstencil.w * img.aspect_ratio;
			}
			else
			{
				// the image is wider than the crop stencil, so set its height
				// to the height of the crop stencil and let the width overflow
				img.display.orig_w = cropstencil.h / img.aspect_ratio;
				img.display.orig_h = cropstencil.h;
			}
		}

		// the initial slider position is for a zoom factor of 1.
		set_initial_positions(uncropped_image_obj, form_params, zoom_slider_handle_obj, img, cropstencil, min_zoom, max_zoom, slider_range );

		// make the image and the slider handle draggable
		image_dragger_obj = $("image_dragger");
		Drag.init(image_dragger_obj, uncropped_image_obj, -5000, 5000, -5000, 5000);
		Drag.init(zoom_slider_handle_obj, null, 0, slider_range, 0, 0);
		
		// set the callbacks to be called when the image and slider handle are dragged
		uncropped_image_obj.onDrag = function(x, y)
		{
			// let the image be dragged around, but make sure the crop stencil
			// always stays inside the image.
			img.display.l = x;
			img.display.t = y;
			img = ensure_stencil_is_inside_image(img, cropstencil);
			set_dom_obj_dimensions(uncropped_image_obj, img.display);
			set_crop_form_params(form_params, img, cropstencil);
		}
		zoom_slider_handle_obj.onDrag = function(x, y)
		{
			// zoom the image in and out around its center.
			slider_pos = x / slider_range;
			zoom = (slider_pos * (max_zoom - min_zoom)) + min_zoom;
			center_x = img.display.l + (img.display.w / 2);
			center_y = img.display.t + (img.display.h / 2);
			
			if (form_params.enlarge_on_save.value !== 'false') {
				form_params.enlarge_on_save.value = zoom;
			}

			img.display.w = img.display.orig_w * zoom;
			img.display.h = img.display.orig_h * zoom;
			img.display.l = center_x - (img.display.w / 2);
			img.display.t = center_y - (img.display.h / 2);
			
			img = ensure_stencil_is_inside_image(img, cropstencil);
			set_dom_obj_dimensions(uncropped_image_obj, img.display);
			set_crop_form_params(form_params, img, cropstencil);
		}
		
		// set the callbacks to be called when the reset link is clicked
		if( $("crop_reset_btn") )
		{
			reset_link = $("crop_reset_btn");
			reset_link.onclick = function()
			{
				set_initial_positions(uncropped_image_obj, form_params, zoom_slider_handle_obj, img, cropstencil, min_zoom, max_zoom, slider_range );
			}
		}
		
		// hide the loading overlay
		$j('#crop_loading_overlay').hide();
	}
}

function set_initial_positions(uncropped_image_obj, form_params, zoom_slider_handle_obj, img, cropstencil, min_zoom, max_zoom, slider_range )
{
	img.display.w = img.display.orig_w;
	img.display.h = img.display.orig_h;
	img.display.l = cropstencil.l - ((img.display.w - cropstencil.w) / 2);
	img.display.t = cropstencil.t - ((img.display.h - cropstencil.h) / 2);
	set_dom_obj_dimensions(uncropped_image_obj, img.display);
	set_crop_form_params(form_params, img, cropstencil);
	zoom_slider_handle_obj.style.left = parseInt(((1 - min_zoom) / max_zoom) * slider_range) + 'px';
}

function set_crop_form_params( form_params, img, cropstencil )
{ 
	form_params.crop_left.value = ((cropstencil.l - img.display.l) / img.display.w) * img.w;
	form_params.crop_top.value = ((cropstencil.t - img.display.t) / img.display.h) * img.h;
	form_params.crop_width.value = (cropstencil.w / img.display.w) * img.w;
	form_params.crop_height.value = (cropstencil.h / img.display.h) * img.h;
	form_params.stencil_width.value = (img.display.w <= cropstencil.w) ? img.display.w : cropstencil.w;
	form_params.stencil_height.value = (img.display.h <= cropstencil.h) ? img.display.h : cropstencil.h;
	
	//alert(form_params.stencil_width.value);
}

function ensure_stencil_is_inside_image(img, cropstencil)
{ 
	//	if image is not as wide as stencil, then center it
	if (img.display.w <= cropstencil.w) {
		img.display.l = cropstencil.l + ((cropstencil.w - img.display.w) / 2);
	}
	else {
		// test the left edge
		if( img.display.l > cropstencil.l )
		{
			img.display.l = cropstencil.l;
		}
		
		// test the right edge
		if( (img.display.l + img.display.w) < (cropstencil.l + cropstencil.w) )
		{
			img.display.l = (cropstencil.l + cropstencil.w) - img.display.w;
		}
	}
	
	//	if image is not as tall as stencil, vertically center it
	if (img.display.h <= cropstencil.h) {
		img.display.t = cropstencil.t + ((cropstencil.h - img.display.h) / 2);
	}
	else {
		// test the top edge
		if( img.display.t > cropstencil.t )
		{
			img.display.t = cropstencil.t;
		}
	
		// test the bottom edge
		if( (img.display.t + img.display.h) < (cropstencil.t + cropstencil.h) )
		{
			img.display.t = (cropstencil.t + cropstencil.h) - img.display.h;
		}
	}
	
	return img;
}

// this is currently unused, but would be useful if you are not using
// ensure_stencil_is_inside_image() -- in that case, this method will add
// some stickiness to the stencil edges when dragging the image around.
function process_stencil_stickiness(img, cropstencil, stencil_stickiness)
{ 
	// test the left edge
	if( dist( img.display.l, cropstencil.l ) <= stencil_stickiness )
	{
		img.display.l = cropstencil.l;
	}
	// test the top edge
	if( dist( img.display.t, cropstencil.t ) <= stencil_stickiness )
	{
		img.display.t = cropstencil.t;
	}
	// test the right edge
	if( dist( img.display.l + img.display.w, cropstencil.l + cropstencil.w ) <= stencil_stickiness )
	{
		img.display.l = (cropstencil.l + cropstencil.w) - img.display.w;
	}
	// test the bottom edge
	if( dist( img.display.t + img.display.h, cropstencil.t + cropstencil.h ) <= stencil_stickiness )
	{
		img.display.t = (cropstencil.t + cropstencil.h) - img.display.h;
	}
	
	return img;
}

function set_dom_obj_dimensions(obj, rect)
{
	obj.style.left = rect.l + 'px';
	obj.style.top = rect.t + 'px';
	obj.style.width = rect.w + 'px';
	obj.style.height = rect.h + 'px';
}

function dist(a, b)
{ 
	delta = a - b;
	return Math.sqrt(delta * delta);
}


/*
 ### jQuery Star Rating Plugin v2.4 - 2008-07-15 ###
 * http://www.fyneworks.com/ - diego@fyneworks.com
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 ###
 Project: http://plugins.jquery.com/project/MultipleFriendlyStarRating
 Website: http://www.fyneworks.com/jquery/star-rating/
*/
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}(';6(13.H)(7($){$.3={q:\'14 15\',I:\'\',h:0,J:16,5:{},8:{x:7(n,a,b,c){4.j(n);$(a).K(\'.r\'+n).L().k(\'17\'+(c||\'M\'));g d=$(a).s(\'a\');9=d.t();6(b.N)b.N.y($.3.5[n].e[0],[9,d[0]])},j:7(n,a,b){$.3.5[n].e.18(\'.r\'+n).z(\'u\').z(\'19\')},m:7(n,a,b){6(!$($.3.5[n].o).1a(\'.q\'))$($.3.5[n].o).K(\'.r\'+n).L().k(\'u\');g c=$(a).s(\'a\');9=c.t();6(b.O)b.O.y($.3.5[n].e[0],[9,c[0]])},p:7(n,a,b){$.3.5[n].o=a;g c=$(a).s(\'a\');9=c.t();$.3.5[n].e.9(9);$.3.8.j(n,a,b);$.3.8.m(n,a,b);6(b.P)b.P.y($.3.5[n].e[0],[9,c[0]])}}};$.Q.3=7(d){6(4.R==0)A 4;d=$.S({},$.3,d||{});4.1b(7(i){g a=$.S({},d||{},($.T?$(4).T():($.1c?$(4).1d():1e))||{});g n=4.U;6(!$.3.5[n])$.3.5[n]={B:0};i=$.3.5[n].B;$.3.5[n].B++;$.3.5[n].l=$.3.5[n].l||a.l||$(4).1f(\'C\');6(i==0){$.3.5[n].e=$(\'<V W="1g" U="\'+n+\'" D=""\'+(a.l?\' C="C"\':\'\')+\'>\');$(4).X($.3.5[n].e);6($.3.5[n].l||a.1h){}Y{$(4).X($(\'<w Z="q"><a E="\'+a.q+\'">\'+a.I+\'</a></w>\').10(7(){$.3.8.j(n,4,a);$(4).k(\'u\')}).11(7(){$.3.8.m(n,4,a);$(4).z(\'u\')}).p(7(){$.3.8.p(n,4,a)}))}};f=$(\'<w Z="12"><a E="\'+(4.E||4.D)+\'">\'+4.D+\'</a></w>\');$(4).1i(f);6(a.1j)a.h=2;6(1k a.h==\'1l\'&&a.h>0){g b=($.Q.F?$(f).F():0)||a.J;g c=(i%a.h),G=1m.1n(b/a.h);$(f).F(G).1o(\'a\').1p({\'1q-1r\':\'-\'+(c*G)+\'1s\'})};$(f).k(\'r\'+n);6($.3.5[n].l){$(f).k(\'1t\')}Y{$(f).k(\'1u\').10(7(){$.3.8.j(n,4,a);$.3.8.x(n,4,a,\'M\')}).11(7(){$.3.8.j(n,4,a);$.3.8.m(n,4,a)}).p(7(){$.3.8.p(n,4,a)})};6(4.1v)$.3.5[n].o=f;$(4).1w();6(i+1==4.R)$.3.8.m(n,4,a)});1x(n 1y $.3.5)(7(c,v,n){6(!c)A;$.3.8.x(n,c,d||{},\'1z\');$(v).9($(c).s(\'a\').t())})($.3.5[n].o,$.3.5[n].e,n);A 4};$(7(){$(\'V[@W=1A].12\').3()})})(H);',62,99,'|||rating|this|groups|if|function|event|val|||||valueElem|eStar|var|split||drain|addClass|readOnly|reset||current|click|cancel|star_group_|children|text|star_on||div|fill|apply|removeClass|return|count|disabled|value|title|width|spw|jQuery|cancelValue|starWidth|prevAll|andSelf|hover|focus|blur|callback|fn|length|extend|metadata|name|input|type|before|else|class|mouseover|mouseout|star|window|Cancel|Rating||star_|siblings|star_hover|is|each|meta|data|null|attr|hidden|required|after|half|typeof|number|Math|floor|find|css|margin|left|px|star_readonly|star_live|checked|remove|for|in|on|radio'.split('|'),0,{}))

/*
 * Autocomplete - jQuery plugin 1.0.2
 *
 * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
 *
 */;(function($){$.fn.extend({autocomplete:function(urlOrData,options){var isUrl=typeof urlOrData=="string";options=$.extend({},$.Autocompleter.defaults,{url:isUrl?urlOrData:null,data:isUrl?null:urlOrData,delay:isUrl?$.Autocompleter.defaults.delay:10,max:options&&!options.scroll?10:150},options);options.highlight=options.highlight||function(value){return value;};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new $.Autocompleter(this,options);});},result:function(handler){return this.bind("result",handler);},search:function(handler){return this.trigger("search",[handler]);},flushCache:function(){return this.trigger("flushCache");},setOptions:function(options){return this.trigger("setOptions",[options]);},unautocomplete:function(){return this.trigger("unautocomplete");}});$.Autocompleter=function(input,options){var KEY={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var $input=$(input).attr("autocomplete","off").addClass(options.inputClass);var timeout;var previousValue="";var cache=$.Autocompleter.Cache(options);var hasFocus=0;var lastKeyPressCode;var config={mouseDownOnSelect:false};var select=$.Autocompleter.Select(options,input,selectCurrent,config);var blockSubmit;$.browser.opera&&$(input.form).bind("submit.autocomplete",function(){if(blockSubmit){blockSubmit=false;return false;}});$input.bind(($.browser.opera?"keypress":"keydown")+".autocomplete",function(event){lastKeyPressCode=event.keyCode;switch(event.keyCode){case KEY.UP:event.preventDefault();if(select.visible()){select.prev();}else{onChange(0,true);}break;case KEY.DOWN:event.preventDefault();if(select.visible()){select.next();}else{onChange(0,true);}break;case KEY.PAGEUP:event.preventDefault();if(select.visible()){select.pageUp();}else{onChange(0,true);}break;case KEY.PAGEDOWN:event.preventDefault();if(select.visible()){select.pageDown();}else{onChange(0,true);}break;case options.multiple&&$.trim(options.multipleSeparator)==","&&KEY.COMMA:case KEY.TAB:case KEY.RETURN:if(selectCurrent()){event.preventDefault();blockSubmit=true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout=setTimeout(onChange,options.delay);break;}}).focus(function(){hasFocus++;}).blur(function(){hasFocus=0;if(!config.mouseDownOnSelect){hideResults();}}).click(function(){if(hasFocus++>1&&!select.visible()){onChange(0,true);}}).bind("search",function(){var fn=(arguments.length>1)?arguments[1]:null;function findValueCallback(q,data){var result;if(data&&data.length){for(var i=0;i<data.length;i++){if(data[i].result.toLowerCase()==q.toLowerCase()){result=data[i];break;}}}if(typeof fn=="function")fn(result);else $input.trigger("result",result&&[result.data,result.value]);}$.each(trimWords($input.val()),function(i,value){request(value,findValueCallback,findValueCallback);});}).bind("flushCache",function(){cache.flush();}).bind("setOptions",function(){$.extend(options,arguments[1]);if("data"in arguments[1])cache.populate();}).bind("unautocomplete",function(){select.unbind();$input.unbind();$(input.form).unbind(".autocomplete");});function selectCurrent(){var selected=select.selected();if(!selected)return false;var v=selected.result;previousValue=v;if(options.multiple){var words=trimWords($input.val());if(words.length>1){v=words.slice(0,words.length-1).join(options.multipleSeparator)+options.multipleSeparator+v;}v+=options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result",[selected.data,selected.value]);return true;}function onChange(crap,skipPrevCheck){if(lastKeyPressCode==KEY.DEL){select.hide();return;}var currentValue=$input.val();if(!skipPrevCheck&&currentValue==previousValue)return;previousValue=currentValue;currentValue=lastWord(currentValue);if(currentValue.length>=options.minChars){$input.addClass(options.loadingClass);if(!options.matchCase)currentValue=currentValue.toLowerCase();request(currentValue,receiveData,hideResultsNow);}else{stopLoading();select.hide();}};function trimWords(value){if(!value){return[""];}var words=value.split(options.multipleSeparator);var result=[];$.each(words,function(i,value){if($.trim(value))result[i]=$.trim(value);});return result;}function lastWord(value){if(!options.multiple)return value;var words=trimWords(value);return words[words.length-1];}function autoFill(q,sValue){if(options.autoFill&&(lastWord($input.val()).toLowerCase()==q.toLowerCase())&&lastKeyPressCode!=KEY.BACKSPACE){$input.val($input.val()+sValue.substring(lastWord(previousValue).length));$.Autocompleter.Selection(input,previousValue.length,previousValue.length+sValue.length);}};function hideResults(){clearTimeout(timeout);timeout=setTimeout(hideResultsNow,200);};function hideResultsNow(){var wasVisible=select.visible();select.hide();clearTimeout(timeout);stopLoading();if(options.mustMatch){$input.search(function(result){if(!result){if(options.multiple){var words=trimWords($input.val()).slice(0,-1);$input.val(words.join(options.multipleSeparator)+(words.length?options.multipleSeparator:""));}else
$input.val("");}});}if(wasVisible)$.Autocompleter.Selection(input,input.value.length,input.value.length);};function receiveData(q,data){if(data&&data.length&&hasFocus){stopLoading();select.display(data,q);autoFill(q,data[0].value);select.show();}else{hideResultsNow();}};function request(term,success,failure){if(!options.matchCase)term=term.toLowerCase();var data=cache.load(term);if(data&&data.length){success(term,data);}else if((typeof options.url=="string")&&(options.url.length>0)){var extraParams={timestamp:+new Date()};$.each(options.extraParams,function(key,param){extraParams[key]=typeof param=="function"?param():param;});$.ajax({mode:"abort",port:"autocomplete"+input.name,dataType:options.dataType,url:options.url,data:$.extend({q:lastWord(term),limit:options.max},extraParams),success:function(data){var parsed=options.parse&&options.parse(data)||parse(data);cache.add(term,parsed);success(term,parsed);}});}else{select.emptyList();failure(term);}};function parse(data){var parsed=[];var rows=data.split("\n");for(var i=0;i<rows.length;i++){var row=$.trim(rows[i]);if(row){row=row.split("|");parsed[parsed.length]={data:row,value:row[0],result:options.formatResult&&options.formatResult(row,row[0])||row[0]};}}return parsed;};function stopLoading(){$input.removeClass(options.loadingClass);};};$.Autocompleter.defaults={inputClass:"ac_input",resultsClass:"ac_results",loadingClass:"ac_loading",minChars:1,delay:400,matchCase:false,matchSubset:true,matchContains:false,cacheLength:10,max:100,mustMatch:false,extraParams:{},selectFirst:true,formatItem:function(row){return row[0];},formatMatch:null,autoFill:false,width:0,multiple:false,multipleSeparator:", ",highlight:function(value,term){return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"<strong>$1</strong>");},scroll:true,scrollHeight:180};$.Autocompleter.Cache=function(options){var data={};var length=0;function matchSubset(s,sub){if(!options.matchCase)s=s.toLowerCase();var i=s.indexOf(sub);if(i==-1)return false;return i==0||options.matchContains;};function add(q,value){if(length>options.cacheLength){flush();}if(!data[q]){length++;}data[q]=value;}function populate(){if(!options.data)return false;var stMatchSets={},nullData=0;if(!options.url)options.cacheLength=1;stMatchSets[""]=[];for(var i=0,ol=options.data.length;i<ol;i++){var rawValue=options.data[i];rawValue=(typeof rawValue=="string")?[rawValue]:rawValue;var value=options.formatMatch(rawValue,i+1,options.data.length);if(value===false)continue;var firstChar=value.charAt(0).toLowerCase();if(!stMatchSets[firstChar])stMatchSets[firstChar]=[];var row={value:value,data:rawValue,result:options.formatResult&&options.formatResult(rawValue)||value};stMatchSets[firstChar].push(row);if(nullData++<options.max){stMatchSets[""].push(row);}};$.each(stMatchSets,function(i,value){options.cacheLength++;add(i,value);});}setTimeout(populate,25);function flush(){data={};length=0;}return{flush:flush,add:add,populate:populate,load:function(q){if(!options.cacheLength||!length)return null;if(!options.url&&options.matchContains){var csub=[];for(var k in data){if(k.length>0){var c=data[k];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub.push(x);}});}}return csub;}else
if(data[q]){return data[q];}else
if(options.matchSubset){for(var i=q.length-1;i>=options.minChars;i--){var c=data[q.substr(0,i)];if(c){var csub=[];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub[csub.length]=x;}});return csub;}}}return null;}};};$.Autocompleter.Select=function(options,input,select,config){var CLASSES={ACTIVE:"ac_over"};var listItems,active=-1,data,term="",needsInit=true,element,list;function init(){if(!needsInit)return;element=$("<div/>").hide().addClass(options.resultsClass).css("position","absolute").appendTo(document.body);list=$("<ul/>").appendTo(element).mouseover(function(event){if(target(event).nodeName&&target(event).nodeName.toUpperCase()=='LI'){active=$("li",list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event){$(target(event)).addClass(CLASSES.ACTIVE);select();input.focus();return false;}).mousedown(function(){config.mouseDownOnSelect=true;}).mouseup(function(){config.mouseDownOnSelect=false;});if(options.width>0)element.css("width",options.width);needsInit=false;}function target(event){var element=event.target;while(element&&element.tagName!="LI")element=element.parentNode;if(!element)return[];return element;}function moveSelect(step){listItems.slice(active,active+1).removeClass(CLASSES.ACTIVE);movePosition(step);var activeItem=listItems.slice(active,active+1).addClass(CLASSES.ACTIVE);if(options.scroll){var offset=0;listItems.slice(0,active).each(function(){offset+=this.offsetHeight;});if((offset+activeItem[0].offsetHeight-list.scrollTop())>list[0].clientHeight){list.scrollTop(offset+activeItem[0].offsetHeight-list.innerHeight());}else if(offset<list.scrollTop()){list.scrollTop(offset);}}};function movePosition(step){active+=step;if(active<0){active=listItems.size()-1;}else if(active>=listItems.size()){active=0;}}function limitNumberOfItems(available){return options.max&&options.max<available?options.max:available;}function fillList(){list.empty();var max=limitNumberOfItems(data.length);for(var i=0;i<max;i++){if(!data[i])continue;var formatted=options.formatItem(data[i].data,i+1,max,data[i].value,term);if(formatted===false)continue;var li=$("<li/>").html(options.highlight(formatted,term)).addClass(i%2==0?"ac_even":"ac_odd").appendTo(list)[0];$.data(li,"ac_data",data[i]);}listItems=list.find("li");if(options.selectFirst){listItems.slice(0,1).addClass(CLASSES.ACTIVE);active=0;}if($.fn.bgiframe)list.bgiframe();}return{display:function(d,q){init();data=d;term=q;fillList();},next:function(){moveSelect(1);},prev:function(){moveSelect(-1);},pageUp:function(){if(active!=0&&active-8<0){moveSelect(-active);}else{moveSelect(-8);}},pageDown:function(){if(active!=listItems.size()-1&&active+8>listItems.size()){moveSelect(listItems.size()-1-active);}else{moveSelect(8);}},hide:function(){element&&element.hide();listItems&&listItems.removeClass(CLASSES.ACTIVE);active=-1;},visible:function(){return element&&element.is(":visible");},current:function(){return this.visible()&&(listItems.filter("."+CLASSES.ACTIVE)[0]||options.selectFirst&&listItems[0]);},show:function(){var offset=$(input).offset();element.css({width:typeof options.width=="string"||options.width>0?options.width:$(input).width(),top:offset.top+input.offsetHeight,left:offset.left}).show();if(options.scroll){list.scrollTop(0);list.css({maxHeight:options.scrollHeight,overflow:'auto'});if($.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var listHeight=0;listItems.each(function(){listHeight+=this.offsetHeight;});var scrollbarsVisible=listHeight>options.scrollHeight;list.css('height',scrollbarsVisible?options.scrollHeight:listHeight);if(!scrollbarsVisible){listItems.width(list.width()-parseInt(listItems.css("padding-left"))-parseInt(listItems.css("padding-right")));}}}},selected:function(){var selected=listItems&&listItems.filter("."+CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected&&selected.length&&$.data(selected[0],"ac_data");},emptyList:function(){list&&list.empty();},unbind:function(){element&&element.remove();}};};$.Autocompleter.Selection=function(field,start,end){if(field.createTextRange){var selRange=field.createTextRange();selRange.collapse(true);selRange.moveStart("character",start);selRange.moveEnd("character",end);selRange.select();}else if(field.setSelectionRange){field.setSelectionRange(start,end);}else{if(field.selectionStart){field.selectionStart=start;field.selectionEnd=end;}}field.focus();};})(jQuery);

/*
 *
 * Copyright (c) 2006-2008 Sam Collett (http://www.texotela.co.uk)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Version 2.2.4
 * Demo: http://www.texotela.co.uk/code/jquery/select/
 *
 * $LastChangedDate: 2008-06-17 17:27:25 +0100 (Tue, 17 Jun 2008) $
 * $Rev: 5727 $
 *
 */
;(function(h){h.fn.addOption=function(){var j=function(a,f,c,g){var d=document.createElement("option");d.value=f,d.text=c;var b=a.options;var e=b.length;if(!a.cache){a.cache={};for(var i=0;i<e;i++){a.cache[b[i].value]=i}}if(typeof a.cache[f]=="undefined")a.cache[f]=e;a.options[a.cache[f]]=d;if(g){d.selected=true}};var k=arguments;if(k.length==0)return this;var l=true;var m=false;var n,o,p;if(typeof(k[0])=="object"){m=true;n=k[0]}if(k.length>=2){if(typeof(k[1])=="boolean")l=k[1];else if(typeof(k[2])=="boolean")l=k[2];if(!m){o=k[0];p=k[1]}}this.each(function(){if(this.nodeName.toLowerCase()!="select")return;if(m){for(var a in n){j(this,a,n[a],l)}}else{j(this,o,p,l)}});return this};h.fn.ajaxAddOption=function(c,g,d,b,e){if(typeof(c)!="string")return this;if(typeof(g)!="object")g={};if(typeof(d)!="boolean")d=true;this.each(function(){var f=this;h.getJSON(c,g,function(a){h(f).addOption(a,d);if(typeof b=="function"){if(typeof e=="object"){b.apply(f,e)}else{b.call(f)}}})});return this};h.fn.removeOption=function(){var d=arguments;if(d.length==0)return this;var b=typeof(d[0]);var e,i;if(b=="string"||b=="object"||b=="function"){e=d[0];if(e.constructor==Array){var j=e.length;for(var k=0;k<j;k++){this.removeOption(e[k],d[1])}return this}}else if(b=="number")i=d[0];else return this;this.each(function(){if(this.nodeName.toLowerCase()!="select")return;if(this.cache)this.cache=null;var a=false;var f=this.options;if(!!e){var c=f.length;for(var g=c-1;g>=0;g--){if(e.constructor==RegExp){if(f[g].value.match(e)){a=true}}else if(f[g].value==e){a=true}if(a&&d[1]===true)a=f[g].selected;if(a){f[g]=null}a=false}}else{if(d[1]===true){a=f[i].selected}else{a=true}if(a){this.remove(i)}}});return this};h.fn.sortOptions=function(e){var i=h(this).selectedValues();var j=typeof(e)=="undefined"?true:!!e;this.each(function(){if(this.nodeName.toLowerCase()!="select")return;var c=this.options;var g=c.length;var d=[];for(var b=0;b<g;b++){d[b]={v:c[b].value,t:c[b].text}}d.sort(function(a,f){o1t=a.t.toLowerCase(),o2t=f.t.toLowerCase();if(o1t==o2t)return 0;if(j){return o1t<o2t?-1:1}else{return o1t>o2t?-1:1}});for(var b=0;b<g;b++){c[b].text=d[b].t;c[b].value=d[b].v}}).selectOptions(i,true);return this};h.fn.selectOptions=function(g,d){var b=g;var e=typeof(g);if(e=="object"&&b.constructor==Array){var i=this;h.each(b,function(){i.selectOptions(this,d)})};var j=d||false;if(e!="string"&&e!="function"&&e!="object")return this;this.each(function(){if(this.nodeName.toLowerCase()!="select")return this;var a=this.options;var f=a.length;for(var c=0;c<f;c++){if(b.constructor==RegExp){if(a[c].value.match(b)){a[c].selected=true}else if(j){a[c].selected=false}}else{if(a[c].value==b){a[c].selected=true}else if(j){a[c].selected=false}}}});return this};h.fn.copyOptions=function(g,d){var b=d||"selected";if(h(g).size()==0)return this;this.each(function(){if(this.nodeName.toLowerCase()!="select")return this;var a=this.options;var f=a.length;for(var c=0;c<f;c++){if(b=="all"||(b=="selected"&&a[c].selected)){h(g).addOption(a[c].value,a[c].text)}}});return this};h.fn.containsOption=function(g,d){var b=false;var e=g;var i=typeof(e);var j=typeof(d);if(i!="string"&&i!="function"&&i!="object")return j=="function"?this:b;this.each(function(){if(this.nodeName.toLowerCase()!="select")return this;if(b&&j!="function")return false;var a=this.options;var f=a.length;for(var c=0;c<f;c++){if(e.constructor==RegExp){if(a[c].value.match(e)){b=true;if(j=="function")d.call(a[c],c)}}else{if(a[c].value==e){b=true;if(j=="function")d.call(a[c],c)}}}});return j=="function"?this:b};h.fn.selectedValues=function(){var a=[];this.selectedOptions().each(function(){a[a.length]=this.value});return a};h.fn.selectedTexts=function(){var a=[];this.selectedOptions().each(function(){a[a.length]=this.text});return a};h.fn.selectedOptions=function(){return this.find("option:selected")}})(jQuery);

/*  Prototype JavaScript framework, version 1.6.0.3
 *  (c) 2005-2008 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.6.0.3',

  Browser: {
    IE:     !!(window.attachEvent &&
      navigator.userAgent.indexOf('Opera') === -1),
    Opera:  navigator.userAgent.indexOf('Opera') > -1,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
      navigator.userAgent.indexOf('KHTML') === -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  },

  BrowserFeatures: {
    XPath: !!document.evaluate,
    SelectorsAPI: !!document.querySelector,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div')['__proto__'] &&
      document.createElement('div')['__proto__'] !==
        document.createElement('form')['__proto__']
  },

  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

  emptyFunction: function() { },
  K: function(x) { return x }
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: function() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      var subclass = function() { };
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;

    return klass;
  }
};

Class.Methods = {
  addMethods: function(source) {
    var ancestor   = this.superclass && this.superclass.prototype;
    var properties = Object.keys(source);

    if (!Object.keys({ toString: true }).length)
      properties.push("toString", "valueOf");

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments) };
        })(property).wrap(method);

        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }

    return this;
  }
};

var Abstract = { };

Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (Object.isUndefined(object)) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : String(object);
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (Object.isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = Object.toJSON(object[property]);
      if (!Object.isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  },

  toQueryString: function(object) {
    return $H(object).toQueryString();
  },

  toHTML: function(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({ }, object);
  },

  isElement: function(object) {
    return !!(object && object.nodeType == 1);
  },

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

  isHash: function(object) {
    return object instanceof Hash;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});

Object.extend(Function.prototype, {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
      .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;
  },

  bind: function() {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = $A(arguments);
    return function() {
      return __method.apply(this, args.concat($A(arguments)));
    }
  },

  delay: function() {
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

  defer: function() {
    var args = [0.01].concat($A(arguments));
    return this.delay.apply(this, args);
  },

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
  }
});

Date.prototype.toJSON = function() {
  return '"' + this.getUTCFullYear() + '-' +
    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
    this.getUTCDate().toPaddedString(2) + 'T' +
    this.getUTCHours().toPaddedString(2) + ':' +
    this.getUTCMinutes().toPaddedString(2) + ':' +
    this.getUTCSeconds().toPaddedString(2) + 'Z"';
};

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) { }
    }

    return returnValue;
  }
};

RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  execute: function() {
    this.callback(this);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
});
Object.extend(String, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = Object.isUndefined(count) ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return String(this);
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = Object.isUndefined(truncation) ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  escapeHTML: function() {
    var self = arguments.callee;
    self.text.data = this;
    return self.div.innerHTML;
  },

  unescapeHTML: function() {
    var div = new Element('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        var value = pair.length > 1 ? pair.join('=') : pair[0];
        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  times: function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  },

  toJSON: function() {
    return this.inspect(true);
  },

  unfilterJSON: function(filter) {
    return this.sub(filter || Prototype.JSONFilter, '#{1}');
  },

  isJSON: function() {
    var str = this;
    if (str.blank()) return false;
    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  },

  evalJSON: function(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  },

  include: function(pattern) {
    return this.indexOf(pattern) > -1;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  empty: function() {
    return this == '';
  },

  blank: function() {
    return /^\s*$/.test(this);
  },

  interpolate: function(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }
});

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (Object.isFunction(replacement)) return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
};

String.prototype.parseQuery = String.prototype.toQueryParams;

Object.extend(String.prototype.escapeHTML, {
  div:  document.createElement('div'),
  text: document.createTextNode('')
});

String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return '';

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3];
      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
      match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
        ctx = ctx[comp];
        if (null == ctx || '' == match[3]) break;
        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
        match = pattern.exec(expr);
      }

      return before + String.interpret(ctx);
    });
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    try {
      this._each(function(value) {
        iterator.call(context, value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    var index = -number, slices = [], array = this.toArray();
    if (number < 1) return array;
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator.call(context, value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator.call(context, value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator.call(context, value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    var result;
    this.each(function(value, index) {
      if (iterator.call(context, value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator.call(context, value, index));
    });
    return results;
  },

  include: function(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    this.each(function(value, index) {
      memo = iterator.call(context, memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator.call(context, value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    return this.map(function(value, index) {
      return {
        value: value,
        criteria: iterator.call(context, value, index)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
});
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];
    // In Safari, only use the `toArray` method if it's not a NodeList.
    // A NodeList is a function, has an function `item` property, and a numeric
    // `length` property. Adapted from Google Doctype.
    if (!(typeof iterable === 'function' && typeof iterable.length ===
        'number' && typeof iterable.item === 'function') && iterable.toArray)
      return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(Object.isArray(value) ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  },

  intersect: function(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  },

  toJSON: function() {
    var results = [];
    this.each(function(object) {
      var value = Object.toJSON(object);
      if (!Object.isUndefined(value)) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }
});

// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
};

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
  var n = this.slice(0, i).reverse().indexOf(item);
  return (n < 0) ? n : i - n - 1;
};

Array.prototype.toArray = Array.prototype.clone;

function $w(string) {
  if (!Object.isString(string)) return [];
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if (Prototype.Browser.Opera){
  Array.prototype.concat = function() {
    var array = [];
    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for (var i = 0, length = arguments.length; i < length; i++) {
      if (Object.isArray(arguments[i])) {
        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  };
}
Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator, context) {
    $R(0, this, true).each(iterator, context);
    return this;
  },

  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },

  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {

  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  return {
    initialize: function(object) {
      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    },

    _each: function(iterator) {
      for (var key in this._object) {
        var value = this._object[key], pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
      }
    },

    set: function(key, value) {
      return this._object[key] = value;
    },

    get: function(key) {
      // simulating poorly supported hasOwnProperty
      if (this._object[key] !== Object.prototype[key])
        return this._object[key];
    },

    unset: function(key) {
      var value = this._object[key];
      delete this._object[key];
      return value;
    },

    toObject: function() {
      return Object.clone(this._object);
    },

    keys: function() {
      return this.pluck('key');
    },

    values: function() {
      return this.pluck('value');
    },

    index: function(value) {
      var match = this.detect(function(pair) {
        return pair.value === value;
      });
      return match && match.key;
    },

    merge: function(object) {
      return this.clone().update(object);
    },

    update: function(object) {
      return new Hash(object).inject(this, function(result, pair) {
        result.set(pair.key, pair.value);
        return result;
      });
    },

    toQueryString: function() {
      return this.inject([], function(results, pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

        if (values && typeof values == 'object') {
          if (Object.isArray(values))
            return results.concat(values.map(toQueryPair.curry(key)));
        } else results.push(toQueryPair(key, values));
        return results;
      }).join('&');
    },

    inspect: function() {
      return '#<Hash:{' + this.map(function(pair) {
        return pair.map(Object.inspect).join(': ');
      }).join(', ') + '}>';
    },

    toJSON: function() {
      return Object.toJSON(this.toObject());
    },

    clone: function() {
      return new Hash(this);
    }
  }
})());

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
};

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (Object.isFunction(responder[callback])) {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) { }
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate:   function() { Ajax.activeRequestCount++ },
  onComplete: function() { Ajax.activeRequestCount-- }
});

Ajax.Base = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    else if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  }
});

Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

    try {
      var response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch('onCreate', this, response);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300);
  },

  getStatus: function() {
    try {
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + response.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(response, response.headerJSON);
      } catch (e) {
        this.dispatchException(e);
      }

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name) || null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval((this.transport.responseText || '').unfilterJSON());
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
    }

    if(readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

  status:      0,
  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function() {
    try {
      return this.transport.statusText || '';
    } catch (e) { return '' }
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: function() {
    try {
      return this.getAllResponseHeaders();
    } catch (e) { return null }
  },

  getResponseHeader: function(name) {
    return this.transport.getResponseHeader(name);
  },

  getAllResponseHeaders: function() {
    return this.transport.getAllResponseHeaders();
  },

  _getHeaderJSON: function() {
    var json = this.getHeader('X-JSON');
    if (!json) return null;
    json = decodeURIComponent(escape(json));
    try {
      return json.evalJSON(this.request.options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  },

  _getResponseJSON: function() {
    var options = this.request.options;
    if (!options.evalJSON || (options.evalJSON != 'force' &&
      !(this.getHeader('Content-type') || '').include('application/json')) ||
        this.responseText.blank())
          return null;
    try {
      return this.responseText.evalJSON(options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  }
});

Ajax.Updater = Class.create(Ajax.Request, {
  initialize: function($super, container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    };

    options = Object.clone(options);
    var onComplete = options.onComplete;
    options.onComplete = (function(response, json) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, json);
    }).bind(this);

    $super(url, options);
  },

  updateContent: function(responseText) {
    var receiver = this.container[this.success() ? 'success' : 'failure'],
        options = this.options;

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
      if (options.insertion) {
        if (Object.isString(options.insertion)) {
          var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }
        else options.insertion(receiver, responseText);
      }
      else receiver.update(responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = { };
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(Element.extend(query.snapshotItem(i)));
    return results;
  };
}

/*--------------------------------------------------------------------------*/

if (!window.Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  // DOM level 2 ECMAScript Language Binding
  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }
    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
  if (element) this.Element.prototype = element.prototype;
}).call(window);

Element.cache = { };

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    element = $(element);
    element.style.display = 'none';
    return element;
  },

  show: function(element) {
    element = $(element);
    element.style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);
    content = Object.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  replace: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    else if (!Object.isElement(content)) {
      content = Object.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = $(element);

    if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = $(element);
    if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes || { });
    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    else wrapper = new Element('div', wrapper);
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $(element).select("*");
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (Object.isString(selector))
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = element.ancestors();
    return Object.isNumber(expression) ? ancestors[expression] :
      Selector.findElement(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return element.firstDescendant();
    return Object.isNumber(expression) ? element.descendants()[expression] :
      Element.select(element, expression)[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    var previousSiblings = element.previousSiblings();
    return Object.isNumber(expression) ? previousSiblings[expression] :
      Selector.findElement(previousSiblings, expression, index);
  },

  next: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    var nextSiblings = element.nextSiblings();
    return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  },

  select: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  adjacent: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element.parentNode, args).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = element.readAttribute('id'), self = arguments.callee;
    if (id) return id;
    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    element.writeAttribute('id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = $(element);
    var attributes = { }, t = Element._attributeTranslations.write;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = Object.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t.names[attr] || attr;
      value = attributes[attr];
      if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!element.hasClassName(className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    return element[element.hasClassName(className) ?
      'removeClassName' : 'addClassName'](className);
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.blank();
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (ancestor.contains)
      return ancestor.contains(element) && ancestor !== element;

    while (element = element.parentNode)
      if (element == ancestor) return true;

    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = element.cumulativeOffset();
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value || value == 'auto') {
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return $(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = $(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = element.getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (Prototype.Browser.Opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName.toUpperCase() == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'absolute') return element;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
    return element;
  },

  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'relative') return element;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
    return element;
  },

  cumulativeScrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return $(element);

    return $(document.body);
  },

  viewportOffset: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') == 'absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return Element._returnOffset(valueL, valueT);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    // find page position of source
    source = $(source);
    var p = source.viewportOffset();

    // find coordinate system to use
    element = $(element);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
    return element;
  }
};

Element.Methods.identify.counter = 1;

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};

if (Prototype.Browser.Opera) {
  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'left': case 'top': case 'right': case 'bottom':
          if (proceed(element, 'position') === 'static') return null;
        case 'height': case 'width':
          // returns '0px' for hidden elements; we want it to return null
          if (!Element.visible(element)) return null;

          // returns the border-box dimensions rather than the content-box
          // dimensions, so we subtract padding and borders from the value
          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );

  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (Prototype.Browser.IE) {
  // IE doesn't report offsets correctly for static elements, so we change them
  // to "relative" to get the values, then change them back.
  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
    function(proceed, element) {
      element = $(element);
      // IE throws an error if element is not in document
      try { element.offsetParent }
      catch(e) { return $(document.body) }
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);
      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    }
  );

  $w('positionedOffset viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        try { element.offsetParent }
        catch(e) { return Element._returnOffset(0,0) }
        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);
        // Trigger hasLayout on the offset parent so that IE6 reports
        // accurate offsetTop and offsetLeft values for position: fixed.
        var offsetParent = element.getOffsetParent();
        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
          offsetParent.setStyle({ zoom: 1 });
        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );
  });

  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
    function(proceed, element) {
      try { element.offsetParent }
      catch(e) { return Element._returnOffset(0,0) }
      return proceed(element);
    }
  );

  Element.Methods.getStyle = function(element, style) {
    element = $(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  Element.Methods.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = $(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };

  Element._attributeTranslations = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _flag: function(element, attribute) {
          return $(element).hasAttribute(attribute) ? attribute : null;
        },
        style: function(element) {
          return element.style.cssText.toLowerCase();
        },
        title: function(element) {
          return element.title;
        }
      }
    }
  };

  Element._attributeTranslations.write = {
    names: Object.extend({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr,
      src:         v._getAttr,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);
}

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };

  // Safari returns margins on body which is incorrect if the child is absolutely
  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
  // KHTML/WebKit only.
  Element.Methods.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return Element._returnOffset(valueL, valueT);
  };
}

if (Prototype.Browser.IE || Prototype.Browser.Opera) {
  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
  Element.Methods.update = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);

    content = Object.toHTML(content);
    var tagName = element.tagName.toUpperCase();

    if (tagName in Element._insertionTranslations.tags) {
      $A(element.childNodes).each(function(node) { element.removeChild(node) });
      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

if ('outerHTML' in document.createElement('div')) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) {
      element.parentNode.replaceChild(content, element);
      return element;
    }

    content = Object.toHTML(content);
    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next();
      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
      parent.removeChild(element);
      if (nextSibling)
        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
      else
        fragments.each(function(node) { parent.appendChild(node) });
    }
    else element.outerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

Element._returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

Element._getContentFromAnonymousElement = function(tagName, html) {
  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  Object.extend(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(Element._insertionTranslations);

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    attribute = Element._attributeTranslations.has[attribute] || attribute;
    var node = $(element).getAttributeNode(attribute);
    return !!(node && node.specified);
  }
};

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

if (!Prototype.BrowserFeatures.ElementExtensions &&
    document.createElement('div')['__proto__']) {
  window.HTMLElement = { };
  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
  Prototype.BrowserFeatures.ElementExtensions = true;
}

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || element._extendedByPrototype ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
      tagName = element.tagName.toUpperCase(), property, value;

    // extend methods for specific tags
    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    for (property in methods) {
      value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      // extend methods for all tags (Safari doesn't need this)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

Element.hasAttribute = function(element, attribute) {
  if (element.hasAttribute) return element.hasAttribute(attribute);
  return Element.Methods.Simulated.hasAttribute(element, attribute);
};

Element.addMethods = function(methods) {
  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (!methods) {
    Object.extend(Form, Form.Methods);
    Object.extend(Form.Element, Form.Element.Methods);
    Object.extend(Element.Methods.ByTag, {
      "FORM":     Object.clone(Form.Methods),
      "INPUT":    Object.clone(Form.Element.Methods),
      "SELECT":   Object.clone(Form.Element.Methods),
      "TEXTAREA": Object.clone(Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var tagName = methods;
    methods = arguments[1];
  }

  if (!tagName) Object.extend(Element.Methods, methods || { });
  else {
    if (Object.isArray(tagName)) tagName.each(extend);
    else extend(tagName);
  }

  function extend(tagName) {
    tagName = tagName.toUpperCase();
    if (!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName] = { };
    Object.extend(Element.Methods.ByTag[tagName], methods);
  }

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    for (var property in methods) {
      var value = methods[property];
      if (!Object.isFunction(value)) continue;
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = value.methodize();
    }
  }

  function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "FrameSet", "IFRAME": "IFrame"
    };
    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    window[klass] = { };
    window[klass].prototype = document.createElement(tagName)['__proto__'];
    return window[klass];
  }

  if (F.ElementExtensions) {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

  Object.extend(Element, Element.Methods);
  delete Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh();
  Element.cache = { };
};

document.viewport = {
  getDimensions: function() {
    var dimensions = { }, B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      if (B.WebKit && !document.evaluate) {
        // Safari <3.0 needs self.innerWidth/Height
        dimensions[d] = self['inner' + D];
      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
        // Opera <9.5 needs document.body.clientWidth/Height
        dimensions[d] = document.body['client' + D]
      } else {
        dimensions[d] = document.documentElement['client' + D];
      }
    });
    return dimensions;
  },

  getWidth: function() {
    return this.getDimensions().width;
  },

  getHeight: function() {
    return this.getDimensions().height;
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
  }
};
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();

    if (this.shouldUseSelectorsAPI()) {
      this.mode = 'selectorsAPI';
    } else if (this.shouldUseXPath()) {
      this.mode = 'xpath';
      this.compileXPathMatcher();
    } else {
      this.mode = "normal";
      this.compileMatcher();
    }

  },

  shouldUseXPath: function() {
    if (!Prototype.BrowserFeatures.XPath) return false;

    var e = this.expression;

    // Safari 3 chokes on :*-of-type and :empty
    if (Prototype.Browser.WebKit &&
     (e.include("-of-type") || e.include(":empty")))
      return false;

    // XPath can't do namespaced attributes, nor can it read
    // the "checked" property from DOM nodes
    if ((/(\[[\w-]*?:|:checked)/).test(e))
      return false;

    return true;
  },

  shouldUseSelectorsAPI: function() {
    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;

    if (!Selector._div) Selector._div = new Element('div');

    // Make sure the browser treats the selector as valid. Test on an
    // isolated element to minimize cost of this check.
    try {
      Selector._div.querySelector(this.expression);
    } catch(e) {
      return false;
    }

    return true;
  },

  compileMatcher: function() {
    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

    if (Selector._cache[e]) {
      this.matcher = Selector._cache[e];
      return;
    }

    this.matcher = ["this.matcher = function(root) {",
                    "var r = root, h = Selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
            new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression] = this.matcher;
  },

  compileXPathMatcher: function() {
    var e = this.expression, ps = Selector.patterns,
        x = Selector.xpath, le, m;

    if (Selector._cache[e]) {
      this.xpath = Selector._cache[e]; return;
    }

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.xpath = this.matcher.join('');
    Selector._cache[this.expression] = this.xpath;
  },

  findElements: function(root) {
    root = root || document;
    var e = this.expression, results;

    switch (this.mode) {
      case 'selectorsAPI':
        // querySelectorAll queries document-wide, then filters to descendants
        // of the context element. That's not what we want.
        // Add an explicit context to the selector if necessary.
        if (root !== document) {
          var oldId = root.id, id = $(root).identify();
          e = "#" + id + " " + e;
        }

        results = $A(root.querySelectorAll(e)).map(Element.extend);
        root.id = oldId;

        return results;
      case 'xpath':
        return document._getElementsByXPath(this.xpath, root);
      default:
       return this.matcher(root);
    }
  },

  match: function(element) {
    this.tokens = [];

    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    var le, p, m;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          // use the Selector.assertions methods unless the selector
          // is too complex.
          if (as[i]) {
            this.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            // reluctantly do a document-wide search
            // and look for a match in the array
            return this.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = this.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

  toString: function() {
    return this.expression;
  },

  inspect: function() {
    return "#<Selector:" + this.expression.inspect() + ">";
  }
});

Object.extend(Selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = Selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
      'checked':     "[@checked]",
      'disabled':    "[(@disabled) and (@type!='hidden')]",
      'enabled':     "[not(@disabled) and (@type!='hidden')]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, v;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return Selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: {
    // combinators must be listed first
    // (and descendant needs to be last combinator)
    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,

    // selectors follow
    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:           /^#([\w\-\*]+)(\b|$)/,
    className:    /^\.([\w\-\*]+)(\b|$)/,
    pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
  },

  // for Selector.match and Element#match
  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

  handlers: {
    // UTILITY FUNCTIONS
    // joins two collections
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },

    // marks an array of nodes for counting
    mark: function(nodes) {
      var _true = Prototype.emptyFunction;
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = _true;
      return nodes;
    },

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = undefined;
      return nodes;
    },

    // mark each child node with its position (for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index: function(parentNode, reverse, ofType) {
      parentNode._countedByPrototype = Prototype.emptyFunction;
      if (reverse) {
        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          var node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
      }
    },

    // filters out duplicates and extends all nodes
    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._countedByPrototype) {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },

    // COMBINATOR FUNCTIONS
    descendant: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    // TOKEN FUNCTIONS
    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {
          // fastlane for ordinary descendant combinators
          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return Selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = Selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return Selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },

    // handles the an+b logic
    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },

    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._countedByPrototype) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        // IE treats comments as element nodes
        if (node.tagName == '!' || node.firstChild) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = Selector.handlers, selectorType, m;
      var exclusions = new Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._countedByPrototype) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled && (!node.type || node.type !== 'hidden'))
          results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
    '$=': function(nv, v) { return nv.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     '-').include('-' + (v || "").toUpperCase() + '-'); }
  },

  split: function(expression) {
    var expressions = [];
    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    return expressions;
  },

  matchElements: function(elements, expression) {
    var matches = $$(expression), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (Object.isNumber(expression)) {
      index = expression; expression = false;
    }
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    expressions = Selector.split(expressions.join(','));
    var results = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
});

if (Prototype.Browser.IE) {
  Object.extend(Selector.handlers, {
    // IE returns comment nodes on getElementsByTagName("*").
    // Filter them out.
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        if (node.tagName !== "!") a.push(node);
      return a;
    },

    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node.removeAttribute('_countedByPrototype');
      return nodes;
    }
  });
}

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (Object.isUndefined(options.hash)) options.hash = true;
    var key, value, submitted = false, submit = options.submit;

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {
            // a key is already present; construct an array of values
            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return options.hash ? data : Object.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, options) {
    return Form.serializeElements(Form.getElements(form), options);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('disable');
    return form;
  },

  enable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('enable');
    return form;
  },

  findFirstElement: function(form) {
    var elements = $(form).getElements().findAll(function(element) {
      return 'hidden' != element.type && !element.disabled;
    });
    var firstByIndex = elements.findAll(function(element) {
      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
    }).sortBy(function(element) { return element.tabIndex }).first();

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  },

  request: function(form, options) {
    form = $(form), options = Object.clone(options || { });

    var params = options.parameters, action = form.readAttribute('action') || '';
    if (action.blank()) action = window.location.href;
    options.parameters = form.serialize(true);

    if (params) {
      if (Object.isString(params)) params = params.toQueryParams();
      Object.extend(options.parameters, params);
    }

    if (form.hasAttribute('method') && !options.method)
      options.method = form.method;

    return new Ajax.Request(action, options);
  }
};

/*--------------------------------------------------------------------------*/

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
};

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = { };
        pair[element.name] = value;
        return Object.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  setValue: function(element, value) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    Form.Element.Serializers[method](element, value);
    return element;
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    try {
      element.focus();
      if (element.select && (element.tagName.toLowerCase() != 'input' ||
          !['button', 'reset', 'submit'].include(element.type)))
        element.select();
    } catch (e) { }
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.disabled = false;
    return element;
  }
};

/*--------------------------------------------------------------------------*/

var Field = Form.Element;
var $F = Form.Element.Methods.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element, value) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element, value);
      default:
        return Form.Element.Serializers.textarea(element, value);
    }
  },

  inputSelector: function(element, value) {
    if (Object.isUndefined(value)) return element.checked ? element.value : null;
    else element.checked = !!value;
  },

  textarea: function(element, value) {
    if (Object.isUndefined(value)) return element.value;
    else element.value = value;
  },

  select: function(element, value) {
    if (Object.isUndefined(value))
      return this[element.type == 'select-one' ?
        'selectOne' : 'selectMany'](element);
    else {
      var opt, currentValue, single = !Object.isArray(value);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        currentValue = this.optionValue(opt);
        if (single) {
          if (currentValue == value) {
            opt.selected = true;
            return;
          }
        }
        else opt.selected = value.include(currentValue);
      }
    }
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
};

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  initialize: function($super, element, frequency, callback) {
    $super(callback, frequency);
    this.element   = $(element);
    this.lastValue = this.getValue();
  },

  execute: function() {
    var value = this.getValue();
    if (Object.isString(this.lastValue) && Object.isString(value) ?
        this.lastValue != value : String(this.lastValue) != String(value)) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
});

Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = Class.create({
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback, this);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
});

Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) var Event = { };

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      event = Event.extend(event);

      var node          = event.target,
          type          = event.type,
          currentTarget = event.currentTarget;

      if (currentTarget && currentTarget.tagName) {
        // Firefox screws up the "click" event when moving between radio buttons
        // via arrow keys. It also screws up the "load" and "error" events on images,
        // reporting the document as the target instead of the original image.
        if (type === 'load' || type === 'error' ||
          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
            && currentTarget.type === 'radio'))
              node = currentTarget;
      }
      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
      return Element.extend(node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return Selector.findElement(elements, expression, 0);
    },

    pointer: function(event) {
      var docElement = document.documentElement,
      body = document.body || { scrollLeft: 0, scrollTop: 0 };
      return {
        x: event.pageX || (event.clientX +
          (docElement.scrollLeft || body.scrollLeft) -
          (docElement.clientLeft || 0)),
        y: event.pageY || (event.clientY +
          (docElement.scrollTop || body.scrollTop) -
          (docElement.clientTop || 0))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }


  // Internet Explorer needs to remove event handlers on page unload
  // in order to avoid memory leaks.
  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  // Safari has a dummy event handler on page unload so that it won't
  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
  // object when page is returned to via the back button using its bfcache.
  if (Prototype.Browser.WebKit) {
    window.addEventListener('unload', Prototype.emptyFunction, false);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    document.loaded = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  // Deprecation layer -- use newer Element methods now (1.5.2).

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

/*--------------------------------------------------------------------------*/

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

/*--------------------------------------------------------------------------*/

Element.addMethods();

/*
  Proto!MultiSelect
  Copyright: InteRiders <http://interiders.com/> - Distributed under MIT - Keep this message! 
*/

// Added key contstant for COMMA watching happiness
Object.extend(Event, { KEY_COMMA: 188, KEY_SPACE: 32 });

var ResizableTextbox = Class.create({ 
  initialize: function(element, options) { 
    var that = this;
    this.options = $H({
      min: 5,
      max: 500,
      step: 7
    });
    this.options.update(options);
    this.el = $(element);
    this.width = this.el.offsetWidth;
    this.el.observe(
      'keyup', function() {
        var newsize = that.options.get('step') * $F(this).length;
        if(newsize <= that.options.get('min')) newsize = that.width;
        /* Commented out by Miles Romney
           These lines were causing the input width to go to 0 in Facebox contexts
        if(! ($F(this).length == this.retrieveData('rt-value') || newsize <= that.options.min || newsize >= that.options.max))
          this.setStyle({'width': newsize});
        */
      }).observe('keydown', function() {
        this.cacheData('rt-value', $F(this).length);
      }
    );
  }
});

var TextboxList = Class.create({
  initialize: function(element, options) {
    this.options = $H({
      // onFocus: $empty,
      // onBlur: $empty,
      // onInputFocus: $empty,
      // onInputBlur: $empty,
      // onBoxFocus: $empty,
      // onBoxBlur: $empty,
      // onBoxDispose: $empty,
      resizable: {},
      className: 'bit',
      separator: ',',
      extrainputs: true,
      startinput: true,
      hideempty: true,
      newValues: true,
      newValueDelimiters: [',',','],
      spaceReplace: '',
      fetchFile: undefined,
      fetchMethod: 'get',
      results: 10,
      maxResults: 0, // 0 = set to default (which is 10 (see FacebookList class)),
      wordMatch: false,
      onEmptyInput: function(input){},
      caseSensitive: false,
      regexSearch: true
    });
    this.options.update(options);
    this.element = $(element).hide();
    this.bits = new Hash();
    this.events = new Hash();
    this.count = 0;
    this.current = false;
    this.maininput = this.createInput({'class': 'maininput'});
    this.holder = new Element('ul', {
      'class': 'holder'
    }).insert(this.maininput);
    this.element.insert({'before':this.holder});
    this.holder.observe('click', function(event){
      event.stop();
      if(this.maininput != this.current) this.focus(this.maininput);
    }.bind(this));
    this.makeResizable(this.maininput);
    this.setEvents();
    // Added by flavio
    // The bits property hold values to be submitted, make sure it's filled with the provided element values
    // or it won't retain previous ones by replacing with new ones.
    var h = new Hash();
    // id is a string to be used as LI items' id, used to manipulate the facebooklist and get/set values to submoite on the element
    var id = $(element).identify() + "_" + this.options.get("className");
    var count = 0;
    this.element.value.split(',').each( function(e){ h.set( (id + '_' + count++), e) } );
    this.bits = h;
    // 
  },
  
  setEvents: function() {
    document.observe(Prototype.Browser.IE ? 'keydown' : 'keypress', function(e) {
      if(! this.current) return;
      if(this.current.retrieveData('type') == 'box' && e.keyCode == Event.KEY_BACKSPACE) e.stop();
    }.bind(this));
         
    document.observe(
      'keyup', function(e) {
        e.stop();
        if(! this.current) return;
        switch(e.keyCode){
          case Event.KEY_LEFT: return this.move('left');
          case Event.KEY_RIGHT: return this.move('right');
          case Event.KEY_DELETE:
          case Event.KEY_BACKSPACE: return this.moveDispose();
        }
      }.bind(this)).observe(  
      'click', function() { document.fire('blur'); }.bindAsEventListener(this)
    );
  },
  
  update: function() {
    this.element.value = this.bits.values().join(this.options.get('separator'));
    return this;
  },
  
  add: function(text, html) {
    var id = this.id_base + '_' + this.count++;
    var el = this.createBox($pick(html, text), {'id': id, 'class': this.options.get('className'), 'newValue' : text.newValue ? 'true' : 'false'});
    (this.current || this.maininput).insert({'before':el});                                         
    el.observe('click', function(e) {
      e.stop();
      this.focus(el);
    }.bind(this));
    this.bits.set(id, text.value);
    // Dynamic updating... why not?
    this.update();
    if(this.options.get('extrainputs') && (this.options.get('startinput') || el.previous())) this.addSmallInput(el,'before');
    return el;
  },
  
  addSmallInput: function(el, where) {
    var input = this.createInput({'class': 'smallinput'});
    el.insert({}[where] = input);
    input.cacheData('small', true);
    this.makeResizable(input);
    if(this.options.get('hideempty')) input.hide();
    return input;
  },
  
  dispose: function(el) {
    this.bits.unset(el.id);
    // Dynamic updating... why not?
    this.update();
    if(el.previous() && el.previous().retrieveData('small')) el.previous().remove();
    if(this.current == el) this.focus(el.next());
    if(el.retrieveData('type') == 'box') el.onBoxDispose(this);
    el.remove();
    return this;
  },
  
  focus: function(el, nofocus) {
    if(! this.current) el.fire('focus');
    else if(this.current == el) return this;
    this.blur();
    el.addClassName(this.options.get('className') + '-' + el.retrieveData('type') + '-focus');
    if(el.retrieveData('small')) el.setStyle({'display': 'block'});
    if(el.retrieveData('type') == 'input') {
      el.onInputFocus(this);
      if(! nofocus) this.callEvent(el.retrieveData('input'), 'focus');
    }
    else el.fire('onBoxFocus');
    this.current = el;
    return this;
  },
  
  blur: function(noblur) {
    if(! this.current) return this;
    if(this.current.retrieveData('type') == 'input') {
      var input = this.current.retrieveData('input');
      if(! noblur) this.callEvent(input, 'blur');
      input.onInputBlur(this);
    }
    else this.current.fire('onBoxBlur');
    if(this.current.retrieveData('small') && ! input.get('value') && this.options.get('hideempty')) 
      this.current.hide();
    this.current.removeClassName(this.options.get('className') + '-' + this.current.retrieveData('type') + '-focus');
    this.current = false;
    return this;
  },
  
  createBox: function(text, options) {
    return new Element('li', options).addClassName(this.options.get('className') + '-box').update(text.caption + '&#160;').cacheData('type', 'box');
  },
  
  createInput: function(options) {
    var li = new Element('li', {'class': this.options.get('className') + '-input'});
    var el = new Element('input', Object.extend(options,{'type': 'text', 'autocomplete':'off'}));
    el.observe('click', function(e) { e.stop(); }).observe('focus', function(e) { if(! this.isSelfEvent('focus')) this.focus(li, true); }.bind(this)).observe('blur', function() { if(! this.isSelfEvent('blur')) this.blur(true); }.bind(this)).observe('keydown', function(e) { this.cacheData('lastvalue', this.value).cacheData('lastcaret', this.getCaretPosition()); });
    var tmp = li.cacheData('type', 'input').cacheData('input', el).insert(el);
    return tmp;
  },
  
  callEvent: function(el, type) {
    this.events.set(type, el);
    el[type]();
  },
  
  isSelfEvent: function(type) {
    return (this.events.get(type)) ? !! this.events.unset(type) : false;
  },
  
  makeResizable: function(li) {
    var el = li.retrieveData('input');
    el.cacheData('resizable', new ResizableTextbox(el, Object.extend(this.options.get('resizable'),{min: el.offsetWidth, max: (this.element.getWidth()?this.element.getWidth():0)})));
    return this;
  },
  
  checkInput: function() {
    var input = this.current.retrieveData('input');
    return (! input.retrieveData('lastvalue') || (input.getCaretPosition() === 0 && input.retrieveData('lastcaret') === 0));
  },
  
  move: function(direction) {
    var el = this.current[(direction == 'left' ? 'previous' : 'next')]();
    if(el && (! this.current.retrieveData('input') || ((this.checkInput() || direction == 'right')))) this.focus(el);
    return this;
  },
  
  moveDispose: function() {
    if(this.current.retrieveData('type') == 'box') return this.dispose(this.current);
    if(this.checkInput() && this.bits.keys().length && this.current.previous()) return this.focus(this.current.previous());
  },

  // Added by Nelson Fernandez 11/30/2009
  // clear the internal state
  reset: function() {
    this.bits = new Hash();
    this.update();
  }
});

//helper functions 
Element.addMethods({
  getCaretPosition: function() {
    if (this.createTextRange) {
      var r = document.selection.createRange().duplicate();
      r.moveEnd('character', this.value.length);
      if (r.text === '') return this.value.length;
      return this.value.lastIndexOf(r.text);
    } else return this.selectionStart;
  },
  cacheData: function(element, key, value) { 
    if (Object.isUndefined(this[$(element).identify()]) || !Object.isHash(this[$(element).identify()]))
      this[$(element).identify()] = $H();
    this[$(element).identify()].set(key,value);
    return element;
  },
  retrieveData: function(element,key) {
    return this[$(element).identify()].get(key);
  }  
});

function $pick(){for(var B=0,A=arguments.length;B<A;B++){if(!Object.isUndefined(arguments[B])){return arguments[B];}}return null;}

var FacebookList = Class.create(TextboxList, { 
  initialize: function($super,element, autoholder, options, func) {
    $super(element, options);
    this.loptions = $H({    
      autocomplete: {
        'opacity': 1,
        'maxresults': 10,
        'minchars': 1
      }
    });
    
    this.id_base = $(element).identify() + "_" + this.options.get("className");
    
    this.data = [];
    this.data_searchable = [];
    this.autoholder = $(autoholder).setOpacity(this.loptions.get('autocomplete').opacity);
    this.autoholder.observe('mouseover',function() {this.curOn = true;}.bind(this)).observe('mouseout',function() {this.curOn = false;}.bind(this));
    this.autoresults = this.autoholder.select('ul').first();
    var children = this.autoresults.select('li');
    // Added by flavio
    // this line was not working, it sets the bits/element values to null so previous element values were lost.
    //     children.each(function(el) { this.add({value:el.readAttribute('value'),caption:el.innerHTML}); }, this);
    // replaced by:
    var count = 0;
    children.each(function(el) { 
        this.add({value: this.bits.get(this.id_base + '_' + count++), caption: el.innerHTML});
      }, this);
    //
    
    // Loading the options list only once at initialize. 
    // This would need to be further extended if the list was exceptionally long
    if (!Object.isUndefined(this.options.get('fetchFile'))) {
      new Ajax.Request(this.options.get('fetchFile'), {
        method: this.options.get('fetchMethod'),
        onSuccess: function(transport) {
          transport.responseText.evalJSON(true).each(function(t) {
            this.autoFeed(t) }.bind(this));
          }.bind(this)
        }
      );
    }
  },
  
  autoShow: function(search) {
    this.autoholder.setStyle({'display': 'block'});
    this.autoholder.descendants().each(function(e) { e.hide() });
    if(! search || ! search.strip() || (! search.length || search.length < this.loptions.get('autocomplete').minchars)) {
      this.autoholder.select('.default').first().setStyle({'display': 'block'});
      this.resultsshown = false;
    } else {
      this.resultsshown = true;
      this.autoresults.setStyle({'display': 'block'}).update('');
      if (!this.options.get('regexSearch')) {
        var matches = new Array();
        if (search) {
          if (!this.options.get('caseSensitive')) {
            search = search.toLowerCase();
          }
          var matches_found = 0;
          for (var i=0,len=this.data_searchable.length; i<len; i++) {
            if (this.data_searchable[i].indexOf(search) >= 0) {
              matches[matches_found++] = this.data[i];
            }
          }
        }
      } else {
        if (this.options.get('wordMatch')) {
          var regexp = new RegExp("(^|\\s)"+search,(!this.options.get('caseSensitive') ? 'i' : ''));
        } else {
          var regexp = new RegExp(search,(!this.options.get('caseSensitive') ? 'i' : ''));
          var matches = this.data.filter( 
            function(str) { 
            return str ? regexp.test(str.evalJSON(true).caption) : false;
          });
        }
      }
      var count = 0;
      matches.each(
        function(result, ti) {
          count++;
          if(ti >= (this.options.get('maxResults') ? this.options.get('maxResults') : this.loptions.get('autocomplete').maxresults)) return;
          var that = this;
          var el = new Element('li');
          el.observe('click',function(e) { 
              e.stop();
              that.autoAdd(this); 
            }
          ).observe('mouseover', function() { that.autoFocus(this); } ).update(
            this.autoHighlight(result.evalJSON(true).caption, search)
          );
          this.autoresults.insert(el);
          el.cacheData('result', result.evalJSON(true));
          if(ti == 0) this.autoFocus(el);
        }, 
        this
      );
    }
    // commented out for better RE styles
    // if (count > this.options.get('results'))
    //   this.autoresults.setStyle({'height': (this.options.get('results')*24)+'px'});
    // else
    //   this.autoresults.setStyle({'height': (count?(count*24):0)+'px'});
    return this;
  },
  
  autoHighlight: function(html, highlight) {
    return html.gsub(new RegExp(highlight,'i'), function(match) {
      return '<em>' + match[0] + '</em>';
    });
  },
  
  autoHide: function() {
    this.resultsshown = false;
    this.autoholder.hide();
    return this;
  },
  
  autoFocus: function(el) {
    if(! el) return;
    if(this.autocurrent) this.autocurrent.removeClassName('auto-focus');
    this.autocurrent = el.addClassName('auto-focus');
    return this;
  },
  
  autoMove: function(direction) {
    if(!this.resultsshown) return;
    this.autoFocus(this.autocurrent[(direction == 'up' ? 'previous' : 'next')]());
    this.autoresults.scrollTop = this.autocurrent.positionedOffset()[1]-this.autocurrent.getHeight();
    return this;
  },
  
  autoFeed: function(text) {
    var with_case = this.options.get('caseSensitive');
    if (this.data.indexOf(Object.toJSON(text)) == -1) {
      this.data.push(Object.toJSON(text));
      this.data_searchable.push(with_case ? Object.toJSON(text).evalJSON(true).caption : Object.toJSON(text).evalJSON(true).caption.toLowerCase());
    }
    return this;
  },
  
  autoAdd: function(el) {
    if(this.newvalue && this.options.get("newValues")) {
      this.add({caption: el.value, value: el.value, newValue: true});
      var input = el;
    } else if(!el || ! el.retrieveData('result')) {
      return;
    } else {
      this.add(el.retrieveData('result'));
      delete this.data[this.data.indexOf(Object.toJSON(el.retrieveData('result')))];
      var input = this.lastinput || this.current.retrieveData('input');
    }
    this.autoHide();
    input.clear().focus();
    return this;
  },
  
  createInput: function($super,options) {
    var li = $super(options);
    var input = li.retrieveData('input');
    input.observe('keydown', function(e) {
      this.dosearch = false;
      this.newvalue = false;
      
      switch(e.keyCode) {
        case Event.KEY_UP: e.stop(); return this.autoMove('up');
        case Event.KEY_DOWN: e.stop(); return this.autoMove('down');
        case Event.KEY_COMMA:
          if(this.options.get('newValues')) {
            new_value_el = this.current.retrieveData('input');
            new_value_el.value = new_value_el.value.strip().gsub(",","");
            if(!this.options.get("spaceReplace").blank()) new_value_el.value.gsub(" ", this.options.get("spaceReplace"));
            if(!new_value_el.value.blank()) {
              e.stop();
              this.newvalue = true;
              this.autoAdd(new_value_el);
            }
          }
          break;
        case Event.KEY_RETURN:
          // If the text input is blank and the user hits Enter call the
          // onEmptyInput callback.
          if (String('').valueOf() == String(this.current.retrieveData('input').getValue()).valueOf()) {
            this.options.get("onEmptyInput")();
          }
          e.stop();
          if(! this.autocurrent) break;
          this.autoAdd(this.autocurrent);
          this.autocurrent = false;
          this.autoenter = true;
          break;
        case Event.KEY_ESC: 
          this.autoHide();
          if(this.current && this.current.retrieveData('input'))
            this.current.retrieveData('input').clear();
          break;
        default: this.dosearch = true;
      }
    }.bind(this));
    input.observe('keyup',function(e) {
      switch(e.keyCode) {
        case Event.KEY_UP: 
        case Event.KEY_DOWN: 
        case Event.KEY_RETURN:
        case Event.KEY_ESC: 
          break;
        default:
          // Removed Ajax.Request from here and moved to initialize, 
          // now doesn't create server queries every search but only 
          // refreshes the list on initialize (page load)
          if(this.searchTimeout) clearTimeout(this.searchTimeout);
            this.searchTimeout = setTimeout(function(){
            if(this.dosearch) this.autoShow(input.value);
          }.bind(this), 250);
      }
    }.bind(this));
    input.observe(Prototype.Browser.IE ? 'keydown' : 'keypress', function(e) {
      if(this.autoenter) e.stop();
      this.autoenter = false;
    }.bind(this));
    return li;
  },
  
  createBox: function($super,text, options) {
    var li = $super(text, options);
    li.observe('mouseover',function() {
      this.addClassName('bit-hover');
    }).observe('mouseout',function() { 
      this.removeClassName('bit-hover') 
    });
    var a = new Element('a', {
      'href': '#',
      'class': 'closebutton'
    });
    // Some browsers (particularly, Firefox 3.0) will render this wrong, fix it.
    var ua = /Firefox\/3\.0/;
    var result = navigator.userAgent.match(ua);
    if (result != null) {
      a.style.marginTop = "-10px";  // you might need to adjust this depending on surronding layout
    } else {
      a.style.marginTop = "4px";
    }
    // default left margin is 3px, handled from stylesheet
    a.observe('click',function(e) {
      e.stop();
      if(! this.current) this.focus(this.maininput);
      this.dispose(li);
    }.bind(this));
    li.insert(a).cacheData('text', Object.toJSON(text));
    return li;
  }
});

Element.addMethods({
  onBoxDispose: function(item,obj) {
  // Set to not to "add back" values in the drop-down upon delete if they were new values
  item = item.retrieveData('text').evalJSON(true);
  if(!item.newValue)
      obj.autoFeed(item); 
  },
  onInputFocus: function(el,obj) { obj.autoShow(); },
  onInputBlur: function(el,obj) {
    obj.lastinput = el;
    if(!obj.curOn) {
        obj.blurhide = obj.autoHide.bind(obj).delay(0.1);
    }
  },
  filter: function(D,E) { var C=[];for(var B=0,A=this.length;B<A;B++){if(D.call(E,this[B],B,this)){C.push(this[B]);}} return C; }
});
  
/* Copyright: InteRiders <http://interiders.com/> - Distributed under MIT - Keep this message! */ 


/**
 * Ajaxlinks
 *
 * Convert conventional links to ajaxs ones.
 *
 * use example:
 *    $('#container').ajaxlinks()                          // all the anchors under #container will be converted
 *    $('#container').ajaxlinks({anchor:'css filter'})     // all the anchors under #container and with that css filter
 *    $('#container').ajaxlinks({spinner:'#spinner')       // element to show/hide when start/stop the ajax call
 *    $('#container').ajaxlinks({authToken:'asdfasdf')     // Rails feature... send a token to mitigate XSS
 *
 * And for the same price a ajaxlinks for will-paginate
 * example:
 *    $('#container).wpaginate                             // unobstructive javascript links
 *
 *
 * by nelson fernandez (c) 2009 - MIT Licence -  nelson@netflux.com.ar
 *
 **/

(function($) {
  var o;
  $.fn.ajaxlinks = function(param) {
    o = $.extend( {
      spinner: null,
      authToken: null,
      anchor: 'a'
    }, param || {});

    return this.each(function() {
      $(this).find(o.anchor).click(linkHandler);
      var global_type = o.type || 'GET';

      function linkHandler(e) {
        var href = $(e.target).attr('href');
        var ajaxData = { async: true, 
                         beforeSend: function(request){ toggleSpinner(); }, 
                         complete: function(request){ toggleSpinner() },
                         dataType: 'script',
                         url: href,
                         type: global_type
                       }
        if (o.authToken)
          $.extend( ajaxData, {data:'authenticity_token=' + encodeURIComponent(o.authToken)} );
        $.ajax(ajaxData);
        return false;
      };

      function toggleSpinner() {
        if (o.spinner)
          $(o.spinner).toggle();
      }
    });
  };

  $.fn.wpaginate = function(param) {
    $(this).ajaxlinks($.extend( param, {anchor:'div.pagination a'}));
  }

})(jQuery);


/*
// jQuery multiSelect
//
// Version 1.0.2 beta
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 10 May 2009
//
// Visit http://abeautifulsite.net/notebook.php?article=62 for more information
//
// Usage: $('#control_id').multiSelect( options, callback )
//
// Options:  selectAll          - whether or not to display the Select All option; true/false, default = true
//           selectAllText      - text to display for selecting/unselecting all options simultaneously
//           noneSelected       - text to display when there are no selected items in the list
//           oneOrMoreSelected  - text to display when there are one or more selected items in the list
//                                (note: you can use % as a placeholder for the number of items selected).
//                                Use * to show a comma separated list of all selected; default = '% selected'
//
// Dependencies:  jQuery 1.2.6 or higher (http://jquery.com/)
//
// Change Log:
//
//		1.0.1	- Updated to work with jQuery 1.2.6+ (no longer requires the dimensions plugin)
//				- Changed $(this).offset() to $(this).position(), per James' and Jono's suggestions
//
//		1.0.2	- Fixed issue where dropdown doesn't scroll up/down with keyboard shortcuts
//				- Changed '$' in setTimeout to use 'jQuery' to support jQuery.noConflict
//				- Renamed from jqueryMultiSelect.* to jquery.multiSelect.* per the standard recommended at
//				  http://docs.jquery.com/Plugins/Authoring (does not affect API methods)
//
// Licensing & Terms of Use
// 
// This plugin is dual-licensed under the GNU General Public License and the MIT License and
// is copyright 2008 A Beautiful Site, LLC. 
//	
*/
if(jQuery) (function($){
	
	$.extend($.fn, {
		multiSelect: function(o, callback) {
			// Default options
			if( !o ) var o = {};
			if( o.selectAll == undefined ) o.selectAll = true;
			if( o.selectAllText == undefined ) o.selectAllText = "Select All";
			if( o.noneSelected == undefined ) o.noneSelected = 'Select options';
			if( o.oneOrMoreSelected == undefined ) o.oneOrMoreSelected = '% selected';
			
			// Initialize each multiSelect
			$(this).each( function() {
				var select = $(this);
				var html = '<input type="text" readonly="readonly" class="multiSelect" value="" style="cursor: default;" />';
				html += '<div class="multiSelectOptions" style="position: absolute; z-index: 99999; display: none;">';
				if( o.selectAll ) html += '<label class="selectAll"><input type="checkbox" class="selectAll" />' + o.selectAllText + '</label>';
				$(select).find('OPTION').each( function() {
          var obj = $(this);
					if( obj.val() != '' ) {
						html += '<label><input type="checkbox" name="' + $(select).attr('name') + '" value="' + obj.val() + '"';
						if( obj.attr('selected') ) html += ' checked="checked"';
						//if( obj.attr('defaultSelected') ) html += ' checked="checked"';
						html += ' />' + obj.html() + '</label>';
					}
				});
				html += '</div>';
				$(select).after(html);
				
				// Events
				$(select).next('.multiSelect')
          .mouseover( function() { $(this).addClass('hover'); })
          .mouseout( function() { $(this).removeClass('hover'); })
          .click( function() {
              var that = $(this);
              if ( that.hasClass('active') ) {
                that.multiSelectOptionsHide();
              } else {
                that.multiSelectOptionsShow();
              }
              return false; })
          .focus( function() { $(this).addClass('focus'); })     // So it can be styled with CSS 
          .blur( function() { $(this).removeClass('focus'); });  // So it can be styled with CSS
				
				// Determine if Select All should be checked initially
				if( o.selectAll ) {
					var sa = true;
					$(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT:checkbox').not('.selectAll').each( function() {
						if( !$(this).attr('checked') ) sa = false;
					});
					if( sa ) $(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT.selectAll').attr('checked', true).parent().addClass('checked');
				}
				
				// Handle Select All
				$(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT.selectAll').click( function() {
					if( $(this).attr('checked') == true ) $(this).parent().parent().find('INPUT:checkbox').attr('checked', true).parent().addClass('checked'); else $(this).parent().parent().find('INPUT:checkbox').attr('checked', false).parent().removeClass('checked');
				});
				
				// Handle checkboxes
				$(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT:checkbox').click( function() {
          var that=$(this);
					that.parent().parent().multiSelectUpdateSelected(o);
					that.parent().parent().find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked');
					that.parent().parent().prev('.multiSelect').focus();
					if( !that.attr('checked') ) that.parent().parent().find('INPUT:checkbox.selectAll').attr('checked', false).parent().removeClass('checked');
					if( callback ) callback(that);
				});
				
				// Initial display
				$(select).next('.multiSelect').next('.multiSelectOptions').each( function() {
          var that=$(this);
					that.multiSelectUpdateSelected(o);
					that.find('INPUT:checked').parent().addClass('checked');
				});
				
				// Handle hovers
				$(select).next('.multiSelect').next('.multiSelectOptions').find('LABEL').mouseover( function() {
          var that=$(this);
					that.parent().find('LABEL').removeClass('hover');
					that.addClass('hover');
				}).mouseout( function() {
					$(this).parent().find('LABEL').removeClass('hover');
				});
				
				// Keyboard
				$(select).next('.multiSelect').keydown( function(e) {
          var that=$(this);
					// Is dropdown visible?
					if( that.next('.multiSelectOptions').is(':visible') ) {
						// Dropdown is visible
						// Tab
						if( e.keyCode == 9 ) {
							that.addClass('focus').trigger('click'); // esc, left, right - hide
							that.focus().next(':input').focus();
							return true;
						}
						
						// ESC, Left, Right
						if( e.keyCode == 27 || e.keyCode == 37 || e.keyCode == 39 ) {
							// Hide dropdown
							that.addClass('focus').trigger('click');
						}
						// Down
						if( e.keyCode == 40 ) {
							if( !that.next('.multiSelectOptions').find('LABEL').hasClass('hover') ) {
								// Default to first item
								that.next('.multiSelectOptions').find('LABEL:first').addClass('hover');
							} else {
								// Move down, cycle to top if on bottom
								that.next('.multiSelectOptions').find('LABEL.hover').removeClass('hover').next('LABEL').addClass('hover');
								if( !that.next('.multiSelectOptions').find('LABEL').hasClass('hover') ) {
									that.next('.multiSelectOptions').find('LABEL:first').addClass('hover');
								}
							}
							
							// Adjust the viewport if necessary
							that.multiSelectAdjustViewport(that);
							
							return false;
						}
						// Up
						if( e.keyCode == 38 ) {
							if( !that.next('.multiSelectOptions').find('LABEL').hasClass('hover') ) {
								// Default to first item
								that.next('.multiSelectOptions').find('LABEL:first').addClass('hover');
							} else {
								// Move up, cycle to bottom if on top
								that.next('.multiSelectOptions').find('LABEL.hover').removeClass('hover').prev('LABEL').addClass('hover');
								if( !that.next('.multiSelectOptions').find('LABEL').hasClass('hover') ) {
									that.next('.multiSelectOptions').find('LABEL:last').addClass('hover');
								}
							}
							
							// Adjust the viewport if necessary
							that.multiSelectAdjustViewport(that);
							
							return false;
						}
						// Enter, Space
						if( e.keyCode == 13 || e.keyCode == 32 ) {
							// Select All
							if( that.next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').hasClass('selectAll') ) {
								if( that.next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked') ) {
									// Uncheck all
									that.next('.multiSelectOptions').find('INPUT:checkbox').attr('checked', false).parent().removeClass('checked');
								} else {
									// Check all
									that.next('.multiSelectOptions').find('INPUT:checkbox').attr('checked', true).parent().addClass('checked');
								}
								that.next('.multiSelectOptions').multiSelectUpdateSelected(o);
								if( callback ) callback(that);
								return false;
							}
							// Other checkboxes
							if( that.next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked') ) {
								// Uncheck
								that.next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked', false);
								that.next('.multiSelectOptions').multiSelectUpdateSelected(o);
								that.next('.multiSelectOptions').find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked');
								// Select all status can't be checked at this point
								that.next('.multiSelectOptions').find('INPUT:checkbox.selectAll').attr('checked', false).parent().removeClass('checked');
								if( callback ) callback($(this));
							} else {
								// Check
								that.next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked', true);
								that.next('.multiSelectOptions').multiSelectUpdateSelected(o);
								that.next('.multiSelectOptions').find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked');
								if( callback ) callback($(this));
							}
						}
						return false;
					} else {
						// Dropdown is not visible
						if( e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13 || e.keyCode == 32 ) { // down, enter, space - show
							// Show dropdown
							that.removeClass('focus').trigger('click');
							that.next('.multiSelectOptions').find('LABEL:first').addClass('hover');
							return false;
						}
						//  Tab key
						if( e.keyCode == 9 ) {
							// Shift focus to next INPUT element on page
							that.focus().next(':input').focus();
							return true;
						}
					}
					// Prevent enter key from submitting form
					if( e.keyCode == 13 ) return false;
				});
				
				// Eliminate the original form element
				$(select).remove();
			});
			
		},
		
		// Hide the dropdown
		multiSelectOptionsHide: function() {
			$(this).removeClass('active').next('.multiSelectOptions').hide();
		},
		
		// Show the dropdown
		multiSelectOptionsShow: function() {
      var obj = $(this);
			// Hide any open option boxes
			$('.multiSelect').multiSelectOptionsHide();
			obj.next('.multiSelectOptions').find('LABEL').removeClass('hover');
			obj.addClass('active').next('.multiSelectOptions').show();
			
			// Position it
			var offset = obj.position();
      var multiSelectOptions = obj.next('.multiSelectOptions');
			multiSelectOptions.css({ top: offset.top + obj.outerHeight() + 'px', left: offset.left + 'px'  });
			
			// Disappear on hover out
			multiSelectCurrent = obj;
			var timer = '';
			multiSelectOptions.hover( 
          function() { clearTimeout(timer); }, 
          function() { timer = setTimeout('jQuery(multiSelectCurrent).multiSelectOptionsHide(); $(multiSelectCurrent).unbind("mouseenter mouseleave");', 250);
			});
		},
		
		// Update the textbox with the total number of selected items
		multiSelectUpdateSelected: function(o) {
			var i = 0, s = '', obj = $(this);
			obj.find('INPUT:checkbox:checked').not('.selectAll').each( function() { i++; })
			if( i == 0 ) {
				obj.prev('INPUT.multiSelect').val( o.noneSelected );
			} else {
				if( o.oneOrMoreSelected == '*' ) {
					var display = '';
					obj.find('INPUT:checkbox:checked').each( function() {
            var that = $(this);
						if( that.parent().text() != o.selectAllText ) display = display + that.parent().text() + ', ';
					});
					display = display.substr(0, display.length - 2);
					obj.prev('INPUT.multiSelect').val( display );
				} else {
					obj.prev('INPUT.multiSelect').val( o.oneOrMoreSelected.replace('%', i) );
				}
			}
		},
		
		// Ensures that the selected item is always in the visible portion of the dropdown (for keyboard controls)
		multiSelectAdjustViewport: function(el) {
			// Calculate positions of elements
			var i = 0;
			var selectionTop = 0, selectionHeight = 0;
			$(el).next('.multiSelectOptions').find('LABEL').each( function() {
				if( $(this).hasClass('hover') ) { selectionTop = i; selectionHeight = $(this).outerHeight(); return; }
				i += $(this).outerHeight();
			});
			var divScroll = $(el).next('.multiSelectOptions').scrollTop();
			var divHeight = $(el).next('.multiSelectOptions').height();
			// Adjust the dropdown scroll position
			$(el).next('.multiSelectOptions').scrollTop(selectionTop - ((divHeight / 2) - (selectionHeight / 2)));
		}
		
	});
	
})(jQuery);


(function(A){A.jScrollPane={active:[]};A.fn.jScrollPane=function(C){C=A.extend({},A.fn.jScrollPane.defaults,C);var B=function(){return false};return this.each(function(){var O=A(this);O.css("overflow","hidden");var X=this;if(A(this).parent().is(".jScrollPaneContainer")){var Ac=C.maintainPosition?O.position().top:0;var L=A(this).parent();var d=L.innerWidth();var Ad=L.outerHeight();var M=Ad;A(">.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown",L).remove();O.css({top:0})}else{var Ac=0;this.originalPadding=O.css("paddingTop")+" "+O.css("paddingRight")+" "+O.css("paddingBottom")+" "+O.css("paddingLeft");this.originalSidePaddingTotal=(parseInt(O.css("paddingLeft"))||0)+(parseInt(O.css("paddingRight"))||0);var d=O.innerWidth();var Ad=O.innerHeight();var M=Ad;O.wrap(A("<div></div>").attr({className:"jScrollPaneContainer"}).css({height:Ad+"px",width:d+"px"}));A(document).bind("emchange",function(Ae,Af,p){O.jScrollPane(C)})}if(C.reinitialiseOnImageLoad){var N=A.data(X,"jScrollPaneImagesToLoad")||A("img",O);var G=[];if(N.length){N.each(function(p,Ae){A(this).bind("load",function(){if(A.inArray(p,G)==-1){G.push(Ae);N=A.grep(N,function(Ag,Af){return Ag!=Ae});A.data(X,"jScrollPaneImagesToLoad",N);C.reinitialiseOnImageLoad=false;O.jScrollPane(C)}}).each(function(Af,Ag){if(this.complete||this.complete===undefined){this.src=this.src}})})}}var o=this.originalSidePaddingTotal;var l={height:"auto",width:d-C.scrollbarWidth-C.scrollbarMargin-o+"px"};if(C.scrollbarOnLeft){l.paddingLeft=C.scrollbarMargin+C.scrollbarWidth+"px"}else{l.paddingRight=C.scrollbarMargin+"px"}O.css(l);var m=O.outerHeight();var i=Ad/m;if(i<0.99){var H=O.parent();H.append(A("<div></div>").attr({className:"jScrollPaneTrack"}).css({width:C.scrollbarWidth+"px"}).append(A("<div></div>").attr({className:"jScrollPaneDrag"}).css({width:C.scrollbarWidth+"px"}).append(A("<div></div>").attr({className:"jScrollPaneDragTop"}).css({width:C.scrollbarWidth+"px"}),A("<div></div>").attr({className:"jScrollPaneDragBottom"}).css({width:C.scrollbarWidth+"px"}))));var z=A(">.jScrollPaneTrack",H);var P=A(">.jScrollPaneTrack .jScrollPaneDrag",H);if(C.showArrows){var g;var Ab;var S;var r;var j=function(){if(r>4||r%4==0){y(u+Ab*b)}r++};var K=function(p){A("html").unbind("mouseup",K);g.removeClass("jScrollActiveArrowButton");clearInterval(S)};var Z=function(){A("html").bind("mouseup",K);g.addClass("jScrollActiveArrowButton");r=0;j();S=setInterval(j,100)};H.append(A("<a></a>").attr({href:"javascript:;",className:"jScrollArrowUp"}).css({width:C.scrollbarWidth+"px"}).html("Scroll up").bind("mousedown",function(){g=A(this);Ab=-1;Z();this.blur();return false}).bind("click",B),A("<a></a>").attr({href:"javascript:;",className:"jScrollArrowDown"}).css({width:C.scrollbarWidth+"px"}).html("Scroll down").bind("mousedown",function(){g=A(this);Ab=1;Z();this.blur();return false}).bind("click",B));var Q=A(">.jScrollArrowUp",H);var J=A(">.jScrollArrowDown",H);if(C.arrowSize){M=Ad-C.arrowSize-C.arrowSize;z.css({height:M+"px",top:C.arrowSize+"px"})}else{var s=Q.height();C.arrowSize=s;M=Ad-s-J.height();z.css({height:M+"px",top:s+"px"})}}var w=A(this).css({position:"absolute",overflow:"visible"});var D;var Y;var b;var u=0;var V=i*Ad/2;var a=function(Ae,Ag){var Af=Ag=="X"?"Left":"Top";return Ae["page"+Ag]||(Ae["client"+Ag]+(document.documentElement["scroll"+Af]||document.body["scroll"+Af]))||0};var f=function(){return false};var v=function(){n();D=P.offset(false);D.top-=u;Y=M-P[0].offsetHeight;b=2*C.wheelSpeed*Y/m};var E=function(p){v();V=a(p,"Y")-u-D.top;A("html").bind("mouseup",T).bind("mousemove",h);if(A.browser.msie){A("html").bind("dragstart",f).bind("selectstart",f)}return false};var T=function(){A("html").unbind("mouseup",T).unbind("mousemove",h);V=i*Ad/2;if(A.browser.msie){A("html").unbind("dragstart",f).unbind("selectstart",f)}};var y=function(Ae){Ae=Ae<0?0:(Ae>Y?Y:Ae);u=Ae;P.css({top:Ae+"px"});var Af=Ae/Y;w.css({top:((Ad-m)*Af)+"px"});O.trigger("scroll");if(C.showArrows){Q[Ae==0?"addClass":"removeClass"]("disabled");J[Ae==Y?"addClass":"removeClass"]("disabled")}};var h=function(p){y(a(p,"Y")-D.top-V)};var q=Math.max(Math.min(i*(Ad-C.arrowSize*2),C.dragMaxHeight),C.dragMinHeight);P.css({height:q+"px"}).bind("mousedown",E);var k;var R;var I;var t=function(){if(R>8||R%4==0){y((u-((u-I)/2)))}R++};var Aa=function(){clearInterval(k);A("html").unbind("mouseup",Aa).unbind("mousemove",e)};var e=function(p){I=a(p,"Y")-D.top-V};var U=function(p){v();e(p);R=0;A("html").bind("mouseup",Aa).bind("mousemove",e);k=setInterval(t,100);t()};z.bind("mousedown",U);H.bind("mousewheel",function(Ae,Ag){v();n();var Af=u;y(u-Ag*b);var p=Af!=u;return !p});var F;var W;function c(){var p=(F-u)/C.animateStep;if(p>1||p<-1){y(u+p)}else{y(F);n()}}var n=function(){if(W){clearInterval(W);delete F}};var x=function(Af,p){if(typeof Af=="string"){$e=A(Af,O);if(!$e.length){return}Af=$e.offset().top-O.offset().top}H.scrollTop(0);n();var Ae=-Af/(Ad-m)*Y;if(p||!C.animateTo){y(Ae)}else{F=Ae;W=setInterval(c,C.animateInterval)}};O[0].scrollTo=x;O[0].scrollBy=function(Ae){var p=-parseInt(w.css("top"))||0;x(p+Ae)};v();x(-Ac,true);A("*",this).bind("focus",function(Ah){var Ag=A(this);var Aj=0;while(Ag[0]!=O[0]){Aj+=Ag.position().top;Ag=Ag.offsetParent()}var p=-parseInt(w.css("top"))||0;var Ai=p+Ad;var Af=Aj>p&&Aj<Ai;if(!Af){var Ae=Aj-C.scrollbarMargin;if(Aj>p){Ae+=A(this).height()+15+C.scrollbarMargin-Ad}x(Ae)}});if(location.hash){x(location.hash)}A(document).bind("click",function(Ae){$target=A(Ae.target);if($target.is("a")){var p=$target.attr("href");if(p.substr(0,1)=="#"){x(p)}}});A.jScrollPane.active.push(O[0])}else{O.css({height:Ad+"px",width:d-this.originalSidePaddingTotal+"px",padding:this.originalPadding});O.parent().unbind("mousewheel")}})};A.fn.jScrollPane.defaults={scrollbarWidth:10,scrollbarMargin:5,wheelSpeed:18,showArrows:false,arrowSize:0,animateTo:false,dragMinHeight:1,dragMaxHeight:99999,animateInterval:100,animateStep:3,maintainPosition:true,scrollbarOnLeft:false,reinitialiseOnImageLoad:false};A(window).bind("unload",function(){var C=A.jScrollPane.active;for(var B=0;B<C.length;B++){C[B].scrollTo=C[B].scrollBy=null}})})(jQuery);