gQuery.store({

	"jlg.user.acegiCookieName"				:  	"_rgid",
	"jlg.user.cookieDomain"					: 	"rentexpert.com",
	"jlg.user.cookiePath" 					: 	"/",
	"jlg.user.permanentCookieName"			: 	"_jlg_remember",
	"jlg.user.sessionCookieName"			:  	"_jlg_session",
	"rex.vacationPlanner.journalCookieName"	: 	"myrxj",
	"rex.vacationPlanner.rentalsCookieName"	: 	"myrxr",
	"validationEngine.inlineValidation"		: 	false
});


(function() {

	var m_email = null;
	var m_flags = {};
	var m_parsedAcegi = false;
	var m_parsedSession = false;	
	var m_uid = null;
	var m_userMonitors = [];

	function notifyUserMonitors() {

		for( var index = 0, len = m_userMonitors.length; index < len; index++ ) { (m_userMonitors[ index ])( m_uid ); }
	};

	function parseAcegiCookie( p_reload ) {

		if( !m_parsedAcegi || p_reload ) {

			m_parsedAcegi = true;
			var cookie = gQuery.cookie( gQuery.fetch( "jlg.user.acegiCookieName" ) );
			if( !cookie ) { return; }		
			var cookieData = gQuery.decode64( cookie ).split( /:/ );
			m_email = cookieData[ 0 ];
			var flagNames = (cookieData[ 3 ] || '').split( /,/ ) || [];
			for( var index = 0, len = flagNames.length; index < len; index++ ) {

				var name = gQuery.trim( flagNames[ index ] || "" );
				if( name == "1" ) { name = "o"; }
				if( name && name.length > 0 ) { m_flags[ name ] = true; }
			}
		}
	};

	function parseSessionCookie( p_reload ) {

		if( !m_parsedSession || p_reload ) {

			m_parsedSession = true;
			var cookie = gQuery.cookie( gQuery.fetch( "jlg.user.sessionCookieName" ) );
			if( !cookie ) { return; }		
			var cookieData = gQuery.decode64( cookie ).split( /:/ );
			m_uid = gQuery.decode64( cookieData[ 0 ] );
		}
	};

	window.JgUser = {

		email: function( p_reload ) {

			parseAcegiCookie( p_reload );
			if( p_reload ) { parseSessionCookie( p_reload ); }
			return m_email;
		},

		flag: function( p_name ) {

			parseAcegiCookie();
			return m_flags[ p_name ] ? true : false;
		},

		ifUserChanged: function( p_fn ) {

			m_userMonitors.push( p_fn );
			if( m_uid !== null ) { p_fn( m_uid ); }
		},

		uid: function( p_reload ) {

			oldUid = m_uid;
			parseSessionCookie( p_reload );
			if( p_reload ) { parseAcegiCookie( p_reload ); }
			if( oldUid != m_uid ) { notifyUserMonitors(); }
			return m_uid;
		}
	};

	gQuery( document ).ready( function() { JgUser.uid(); } );
})();


(function() {

	var m_isLocked = null;
	var m_juid = null;
	var m_pageJuid = null;
	var m_juidMonitors = [];
	var m_lockMonitors = [];
	var m_rentalMonitors = [];
	var m_rentals = null;

	function noJournal( p_oldJuid, p_options ) {

		if( p_options.create ) {

			gQuery.ajax({ url: "/journals/assign.js", dataType: "script", success: afterJuidSet( p_oldJuid, p_options.afterSet ) });

		} else {
			
			if( m_isLocked !== true ) {

				m_isLocked = true;
				notifyLockMonitors();

			}
			if( p_oldJuid != m_juid ) { notifyJuidMonitors(); }
		}
	};

	function loginOrRegister( p_fn ) {

		CoolLightbox.show(

			"",
			gQuery.joinUrl(

				gQuery.fetch( "rx.vacationJournal.loginOrRegisterUrl", "/journals/login_or_register" ),
				{ callback: window.top.gQuery.callback( p_fn ) }

			) + "&TB_iframe=true&height=320&width=785&background=#fff",
			""
		);
	};

	function notifyJuidMonitors() {

		for( var index = 0, len = m_juidMonitors.length; index < len; index++ ) { (m_juidMonitors[ index ])( m_juid ); }
	};

	function notifyLockMonitors() {

		for( var index = 0, len = m_lockMonitors.length; index < len; index++ ) { (m_lockMonitors[ index ])( m_isLocked ); }
	};

	function notifyRentalMonitors() {

		for( var index = 0, len = m_rentalMonitors.length; index < len; index++ ) { (m_rentalMonitors[ index ])( m_rentals ); }
	};

	function afterJuidSet( p_juid, p_fn ) {

		return function() {

			if( p_juid != VacationJournal.juid({ reload: true }) ) {

				if( p_fn ) { p_fn( m_juid ); }
				notifyJuidMonitors();
			}
		};
	};

	window.VacationJournal = {

		addNote: function( p_id ) {

			VacationJournal.requireJournal( function() {

				// Update the UI
				gQuery( ".add_favorite_" + p_id ).children().hide().filter( ".i_favpend" ).css({ display: "block" });

				// Add the rental via ajax
				gQuery.ajax({ url: gQuery.joinUrl( "/notes/create.js", {
		
					"note[rank]": 26,
					"noteable[rental_id]": p_id,
					model: "rental"
	
				} ), dataType: "script" });
			} );
		},

		fbConnect: function( p_options ) {

			gQuery.ajax({

				data: p_options.juid ? { juid: p_options.juid } : {},
				dataType: "script",
				success: p_options.callback,
				url: "/journals/connect_to_fb.js"
			});
		},

		fbLogin: function( p_options ) {

			p_options = p_options || {};
			FB.Connect.requireSession( function() { VacationJournal.fbConnect( p_options ); }, null, true );
		},

		ifJournalChanged: function( p_fn ) {

			m_juidMonitors.push( p_fn );
			if( m_juid !== null ) { p_fn( m_juid ); }
		},

		ifLockChanged: function( p_fn ) {

			m_lockMonitors.push( p_fn );
			if( m_isLocked !== null ) { p_fn( m_isLocked ); }
		},

		ifRentalsChanged: function( p_fn ) {

			m_rentalMonitors.push( p_fn );
			if( m_rentals !== null ) { p_fn( m_rentals ); }		
		},

		juid: function( p_options ) {

			p_options = p_options || {};
			if( m_juid && !p_options.reload ) { return m_juid; }
			var oldJuid = m_juid;
			m_juid = null;
			var jcookie = gQuery.cookie( gQuery.fetch( "rex.vacationPlanner.journalCookieName" ) );
			if( !jcookie ) {

				noJournal( oldJuid, p_options );

			} else {

				m_juid = gQuery.decode64( gQuery.decode64( jcookie ).split( /:/ )[ 0 ] );
				if( !m_juid ) {

					noJournal( oldJuid, p_options );

				} else {

					if( oldJuid != m_juid ) { notifyJuidMonitors(); }				
					if( p_options.afterSet ) { try { p_options.afterSet(); } catch( e ) {} }
					if( m_pageJuid === null || m_pageJuid == m_juid ) {

						if( m_isLocked !== false ) {

							m_isLocked = false;
							notifyLockMonitors();
						}
					} else if( m_isLocked !== true ) {

						m_isLocked = true;
						notifyLockMonitors();
					}
				}
			}
			return m_juid;
		},

		logout: function() {

			gQuery.cookie( gQuery.fetch( "rex.vacationPlanner.journalCookieName" ), '', { domain: gQuery.fetch( "jlg.user.cookieDomain" ), expires: 0 });
			gQuery.cookie( gQuery.fetch( "rex.vacationPlanner.rentalsCookieName" ), '', { domain: gQuery.fetch( "jlg.user.cookieDomain" ), expires: 0 });
			VacationJournal.juid({ reload: true });
			VacationJournal.rentals({ reload: true });
		},

		pageJuid: function( p_juid ) {

			if( arguments.length > 0 && m_pageJuid != p_juid ) {

				var oldValue = m_pageJuid;
				m_pageJuid = p_juid;
				if( m_pageJuid === null || VacationJournal.juid() == m_pageJuid )  {

					if( m_isLocked !== false ) {

						m_isLocked = false;
						notifyLockMonitors();
					}
				} else if( m__isLocked !== true ) {

					m_isLocked = true;
					notifyLockMonitors();
				}
			}
			return m_pageJuid;
		},

		removeNote: function( p_id ) {

			try {

				gQuery.ajax({ url: gQuery.joinUrl( "/notes/destroy.js", { id: p_id } ), dataType: "script" });
				gQuery( "#note_box_" + p_id ).remove();
				if( gQuery( ".remove_favorite" ).size() === 0 ) { gQuery( "#notebook_intro" ).show(); }

			} catch( e ) {}
		},

		rentals: function( p_options ) {

			p_options = p_options || {};
			if( m_rentals && !p_options.reload ) { return m_rentals; }
			var jcookie = gQuery.cookie( gQuery.fetch( "rex.vacationPlanner.rentalsCookieName" ) );
			m_rentals = jcookie ? jcookie.split( /\./ ) : [];
			notifyRentalMonitors();
			if( p_options.afterSet ) { try { p_options.afterSet(); } catch( e ) {} }
			return m_rentals;
		},

		requireJournal: function( p_fn ) {

			if( VacationJournal.juid() === null ) {

				if( JgUser.uid() ) {

					VacationJournal.juid({ create: true, afterSet: p_fn });

				} else if( typeof FB !== "undefined" && FB ) {

					FB.ensureInit( function() {

						FB_RequireFeatures( [ "Connect" ], function() {

							FB.Connect.get_status().waitUntilReady( function( p_status ) {

								switch ( p_status ) {

									case FB.ConnectState.connected:
										VacationJournal.juid({ create: true, afterSet: p_fn });
										break;
									
									default:
										loginOrRegister( p_fn );
										break;
								}
							} );
						} );
					} );
				} else {

					loginOrRegister( p_fn );
				}
			} else if( p_fn ) {

				p_fn();
			}
		}
	};

	gQuery( document ).ready( function() {

		VacationJournal.juid();
		VacationJournal.rentals();
	} );
})();


/*  Prototype JavaScript framework, version 1.6.0.1
 *  (c) 2005-2007 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.1',

  Browser: {
    IE:     !!(window.attachEvent && !window.opera),
    Opera:  !!window.opera,
    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,
    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 = Object.extend((function(m) {
          return function() { return ancestor[m].apply(this, arguments); };
        })(property).wrap(method), {
          valueOf:  function() { return method; },
          toString: function() { return method.toString(); }
        });
      }
      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() : object.toString();
    } 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 && object.constructor === Array;
  },

  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].split(",").invoke("strip");
    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);
  },

  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;
    this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
	return this._methodized;
  }
});

Function.prototype.defer = Function.prototype.delay.curry(0.01);

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) {
		match = source.match(pattern);
      if (match) {
        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.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);

window.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);
    }.bind(this));
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

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

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

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

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

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

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

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

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

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

    this.each(function(value, index) {
      if (filter.match(value)) { results.push(iterator(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) {
    iterator = iterator.bind(context);
    this.each(function(value, index) {
      memo = iterator(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 ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result === null || value >= result) { result = value; }
    });
    return result;
  },

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

  partition: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator(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) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index)) { results.push(value); }
    });
    return results;
  },

  sortBy: function(iterator, context) {
    iterator = iterator.bind(context);
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(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, results = new Array(length);
  while (length--) { results[length] = iterable[length]; }
  return results;
};

if (Prototype.Browser.WebKit) {

  function $A (iterable) {
    if (!iterable) { return []; }
    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
        iterable.toArray) { return iterable.toArray(); }
    var length = iterable.length, 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 = [];
	var i ;
	var length;
    for (i = 0, length = this.length; i < length; i++) { array.push(this[i]); }
    for (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) {
    $R(0, this, true).each(iterator);
    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();
});
window.$H = function (object) {
  return new Hash(object);
};

window.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) {
      this._object[key] = value;
		return this._object[key];
    },

    get: function(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.map(function(pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

        if (values && typeof values == 'object') {
          if (Object.isArray(values)) { return values.map(toQueryPair.curry(key)).join('&'); }
        }
        return toQueryPair(key, values);
      }).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;
  }
});

window.$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;

	params = Object.toQueryString(params);
    if (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 && 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 (e2) {
      this.dispatchException(e);
    }

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

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name);
    } 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;
	this.transport  = request.transport;
    var transport  = request.transport;
	this.readyState = transport.readyState;
	var 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);
    } 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);
    } 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(); }

	receiver = $(receiver);
    if (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);
  }
});
window.$ = 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 || { });
}).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).style.display = 'none';
    return element;
  },

  show: function(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, t, range;

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

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

      content = Object.toHTML(content);

      range = element.ownerDocument.createRange();
      t.initializeRange(element, range);
      t.insert(element, range.createContextualFragment(content.stripScripts()));

      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 = [];
	element = element[property];
    while (element) {
      if (element.nodeType == 1) { elements.push(Element.extend(element)); }
		element = element[property];
	}
    return elements;
  },

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

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

  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 expression ? Selector.findElement(ancestors, expression, index) :
      ancestors[index || 0];
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) { return element.firstDescendant(); }
    var descendants = element.descendants();
    return expression ? Selector.findElement(descendants, expression, index) :
      descendants[index || 0];
  },

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

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

  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);
    var originalAncestor = ancestor;

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

    if (element.sourceIndex && !Prototype.Browser.Opera) {
      var e = element.sourceIndex, a = ancestor.sourceIndex,
       nextAncestor = ancestor.nextSibling;
      if (!nextAncestor) {
        do { ancestor = ancestor.parentNode; }
        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
      }
      if (nextAncestor) { return (e > a && e < nextAncestor.sourceIndex); }
    }

	element = element.parentNode;
    while (element) {
      if (element == originalAncestor) { return true; }
		element = element.parentNode;
	}
    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) {
      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 (window.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 == 'BODY') { break; }
        var p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') { break; }
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'absolute') { return; }
    // 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; }
    // 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) {
	// BEGIN JLG:
	// Fixed bug where the autocomplete dropdown appears in the wrong location in IE8
    // if (element.offsetParent) { return $(element.offsetParent); }
    if (element.offsetParent && Element.visible(element)) { return $(element.offsetParent); }
	// END JLG:
    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; }

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

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

    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 (!document.createRange || Prototype.Browser.Opera) {
  Element.Methods.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 t = Element._insertionTranslations, content, position, pos, tagName;

    for (position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      pos      = t[position];

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

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

      if (t.tags[tagName]) {
        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
        if (position == 'top' || position == 'after') { fragments.reverse(); }
        fragments.each(pos.insert.curry(element));
      }
      else { element.insertAdjacentHTML(pos.adjacency, content.stripScripts()); }

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

    return element;
  };
}

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) {
  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        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;
      }
    );
  });

  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') {
		value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/);
      if (value) {
        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.clone(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').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 == '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 (document.createElement('div').outerHTML) {
  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];
  div.innerHTML = t[0] + html + t[1];
  t[2].times(function() { div = div.firstChild; });
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: {
    adjacency: 'beforeBegin',
    insert: function(element, node) {
      element.parentNode.insertBefore(node, element);
    },
    initializeRange: function(element, range) {
      range.setStartBefore(element);
    }
  },
  top: {
    adjacency: 'afterBegin',
    insert: function(element, node) {
      element.insertBefore(node, element.firstChild);
    },
    initializeRange: function(element, range) {
      range.selectNodeContents(element);
      range.collapse(true);
    }
  },
  bottom: {
    adjacency: 'beforeEnd',
    insert: function(element, node) {
      element.appendChild(node);
    }
  },
  after: {
    adjacency: 'afterEnd',
    insert: function(element, node) {
      element.parentNode.insertBefore(node, element.nextSibling);
    },
    initializeRange: function(element, range) {
      range.setStartAfter(element);
    }
  },
  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() {
  this.bottom.initializeRange = this.top.initializeRange;
  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, 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 = { };
    var B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      dimensions[d] = (B.WebKit && !document.evaluate) ? window['inner' + D] :
        (B.Opera) ? document.body['client' + 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. */

window.Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();
    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(this.expression)) { return false; }

    return true;
  },

  compileMatcher: function() {
    if (this.shouldUseXPath()) { return this.compileXPathMatcher(); }

    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];
		m = e.match(p);
        if (m) {
          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) {
		m = e.match(ps[i]);
        if (m) {
          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;
    if (this.xpath) { return document._getElementsByXPath(this.xpath, root); }
    return this.matcher(root);
  },

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

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

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (i in ps) {
        p = ps[i];
		m = e.match(p);
        if (m) {
          // 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 (i = 0, token = this.tokens[i]; token; token = this.tokens[++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 or translate(text(), ' \t\r\n', '') = '')]",
      'checked':     "[@checked]",
      'disabled':    "[@disabled]",
      'enabled':     "[not(@disabled)]",
      '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) {
			m = e.match(p[i]);
            if (m) {
              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'; }
		mm = formula.match(/^(\d+)$/);
        if (mm) { return '[' + fragment + "= " + mm[1] + ']'; }
		mm = formula.match(/^(\-?\d*)?n(([+\-])(\d+))?/);
        if (mm) { // 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 = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); 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]+)\]/,
    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 Selector.operators[matches[2]](nodeValue, matches[3]);
    }
  },

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

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

    unmark: function(nodes) {
      for (var i = 0, node = nodes[i]; node; node = nodes[++i]) { node._counted = 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) {
		var i, j, nodes, node;
	
      parentNode._counted = true;
      if (reverse) {
        for (nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._counted)) { node.nodeIndex = j++; }
        }
      } else {
        for (i = 0, j = 1, nodes = parentNode.childNodes, node = nodes[i]; node; node = nodes[++i]) {
          if (node.nodeType == 1 && (!ofType || node._counted)) { 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])._counted) {
          n._counted = true;
          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 = nodes[i]; node; node = nodes[++i]) { h.concat(results, node.getElementsByTagName('*')); }
      return results;
    },

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

    adjacent: function(nodes) {
      for (var i = 0, results = [], node = nodes[i]; node; node = nodes[++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 = nodes[i]; node; node = nodes[++i]) { h.concat(results, Element.nextSiblings(node)); }
      return results;
    },

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

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

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

    id: function(nodes, root, id, combinator) {
		var i;
		var node;
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) { return []; }
      if (!nodes && root == document) { return [targetNode]; }
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (i = 0, node = nodes[i]; node; node = nodes[++i]) {
              if (targetNode.parentNode == node) { return [targetNode]; }
			}
          } else if (combinator == 'descendant') {
            for (i = 0, node = nodes[i]; node; node = nodes[++i]) {
              if (Element.descendantOf(targetNode, node)) { return [targetNode]; }
			}
          } else if (combinator == 'adjacent') {
            for (i = 0, node = nodes[i]; node; node = nodes[++i]) {
              if (Selector.handlers.previousElementSibling(targetNode) == node) { return [targetNode]; }
			}
          } else { nodes = h[combinator](nodes); }
        }
        for (i = 0, node = nodes[i]; node; node = nodes[++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 = nodes[i], nodeClassName; node; node = nodes[++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) {
      if (!nodes) { nodes = root.getElementsByTagName("*"); }
      var results = [];
      for (var i = 0, node = nodes[i]; node; node = nodes[++i]) {
        if (Element.hasAttribute(node, attr))  { results.push(node); }
	  }
      return results;
    },

    attr: function(nodes, root, attr, value, operator) {
      if (!nodes) { nodes = root.getElementsByTagName("*"); }
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node = nodes[i]; node; node = nodes[++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 = nodes[i]; node; node = nodes[++i]) {
        if (Selector.handlers.previousElementSibling(node)) { continue; }
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node = nodes[i]; node; node = nodes[++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 = nodes[i]; node; node = nodes [++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) {

		var i, node;
      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 (i = 0, node = nodes[i]; node; node = nodes[++i]) {
        if (!node.parentNode._counted) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (i = 0, node = nodes[i]; node; node = nodes[++i]) {
          if (node.nodeIndex == formula) { results.push(node); }
		}
      } else {
	
		m = formula.match(/^(\-?\d*)?n(([+\-])(\d+))?/);
		if (m) { // 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 (i = 0, node = nodes[i], l = indices.length; node; node = nodes[++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 = nodes[i]; node; node = nodes[++i]) {
        // IE treats comments as element nodes
        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) { 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 = nodes[i]; node; node = nodes[++i]) {
        if (!node._counted) { results.push(node); }
	  }
      h.unmark(exclusions);
      return results;
    },

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

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

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node = nodes[i]; node; node = nodes[++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.startsWith(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() + '-'); }
  },

  matchElements: function(elements, expression) {
    var matches = new Selector(expression).findElements(), h = Selector.handlers;
    h.mark(matches);
	// BEGIN JLG:
    // for (var i = 0, results = [], element = elements[i]; element; element = element[++i]) {
    for (var i = 0, results = [], element = elements[i]; element; element = elements[++i]) {
	// END JLG:	
      if (element._counted) { 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) {
    var exprs = expressions.join(',');
    expressions = [];
    exprs.scan(/(([\w#:.~>+()\s\-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    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) {
  // IE returns comment nodes on getElementsByTagName("*").
  // Filter them out.
  Selector.handlers.concat = function(a, b) {
    for (var i = 0, node = b[i]; node; node = b[++i]) {
      if (node.tagName !== "!") { a.push(node); }
	}
    return a;
  };
}

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
window.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 != '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.blur();
    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, index) {
    if (Object.isUndefined(index)) {
      return this[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element);
	}
    else {
      var opt, value, single = !Object.isArray(index);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        value = this.optionValue(opt);
        if (single) {
          if (value == index) {
            opt.selected = true;
            return;
          }
        }
        else { opt.selected = index.include(value); }
      }
    }
  },

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

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

    for (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) { window.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) {
      var node = Event.extend(event).target;
      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : 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) {
      return {
        x: event.pageX || (event.clientX +
          (document.documentElement.scrollLeft || document.body.scrollLeft)),
        y: event.pageY || (event.clientY +
          (document.documentElement.scrollTop || document.body.scrollTop))
      };
    },

    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._eventID) { return element._eventID; }
    arguments.callee.id = arguments.callee.id || 1;
	element._eventID = ++arguments.callee.id;
    return element._eventID;
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) { return "dataavailable"; }
    return eventName;
  }

  function getCacheForID(id) {
	cache[id] = cache[id] || { };
    return cache[id];
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
	c[eventName] = c[eventName] || [];
    return 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; }
	}
  }

  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  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) {
	
		var event;
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent) { element = document.documentElement; }

      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()
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer, fired = false;

  function fireContentLoadedEvent() {
    if (fired) { return; }
    if (timer) { window.clearInterval(timer); }
    document.fire("dom:loaded");
    fired = 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 = nodes[i], cn; child; child = nodes[++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();

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/ 

// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var color = '#';
	var i;
  if (this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    i=0; do { color += parseInt(cols[i], 10).toColorPart(); } while (++i<3);  
  } else {  
    if (this.slice(0,1) == '#') {  
      if (this.length==4) {
	
			for(i=1;i<4;i++) { color += (this.charAt(i) + this.charAt(i)).toLowerCase();  }
		}
      if (this.length==7) { color = this.toLowerCase(); }
    }  
  }  
  return (color.length==7 ? color : (arguments[0] || this));  
};

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

Element.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
};

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  element.setStyle({fontSize: (percent/100) + 'em'});   
  if (Prototype.Browser.WebKit) { window.scrollBy(0,0); }
  return element;
};

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
};

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

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

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + 0.5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
    },
    pulse: function(pos, pulses) { 
      pulses = pulses || 5; 
      return (
        ((pos % (1/pulses)) * pulses).round() === 0 ? 
              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
        );
    },
    spring: function(pos) { 
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) { tagifyStyle += ';zoom:1'; }
    
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') || 
        Object.isFunction(element)) && 
       (element.length)) { elements = element; }
    else {
      elements = $(element).childNodes;
	}
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || { });
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      var eff = new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || { });
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(Enumerable, {
  initialize: function() {
    this.effects  = [];
    this.interval = null;    
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = Object.isString(effect.options.queue) ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle'; }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) { this.effects.push(effect); }
    
    if (!this.interval) { this.interval = setInterval(this.loop.bind(this), 15); }
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect; });
    if (this.effects.length === 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++)  { this.effects[i] && this.effects[i].loop(timePos); }
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) { return queueName; }
    
    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    if (options && options.transition === false) { options.transition = Effect.Transitions.linear; }
    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;
    
    eval('this.render = function(pos){ '+
      'if (this.state=="idle"){this.state="running";'+
      codeForEvent(this.options,'beforeSetup')+
      (this.setup ? 'this.setup();':'')+ 
      codeForEvent(this.options,'afterSetup')+
      '};if (this.state=="running"){'+
      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
      'this.position=pos;'+
      codeForEvent(this.options,'beforeUpdate')+
      (this.update ? 'this.update(pos);':'')+
      codeForEvent(this.options,'afterUpdate')+
      '}}');
    
    this.event('beforeStart');
    if (!this.options.sync) { Effect.Queues.get(Object.isString(this.options.queue) ?  'global' : this.options.queue.scope).add(this); }
  },
  loop: function(timePos) {
    if (timePos >= this.startOn) {
      if (timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if (this.finish) { this.finish(); }
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync) { Effect.Queues.get(Object.isString(this.options.queue) ?  'global' : this.options.queue.scope).remove(this); }
    this.state = 'finished';
  },
  event: function(eventName) {
    if (this.options[eventName + 'Internal']) { this.options[eventName + 'Internal'](this); }
    if (this.options[eventName]) { this.options[eventName](this); }
  },
  inspect: function() {
    var data = $H();
    for(var property in this) {
      if (!Object.isFunction(this[property])) { data.set(property, this[property]); }
	}
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if (effect.finish) { effect.finish(position); }
      effect.event('afterFinish');
    });
  }
});

Effect.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(), 
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) : 
      function(value) { object[method] = value; };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

Effect.Event = Class.create(Effect.Base, {
  initialize: function() {
    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) { throw(Effect._elementDoesNotExistError); }
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) { this.element.setStyle({zoom: 1}); }
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || { });
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) { throw(Effect._elementDoesNotExistError); }
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
};

Effect.Scale = Class.create(Effect.Base, {
  initialize: function(element, percent) {
    this.element = $(element);
    if (!this.element) { throw(Effect._elementDoesNotExistError); }
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || { });
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle = { };
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if (fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if (this.options.scaleMode=='box') { this.dims = [this.element.offsetHeight, this.element.offsetWidth]; }
    if (/^content/.test(this.options.scaleMode)) { this.dims = [this.element.scrollHeight, this.element.scrollWidth]; }
    if (!this.dims) {
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
	}
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if (this.options.scaleContent && this.fontSize) { this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); }
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) { this.element.setStyle(this.originalStyle); }
  },
  setDimensions: function(height, width) {
    var d = { };
    if (this.options.scaleX) { d.width = width.round() + 'px'; }
    if (this.options.scaleY) { d.height = height.round() + 'px'; }
    if (this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if (this.elementPositioning == 'absolute') {
        if (this.options.scaleY) { d.top = this.originalTop-topd + 'px'; }
        if (this.options.scaleX) { d.left = this.originalLeft-leftd + 'px'; }
      } else {
        if (this.options.scaleY) { d.top = -topd + 'px'; }
        if (this.options.scaleX) { d.left = -leftd + 'px'; }
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) { throw(Effect._elementDoesNotExistError); }
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = { };
    if (!this.options.keepBackgroundImage) {
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage: 'none'});
    }
    if (!this.options.endcolor) { this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); }
    if (!this.options.restorecolor) { this.options.restorecolor = this.element.getStyle('background-color'); }
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16); }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]; }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
    scrollOffsets = document.viewport.getScrollOffsets(),
    elementOffsets = $(element).cumulativeOffset(),
    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  

  if (options.offset) { elementOffsets[1] += options.offset; }

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1] > max ? max : elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()); }
  );
};

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
    from: element.getOpacity() || 1.0,
    to:   0.0,
    afterFinishInternal: function(effect) { 
      if (effect.options.to!==0) { return; }
      effect.element.hide().setStyle({opacity: oldOpacity}); 
    }
  }, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show(); 
  }}, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { 
    opacity: element.getInlineOpacity(), 
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element);
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().setStyle(oldStyle); }
     }, arguments[1] || { })
   );
};

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      } 
    }, arguments[1] || { })
  );
};

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || { }));
};

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      var es = new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { 
          effect.element.makePositioned().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
        }
      });
    }
  }, arguments[1] || { }));
};

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
        } 
      }, arguments[1] || { }));
};

Effect.Shake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    var em = new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    var em = new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    var em = new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    var em = new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    var em = new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}); }}); }}); }}); }}); }});
};

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) { effect.element.setStyle({top: ''}); }
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || { })
  );
};

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    // scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) { effect.element.setStyle({top: ''}); }
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, { 
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      effect.element.makeClipping(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping(); 
    }
  });
};

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) {
      effect.element.hide().makeClipping().makePositioned();
    },
    afterFinishInternal: function(effect) {
      var ep = new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'}).show(); 
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
             }
           }, options)
      );
    }
  });
};

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { };
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)); };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
};

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    var es = new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || { }));
};

Effect.Morph = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) { throw(Effect._elementDoesNotExistError); }
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });
    
    if (!Object.isString(options.style)) { this.style = $H(options.style); }
    else {
      if (options.style.include(':')) {
        this.style = options.style.parseStyle();
      } else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        };
      }
    }
    this.start(options);
  },
  
  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) { color = '#ffffff'; }
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 ) ;
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if (property == 'opacity') {
        value = parseFloat(value);
        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) { this.element.setStyle({zoom: 1}); }
      } else if (Element.CSS_LENGTH.test(value)) {
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] : null;
      }

      var originalValue = this.element.getStyle(property);
      return { 
        style: property.camelize(), 
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      );
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--) {
      style[(transform = this.transforms[i]).style] = 
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
            (transform.unit === null ? '' : transform.unit);
	}
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     track.keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)); });
      }).flatten(),
      this.options);
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');
  
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit) { style = new Element('div',{style:this}).style; }
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }
  
  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) { styleRules.set(property, style[property]);  }
  });
  
  if (Prototype.Browser.IE && this.include('opacity')) { styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); }

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
      hash.set(property, css[property]);
      return hash;
    });
    if (!styles.opacity) { styles.set('opacity', element.getOpacity()); }
    return styles;
  };
}

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    var em = new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element);
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    var eff = new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    var eff = new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) { 
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    };
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);


// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality 
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least, 
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method 
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most 
// useful when one of the tokens is \n (a newline), as it 
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined') { throw("controls.js requires including script.aculo.us' effects.js library"); }

var Autocompleter = { };
Autocompleter.Base = Class.create({
  baseInitialize: function(element, update, options) {
    element          = $(element);
    this.element     = element; 
    this.update      = $(update);  
    this.hasFocus    = false; 
    this.changed     = false; 
    this.active      = false; 
    this.index       = 0;     
    this.entryCount  = 0;
    this.oldElementValue = this.element.value;

    if(this.setOptions) {
      this.setOptions(options);
	}
    else {
      this.options = options || { };
	}

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow || 
      function(element, update){ 
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false, 
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide || 
      function(element, update){ var ef = new Effect.Fade(update,{duration:0.15}); };

    if(typeof(this.options.tokens) == 'string')  { this.options.tokens = new Array(this.options.tokens); }
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n')) { this.options.tokens.push('\n'); }

    this.observer = null;
    
    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') { this.options.onShow(this.element, this.update); }
    if(!this.iefix && 
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      var ia = new Insertion.After(this.update, 
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) { setTimeout(this.fixIEOverlapping.bind(this), 50); }
  },
  
  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') { this.options.onHide(this.element, this.update); }
    if(this.iefix) { Element.hide(this.iefix); }
  },

  startIndicator: function() {
    if(this.options.indicator) { Element.show(this.options.indicator); }
  },

  stopIndicator: function() {
    if(this.options.indicator)  { Element.hide(this.options.indicator); }
  },

  onKeyPress: function(event) {
    if(this.active) {
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         Event.stop(event);
         return;
      }
	}
     else  {
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) { return; }
	}

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) { clearTimeout(this.observer); }
      this.observer = 
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex) 
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },
  
  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },
  
  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;     
  }, 
  
  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++) {
	
        this.index==i ?  Element.addClassName(this.getEntry(i),"selected") :  Element.removeClassName(this.getEntry(i),"selected");
	  }
      if(this.hasFocus) { 
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },
  
  markPrevious: function() {
    if(this.index > 0) { this.index--; }
      else { this.index = this.entryCount-1; }
    this.getEntry(this.index).scrollIntoView(true);
  },
  
  markNext: function() {
    if(this.index < this.entryCount-1) { this.index++; }
      else { this.index = 0; }
    this.getEntry(this.index).scrollIntoView(false);
  },
  
  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },
  
  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },
  
  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) { value = Element.collectTextNodes(nodes[0], this.options.select); }
    } else {
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
	}
    
    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace) { newValue += whitespace[0]; }
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.value;
    this.element.focus();
    
    if (this.options.afterUpdateElement) { this.options.afterUpdateElement(this.element, selectedElement); }
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount = 
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else { 
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;
      
      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;   
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.value;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null !== this.tokenBounds) { return this.tokenBounds; }
    var value = this.element.value;
    if (value.strip().empty()) { return [-1, 0]; }
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) { prevTokenPos = tp; }
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) { nextTokenPos = tp; }
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index){
    if (newS[index] != oldS[index]) { return index; }
  }
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();
    
    var entry = encodeURIComponent(this.options.paramName) + '=' + 
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams)  { this.options.parameters += '&' + this.options.defaultParams; }
    
    var ar = new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the 
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector' 
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(Autocompleter.Base, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) { 
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars && 
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || (/\s/).test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length) { ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); }
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
};

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId)) { this.options.formId = ''; }
    }
    if (this.options.externalControl) { this.options.externalControl = $(this.options.externalControl); }
    if (!this.options.externalControl) { this.options.externalControlOnly = false; }
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) { return; }
    if (Event.KEY_ESC == e.keyCode) {
      this.handleFormCancellation(e);
	}
    else if (Event.KEY_RETURN == e.keyCode) {
      this.handleFormSubmission(e);
	}
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode) { btn.onclick = this._boundCancelHandler; }
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses) { link.className += ' ' + extraClasses; }
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !(/\r|\n/).test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) { fld.size = size; }
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur) { fld.onblur = this._boundSubmitHandler; }
    this._controls.editor = fld;
    if (this.options.loadTextURL) { this.loadExternalText(); }
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) { return; }
      ipe._form.appendChild(document.createTextNode(text));
    }
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase()) { this._form.appendChild(document.createElement('br')); }
    if (this.options.onFormCustomization) { this.options.onFormCustomization(this, this._form); }
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML) { this.element.innerHTML = this._oldInnerHTML; }
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) { return; }
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl) { this.options.externalControl.hide(); }
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL) { this.postProcessEditField(); }
    if (e) { Event.stop(e); }
  },
  enterHover: function(e) {
    if (this.options.hoverClassName) { this.element.addClassName(this.options.hoverClassName); }
    if (this._saving) { return; }
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML;
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) { Event.stop(e); }
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
	var options;
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params)) { params = params.toQueryParams(); }
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      var au = new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      var ar = new Ajax.Request(this.url, options);
    }
    if (e) { Event.stop(e); }
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl) { this.options.externalControl.show(); }
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName) { this.element.removeClassName(this.options.hoverClassName); }
    if (this._saving) { return; }
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags) { text = text.stripTags(); }
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    var ar = new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc) { $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); }
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly) { this.element.observe(pair.key, listener); }
      if (this.options.externalControl) { this.options.externalControl.observe(pair.key, listener); }
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) { return; }
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly) { this.element.stopObserving(pair.key, pair.value); }
      if (this.options.externalControl) { this.options.externalControl.stopObserving(pair.key, pair.value); }
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL) {
      this.loadCollection();
	}
    else {
      this.checkForExternalText();
	}
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!(/^\[.*\]$/).test(js)) { throw 'Server returned an invalid collection representation.'; }
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    var ar = new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL) {
      this.loadExternalText();
	}
    else { 
      this.buildOptionList();
	}
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    var ar = new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) { return; }
  function fallback(name, expr) {
    if (name in options || expr === undefined) { return; }
    options[name] = expr;
  }
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : options.cancelLink == options.cancelButton === false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : options.okLink == options.okButton === false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      var ef = new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect) { ipe._effect.cancel(); }
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer, 
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element); 
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) { return; }
    if(this.timer) { clearTimeout(this.timer); }
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
});


// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Scriptaculous = {
  Version: '1.8.1',
  require: function(libraryName) {
    // inserting via DOM fails in Safari 2.0, so brute force approach
    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
  },
  REQUIRED_PROTOTYPE: '1.6.0',
  load: function() {
    function convertVersionString(versionString){
      var r = versionString.split('.');
      return parseInt(r[0], 10)*100000 + parseInt(r[1], 10)*1000 + parseInt(r[2], 10);
    }
 
    if((typeof Prototype=='undefined') || 
       (typeof Element == 'undefined') || 
       (typeof Element.Methods=='undefined') ||
       (convertVersionString(Prototype.Version) < 
        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) {
       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
        Scriptaculous.REQUIRED_PROTOTYPE);
	}
    
    $A(document.getElementsByTagName("script")).findAll( function(s) {
      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/));
    }).each( function(s) {
      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
      var includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
       function(include) { Scriptaculous.require(path+include+'.js'); });
    });
  }
};

Scriptaculous.load();

if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.module = JLG.module || {};
JLG.module.validation = JLG.module.validation || {};
JLG.module.validation.ClassMethods = {

	attrAccessor: function (p_attr, p_options) {

		// Add the attribute
		p_options = p_options || {};
		p_options.__element = {};
		p_options.__element.__attrName = p_attr;
		(this.__attributes = this.__attributes || {}) [p_attr] = p_options;

		// Create a getter for the attribute
		var getter = "get" + this.camelize (p_attr);
		this [getter] = function () {return p_options.__element;};
	},

	column: function( p_attr, p_options ) {

		// Add the attribute
		p_options = p_options || {};
		p_options.__column = true;
		(this.__attributes = this.__attributes || {})[ p_attr ] = p_options;

		// Create a getter for the column
		var getter = "get" + this.camelize( p_attr );
		this[ getter ] = function() {

			if (!p_options.el) { p_options.el = $( (this.getInstanceVariable() ? (this.getInstanceVariable() + "_") : "") + p_attr ); }
			return p_options.el;
		};
	},

	validatesEach: function( p_attrs, p_pfn ) {

		// Make sure the arguments are good
		if (gQuery.typeOf( p_attrs ) != "array") { p_attrs = [ p_attrs ]; }
		for (var index = 0, len = p_attrs.length; index < len; ++index) { this.getObjectModel().__addValidation( p_attrs[ index ], p_pfn ); }
	},

	validatesFormatOf: function( p_attrs, p_options ) {

		p_options = p_options || {};
		this.getObjectModel().__addDefaultValidation( p_attr, p_options, function( p_record, p_attr, p_value ) {

			// Do we need to execute the evaluation?
			if (p_options.condition && !p_options.condition.apply( this, [ p_record, p_attr, p_value ] )) { return; }

			// Does the value match?
			if (p_options.test && !p_options.test.test( p_value )) { p_record.addError( p_attr, p_options.message ); }
		});
	},

	validatesNumericalityOf: function( p_attr, p_options ) {

		p_options = p_options || {};
		this.getObjectModel ().__addDefaultValidation( p_attr, p_options, function( p_record, p_attr, p_value ) {

			// Do we need to execute the evaluation?
			if (p_options.condition && !p_options.condition.apply( this, [ p_record, p_attr, p_value ] )) { return;	}

			// Is the field present?
			var value = null;
			try {

				p_value = gQuery.trim( String( p_value ) );
				if (( /^[\-\+]?[0-9,\.]*$/ ).test( p_value )) {

					value = parseFloat( p_value );
					if (isNaN( value )) { value = null; }
					if (value && p_options.onlyInteger) {

						intValue = parseInt( p_value, 10 );
						if (isNaN( intValue ) || intValue != value) { value = null; }
					}
				}
			} catch( e ) {
			}

			// Extra validations
			if (value && p_options.equalTo && value != p_options.equalTo) { value = null; }
			if (value && p_options.greaterThan && value >= p_options.greaterThan) { value = null; }
			if (value && p_options.greaterThanOrEqualTo && value > p_options.greaterThanOrEqualTo) { value = null; }
			if (value && p_options.lessThan && value <= p_options.lessThan) { value = null; }
			if (value && p_options.lessThanOrEqualTo && value < p_options.lessThanOrEqualTo) { value = null; }
			if (value === null) { p_record.addError( p_attr, p_options.message ); }
		});
	},

	validatesPresenceOf: function( p_attr, p_options ) {

		p_options = p_options || {};
		this.getObjectModel ().__addDefaultValidation( p_attr, p_options, function( p_record, p_attr, p_value ) {

			// Do we need to execute the evaluation?
			if (p_options.condition && !p_options.condition.apply( this, [ p_record, p_attr, p_value ] )) { return;	}

			// Is the field present?
			if (!p_value || gQuery.trim( p_value ).length === 0) { p_record.addError( p_attr, p_options.message ); }
		});
	},

	__addDefaultValidation: function( p_attr, p_options, p_pfn ) {

		// Initialize
		p_options = p_options || {};

		// Make sure the attribute is valid
		if (!this.__attributes || !this.__attributes[ p_attr ]) { throw "Unrecognized attribute: " + p_attr; }
		if (!p_options.message) { throw "No error message specified"; }
		this.getObjectModel().__addValidation( p_attr, p_pfn );
	},

	__addValidation: function (p_attr, p_pfn) {

		// Add the validation
		this.__validations = this.__validations || {};
		(this.__validations [p_attr] = this.__validations [p_attr] || []).push (p_pfn);
	}
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.module = JLG.module || {};
JLG.module.validation = JLG.module.validation || {};
JLG.module.validation.InstanceMethods = {

	addError: function (p_attr, p_message) {

		this.__errors = this.__errors || {};
		(this.__errors [p_attr] = this.__errors [p_attr] || []).push (p_message);
	},

	afterAttrBlurred: function( p_event ) {

		// Any errors on the field?
		var el = p_event.target;
		var attr = el.__attrName;
		if( el == this.getFocusEl() ) {

			// Do we have an attribute to validate?
			if( attr && this.getAttrOptions( attr ).validateOnBlur ) {

				var result;

				// Have we already validated the event?
				if( !p_event.__validated ) {

					result = this.validateAttr( attr );
					p_event.__validated = true;

				} else {

					result = this.hasAttrErrors( attr );
				}
				if( !result ) {

					// Display the errors
					alert( this.getAttrErrors( attr ).join( "\n" ) );
					setTimeout( function() {

						el.focus();
						if( gQuery( el ).attr( "type" ).toLowerCase() == "text" || el.nodeName.toLowerCase() == "textarea" ) { el.select(); }

					}.bind( this ), 50 );

				} else {

					this.setFocusEl( null );
				}
			} else {

				this.setFocusEl( null );
			}
		}
	},

	afterAttrFocused: function( p_event ) {

		if( !this.__focusEl ) { this.__focusEl = p_event.target; }
	},

	camelize: function (p_value) {

		var camel = "";
		for (var words = p_value.match(/([0-9a-zA-Z]*)/g), index = 0, len = words.length; index < len; ++index) {

			var word = words [index];
			camel += word.substr (0, 1).toUpperCase () + word.substr (1);
		}
		return camel;
	},

	getAttrEl: function( p_attr ) {

		var attr = this.getAttrOptions( p_attr );
		if( attr ) {

			if( attr.__column ) {

				if( !attr.el ) {

					attr.el = gQuery( "#" + (this.getInstanceVariable() ? (this.getInstanceVariable() + "_") : "") + p_attr )[ 0 ];
					if( attr.el ) { attr.el.__attrName = p_attr; }
				}
				attr = attr.el;

			} else {

				attr = attr.__element;
			}
		}
		return attr;
	},

	getAttrErrors: function (p_attr) {

		return (this.__errors || {}) [p_attr] || [];
	},

	getAttrNameFor: function( p_el ) {

		return p_el.__attrName;
	},

	getAttrOptions: function( p_attr ) {

		return this.__attributes[ p_attr ];
	},

	getAttrValue: function( p_attr ) {

		var attr = this.getAttrEl( p_attr );
		return (attr ? attr.value : null);
	},

	getErrors: function () {

		return this.__errors || {};
	},

	getFocusEl: function () {

		return this.__focusEl;
	},

	getInstanceVariable: function() { return null; },

	hasAttrErrors: function( p_attr ) { return (this.getAttrErrors( p_attr ).length !== 0); },

	isValid: function() { return this.validate(); },

	setAttrValue: function (p_attr, p_value) {

		var attr = this.getAttrEl (p_attr);
		if (attr) {attr.value = p_value;}
	},

	setFocusEl: function (p_value) {

		this.__focusEl = p_value;
	},

	updateAttributes: function (p_attrs) {

		for (var attr in p_attrs) {

			var el = this.getAttrEl (attr);
			if (!el) {throw "Unrecognized attribute: " + attr;}
			el.value = p_attrs [attr];
		}
	},

	validate: function() {

		// Loop through each attribute with a validation
		this.__errors = null;
		for (var attr in this.__validations) { this.validateAttr( attr ); }
		return (this.__errors === null);
	},

	validateAndAlert: function() {

		if (!this.isValid()) {

			var errors = this.getErrors();
			for (attr in errors) {

				el = this.getAttrEl( attr );
				alert( errors[ attr ].join( "\n" ) );
				el.focus();
				el.select();
				break;
				// NOTREACHED
			}
			return false;
			// NOTREACHED
		}
		return true;
	},

	validateAttr: function (p_attr) {

		// Run the validations for the given attribute
		var value = this.getAttrValue (p_attr);
		for (var validations = this.__validations [p_attr] || [], index = 0, len = validations.length; index < len; ++index) {

			// Run the next validation
			(validations [index]).apply (this, [ this, p_attr, value ]);
		}

		// Any errors on the attribute
		var errors = this.getAttrErrors (p_attr);
		if (errors.length > 0 && this.renderErrors) {

			this.renderErrors (p_attr, errors);
		}
		return (errors.length === 0);
	},

	validateEventSrc: function (p_event) {

		var result;
		var el = p_event.src ();
		var attr = this.getAttrNameFor (el);

		// Already validated?
		if (!(p_event.event () || {}).__validated) {

			result = this.validateAttr (attr);
			if (p_event.event ()) {p_event.event ().__validated = true;}

		} else {

			result = !this.hasAttrErrors (attr);
		}
		return result;
	}
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.module = JLG.module || {};
JLG.module.validation = JLG.module.validation || {};
JLG.module.validation.Base = JLG.module.validation.Base || {

	extend: function( p_klass ) {

		var cm = JLG.module.validation.ClassMethods;
		var im = JLG.module.validation.InstanceMethods;
		var proto = p_klass.prototype;
		var pfn;

		// Add instance methods
		for( pfn in im ) { if( !proto[ pfn ] ) { proto[ pfn ] = im[ pfn ]; } }
		if( !proto.getObjectModel ) { proto.getObjectModel = function() { return p_klass; }; }

		// Add class methods
		for( pfn in cm ) { if( !p_klass[ pfn ] ) { p_klass[ pfn ] = cm[ pfn ].bind( proto ); } }

		// Add our own initializer
		proto.__initialize_without_validations = proto.initialize;
		proto.initialize = function() {

			// Setup event handlers
			for( var attr in (this.__attributes || {}) ) {

				// Find the options for the attribute
				var options = this.__attributes[ attr ];
				var el = this.getAttrEl( attr );
				if( el ) {

					// Validate the attribute whenever it changes?
					gQuery( el ).bind( "blur", gQuery.bindFn( this.afterAttrBlurred, this ) );
					gQuery( el ).bind( "focus", gQuery.bindFn( this.afterAttrFocused, this ) );
				}
			}

			// Call the old initializer
			this.getObjectModel().prototype.__initialize_without_validations.apply( this, arguments );
		};
	}
};


/**
 * Global non-namespaced methods
 */
function _ (p_value) {

    return p_value;
}

/**
 * JLG.core
 */
if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.core = JLG.core || {

	evalScriptTagsIn: function( p_element ) {

		p_element = gQuery( p_element );
	    if( p_element && p_element.length > 0 ) {

			p_element.find( "script" ).each( function() {

				try { eval( gQuery( this ).html().split( "<!--" ).join( "" ).split( "-->" ).join( "" ) ); } catch( e ) {}				
			} );
    	}
	},

    fireEvent: function( p_element, p_eventName ) {

		var event;
		p_element = $( p_element );
		if (p_element == document && document.createEvent && !element.dispatchEvent) { p_element = document.documentElement; }
		if (document.createEvent) {

			event = document.createEvent( "HTMLEvents" );
			event.initEvent( p_eventName, true, true );

		} else {

			event = document.createEventObject();
			event.eventType = p_eventName;
		}
		event.eventName = p_eventName;
		event.memo = {};
		if (document.createEvent) {

			p_element.dispatchEvent( event );

		} else {

			try {

				p_element.fireEvent( "on" + event.eventType, event );

			} catch (e) {

				Element.fire( p_element, event.eventType, event );
			}
		}
		return Event.extend( event );
  	},

    getElement: function( p_element, p_options ) {

        p_options = p_options || {};
        if( gQuery.typeOf( p_element ) == "string" ) {

			if( p_options.parent ) {

				p_element = gQuery( JLG.core.getElement( p_options.parent ) ).find( (p_element.slice( 0, 1 ) == ".") ? p_element : "#" + p_element );

			} else {

				p_element = gQuery( (p_element.slice( 0, 1 ) == ".") ? p_element : "#" + p_element );
			}
			p_element = (p_element && p_element.length > 0) ? p_element[ 0 ] : null;
        }
        return p_element;
    },

	getElementsByTagAndClassName: function( p_tag, p_class, p_parent ) {

		if (typeof( p_tag ) == 'undefined' || p_tag === null) { p_tag = '*'; }
		if (typeof( p_parent ) == 'undefined' || p_parent === null) { p_parent = document; }
		p_parent = $( p_parent );
		if (p_parent === null) { return []; }
		var children = (p_parent.getElementsByTagName( p_tag ) || document.all);
		if (typeof( p_class ) == 'undefined' || p_class === null) { return children; }

		var elements = [];
		for (var i = 0; i < children.length; i++) {

			var child = children[ i ];
			var cls = child.className;
			if (typeof( cls ) != "string") { cls = child.getAttribute( "class" ); }
			if (typeof( cls ) == "string") {

				var classNames = cls.split( ' ' );
				for (var j = 0; j < classNames.length; j++) {

					if (classNames[ j ] == p_class) {

						elements.push( child );
						break;
					}
				}
			}
		}
		if (elements && gQuery.typeOf( elements ) != "array") { elements = [ elements ]; }
		return elements;
	},

	getGeckoVersion: function () {

		var result = -1;

		// Check for firefox user agent
		if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {

		 	result = Number (RegExp.$1);
		}
		return result;
	},

	getIEVersion: function () {

		// Initialize
		var result = -1;

		// Is this browser identifying itself as IE?
		if (navigator.appName == 'Microsoft Internet Explorer') {

			// Get the version information
			var ua = navigator.userAgent;
			var re  = new RegExp ("MSIE ([0-9]{1,}[\\.0-9]{0,})");
			if (re.exec (ua) !== null) {

			    result = parseFloat (RegExp.$1);
			}
		}
		return result;
	},

	getOperaVersion: function () {

		var result = -1;

		if (/Opera/i.test(navigator.userAgent)) {

			result = 1;
		}
		return result;
	},

	getSafariVersion: function () {

		var result = -1;

		if (/WebKit/i.test(navigator.userAgent)) {

			// Get the version information
			var ua = navigator.userAgent;
			var re  = new RegExp ("Version/([0-9]{1,}[\.]{0,1}[0-9]{0,})");
			if (re.exec (ua) !== null) {

			    result = parseFloat (RegExp.$1);

			} else {

				result = 2;
			}
		}
		return result;
	},

	isMac: function () {

		return (/Mac/i.test(navigator.platform));
	},

	join: function () {

		var index;
		var len = arguments.length;
		var result = '';
		var joiner;
		var tmp;
		var core = JLG.core;
		var pfnTo = gQuery.typeOf;
		var pfnJoin = core.join;
		var pfnTrim = core.trim;

		// Anything to join?
		if (len > 0) {

			// Determine the join character and loop through the elements
			joiner = arguments [0];
			for (index = 1; index < len; index++) {

				// Strip spaces from the text and then join
				tmp = arguments [index];
				if ("array" == pfnTo (tmp)) {

				    tmp = pfnJoin.apply (null, [joiner].concat (tmp));
				}
				else {

				    tmp = pfnTrim (tmp);
				}
				if (tmp.length > 0) {

					// Add the value and then a joiner if necessary
					result += tmp;
					if ((index + 1) != len && tmp.charAt (tmp.length - 1) != joiner) {

					    result += joiner;
					}
				}
			}
		}

		// Return the joined string
		return result;
	}
};


/*****
 * Modified version of YAHOO.util.History to work without the rest of YAHOO's code
 */
if (typeof window.JLG == "undefined" || !window.JLG) {window.JLG = {};}
JLG.util = JLG.util || {};
JLG.util.History = (function () {

	var m_tasks = [];

    /**
     * Our hidden IFrame used to store the browsing history.
     *
     * @property _histFrame
     * @type HTMLIFrameElement
     * @default null
     * @private
     */
    var _histFrame = null;

    /**
     * INPUT field (with type="hidden" or type="text") or TEXTAREA.
     * This field keeps the value of the initial state, current state
     * the list of all states across pages within a single browser session.
     *
     * @property _stateField
     * @type HTMLInputElement|HTMLTextAreaElement
     * @default null
     * @private
     */
    var _stateField = null;

    /**
     * Flag used to tell whether JLG.util.History.initialize has been called.
     *
     * @property _initialized
     * @type boolean
     * @default false
     * @private
     */
    var _initialized = false;

    /**
     * List of registered modules.
     *
     * @property _modules
     * @type array
     * @default []
     * @private
     */
    var _modules = [];

    /**
     * List of fully qualified states. This is used only by Safari.
     *
     * @property _fqstates
     * @type array
     * @default []
     * @private
     */
    var _fqstates = [];

    /**
     * location.hash is a bit buggy on Opera. I have seen instances where
     * navigating the history using the back/forward buttons, and hence
     * changing the URL, would not change location.hash. That's ok, the
     * implementation of an equivalent is trivial.
     *
     * @method _getHash
     * @return {string} The hash portion of the document's location
     * @private
     */
    function _getHash() {
        var i, href;
        href = window.top.location.href;
        i = href.indexOf("#");
        return i >= 0 ? href.substr(i + 1) : null;
    }

	function __hasOwnProperty (o, prop) {

	    if (Object.prototype.hasOwnProperty) {

	        return o.hasOwnProperty(prop);
	    }
	    return (typeof o[prop] !== "undefined" && o.constructor.prototype[prop] !== o[prop]);
	}

    /**
     * Stores all the registered modules' initial state and current state.
     * On Safari, we also store all the fully qualified states visited by
     * the application within a single browser session. The storage takes
     * place in the form field specified during initialization.
     *
     * @method _storeStates
     * @private
     */
    function _storeStates() {

        var moduleName, moduleObj, initialStates = [], currentStates = [];

        for (moduleName in _modules) {
            if (__hasOwnProperty(_modules, moduleName)) {
                moduleObj = _modules[moduleName];
                initialStates.push(moduleName + "=" + moduleObj.initialState);
                currentStates.push(moduleName + "=" + moduleObj.currentState);
            }
        }

        _stateField.value = initialStates.join("&") + "|" + currentStates.join("&");

        if (gQuery.safariVersion() >= 0) {
            _stateField.value += "|" + _fqstates.join(",");
        }
    }

    /**
     * Sets the new currentState attribute of all modules depending on the new
     * fully qualified state. Also notifies the modules which current state has
     * changed.
     *
     * @method _handleFQStateChange
     * @param {string} fqstate Fully qualified state
     * @private
     */
    function _handleFQStateChange(fqstate) {

        var i, len, moduleName, moduleObj, modules, states, tokens, currentState;

        if (!fqstate) {
            // Notifies all modules
            for (moduleName in _modules) {
                if (__hasOwnProperty(_modules, moduleName)) {
                    moduleObj = _modules[moduleName];
                    moduleObj.currentState = moduleObj.initialState;
                    moduleObj.onStateChange(unescape(moduleObj.currentState));
                }
            }
            return;
        }

        modules = [];
        states = fqstate.split("&");
        for (i = 0, len = states.length; i < len; i++) {
            tokens = states[i].split("=");
            if (tokens.length === 2) {
                moduleName = tokens[0];
                currentState = tokens[1];
                modules[moduleName] = currentState;
            }
        }

        for (moduleName in _modules) {
            if (__hasOwnProperty(_modules, moduleName)) {
                moduleObj = _modules[moduleName];
                currentState = modules[moduleName];
                if (!currentState || moduleObj.currentState !== currentState) {
                    moduleObj.currentState = currentState || moduleObj.initialState;
                    moduleObj.onStateChange(unescape(moduleObj.currentState));
                }
            }
        }
    }

    /**
     * Update the IFrame with our new state.
     *
     * @method _updateIFrame
     * @private
     * @return {boolean} true if successful. false otherwise.
     */
    function _updateIFrame (fqstate) {

        var html, doc;

        html = '<html><body><div id="state">' + fqstate + '</div></body></html>';

        try {
            doc = _histFrame.contentWindow.document;
            doc.open();
            doc.write(html);
            doc.close();
            return true;
        } catch (e) {
            return false;
        }
    }

    /**
     * Periodically checks whether our internal IFrame is ready to be used.
     *
     * @method _checkIframeLoaded
     * @private
     */
    function _checkIframeLoaded() {

        var doc, elem, fqstate, hash;

        if (!_histFrame.contentWindow || !_histFrame.contentWindow.document) {
            // Check again in 10 msec...
            setTimeout(_checkIframeLoaded, 10);
            return;
        }

        // Start the thread that will have the responsibility to
        // periodically check whether a navigate operation has been
        // requested on the main window. This will happen when
        // JLG.util.History.navigate has been called or after
        // the user has hit the back/forward button.

        doc = _histFrame.contentWindow.document;
        elem = doc.getElementById("state");
        // We must use innerText, and not innerHTML because our string contains
        // the "&" character (which would end up being escaped as "&amp;") and
        // the string comparison would fail...
        fqstate = elem ? elem.innerText : null;

        hash = _getHash();

        setInterval(function () {

            var newfqstate, states, moduleName, moduleObj, newHash, historyLength;

            doc = _histFrame.contentWindow.document;
            elem = doc.getElementById("state");
            // See my comment above about using innerText instead of innerHTML...
            newfqstate = elem ? elem.innerText : null;

            newHash = _getHash();

            if (newfqstate !== fqstate) {

                fqstate = newfqstate;
                _handleFQStateChange(fqstate);

                if (!fqstate) {
                    states = [];
                    for (moduleName in _modules) {
                        if (__hasOwnProperty(_modules, moduleName)) {
                            moduleObj = _modules[moduleName];
                            states.push(moduleName + "=" + moduleObj.initialState);
                        }
                    }
                    newHash = states.join("&");
                } else {
                    newHash = fqstate;
                }

                // Allow the state to be bookmarked by setting the top window's
                // URL fragment identifier. Note that here, we are on IE, and
                // IE does not touch the browser history when setting the hash
                // (unlike all the other browsers). I used to write:
                //     top.location.replace( "#" + hash );
                // but this had a side effect when the page was not the top frame.
                top.location.hash = newHash;
                hash = newHash;

                _storeStates();

            } else if (newHash !== hash) {

                // The hash has changed. The user might have clicked on a link,
                // or modified the URL directly, or opened the same application
                // bookmarked in a specific state using a bookmark. However, we
                // know the hash change was not caused by a hit on the back or
                // forward buttons, or by a call to navigate() (because it would
                // have been handled above) We must handle these cases, which is
                // why we also need to keep track of hash changes on IE!

                // Note that IE6 has some major issues with this kind of user
                // interaction (the history stack gets completely messed up)
                // but it seems to work fine on IE7.

                hash = newHash;

                // Now, store a new history entry. The following will cause the
                // code above to execute, doing all the dirty work for us...
                _updateIFrame(newHash);
            }

        }, 50);

        _initialized = true;

		var jlgIndex;
		var jlgLen;
	    for (jlgIndex = 0, jlgLen = m_tasks.length; jlgIndex < jlgLen; jlgIndex++) {

			//try {

				var jlgTask = m_tasks [jlgIndex];
				var jlgFn = jlgTask.pfn;
                var jlgCtx = window;
				var jlgOverride = jlgTask.override;
				var jlgObj = jlgTask.obj;
                if (jlgOverride) {
                    if (jlgOverride === true) {
                        jlgCtx = jlgObj;
                    } else {
                        jlgCtx = jlgOverride;
                    }
                }
                jlgFn.call(jlgCtx, "onLoad", [], jlgObj);

			// } catch (e) {
			// }
		}
    }

    /**
     * Finish up the initialization of the Browser History Manager.
     *
     * @method _initialize
     * @private
     */
    function _initialize() {

        var i, len, parts, tokens, moduleName, moduleObj, initialStates, initialState, currentStates, currentState, counter, hash;

        // Decode the content of our storage field...
        parts = _stateField.value.split("|");

        if (parts.length > 1) {

            initialStates = parts[0].split("&");
            for (i = 0, len = initialStates.length; i < len; i++) {
                tokens = initialStates[i].split("=");
                if (tokens.length === 2) {
                    moduleName = tokens[0];
                    initialState = tokens[1];
                    moduleObj = _modules[moduleName];
                    if (moduleObj) {
                        moduleObj.initialState = initialState;
                    }
                }
            }

            currentStates = parts[1].split("&");
            for (i = 0, len = currentStates.length; i < len; i++) {
                tokens = currentStates[i].split("=");
                if (tokens.length >= 2) {
                    moduleName = tokens[0];
                    currentState = tokens[1];
                    moduleObj = _modules[moduleName];
                    if (moduleObj) {
                        moduleObj.currentState = currentState;
                    }
                }
            }
        }

        if (parts.length > 2) {
            _fqstates = parts[2].split(",");
        }

        if (gQuery.ieVersion() >= 0) {

            _checkIframeLoaded();

        } else {

            // Start the thread that will have the responsibility to
            // periodically check whether a navigate operation has been
            // requested on the main window. This will happen when
            // JLG.util.History.navigate has been called or after
            // the user has hit the back/forward button.

            // On Safari 1.x and 2.0, the only way to catch a back/forward
            // operation is to watch history.length... We basically exploit
            // what I consider to be a bug (history.length is not supposed
            // to change when going back/forward in the history...) This is
            // why, in the following thread, we first compare the hash,
            // because the hash thing will be fixed in the next major
            // version of Safari. So even if they fix the history.length
            // bug, all this will still work!
            counter = history.length;

            // On Gecko and Opera, we just need to watch the hash...
            hash = _getHash();

            setInterval(function () {

                var state, newHash, newCounter;

                newHash = _getHash();
                newCounter = history.length;
                if (newHash !== hash) {

                    hash = newHash;
                    counter = newCounter;
                    _handleFQStateChange(hash);
                    _storeStates();
                } else if (newCounter !== counter && gQuery.safariVersion() >= 0 && gQuery.safariVersion() < 3 ) {
                    hash = newHash;
                    counter = newCounter;
                    state = _fqstates[counter - 1];
                    _handleFQStateChange(state);
                    _storeStates();
                }

            }, 50);

            _initialized = true;

			var jlgIndex;
			var jlgLen;
		    for (jlgIndex = 0, jlgLen = m_tasks.length; jlgIndex < jlgLen; jlgIndex++) {

				// try {

					var jlgTask = m_tasks [jlgIndex];
					var jlgFn = jlgTask.pfn;
	                var jlgCtx = window;
					var jlgOverride = jlgTask.override;
					var jlgObj = jlgTask.obj;
	                if (jlgOverride) {
	                    if (jlgOverride === true) {
	                        jlgCtx = jlgObj;
	                    } else {
	                        jlgCtx = jlgOverride;
	                    }
	                }
	                jlgFn.call(jlgCtx, "onLoad", [], jlgObj);

				// } catch (e) {
				// }
			}
        }
    }

    return {

        /**
         * Executes the supplied callback when the Browser History Manager is
         * ready. This will execute immediately if called after the Browser
         * History Manager onLoad event has fired.
         *
         * @method onReady
         * @param {function} fn what to execute when the Browser History Manager is ready.
         * @param {object} obj an optional object to be passed back as a parameter to fn.
         * @param {boolean|object} override If true, the obj passed in becomes fn's execution scope.
         */
        onReady: function (fn, obj, override) {

            if (_initialized) {

                setTimeout(function () {
                    var ctx = window;
                    if (override) {
                        if (override === true) {
                            ctx = obj;
                        } else {
                            ctx = override;
                        }
                    }
                    fn.call(ctx, "onLoad", [], obj);
                }, 0);

            } else {

				m_tasks.push({

					pfn: fn,
					obj: obj,
					override: override
				});
            }
        },

        /**
         * Registers a new module.
         *
         * @method register
         * @param {string} module Non-empty string uniquely identifying the
         *     module you wish to register.
         * @param {string} initialState The initial state of the specified
         *     module corresponding to its earliest history entry.
         * @param {function} onStateChange Callback called when the
         *     state of the specified module has changed.
         * @param {object} obj An arbitrary object that will be passed as a
         *     parameter to the handler.
         * @param {boolean} override If true, the obj passed in becomes the
         *     execution scope of the listener.
         */
        register: function (module, initialState, onStateChange, obj, override) {

            var scope, wrappedFn;

            if (typeof module !== "string" || gQuery.trim(module) === "" ||
                typeof initialState !== "string" ||
                typeof onStateChange !== "function") {
                throw new Error("Missing or invalid argument");
            }

            if (_modules[module]) {
                // Here, we used to throw an exception. However, users have
                // complained about this behavior, so we now just return.
                return;
            }

            // Note: A module CANNOT be registered after calling
            // JLG.util.History.initialize. Indeed, we set the initial state
            // of each registered module in JLG.util.History.initialize.
            // If you could register a module after initializing the Browser
            // History Manager, you would not read the correct state using
            // JLG.util.History.getCurrentState when coming back to the
            // page using the back button.
            if (_initialized) {
                throw new Error("All modules must be registered before calling JLG.util.History.initialize");
            }

            // Make sure the strings passed in do not contain our separators "," and "|"
            module = escape(module);
            initialState = escape(initialState);

            // If the user chooses to override the scope, we use the
            // custom object passed in as the execution scope.
            scope = null;
            if (override === true) {
                scope = obj;
            } else {
                scope = override;
            }

            wrappedFn = function (state) {
                return onStateChange.call(scope, state, obj);
            };

            _modules[module] = {
                name: module,
                initialState: initialState,
                currentState: initialState,
                onStateChange: wrappedFn
            };
        },

        /**
         * Initializes the Browser History Manager. Call this method
         * from a script block located right after the opening body tag.
         *
         * @method initialize
         * @param {string|HTML Element} stateField <input type="hidden"> used
         *     to store application states. Must be in the static markup.
         * @param {string|HTML Element} histFrame IFrame used to store
         *     the history (only required on Internet Explorer)
         * @public
         */
        initialize: function (stateField, histFrame) {

            if (_initialized) {
                // The browser history manager has already been initialized.
                return;
            }

            if (gQuery.operaVersion() >= 0) {
                // Opera cannot be supported because of several problems that
                // have been reported to the Opera team, but never addressed:
                //   1) Hash changes are not detected (started happening with
                //      recent versions of Opera)
                //   2) The entire DOM gets cached, so when you come back to
                //      a page, the window's onload event does not get fired,
                //      which prevents us from initializing the browser history
                //      manager.
                // As a consequence, the best thing we can do is to throw an
                // exception. The application should catch it, and degrade
                // gracefully. This is the sad state of history management.
                throw "Unsupported browser.";
				// NOTREACHED
            }

            if (typeof stateField === "string") {
                stateField = document.getElementById(stateField);
            }

            if (!stateField ||
                stateField.tagName.toUpperCase() !== "TEXTAREA" &&
                (stateField.tagName.toUpperCase() !== "INPUT" ||
                 stateField.type !== "hidden" &&
                 stateField.type !== "text")) {
                throw new Error("Missing or invalid argument");
            }

            _stateField = stateField;

            if (gQuery.ieVersion() >= 0) {

                if (typeof histFrame === "string") {
                    histFrame = document.getElementById(histFrame);
                }

                if (!histFrame || histFrame.tagName.toUpperCase() !== "IFRAME") {
                    throw new Error("Missing or invalid argument");
                }

                _histFrame = histFrame;
            }

            // Note that the event utility MUST be included inline in the page.
            // If it gets loaded later (which you may want to do to improve the
            // loading speed of your site), the onDOMReady event never fires,
            // and the history library never gets fully initialized.
			gQuery( document ).ready( _initialize );
        },

        /**
         * Call this method when you want to store a new entry in the browser's history.
         *
         * @method navigate
         * @param {string} module Non-empty string representing your module.
         * @param {string} state String representing the new state of the specified module.
         * @return {boolean} Indicates whether the new state was successfully added to the history.
         * @public
         */
        navigate: function (module, state) {

            var states;

            if (typeof module !== "string" || typeof state !== "string") {
                throw new Error("Missing or invalid argument");
            }

            states = {};
            states[module] = state;

            return JLG.util.History.multiNavigate(states);
        },

        /**
         * Call this method when you want to store a new entry in the browser's history.
         *
         * @method multiNavigate
         * @param {object} states Associative array of module-state pairs to set simultaneously.
         * @return {boolean} Indicates whether the new state was successfully added to the history.
         * @public
         */
        multiNavigate: function (states) {

            var currentStates, moduleName, moduleObj, currentState, fqstate;

            if (typeof states !== "object") {
                throw new Error("Missing or invalid argument");
            }

            if (!_initialized) {
                throw new Error("The Browser History Manager is not initialized");
            }

            for (moduleName in states) {
                if (!_modules[moduleName]) {
                    throw new Error("The following module has not been registered: " + moduleName);
                }
            }

            // Generate our new full state string mod1=xxx&mod2=yyy
            currentStates = [];

            for (moduleName in _modules) {
                if (__hasOwnProperty(_modules, moduleName)) {
                    moduleObj = _modules[moduleName];
                    if (__hasOwnProperty(states, moduleName)) {
                        currentState = states[unescape(moduleName)];
                    } else {
                        currentState = unescape(moduleObj.currentState);
                    }

                    // Make sure the strings passed in do not contain our separators "," and "|"
                    moduleName = escape(moduleName);
                    currentState = escape(currentState);

                    currentStates.push(moduleName + "=" + currentState);
                }
            }

            fqstate = currentStates.join("&");

            if (gQuery.ieVersion() >= 0) {

                return _updateIFrame(fqstate);

            } else {

                // Known bug: On Safari 1.x and 2.0, if you have tab browsing
                // enabled, Safari will show an endless loading icon in the
                // tab. This has apparently been fixed in recent WebKit builds.
                // One work around found by Dav Glass is to submit a form that
                // points to the same document. This indeed works on Safari 1.x
                // and 2.0 but creates bigger problems on WebKit. So for now,
                // we'll consider this an acceptable bug, and hope that Apple
                // comes out with their next version of Safari very soon.
                top.location.hash = fqstate;
                if (gQuery.safariVersion() >= 0 && gQuery.safariVersion() < 3) {
                    // The following two lines are only useful for Safari 1.x
                    // and 2.0. Recent nightly builds of WebKit do not require
                    // that, but unfortunately, it is not easy to differentiate
                    // between the two. Once Safari 2.0 departs the A-grade
                    // list, we can remove the following two lines...
                    _fqstates[history.length] = fqstate;
                    _storeStates();
                }

                return true;

            }
        },

        /**
         * Returns the current state of the specified module.
         *
         * @method getCurrentState
         * @param {string} module Non-empty string representing your module.
         * @return {string} The current state of the specified module.
         * @public
         */
        getCurrentState: function (module) {

            var moduleObj;

            if (typeof module !== "string") {
                throw new Error("Missing or invalid argument");
            }

            if (!_initialized) {
                throw new Error("The Browser History Manager is not initialized");
            }

            moduleObj = _modules[module];
            if (!moduleObj) {
                throw new Error("No such registered module: " + module);
            }

            return unescape(moduleObj.currentState);
        },

        /**
         * Returns the state of a module according to the URL fragment
         * identifier. This method is useful to initialize your modules
         * if your application was bookmarked from a particular state.
         *
         * @method getBookmarkedState
         * @param {string} module Non-empty string representing your module.
         * @return {string} The bookmarked state of the specified module.
         * @public
         */
        getBookmarkedState: function (module) {

            var i, len, idx, hash, states, tokens, moduleName;

            if (typeof module !== "string") {
                throw new Error("Missing or invalid argument");
            }

            // Use location.href instead of location.hash which is already
            // URL-decoded, which creates problems if the state value
            // contained special characters...
            idx = top.location.href.indexOf("#");
            hash = idx >= 0 ? top.location.href.substr(idx + 1) : top.location.href;

            states = hash.split("&");
            for (i = 0, len = states.length; i < len; i++) {
                tokens = states[i].split("=");
                if (tokens.length === 2) {
                    moduleName = tokens[0];
                    if (moduleName === module) {
                        return unescape(tokens[1]);
                    }
                }
            }

            return null;
        },

        /**
         * Returns the value of the specified query string parameter.
         * This method is not used internally by the Browser History Manager.
         * However, it is provided here as a helper since many applications
         * using the Browser History Manager will want to read the value of
         * url parameters to initialize themselves.
         *
         * @method getQueryStringParameter
         * @param {string} paramName Name of the parameter we want to look up.
         * @param {string} queryString Optional URL to look at. If not specified,
         *     this method uses the URL in the address bar.
         * @return {string} The value of the specified parameter, or null.
         * @public
         */
        getQueryStringParameter: function (paramName, url) {

            var i, len, idx, queryString, params, tokens;

            url = url || top.location.href;

            idx = url.indexOf("?");
            queryString = idx >= 0 ? url.substr(idx + 1) : url;

            // Remove the hash if any
            idx = queryString.lastIndexOf("#");
            queryString = idx >= 0 ? queryString.substr(0, idx) : queryString;

            params = queryString.split("&");

            for (i = 0, len = params.length; i < len; i++) {
                tokens = params[i].split("=");
                if (tokens.length >= 2) {
                    if (tokens[0] === paramName) {
                        return unescape(tokens[1]);
                    }
                }
            }

            return null;
        }

    };
})();


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.css = JLG.css || {

	find: function( p_name ) {

        // Try to locate the specified rule
        p_name = p_name.toLowerCase();
		return JLG.css._forEach( function( p_css ) { return (p_css.selectorText.toLowerCase() == p_name); } );
	},

	include: function( p_href, p_media ) {

		// Download the stylesheet
		var head = document.getElementsByTagName( "head" )[ 0 ];
		var css = document.createElement( 'link' );
		css.type = 'text/css';
		css.rel = 'stylesheet';
		css.href = p_href;
		css.media = p_media || 'screen';
		head.appendChild( css );
		return css;
	},

    styleExists: function( p_name ) {

		return ((JLG.css.find( p_name ) === null) ? false : true);
    },

	/**
	 * Protected methods
	 */
	_forEach: function( p_pfn ) {

		var isDone = false;
        var index;
        var len;
		var list;
		var rules;
		var stack = [{ index: 0, rules: document.styleSheets }];

		// Enumarate all of the rule lists
		while (stack.length > 0) {

			// Next item from the list
			list = stack.pop ();
			rules = list.rules;

			// Enumerate the rules
	        for (index = list.index, len = rules.length; index < len; index++) {

				var css;

				try {

		            // Did we find a rule?
					if (rules.item) {

						css = rules.item( index );

					} else {

						css = rules[ index ];
					}

		            // Do we need to further investigate?
		            if (css.cssRules) {

						stack.push({ index: index + 1, rules: rules });
		                rules = css.cssRules;
		                index = -1;
		                len = rules.length || 0;

					} else if (css.rules) {

						stack.push({ index: index + 1, rules: rules });
		                rules = css.rules;
		                index = -1;
		                len = rules.length || 0;

					} else if (css.selectorText) {

						isDone = p_pfn( css );
						if (isDone) {

							break;
							// NOTREACHED
						}
					}
				} catch (e) {}
	        }

			// Found it?
			if (isDone) {

				break;
				// NOTREACHED
			}
		}

		// Return the css
		if (!isDone) {

			css = null;
		}
		return css;
	},

	/**
	 * Member variables
	 */
	_loaded: {}
};


/*!	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/

var swfobject = function() {
	
	var UNDEF = "undefined",
		OBJECT = "object",
		SHOCKWAVE_FLASH = "Shockwave Flash",
		SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
		FLASH_MIME_TYPE = "application/x-shockwave-flash",
		EXPRESS_INSTALL_ID = "SWFObjectExprInst",
		ON_READY_STATE_CHANGE = "onreadystatechange",
		
		win = window,
		doc = document,
		nav = navigator,
		
		plugin = false,
		domLoadFnArr = [main],
		regObjArr = [],
		objIdArr = [],
		listenersArr = [],
		storedAltContent,
		storedAltContentId,
		storedCallbackFn,
		storedCallbackObj,
		isDomLoaded = false,
		isExpressInstallActive = false,
		dynamicStylesheet,
		dynamicStylesheetMedia,
		autoHideShow = true,
	
	/* Centralized function for browser feature detection
		- User agent string detection is only used when no good alternative is possible
		- Is executed directly for optimal performance
	*/	
	ua = function() {
		var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
			u = nav.userAgent.toLowerCase(),
			p = nav.platform.toLowerCase(),
			windows = p ? (/win/).test(p) : /win/.test(u),
			mac = p ? (/mac/).test(p) : /mac/.test(u),
			webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
			ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
			playerVersion = [0,0,0],
			d = null;
		if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
			d = nav.plugins[SHOCKWAVE_FLASH].description;
			if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
				plugin = true;
				ie = false; // cascaded feature detection for Internet Explorer
				d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
				playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
				playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
				playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
			}
		}
		else if (typeof win.ActiveXObject != UNDEF) {
			try {
				var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
				if (a) { // a will return null when ActiveX is disabled
					d = a.GetVariable("$version");
					if (d) {
						ie = true; // cascaded feature detection for Internet Explorer
						d = d.split(" ")[1].split(",");
						playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
			}
			catch(e) {}
		}
		return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
	}(),
	
	/* Cross-browser onDomLoad
		- Will fire an event as soon as the DOM of a web page is loaded
		- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
		- Regular onload serves as fallback
	*/ 
	onDomLoad = function() {
		if (!ua.w3) { return; }
		if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
			callDomLoadFunctions();
		}
		if (!isDomLoaded) {
			if (typeof doc.addEventListener != UNDEF) {
				doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
			}		
			if (ua.ie && ua.win) {
				doc.attachEvent(ON_READY_STATE_CHANGE, function() {
					if (doc.readyState == "complete") {
						doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
						callDomLoadFunctions();
					}
				});
				if (win == top) { // if not inside an iframe
					(function(){
						if (isDomLoaded) { return; }
						try {
							doc.documentElement.doScroll("left");
						}
						catch(e) {
							setTimeout(arguments.callee, 0);
							return;
						}
						callDomLoadFunctions();
					})();
				}
			}
			if (ua.wk) {
				(function(){
					if (isDomLoaded) { return; }
					if (!(/loaded|complete/).test(doc.readyState)) {
						setTimeout(arguments.callee, 0);
						return;
					}
					callDomLoadFunctions();
				})();
			}
			addLoadEvent(callDomLoadFunctions);
		}
	}();
	
	function callDomLoadFunctions() {
		if (isDomLoaded) { return; }
		try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
			var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
			t.parentNode.removeChild(t);
		}
		catch (e) { return; }
		isDomLoaded = true;
		var dl = domLoadFnArr.length;
		for (var i = 0; i < dl; i++) {
			domLoadFnArr[i]();
		}
	}
	
	function addDomLoadEvent(fn) {
		if (isDomLoaded) {
			fn();
		}
		else { 
			domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
		}
	}
	
	/* Cross-browser onload
		- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
		- Will fire an event as soon as a web page including all of its assets are loaded 
	 */
	function addLoadEvent(fn) {
		if (typeof win.addEventListener != UNDEF) {
			win.addEventListener("load", fn, false);
		}
		else if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("load", fn, false);
		}
		else if (typeof win.attachEvent != UNDEF) {
			addListener(win, "onload", fn);
		}
		else if (typeof win.onload == "function") {
			var fnOld = win.onload;
			win.onload = function() {
				fnOld();
				fn();
			};
		}
		else {
			win.onload = fn;
		}
	}
	
	/* Main function
		- Will preferably execute onDomLoad, otherwise onload (as a fallback)
	*/
	function main() { 
		if (plugin) {
			testPlayerVersion();
		}
		else {
			matchVersions();
		}
	}
	
	/* Detect the Flash Player version for non-Internet Explorer browsers
		- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
		  a. Both release and build numbers can be detected
		  b. Avoid wrong descriptions by corrupt installers provided by Adobe
		  c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
		- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
	*/
	function testPlayerVersion() {
		var b = doc.getElementsByTagName("body")[0];
		var o = createElement(OBJECT);
		o.setAttribute("type", FLASH_MIME_TYPE);
		var t = b.appendChild(o);
		if (t) {
			var counter = 0;
			(function(){
				if (typeof t.GetVariable != UNDEF) {
					var d = t.GetVariable("$version");
					if (d) {
						d = d.split(" ")[1].split(",");
						ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
				else if (counter < 10) {
					counter++;
					setTimeout(arguments.callee, 10);
					return;
				}
				b.removeChild(o);
				t = null;
				matchVersions();
			})();
		}
		else {
			matchVersions();
		}
	}
	
	/* Perform Flash Player and SWF version matching; static publishing only
	*/
	function matchVersions() {
		var rl = regObjArr.length;
		if (rl > 0) {
			for (var i = 0; i < rl; i++) { // for each registered object element
				var id = regObjArr[i].id;
				var cb = regObjArr[i].callbackFn;
				var cbObj = {success:false, id:id};
				if (ua.pv[0] > 0) {
					var obj = getElementById(id);
					if (obj) {
						if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
							setVisibility(id, true);
							if (cb) {
								cbObj.success = true;
								cbObj.ref = getObjectById(id);
								cb(cbObj);
							}
						}
						else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
							var att = {};
							att.data = regObjArr[i].expressInstall;
							att.width = obj.getAttribute("width") || "0";
							att.height = obj.getAttribute("height") || "0";
							if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
							if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
							// parse HTML object param element's name-value pairs
							var par = {};
							var p = obj.getElementsByTagName("param");
							var pl = p.length;
							for (var j = 0; j < pl; j++) {
								if (p[j].getAttribute("name").toLowerCase() != "movie") {
									par[p[j].getAttribute("name")] = p[j].getAttribute("value");
								}
							}
							showExpressInstall(att, par, id, cb);
						}
						else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
							displayAltContent(obj);
							if (cb) { cb(cbObj); }
						}
					}
				}
				else {	// if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
					setVisibility(id, true);
					if (cb) {
						var o = getObjectById(id); // test whether there is an HTML object element or not
						if (o && typeof o.SetVariable != UNDEF) { 
							cbObj.success = true;
							cbObj.ref = o;
						}
						cb(cbObj);
					}
				}
			}
		}
	}
	
	function getObjectById(objectIdStr) {
		var r = null;
		var o = getElementById(objectIdStr);
		if (o && o.nodeName == "OBJECT") {
			if (typeof o.SetVariable != UNDEF) {
				r = o;
			}
			else {
				var n = o.getElementsByTagName(OBJECT)[0];
				if (n) {
					r = n;
				}
			}
		}
		return r;
	}
	
	/* Requirements for Adobe Express Install
		- only one instance can be active at a time
		- fp 6.0.65 or higher
		- Win/Mac OS only
		- no Webkit engines older than version 312
	*/
	function canExpressInstall() {
		return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
	}
	
	/* Show the Adobe Express Install dialog
		- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
	*/
	function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
		isExpressInstallActive = true;
		storedCallbackFn = callbackFn || null;
		storedCallbackObj = {success:false, id:replaceElemIdStr};
		var obj = getElementById(replaceElemIdStr);
		if (obj) {
			if (obj.nodeName == "OBJECT") { // static publishing
				storedAltContent = abstractAltContent(obj);
				storedAltContentId = null;
			}
			else { // dynamic publishing
				storedAltContent = obj;
				storedAltContentId = replaceElemIdStr;
			}
			att.id = EXPRESS_INSTALL_ID;
			if (typeof att.width == UNDEF || (!(/%$/).test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
			if (typeof att.height == UNDEF || (!(/%$/).test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
			doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
			var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
				fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
			if (typeof par.flashvars != UNDEF) {
				par.flashvars += "&" + fv;
			}
			else {
				par.flashvars = fv;
			}
			// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
			// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			if (ua.ie && ua.win && obj.readyState != 4) {
				var newObj = createElement("div");
				replaceElemIdStr += "SWFObjectNew";
				newObj.setAttribute("id", replaceElemIdStr);
				obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
				obj.style.display = "none";
				(function(){
					if (obj.readyState == 4) {
						obj.parentNode.removeChild(obj);
					}
					else {
						setTimeout(arguments.callee, 10);
					}
				})();
			}
			createSWF(att, par, replaceElemIdStr);
		}
	}
	
	/* Functions to abstract and display alternative content
	*/
	function displayAltContent(obj) {
		if (ua.ie && ua.win && obj.readyState != 4) {
			// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
			// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			var el = createElement("div");
			obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
			el.parentNode.replaceChild(abstractAltContent(obj), el);
			obj.style.display = "none";
			(function(){
				if (obj.readyState == 4) {
					obj.parentNode.removeChild(obj);
				}
				else {
					setTimeout(arguments.callee, 10);
				}
			})();
		}
		else {
			obj.parentNode.replaceChild(abstractAltContent(obj), obj);
		}
	} 

	function abstractAltContent(obj) {
		var ac = createElement("div");
		if (ua.win && ua.ie) {
			ac.innerHTML = obj.innerHTML;
		}
		else {
			var nestedObj = obj.getElementsByTagName(OBJECT)[0];
			if (nestedObj) {
				var c = nestedObj.childNodes;
				if (c) {
					var cl = c.length;
					for (var i = 0; i < cl; i++) {
						if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
							ac.appendChild(c[i].cloneNode(true));
						}
					}
				}
			}
		}
		return ac;
	}
	
	/* Cross-browser dynamic SWF creation
	*/
	function createSWF(attObj, parObj, id) {
		var r, el = getElementById(id);
		if (ua.wk && ua.wk < 312) { return r; }
		if (el) {
			if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
				attObj.id = id;
			}
			if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
				var att = "";
				for (var i in attObj) {
					if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
						if (i.toLowerCase() == "data") {
							parObj.movie = attObj[i];
						}
						else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							att += ' class="' + attObj[i] + '"';
						}
						else if (i.toLowerCase() != "classid") {
							att += ' ' + i + '="' + attObj[i] + '"';
						}
					}
				}
				var par = "";
				for (var j in parObj) {
					if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
						par += '<param name="' + j + '" value="' + parObj[j] + '" />';
					}
				}
				el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
				objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
				r = getElementById(attObj.id);	
			}
			else { // well-behaving browsers
				var o = createElement(OBJECT);
				o.setAttribute("type", FLASH_MIME_TYPE);
				for (var m in attObj) {
					if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
						if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							o.setAttribute("class", attObj[m]);
						}
						else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
							o.setAttribute(m, attObj[m]);
						}
					}
				}
				for (var n in parObj) {
					if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
						createObjParam(o, n, parObj[n]);
					}
				}
				el.parentNode.replaceChild(o, el);
				r = o;
			}
		}
		return r;
	}
	
	function createObjParam(el, pName, pValue) {
		var p = createElement("param");
		p.setAttribute("name", pName);	
		p.setAttribute("value", pValue);
		el.appendChild(p);
	}
	
	/* Cross-browser SWF removal
		- Especially needed to safely and completely remove a SWF in Internet Explorer
	*/
	function removeSWF(id) {
		var obj = getElementById(id);
		if (obj && obj.nodeName == "OBJECT") {
			if (ua.ie && ua.win) {
				obj.style.display = "none";
				(function(){
					if (obj.readyState == 4) {
						removeObjectInIE(id);
					}
					else {
						setTimeout(arguments.callee, 10);
					}
				})();
			}
			else {
				obj.parentNode.removeChild(obj);
			}
		}
	}
	
	function removeObjectInIE(id) {
		var obj = getElementById(id);
		if (obj) {
			for (var i in obj) {
				if (typeof obj[i] == "function") {
					obj[i] = null;
				}
			}
			obj.parentNode.removeChild(obj);
		}
	}
	
	/* Functions to optimize JavaScript compression
	*/
	function getElementById(id) {
		var el = null;
		try {
			el = doc.getElementById(id);
		}
		catch (e) {}
		return el;
	}
	
	function createElement(el) {
		return doc.createElement(el);
	}
	
	/* Updated attachEvent function for Internet Explorer
		- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
	*/	
	function addListener(target, eventType, fn) {
		target.attachEvent(eventType, fn);
		listenersArr[listenersArr.length] = [target, eventType, fn];
	}
	
	/* Flash Player and SWF content version matching
	*/
	function hasPlayerVersion(rv) {
		var pv = ua.pv, v = rv.split(".");
		v[0] = parseInt(v[0], 10);
		v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
		v[2] = parseInt(v[2], 10) || 0;
		return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
	}
	
	/* Cross-browser dynamic CSS creation
		- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
	*/	
	function createCSS(sel, decl, media, newStyle) {
		if (ua.ie && ua.mac) { return; }
		var h = doc.getElementsByTagName("head")[0];
		if (!h) { return; } // to also support badly authored HTML pages that lack a head element
		var m = (media && typeof media == "string") ? media : "screen";
		if (newStyle) {
			dynamicStylesheet = null;
			dynamicStylesheetMedia = null;
		}
		if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
			// create dynamic stylesheet + get a global reference to it
			var s = createElement("style");
			s.setAttribute("type", "text/css");
			s.setAttribute("media", m);
			dynamicStylesheet = h.appendChild(s);
			if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
				dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
			}
			dynamicStylesheetMedia = m;
		}
		// add style rule
		if (ua.ie && ua.win) {
			if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
				dynamicStylesheet.addRule(sel, decl);
			}
		}
		else {
			if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
				dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
			}
		}
	}
	
	function setVisibility(id, isVisible) {
		if (!autoHideShow) { return; }
		var v = isVisible ? "visible" : "hidden";
		if (isDomLoaded && getElementById(id)) {
			getElementById(id).style.visibility = v;
		}
		else {
			createCSS("#" + id, "visibility:" + v);
		}
	}

	/* Filter to avoid XSS attacks
	*/
	function urlEncodeIfNecessary(s) {
		var regex = /[\\\"<>\.;]/;
		var hasBadChars = regex.exec(s) != null;
		return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
	}
	
	/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
	*/
	var cleanup = function() {
		if (ua.ie && ua.win) {
			window.attachEvent("onunload", function() {
				// remove listeners to avoid memory leaks
				var ll = listenersArr.length;
				for (var i = 0; i < ll; i++) {
					listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
				}
				// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
				var il = objIdArr.length;
				for (var j = 0; j < il; j++) {
					removeSWF(objIdArr[j]);
				}
				// cleanup library's main closures to avoid memory leaks
				for (var k in ua) {
					ua[k] = null;
				}
				ua = null;
				for (var l in swfobject) {
					swfobject[l] = null;
				}
				swfobject = null;
			});
		}
	}();
	
	return {
		/* Public API
			- Reference: http://code.google.com/p/swfobject/wiki/documentation
		*/ 
		registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
			if (ua.w3 && objectIdStr && swfVersionStr) {
				var regObj = {};
				regObj.id = objectIdStr;
				regObj.swfVersion = swfVersionStr;
				regObj.expressInstall = xiSwfUrlStr;
				regObj.callbackFn = callbackFn;
				regObjArr[regObjArr.length] = regObj;
				setVisibility(objectIdStr, false);
			}
			else if (callbackFn) {
				callbackFn({success:false, id:objectIdStr});
			}
		},
		
		getObjectById: function(objectIdStr) {
			if (ua.w3) {
				return getObjectById(objectIdStr);
			}
		},
		
		embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
			var callbackObj = {success:false, id:replaceElemIdStr};
			if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
				setVisibility(replaceElemIdStr, false);
				addDomLoadEvent(function() {
					widthStr += ""; // auto-convert to string
					heightStr += "";
					var att = {};
					if (attObj && typeof attObj === OBJECT) {
						for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
							att[i] = attObj[i];
						}
					}
					att.data = swfUrlStr;
					att.width = widthStr;
					att.height = heightStr;
					var par = {}; 
					if (parObj && typeof parObj === OBJECT) {
						for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
							par[j] = parObj[j];
						}
					}
					if (flashvarsObj && typeof flashvarsObj === OBJECT) {
						for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
							if (typeof par.flashvars != UNDEF) {
								par.flashvars += "&" + k + "=" + flashvarsObj[k];
							}
							else {
								par.flashvars = k + "=" + flashvarsObj[k];
							}
						}
					}
					if (hasPlayerVersion(swfVersionStr)) { // create SWF
						var obj = createSWF(att, par, replaceElemIdStr);
						if (att.id == replaceElemIdStr) {
							setVisibility(replaceElemIdStr, true);
						}
						callbackObj.success = true;
						callbackObj.ref = obj;
					}
					else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
						att.data = xiSwfUrlStr;
						showExpressInstall(att, par, replaceElemIdStr, callbackFn);
						return;
					}
					else { // show alternative content
						setVisibility(replaceElemIdStr, true);
					}
					if (callbackFn) { callbackFn(callbackObj); }
				});
			}
			else if (callbackFn) { callbackFn(callbackObj);	}
		},
		
		switchOffAutoHideShow: function() {
			autoHideShow = false;
		},
		
		ua: ua,
		
		getFlashPlayerVersion: function() {
			return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
		},
		
		hasFlashPlayerVersion: hasPlayerVersion,
		
		createSWF: function(attObj, parObj, replaceElemIdStr) {
			if (ua.w3) {
				return createSWF(attObj, parObj, replaceElemIdStr);
			}
			else {
				return undefined;
			}
		},
		
		showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
			if (ua.w3 && canExpressInstall()) {
				showExpressInstall(att, par, replaceElemIdStr, callbackFn);
			}
		},
		
		removeSWF: function(objElemIdStr) {
			if (ua.w3) {
				removeSWF(objElemIdStr);
			}
		},
		
		createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
			if (ua.w3) {
				createCSS(selStr, declStr, mediaStr, newStyleBoolean);
			}
		},
		
		addDomLoadEvent: addDomLoadEvent,
		
		addLoadEvent: addLoadEvent,
		
		getQueryParamValue: function(param) {
			var q = doc.location.search || doc.location.hash;
			if (q) {
				if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
				if (param == null) {
					return urlEncodeIfNecessary(q);
				}
				var pairs = q.split("&");
				for (var i = 0; i < pairs.length; i++) {
					if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
						return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
					}
				}
			}
			return "";
		},
		
		// For internal usage only
		expressInstallCallback: function() {
			if (isExpressInstallActive) {
				var obj = getElementById(EXPRESS_INSTALL_ID);
				if (obj && storedAltContent) {
					obj.parentNode.replaceChild(storedAltContent, obj);
					if (storedAltContentId) {
						setVisibility(storedAltContentId, true);
						if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
					}
					if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
				}
				isExpressInstallActive = false;
			} 
		}
	};
}();


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
if( !JLG.Template ) {

	JLG.Template = function( p_tpl ) { this.initialize( p_tpl ); };
	gQuery.extend( JLG.Template.prototype, {
	
	   	initialize: function (p_tpl) {

			if (gQuery.typeOf (p_tpl) == "object" && p_tpl.innerHTML) {p_tpl = p_tpl.innerHTML;}
	       	this.m_tpl = p_tpl || "";
			this._compile ();
	   	},

		apply: function( p_data ) {

			var content = [];
			var stack;
			var backtrace = [{ index: 0, stack: this.m_tpl.stack || [] }];

			// Keep looping until everything is processesed
			p_data = p_data || {};
			while (backtrace.length > 0) {

				var bt = backtrace.pop();
				var index;
				var len;

				// Pull a stack off the backtrace
				stack = bt.stack;
				for (index = bt.index, len = stack.length; index < len; index++) {

					var frame = stack[ index ];
					var attr = frame.attr;

					// Process the next frame
					if (attr) {

						var cond = frame.cond;
						var value = this._getValue( p_data, attr );

						// Conditional execution?
						if (cond) {

							if ((cond == '?' && value) || (cond == '!' && !value)) {

								backtrace.push({ index: index + 1, stack: stack });
								stack = frame.stack;
								len = stack.length;
								index = -1;

							} else if (cond != '?' && cond != '!') {

								if (value) {

									content.push( value );

								} else {

									content.push( frame.content || "" );
								}
							}
						} else if (value !== null) {

							content.push( value );

						} else {

							content.push( frame.content || "" );
						}
					} else if (content) {

						content.push( frame.content || "" );
					}
				}
			}

			// Join all of the content
			return content.join( "" );
		},

		/**
		 * Protected methods
		 */
	   	_compile: function () {

	       	var split1 = this.m_tpl.replace (/%7b/i, "{").replace (/%7d/i, "}").split (/\{\{/);
			if ( split1.length > 0 && split1 [0].length === 0 ) { split1.splice ( 0, 1 ); }
	       	var len = split1.length;
	       	var index;
			var backtrace = [];
			var frame = { stack: [] };
			var pfnTrim = gQuery.trim;

	       	// Was the first character a template character?
	       	if (this.m_tpl.length > 0 && this.m_tpl.slice (0, 2) == "{{") {

	           	index = 0;

	       	} else {

	           	index = 1;
	           	frame.stack.push ({ content: split1 [0] });
	       	}

	       	// Loop through the fields
	       	for (; index < len; index++) {

				var field = split1 [index];
				var end = field.indexOf ("}}");
				if (end >= 0) {

					var extra = field.slice (end + 2);
					if (end > 0) {

	                   	var attr = pfnTrim (field.slice (0, end));
	                   	var cond = field.charAt (0);
	                   	var cend = attr.charAt (attr.length - 1);
	                   	if (cend == "?" || cend == "!") {

							frame = backtrace.pop ();

						} else if (cond == '?' || cond == '!') {

							backtrace.push (frame);
							var newFrame = {

								attr: gQuery.trim (attr.slice (1)),
								cond: cond,
								content: "{{" + attr + "}}",
								stack: []
							};
							frame.stack.push (newFrame);
							frame = newFrame;

	                   	} else {

							frame.stack.push({ attr: attr, content: "{{" + attr + "}}" });
						}
	               	}
					if ((end + 1) < field.length) { frame.stack.push ({ content: extra }); }

	           	} else {

					frame.stack.push ({ content: "{{" + field });
				}
	       	}

			// Compiled ok
			this.m_tpl = frame;
	   	},

		_getValue: function( p_data, p_field ) {

			var pfnTrim = gQuery.trim;

			// Loop through the names
			for (var segs = (p_field || "").split( "." ), index = 0, len = segs.length; index < len; ++index) {

				var attr = pfnTrim( segs[ index ] );
				p_data = p_data[ attr ];
				if (!p_data) {

					break;
					// NOTREACHED
				}
			}

			// Return the value
			return ((len === 0) ? null : p_data);
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.popup = JLG.popup || {};
if( !JLG.popup.FormRequest ) {

	JLG.popup.FormRequest = function( p_form, p_url, p_options ) { this.initialize( p_form, p_url, p_options ); };
	gQuery.extend( JLG.popup.FormRequest.prototype, {

		initialize: function (p_form, p_url, p_options) {

			p_options = p_options || {};
		    var key;
			var value;
			var submitted = false;
			var submit = p_options.submit;
			var elements = Form.getElements (p_form);
		    var data = elements.inject({ }, function (p_result, p_element) {

				if (!p_element.disabled && p_element.name) {

					var key = p_element.name;
					var value = p_element.value;
					if (value !== null && ((p_element.type != 'submit' && p_element.type != 'button') || (!submitted && submit !== false && (!submit || key == submit) && (submitted = true)))) {

						// If this is a checkbox, see if it is checked
						if (p_element.type != "checkbox" || p_element.checked) {

							// Already have the key?
							if (key in p_result) {

								// A key is already present; construct an array of values
								if (!gQuery.isArray(p_result [key])) { p_result [key] = [p_result [key]]; }
								p_result [key].push (value);
							}
							else {

								p_result [key] = value;
							}
						}
					}
				}
				return p_result;
			});

			// Create a request
			p_url += (p_url.indexOf ('?') ? '?' : '&') + Object.toQueryString (data);
			if (p_options.usePrototype) {

				var ar = new Ajax.Request (p_url, p_options || {});

			} else {

				var pr = new JLG.popup.Request (p_url, p_options || {});
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.popup = JLG.popup || {};
if( !JLG.popup.History ) {

	JLG.popup.History = function( p_module, p_base ) { this.initialize( p_module, p_base ); };
	gQuery.extend( JLG.popup.History.prototype, {

		initialize: function (p_module, p_base) {

			// Initialize member variables
			this._bookmarked = "/#_JLGHIST_=0";
			this.m_count = 0;
			this._hrefBase = "";
			this._initial = "/#_JLGHIST_=0";
			this._is_active = false;
			this._module = "";
			this._hrefBase = p_base || "";
			this._module = p_module || "";
			this._bookmarked = JLG.util.History.getBookmarkedState (this._module) || "/#_JLGHIST_=0";
			this._initial = this._bookmarked || "/#_JLGHIST_=0";

			// Register the history system
			JLG.util.History.register (this._module, this._initial, function (p_token) { this.go (p_token); }.bind (this));

			// Initialize oursef when the page loads
			JLG.util.History.onReady (function () { this._onReady (); }.bind (this));
		},

		add: function (p_token) {

			// Ignore further history items once we reach the beginning
			if (p_token != "/") { this._is_active = true; }
			p_token += "#_JLGHIST_=" + (++this.m_count);
			this._bookmarked = p_token;
			try { JLG.util.History.navigate (this._module, p_token); } catch (e) {}
		},

		getCurrentPosition: function () {

			return this.m_count;
		},

		go: function (p_token) {

			// Setup an easy exit
			do {

				// History tag present?
				var idxCount = p_token.indexOf ("#_JLGHIST_");
				if (idxCount < 0) {

					// Invalid Token
					break;
					// NOTREACHED
				}

				// Are we going home?
				var pos = parseInt (p_token.substring (idxCount + 11), 10) - 1;
				if (idxCount == 1 && p_token.charAt (0) == "/") {

					// Hide the popup
					this._is_active = false;
					try { JLG.popup.window ().hide (null, { force: true, __historyPos: pos }); } catch (e) {}
					break;
					// NOTREACHED
				}

				// Don't do anything if we are already on the page
				if (p_token == this._bookmarked) {

					// Don't do anything if we are already on the page
					break;
					// NOTREACHED
				}

				// Is the history object disabled?
				if (!this._is_active) {

					// Go home if we are inactive and the user is moving around in history
					if (this.m_count > 0) { history.go (-this.m_count); }
					break;
					// NOTREACHED
				}

				// Pull information from the token
				this.m_count = pos;
				p_token = p_token.substring (0, idxCount);

			    // Asynchronously load the page
				var pr = new JLG.popup.Request (this._hrefBase + p_token);
			}
			while (0);
		},

		/**
		 * Protected methods
		 */
		_onReady: function () {

			var token = JLG.util.History.getCurrentState (this._module);
			if (window.location.hash.substr (1).length > 0) { this.go (token); }
		}
	} );
}

// Initialize the history at startup
JLG.popup.History.__loader = function (p_css) {

	// Helper elements exist?
	var iframe = gQuery( "#jlg_popup_history_iframe" );
	if( !iframe || iframe.length === 0 ) {

		// Add the history elements
		gQuery( "body" ).prepend(

			"<iframe id='jlg_popup_history_iframe' src='/blank.html' style='position:absolute; top:0; left:0; width:1px; height:1px; visibility:hidden;'></iframe>" +
			"<input type='hidden' id='jlg_popup_history_field' />"
		);

		// Call the yahoo initializer
		try {

			// Make sure we have a history object
			JLG.popup.history();

			// Initialize the Yahoo history manager
			JLG.util.History.initialize( "jlg_popup_history_field", "jlg_popup_history_iframe" );

		} catch (e) {
		}
	}
};
gQuery( document ).ready( JLG.popup.History.__loader );


/*
Created By: Chris Campbell
Website: http://particletree.com
Date: 2/1/2006

Inspired by the lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
*/

/*-------------------------------GLOBAL VARIABLES------------------------------------*/

/*
var detect = navigator.userAgent.toLowerCase();
var OS,browser,version,total,thestring;
*/
/*-----------------------------------------------------------------------------------------------*/

//Browser detect script origionally created by Peter Paul Koch at http://www.quirksmode.org/

/*
function getBrowserInfo() {
    if (checkIt('konqueror')) {
        browser = "Konqueror";
        OS = "Linux";
    }
    else if (checkIt('safari')) browser 	= "Safari"
    else if (checkIt('omniweb')) browser 	= "OmniWeb"
    else if (checkIt('opera')) browser 		= "Opera"
    else if (checkIt('webtv')) browser 		= "WebTV";
    else if (checkIt('icab')) browser 		= "iCab"
    else if (checkIt('msie')) browser 		= "Internet Explorer"
    else if (!checkIt('compatible')) {
        browser = "Netscape Navigator"
        version = detect.charAt(8);
    }
    else browser = "An unknown browser";

    if (!version) version = detect.charAt(place + thestring.length);

    if (!OS) {
        if (checkIt('linux')) OS 		= "Linux";
        else if (checkIt('x11')) OS 	= "Unix";
        else if (checkIt('mac')) OS 	= "Mac"
        else if (checkIt('win')) OS 	= "Windows"
        else OS 								= "an unknown operating system";
    }
}

function checkIt(string) {
    place = detect.indexOf(string) + 1;
    thestring = string;
    return place;
}
*/

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

//Event.observe(window, 'load', initialize, false);
//Event.observe(window, 'load', getBrowserInfo, false);

var lightbox = Class.create();

lightbox.prototype = {

    yPos : 0,
    xPos : 0,
	m_origBodHeight: null,
	m_origBodOverflow: null,
	m_origHtmHeight: null,
	m_origHtmOverflow: null,
	m_options: {},

    initialize: function(ctrl) {
		this.activator = null;
       if (ctrl !== null)
       {
            this.content = ctrl.href;
            Event.observe(ctrl, 'click', this.activate.bindAsEventListener(this), false);
            ctrl.onclick = function(){return false;};
       }
       else
       {
            this.content = null;
       }
    },

    // Turn everything on - mainly the IE fixes
    activate: function( p_options ) {

		p_options = gQuery.extend( { margins: { h: 20, w: 20 }, position: 'centered' }, p_options || {} );
		this.m_options = p_options;
		this.activator = p_options.caller;
		var ieVersion = JLG.core.getIEVersion();
        if (ieVersion > 0 && ieVersion < 7) {

            this.getScroll();
			if (!p_options.noScroll) {

				this.setScroll( 0, 0 );
				this.setScrollMode( '100%', 'hidden', '100%', 'hidden' );
			}
			this.hideElements( "select", "hidden" );

        } else if (!p_options.noScroll) {

			this.setScrollMode( '100%', 'hidden', '100%', 'hidden' );
		}
		this.hideElements( "embed", "hidden" );
		this.hideElements( "object", "hidden" );

		// Set the size
		this.m_idResize = this.onBrowserResize.bind( this );
		Event.observe( window, "resize", this.m_idResize );
		this.onBrowserResized();
		this.m_visible = true;
    },

	onBrowserResize: function() {

		if (this.m_visible && this._resizeOnce()) {

	        var pfnTimeout = this.onBrowserResized.bind( this );

			// Outstanding resize event?
		    if (this.m_resizeTo) {

		        // Clear the single click event
		        clearTimeout( this.m_resizeTo );
		        this.m_resizeTo = null;
		    }

	        // Perform the click later
	        this.m_resizeTo = setTimeout( pfnTimeout, 0 );
		}
	},

	onBrowserResized: function() {

		// Clear the timeout
		this.m_resizeTo = null;

		// Calculate information
		var lb = gQuery( '#rex-lightbox' )[ 0 ];
		var dimBrowser = document.viewport.getDimensions();
		var ieVersion = JLG.core.getIEVersion();

		// Fullscreen or centered?
		if (this.m_options.position != 'centered') {

			Element.setStyle( lb, { left: "0px", top: "0px" } );
			if (ieVersion < 7) {

				Element.setStyle( lb, { height: String( dimBrowser.height ) + "px", width: String( dimBrowser.width ) +"px" } );

			} else {

				Element.setStyle( lb, { height: "100%", width: "100%" } );
			}

			// Display the element
			if (lb.style.display == 'none') { this.displayLightbox( "block" ); }

		} else if (this.m_options.width && this.m_options.height) {

			this._staticCenter( lb, dimBrowser );
		}
		else {

			this._dynamicCenter( lb, dimBrowser );
		}
	},

    refresh: function() {

        this.onBrowserResized();
    },

    resetScrollMode: function() {

        this.setScrollMode( this.m_origBodHeight, this.m_origBodOverflow, this.m_origHtmHeight, this.m_origHtmOverflow );
        this.m_origBodHeight = null;
		this.m_origHtmHeight = null;
        this.m_origHtmOverflow = null;
        this.m_origBodOverflow = null;
		// window.scrollTo( 1000, 1000 );
		window.scrollTo( 0, 0 );
    },

    // Ie requires height to 100% and overflow hidden or else you can scroll down past the lightbox
    setScrollMode: function( p_bodHeight, p_bodOverflow, p_htmHeight, p_htmOverflow ) {

       	var bod = document.getElementsByTagName( 'body' )[ 0 ];
		if (p_bodHeight) {

            if (this.m_origBodHeight === null) { this.m_origBodHeight = bod.style.height || ''; }
        	bod.style.height = p_bodHeight;
		}
		if (this.m_origBodOverflow === null) { this.m_origBodOverflow = bod.style.overflow || ''; }
        bod.style.overflow = p_bodOverflow;

        var htm = document.getElementsByTagName( 'html' )[ 0 ];
		if (p_htmHeight) {

            if (this.m_origHtmHeight === null) { this.m_origHtmHeight = htm.style.height || ''; }
        	htm.style.height = p_htmHeight;
		}
		if (this.m_origHtmOverflow === null) { this.m_origHtmOverflow = htm.style.overflow || ''; }
        htm.style.overflow = p_htmOverflow;
    },

    // In IE, select elements hover on top of the lightbox
    hideElements: function (p_elem, visibility){

		var index;
		var len;
		var el;
		var lb = $("rex-lightbox");
		var parent;
        var nodes = document.getElementsByTagName (p_elem);

		// Loop through the elements
        for (index = 0, len = nodes.length; index < len; index++) {

            el = nodes [index];
			for (parent = el.parentNode; parent && parent != lb; parent = parent.parentNode) {}
			if (!parent) {

				el.style.visibility = visibility;
			}
        }
    },

    // Taken from lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
    getScroll: function() {

        if (window.pageYOffset) {

            this.yPos = window.pageYOffset;

        } else if (document.documentElement && document.documentElement.scrollTop){

            this.yPos = document.documentElement.scrollTop;

        } else if (document.body) {

            this.yPos = document.body.scrollTop;
        }
    },

    setScroll: function(x, y){
        window.scrollTo(x, y);
    },

    displayLightbox: function( display ) {

		var overlay = $( 'rex-lb-overlay' );
		var lb = $( 'rex-lightbox' );
		if (lb && overlay) {

			Element.setStyle( lb, { display: display } );
			Element.setStyle( overlay, { display: display } );
		}
    },

    // Example of creating your own functionality once lightbox is initiated
    deactivate: function( p_caller ) {

		if (this.m_visible && (!this.activator || p_caller == this.activator)) {

			// Reset variables
			Event.stopObserving( window, "resize", this.m_idResize );
			this.activator = null;
			this.m_idResize = -1;
			this.m_visible = false;

			// Enable scrolling
			var ieVersion = JLG.core.getIEVersion();
			if (ieVersion > 0 && ieVersion < 7) {

				if (!this.m_options.noScroll) { this.setScroll( 0, this.yPos ); }
				this.hideElements( "select", "visible" );
			}
			if (!this.m_options.noScroll) { this.resetScrollMode(); }
            this.hideElements( "embed", "visible") ;
            this.hideElements( "object", "visible" );

			// Hide the lightbox
	        this.displayLightbox( "none" );

			// Reset remaining variables
			this.m_options = {};
		}
    },

	/**
	 * Protected methods
	 */
	_dynamicCenter: function( p_lb, p_dimBrowser, p_retry, p_dimContainer, p_dimContent, p_dimChild ) {

		var jlb = gQuery( p_lb );
		var elChild = null;
		var dimChildBlock = null;
		var dimChildText;

		var elContainer = this.m_options.frameEl;
		var dimContainerBlock = null;
		var dimContainerText;

		var elContent = this.m_options.displayEl;
		var dimContentBlock = null;
		var dimContentText;
		var marginH = 0;
		var marginW = 0;

		var maxDim = {

			h: this.m_options.height || (p_dimBrowser.height - (this.m_options.margins.h || 100)),
			w: this.m_options.width || (p_dimBrowser.width - (this.m_options.margins.w || 100))
		};

        // Display the lightbox
		jlb.height( maxDim.h ).width( maxDim.w );
		if( !this.m_visible ) { jlb.css({ top: p_dimBrowser.height, left: p_dimBrowser.width }); }
		if( p_lb.style.display == 'none' ) { this.displayLightbox( "block" ); }

		// Calculate dimensions
		if( elContainer ) {

            elContainer = jlb.find( "#" + elContainer + ":last" );
    		if( elContainer && elContainer.length > 0 && !p_retry ) { elContainer.css({ width: "auto", height: "auto" }); }
    	}
		if( elContent ) {

            elContent = jlb.find( "#" + elContent + ":last" );
    		if( elContent && elContent.length > 0 && !p_retry ) { elContent.css({ width: "auto", height: "auto" }); }
		}
		if( (!elContainer || elContainer.length === 0) && (!elContent || elContent.length === 0) ) {

			elChild = jlb.find( "#" + elChild + ":last" );
			var cn = p_lb.childNodes;
			if( cn.length == 1 && elChild && elChild.length > 0 && !p_retry ) { elChild.css({ width: "auto", height: "auto" }); }
		}

		var nodes = p_lb.childNodes;
		var replaced = [];
		var index;
		var len = nodes.length;
		for( index = 0; index < len; index++ ) { replaced.push( nodes[ index ] ); p_lb.removeChild( nodes[ index ] ); }
		jlb.html( "" );
		for( index = 0; index < len; index++ ) { p_lb.appendChild( replaced[ index ] ); }

   		if( elContainer && elContainer.length > 0 ) {

			dimContainerBlock = { h: elContainer.height(), w: elContainer.width() };
			marginH = dimContainerBlock.h;
			marginW = dimContainerBlock.w;
			dimContainerText = { w: Math.max( dimContainerBlock.w, elContainer[ 0 ].scrollWidth ), h: Math.max( dimContainerBlock.h, elContainer[ 0 ].scrollHeight ) };
   		}
		if( elContent && elContent.length > 0 ) {

			dimContentBlock = { h: elContent.height(), w: elContent.width() };
			marginH -= dimContentBlock.h;
			marginW -= dimContentBlock.w;
			dimContentText = { w: Math.max( dimContentBlock.w, elContent[ 0 ].scrollWidth ), h: Math.max( dimContentBlock.h, elContent[ 0 ].scrollHeight ) };
		}

		if( elChild && elChild.length > 0 ) {

			try {

				dimChildBlock = { h: elChild.height(), w: elChild.width() };
				if( p_retry && p_dimChild ) {

					dimChildBlock.h = Math.max( p_dimChild.h, dimChildBlock.h );
					dimChildBlock.w = Math.max( p_dimChild.w, dimChildBlock.w );
				}
				dimChildText = { w: Math.max( dimChildBlock.w, elChild.scrollWidth ), h: Math.max( dimChildBlock.h, elChild.scrollHeight ) };

			} catch( e ) {}
		}

		// Which object is the container?
		if( !dimContainerBlock ) {

			if( dimContentBlock ) {

				elContainer = elContent;
				dimContainerBlock = dimContentBlock;
				dimContainerText = dimContentText;
				elContent = null;
				dimContentBlock = null;
				dimContentText = null;
			}
			else if( dimChildBlock ) {

				elContainer = elChild;
				dimContainerBlock = dimChildBlock;
				dimContainerText = dimChildText;
				elChild = null;
				dimChildBlock = null;
				dimChildText = null;
			}
		}

		// Use dimensions we found somewhere?
		if( dimContainerBlock ) {

			var newContainer = { w: dimContainerBlock.w, h: dimContainerBlock.h };

			// Maximize the block
			newContainer.h = Math.max( newContainer.h, dimContainerText.h );
			newContainer.w = Math.max( newContainer.w, dimContainerText.w );
			if( dimContentBlock ) {

				newContainer.h = Math.max( Math.max( dimContentBlock.h, dimContentText.h + marginH ), newContainer.h );
				newContainer.w = Math.max( Math.max( dimContentBlock.w, dimContentText.w + marginW ), newContainer.w );
			}
			if( dimChildBlock ) {

				newContainer.h = Math.max( Math.max( dimChildBlock.h, dimChildBlock.h ), newContainer.h );
				newContainer.w = Math.max( Math.max( dimChildBlock.w, dimChildBlock.w ), newContainer.w );
			}

			// Shrink if necessary
			if( maxDim.w < newContainer.w ) { newContainer.w = maxDim.w; }
			if( maxDim.h < newContainer.h ) { newContainer.h = maxDim.h; }

			// Set the container and content
			var newDim = null;
			if( dimContentBlock ) {

				elContent.css( 'overflow', "hidden" );
				newDim = { w: newContainer.w - marginW, h: newContainer.h - marginH };
				if( newDim.w != dimContentBlock.w || newDim.h != dimContentBlock.h ) { elContent.height( newDim.h ).width( newDim.w ); }
			}
			if( newContainer.w != dimContainerBlock.w || newContainer.h != dimContainerBlock.h ) { elContainer.height( newContainer.h ).width( newContainer.w ); }

			// Almost done
			if (p_retry && (
				(!p_dimContainer || !newContainer || (p_dimContainer.w >= newContainer.w && p_dimContainer.h >= newContainer.h)) &&
				(!p_dimContent || !dimContentBlock || (p_dimContent.w >= dimContentBlock.w && p_dimContent.h >= dimContentBlock.h)) &&
				(!p_dimChild || !dimChildBlock || (p_dimChild.w >= dimChildBlock.w && p_dimChild.h >= dimChildBlock.h))
			)) {

				// Account for scrolling
				var modified = false;
				if (p_dimContainer) {newContainer = p_dimContainer;}
				if ((dimContainerText.h > newContainer.h || (dimContentBlock && dimContentText.h > (newContainer.h - marginH))) && newContainer.w < (maxDim.w - 32)) {

					modified = true;
					newContainer.w += 32;
				}
				if ((dimContainerText.w > dimContainerBlock.w || (dimContentBlock && dimContentText.w > dimContentBlock.w)) && dimContainerBlock.h < (maxDim.h - 32)) {

					modified = true;
					newContainer.h += 32;
				}

				// Set the container and content
				if( modified ) {

					if( dimContentBlock ) {

						newDim = { w: newContainer.w - marginW, h: newContainer.h - marginH };
						if( newDim.w != dimContentBlock.w || newDim.h != dimContentBlock.h ) { elContent.height( newDim.h ).width( newDim.w ); }
					}
					if( newContainer.w != dimContainerBlock.w || newContainer.h != dimContainerBlock.h ) { elContainer.height( newContainer.h ).width( newContainer.w ); }
				}

				// Set the lightbox dimensions
				jlb.height( newContainer.h ).width( newContainer.width );
				jlb.css({ top: (p_dimBrowser.height - newContainer.h) / 2, left: (p_dimBrowser.width - newContainer.w) / 2 });
				elContent.css( "overflow", "auto" );

			} else {

				// Try again
				this._dynamicCenter( 

					p_lb,
					p_dimBrowser,
					true,
					{ w: 32 + newContainer.w, h: 32 + newContainer.h },
					{ w: 32 + newContainer.w - marginW, h: 32 + newContainer.h - marginH },
					dimChildBlock ? { w: 32 + dimChildBlock.w, h: 32 + dimChildBlock.h } : null
				);
			}
		}
		else {

			jlb.css({ top: 0, left: 0 });
		}
	},

	_resizeOnce: function() {

		var ieVersion = JLG.core.getIEVersion();
		if (ieVersion > 0) {

	        if ( !this.m_resizeFired ) {

	            this.m_resizeFired = true;

	        }  else { 

				this.m_resizeFired = false;
				if ( ieVersion < 7 ) { 

					return false;
					// NOTREACHED

				} else if ( version == 7 ) {

					var width = document.viewport.getDimensions().width;
					if ( width != this.m_resizeWidth ) { 

						this.m_resizeWidth = width;
						return false;
						// NOTREACHED
					}
				}
			}
		}
		return true;
	},

	_staticCenter: function( p_lb, p_dimBrowser ) {

		// Initialize
		var jlb = gQuery( p_lb );
		var elContainer = null;
		if( this.m_options.frameEl ) { elContainer = jlb.find( "#" + this.m_options.frameEl + ":last" ); }
		if( !elContainer || elContainer.length === 0 ) {

			if( this.m_options.displayEl ) { elContainer = jlb.find( "#" + this.m_options.displayEl + ":last" ); }
			if( !elContainer || elContainer.length === 0 ) { elContainer = jlb.children( ":first" ); }
		}

		// Set the dimensions
		jlg.height( this.m_options.height ).width( this.m_options.width );
		if( elContainer ) { elContaner.width( this.m_options.width ).height( this.m_options.height ); }
		jlb.css({ left: (p_dimBrowser.width - dim.w) / 2, top: (p_dimBrowser.height - dim.h) / 2 });

		// Display the element
		if( p_lb.style.display == 'none' ) { this.displayLightbox( "block" ); }
	},

	/***************************************************************
	 * MEMBER VARIABLES
	 ***************************************************************/
	m_resizeFired: false,

	m_resizeTo: null,
	
	m_resizeWidth: 0,

	m_visible: false
};

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

// Onload, make all links that need to trigger a lightbox active
/*
function initialize(){
    addLightboxMarkup();
    lbox = document.getElementsByClassName('lbOn');
    for(i = 0; i < lbox.length; i++) {
        valid = new lightbox(lbox[i]);
    }
}
*/

// Add in markup necessary to make this work. Basically two divs:
// Overlay holds the shadow
// Lightbox is the centered square that the content is put into.
gQuery( document ).ready( function() {

    var bod = document.getElementsByTagName( 'body' )[ 0 ];
    var overlay = document.createElement( 'div' );
    overlay.id = 'rex-lb-overlay';
    var lb = document.createElement( 'div' );
    lb.id = 'rex-lightbox';
	lb.style.display = 'none';
    bod.appendChild( overlay );
    bod.appendChild( lb );
});


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.popup = JLG.popup || {};
if( !JLG.popup.Request ) {

	JLG.popup.Request = function( p_url, p_options ) { this.initialize( p_url, p_options ); };
	gQuery.extend( JLG.popup.Request.prototype, {

		initialize: function( p_url, p_options ) {

			// Display the progress if desired
			p_options = p_options || {};
			if (p_options.showSpinner || (p_options.showSpinner !== false && JLG.popup.defaults.request && JLG.popup.defaults.request.showSpinner)) {

	            p_options.showSpinner = true;
	            JLG.popup.showSpinner( p_options );
			}

		    // Create the script tag
			this.pos = JLG.popup.requests()._nextRequestId();
			this.id = 'json_request_' + this.pos;		
			this.options = p_options;

			// Initialize the URL
			var src = p_url + ((p_url.indexOf( '?' ) < 0) ? '?' : '&') + 'noCacheIE=' + new Date().getTime() + '&jlgreq=' + this.id;
			if (p_options && p_options.query) { src += '&' + p_options.query; }

			// Log us as an active request
			JLG.popup.requests()._activeRequests[ JLG.popup.requests()._activeRequests.length ] = this;
			gQuery.getScript( src );
		},

		remove: function() {

			// Hide the spinner
	        if( this.options.showSpinner ) { JLG.popup.hideSpinner({ __requestPos: this.pos }); }

			// Remove the script tag
			var selfId = this.id;
			gQuery( 'script' ).each( function() {

				if( this.title && this.title == selfId ) { gQuery( this ).remove(); }
			} );
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.popup = JLG.popup || {};
if( !JLG.popup.RequestManager ) {

	JLG.popup.RequestManager = function( p_config ) {

		this._activeRequests = [];
	};
	gQuery.extend( JLG.popup.RequestManager.prototype, {

		// Initialize the Popup namespace
		closeRequest: function( p_id ) {

			// Find the specified id
			for (var index = 0; index < this._activeRequests.length && this._activeRequests [index].id != p_id; index++) {}
			if (index < this._activeRequests.length) {

				// Remove the request from the document
				var request = this._activeRequests[index];
				if (request.options && request.options.on_complete) { request.options.on_complete(); }
				this._activeRequests.splice( index, 1 );
				request.remove();
			}
		},

		_nextRequestId: gQuery.counter ()
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.popup = JLG.popup || {};
if( !JLG.popup.Window ) {

	JLG.popup.Window = function( p_config ) { this.initialize(); };
	gQuery.extend( JLG.popup.Window.prototype, {

		initialize: function() {

		    this.currentOptions = {};
			this.m_archives = [];
			this.m_bookmarkPos = 65535;
			this.m_options = [];
			this.m_archiveDiv = document.createElement( "div" );
			document.body.appendChild( this.m_archiveDiv );
			Element.hide( this.m_archiveDiv );
		},

		archive: function() {

			var result = false;

			if (this.m_ct) {

				this.m_archiveDiv.appendChild( this.m_ct );
				this.m_archives.push( this.m_ct );
				this.m_options.push( this.currentOptions );
				this.m_ct = null;
				this.currentOptions = {};
				result = true;
			}
			return result;
		},

		getName: function () {

			return this.currentOptions.name;
		},

		hide: function (p_window, p_options) {

			p_options = p_options || {};
			var caller = p_options.caller;
			var force = p_options.force;

			// See if this is a bookmark and should be ignored
			if (!p_options.restoring && p_options.__historyPos && this.m_bookmarkPos <= p_options.__historyPos) {

				return;
				// NOTREACHED
			}

			// Just exit if we aren't visible
			if (!this.isVisible) {

				return;
				// NOTREACHED
			}

			// Are we allowed to modify?
			if (force || !this.currentOptions.caller || caller == this.currentOptions.caller) {

				var index;
				var len;

				// Not hiding the current window?
				if (p_window && p_window != this.m_ct) {

					// Find the context to hide
					for (index = 0, len = this.m_options.length; index < len && this.m_archives [index] != p_window; index++) {}
					if (index >= len) {

						// ERROR: Not found
						return false;
						// NOTREACHED
					}

					// Hide the context
					this.m_archives.splice (index, 1);
					this.m_options.splice (index, 1);
					try { Element.remove (p_window); } catch (e) {}
					return true;
					// NOTREACHED
				}

				// Should we just restore and old archive?
				if (!p_options.restoring && this.m_options > 0) {

					// Perform a restore
					this.restore ();
					return true;
					// NOTREACHED
				}

				// No longer visible
				this.isVisible = false;
				if ( force ) { caller = this.m_lightbox.activator; }

				// Remove all previous options
				this._clear ();
				while (this.m_archives.length > 0) {

					this.m_ct = this.m_archives.pop ();
					this.currentOptions = this.m_options.pop () || {};
					this._clear ();
				}

				// Remove current options
				this.m_archives = [];
				this.m_options = [];
				this.m_lb.innerHTML = "";
				this.m_lightbox.deactivate( caller );

				// Reset the bookmark
				this.m_bookmarkPos = JLG.popup.history().getCurrentPosition();
				JLG.popup.history().add( "/" );
			}
		},

	    refresh: function () {

	        this.m_lightbox.refresh ();
	    },

		restore: function( p_options ) {

			this._clear();
			if (this.m_options.length > 0) {

				// Restore the data
				this.m_ct = this.m_archives.pop();
				this.currentOptions = this.m_options.pop() || {};
				this.m_lb.appendChild( Element.remove( this.m_ct ) );
				this._realShow( gQuery.extend( {}, this.currentOptions, p_options || {} ) );

			} else {

				this.hide( null, { restoring: true, force: true } );
			}
		},

		setup: function() {

			// Already setup?
			if (!this.m_isSetup) {

				// Initialize variables
				this.m_lb = $( "rex-lightbox" );
				this.m_lightbox = new lightbox( null );
				this.m_isSetup = true;
			}
		},

		show: function( p_options ) {

			var tag = "{{jlg-popup-content}}";
			var len = tag.length;
			var result = null;

			// Make sure we are setup
			this.setup();

			// Hide the lightbox during modifications
			Element.setStyle( this.m_lb, { display: 'none' } );
			this._clear();

	        // Merge the global default options with the passed-in options
	        p_options = gQuery.extend( {}, JLG.popup.defaults.window || {}, p_options || {} );
			if (!p_options.__spinner) {

				// Pop the existing archive
				if (JLG.popup.m_spinner.hasArchive) {

					this.m_ct = this.m_archives.pop();
					this.currentOptions = this.m_options.pop() || {};
					this._clear();
				}

				// No spinner, no archive
				JLG.popup.m_spinner.count = 0;
				JLG.popup.m_spinner.hasArchive = false;
				JLG.popup.m_spinner.requestPos = JLG.popup.requests()._nextRequestId();
			}

			// Update the lightbox
			if (p_options.force || !this.currentOptions.caller || p_options.caller == this.currentOptions.caller || p_options.owner == this.currentOptions.caller) {

				// Initialize
				this.currentOptions = p_options || {};

				// Is content coming from another element?
				if (!p_options.innerHTML) {

					if (!p_options.content) {

					 	if (p_options.contentEl) {

							p_options.contentEl = $( p_options.contentEl );

						} else {

							p_options.content = "Please Wait ...";
						}
					}

					// Set the layout
					var layout = p_options.layout;
					if (layout) {

						var index = layout.indexOf( tag );
						if (p_options.contentEl) {

							p_options.innerHTML = layout.slice( 0, index ) + "<div id='jlg-popup-window-content-nodes'></div>" + layout.slice( index + len );

						} else {

							p_options.innerHTML = layout.slice( 0, index ) + (p_options.content || "") + layout.slice( index + len );
						}
					} else {

						p_options.innerHTML = p_options.content || "";
					}
				}

				// Set the inner HTML
				JLG.popup.Window.__autoEvalScripts = false;
				result = document.createElement( "div" );
				this.m_ct = result;
				this.m_ct.innerHTML = "<script type='text/javascript'>\n<!--\nJLG.popup.Window.__autoEvalScripts = true;\n//-->\n</script>\n" + p_options.innerHTML;
				this.m_lb.innerHTML = "";
				Element.insert( this.m_lb, this.m_ct );

				// Copy the nodes over
				if (p_options.contentEl) {

					var parent;

					// Find the parent
					if (layout) {

	 					parent = $( "jlg-popup-window-content-nodes" );
						var newParent = parent.parentNode;
						try { Element.remove( parent ); } catch( e ) {}
						parent = newParent;

					} else {

						 parent = this.m_ct;
					}

					// Move the elements over
					var nodes = p_options.contentEl.childNodes || [];
					this.currentOptions.tgtContentEl = parent;
					while (nodes.length > 0) { parent.appendChild( nodes[ 0 ] ); }
				}

				// Show the popup
				this._realShow( p_options );
			}

			// Return the handle
			return result;
		},

		/**
		 * Protected methods
		 */
		_clear: function () {

			if (this.currentOptions.contentEl && this.currentOptions.tgtContentEl) {

				var contentEl = $( this.currentOptions.contentEl );
				var nodes = this.currentOptions.tgtContentEl.childNodes || [];
				while (nodes.length > 0) { contentEl.appendChild( nodes [0] ); }
			}
			if (this.m_ct) {

				try { Element.remove( this.m_ct ); } catch (e) {}
				this.m_ct = null;
			}
			this.currentOptions = {};
		},

		_realShow: function( p_options ) {

			// Show the popup
			p_options = p_options || {};
			this.isVisible = true;
			this.m_lightbox.activate( p_options );

			// Execute scripts
			if (p_options.evalScripts && !JLG.popup.Window.__autoEvalScripts) { JLG.core.evalScriptTagsIn( this.m_ct ); }

			// Add a bookmark
			if (p_options.bookmark) {

				if (p_options.bookmarkUrl) {

					JLG.popup.history().add( p_options.bookmarkUrl );

				} else {

					JLG.popup.history().add( window.location.href );
				}
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.popup = JLG.popup || {};
if( !JLG.popup.Alert ) {

	JLG.popup.Alert = function( p_message, p_title, p_options ) { this.initialize( p_message, p_title, p_options ); };
	gQuery.extend( JLG.popup.Alert.prototype, {

		initialize: function( p_message, p_title, p_options ) {

			var cvs = JLG.popup.Alert;

			// Create the template if it doesn't exist
			this.m_options = {};
			p_options = p_options || {};
			this.m_options = p_options;
			if (!cvs.template) {

				// Initialize the options
				var options = gQuery.extend( {

					layout: "{{?title}}<h3>{{title}}</h3>{{title?}}{{?message}}<p>{{message}}</p>{{message?}}<input type='button' value='{{?okText}}{{okText}}{{okText?}}{{!okText}}Ok{{okText!}}' class='jlg-ok-button' />"

				}, ( JLG.popup.defaults || {} ).alert || {} );

				// Create and compile the template
				cvs.template = gQuery.templatize( options.layout );
			}

			// Apply template to the text
			var content = cvs.template.apply({

				title: p_title,
				message: p_message,
				okText: p_options.okText || "Ok"
			});

			// Create a div to hold the content
			this.m_contentEl = gQuery( document.createElement( "div" ) ).html( content ).hide().appendTo( "body" );

			// Locate the ok button
			this.m_okButton = gQuery( ".jlg-ok-button", this.m_contentEl ).bind( "click", gQuery.bindFn( this._onOk, this ) );

			// Archive any existing display
			p_options = p_options || {};
			if (JLG.popup.window().archive()) {

				this.m_hasArchive = true;
			}

			// Show the popup
			JLG.popup.window().show( gQuery.extend({ contentEl: this.m_contentEl[ 0 ] }, p_options || {} ) );
		},

		/**
		 * Protected methods
		 */
		_onOk: function () {

			// Disconnect the handler
			if (this.m_okButton) { this.m_okButton.unbind(); }

			// Restore any archived popup.  Need to do this before destroying the content element so the inner data can be returned.
			if (this.m_hasArchive) {

				JLG.popup.window ().restore ({bookmark: false});

			} else {

				JLG.popup.window ().hide ();
			}

			// Destroy the element
			if (this.m_contentEl) { this.m_contentEl.remove(); }

			// Call the event handler
			if (this.m_options.onOk) { this.m_options.onOk (); }
			if (this.m_options.onButtonClick) { this.m_options.onButtonClick ("ok"); }
		}
	} );
}

// Define class variables
JLG.popup.Alert.template = null;


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.popup = JLG.popup || {};
if( !JLG.popup.Confirm ) {

	JLG.popup.Confirm = function( p_message, p_title, p_options ) { this.initialize( p_message, p_title, p_options ); };
	gQuery.extend( JLG.popup.Confirm.prototype, {

		initialize: function( p_message, p_title, p_options ) {

			var cvs = JLG.popup.Confirm;

			// Create the template if it doesn't exist
			this.m_options = {};
			p_options = p_options || {};
			this.m_options = p_options;
			if (!cvs.template) {

				// Initialize the options
				var options = gQuery.extend({

					layout: "{{?title}}<h3>{{title}}</h3>{{title?}}{{?message}}<p>{{message}}</p>{{message?}}" +
							"<input type='button' value='{{?yesText}}{{yesText}}{{yesText?}}{{!yesText}}Yes{{yesText!}}' class='jlg-yes-button' />" +
							"<input type='button' value='{{?noText}}{{noText}}{{noText?}}{{!noText}}No{{noText!}}' class='jlg-no-button' />"

				}, (JLG.popup.defaults || {}).confirm || {});

				// Create and compile the template
				cvs.template = gQuery.templatize( options.layout );
			}

			// Apply template to the text
			var content = cvs.template.apply({

				title: p_title,
				message: p_message,
				noText: p_options.noText || "No",
				yesText: p_options.yesText || "Yes"
			});

			// Create a div to hold the content
			this.m_contentEl = gQuery( document.createElement( "div" ) ).html( content ).hide().appendTo( "body" );

			// Locate the ok button
			this.m_noButton = gQuery( ".jlg-no-button", this.m_contentEl ).bind( "click", gQuery.bindFn( this._onNo, this ) );
			this.m_yesButton = gQuery( ".jlg-yes-button", this.m_contentEl ).bind( "click", gQuery.bindFn( this._onYes, this ) );

			// Archive any existing display
			p_options = p_options || {};
			if (JLG.popup.window().archive()) { this.m_hasArchive = true; }

			// Show the popup
			JLG.popup.window().show( gQuery.extend( { contentEl: this.m_contentEl[ 0 ] }, p_options || {} ) );
		},

		/**
		 * Protected methods
		 */
		_onButtonClick: function (p_btn) {

			// Disconnect the handlers
			if (this.m_yesButton) { this.m_yesButton.unbind(); }
			if (this.m_noButton) { this.m_noButton.unbind(); }

			// Restore any archived popup.  Need to do this before destroying the content element so the inner data can be returned.
			if (this.m_hasArchive) {

				JLG.popup.window ().restore ({bookmark: false});

			} else {

				JLG.popup.window ().hide ();
			}

			// Destroy the element
			if (this.m_contentEl) { this.m_contentEl.remove(); }

			// Call the generic handler
			if (this.m_options.onButtonClick) { this.m_options.onButtonClick (p_btn); }
		},

		_onNo: function () {

			// Call the event handler
			this._onButtonClick ("no");
			if (this.m_options.onNo) {

				this.m_options.onNo ();
			}
		},

		_onYes: function () {

			// Call the event handler
			this._onButtonClick ("yes");
			if (this.m_options.onYes) {

				this.m_options.onYes ();
			}
		}
	} );
}

// Define class variables
JLG.popup.Confirm.template = null;


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
if (typeof JLG.popup == "undefined" || typeof JLG.popup.window == "undefined") {

	JLG.popup = gQuery.extend( JLG.popup || {}, {

		alert: function (p_message, p_title, p_options) {

			// Create a new alert box
			var alert = new JLG.popup.Alert (p_message, p_title, p_options);
		},

		confirm: function (p_message, p_title, p_options) {

			// Create a new confirm box
			var confirm = new JLG.popup.Confirm (p_message, p_title, p_options);
		},

	    hideSpinner: function (p_options) {

	        var pop = JLG.popup;
	        var spinner = pop.m_spinner;
	        var dlg = pop.window ();
			p_options = p_options || {};

			// Ignore hides from old requests
			if (p_options.__requestPos && p_options.__requestPos <= spinner.requestPos) {

				return;
				// NOTREACHED
			}

			// Hide the spinner
			--spinner.count;
			if (spinner.count <= 0) {

				spinner.count = 0;
	 			if (dlg.getName () == "jlg-popup-spinner") {

	                if (spinner.hasArchive) {

						spinner.hasArchive = false;
	                    JLG.popup.window ().restore ({bookmark: false});

	                } else {

					    dlg.hide (null, {force: true});
	                }
				}
			}
	    },

		history: function () {

			if (!JLG.popup.m_history) { JLG.popup.m_history = new JLG.popup.History ('jlgdefault'); }
			return JLG.popup.m_history;
		},

		requests: function () {

			if (!JLG.popup.m_requests) {
			
				JLG.popup.m_requests = new JLG.popup.RequestManager ();
			}
			return JLG.popup.m_requests;
		},

	    showSpinner: function( p_options ) {

	        var pop = JLG.popup;
	        var spinner = pop.m_spinner;
	        var dlg = pop.window();

	        // Merge the global default options with the passed-in options
	        p_options = gQuery.extend({

	            caller: dlg.currentOptions.caller,
				layout: "<div class='jlg-popup-spinner'>{jlg-popup-content}</div>",
				name: "jlg-popup-spinner",
				position: 'centered'

	        }, JLG.popup.defaults.spinner || {}, p_options || {});

	        // Store current state if a spinner isn't displayed
	        if (dlg.isVisible && dlg.getName() != "jlg-popup-spinner") {

	            // Determine the previous content
	            if (dlg.archive()) {

	                spinner.hasArchive = true;

	            } else {

		        	spinner.hasArchive = false;
				}
	        }

			// Show the spinner
			++spinner.count;
			dlg.show( gQuery.extend( {}, p_options, { __spinner: true } ) );
	    },

		window: function() {

			if (!JLG.popup.m_window) { JLG.popup.m_window = new JLG.popup.Window(); }
			return JLG.popup.m_window;
		},

		/**
		 * Member variables
		 */
		m_history: null,

		m_requests: null,

		m_spinner: {

	    	count: 0,

	        hasArchive: false,

			requestPos: -1
		},
	
		m_window: null
	});

	// Default configuration options
	JLG.popup.defaults = {};
}


/**
 * Global variables
 */
if (typeof window.Zukima == "undefined" || !window.Zukima) {

	var Zukima = {};
	Zukima.Configuration = {highlight: []};
}

function enable_form_fields (p_form)
{
	var elements = Form.getElements(p_form);
    for (var index = 0; index < elements.length; ++index)
		{elements[index].disabled = false;}
    return true;
}

/**
 * class ConfigurationOption
 */
ConfigurationOption = function( p_id, p_options ) { this.initialize( p_id, p_options ); };
gQuery.extend( ConfigurationOption.prototype, {

	initialize: function( p_id, p_options ) {

		var index;
		var toggle;

        // Member variables
        this.options = p_options || {};
        this.element = gQuery( p_id );
	    this.toggle_on = [];
	    this.toggle_off = [];
		if (this.options.toggle_on) {

		    // Initialize the array
			toggle = p_options.toggle_on;
		    for (index = 0; index < toggle.length; index++) {

				var el = gQuery( toggle[ index ] );
				if (el) { this.toggle_on.push( el ); }
			}
		}
		if (this.options.toggle_off) {

		    // Initialize the array
			toggle = p_options.toggle_off;
		    for (index = 0; index < toggle.length; index++) {

				var el = gQuery( toggle[ index ] );
				this.toggle_off.push( el );
			}
		}

        // Setup the event handler
        this.element.bind( "click", gQuery.bindFn( this.on_click, this ) );
        this.highlight();
    },

	on_click: function( p_event ) {

		this.highlight();
	},

	highlight: function()
	{
	    var index;
	    var on_display;
		var off_display;

	    // Selected?
	    if (this.element.attr( "checked" ) ) {

			on_display = '';
			off_display = 'none';
		}
		else {

			on_display = 'none';
			off_display = '';
		}

	    // Display the toggle elements
	    for (index = 0; index < this.toggle_on.length; index++) { this.toggle_on[ index ].css( "display", on_display ); }
	    for (index = 0; index < this.toggle_off.length; index++) { this.toggle_off[ index ].css( "display", off_display ); }
	}
} );

ToggledSelect = function( p_id, p_options ) { this.initialize( p_id, p_options ); };
gQuery.extend( ToggledSelect.prototype, {

	initialize: function( p_id, p_toggles ) {

		var index;
		var entry;
		var elm;
		var elements;
		var ids;

        // Member variables
        this.element = gQuery(p_id);
		this.toggles = {};
		this.toggle_on = null;

		// Anything to toggle?
		if (p_toggles) {

		    // Initialize the array
			for (entry in p_toggles) {

				// Create an array of elements
				elements = [];
				ids = p_toggles[ entry ];
				if (ids) {

					// Instantiate the element
					for (index = 0; index < ids.length; index++) {

						// Get and hide the element
						elm = gQuery(ids[ index ]);
						if (elm) {

							elements[ elements.length ] = elm;
							elm.hide();
						}
					}
				}
				this.toggles[ entry ] = elements;
			}

			// Store the existing toggle elements
			this.toggle_on = this.toggles[ this.element.val() ];
		}

        // Setup the event handler
		this.element.bind( "change", gQuery.bindFn( this.on_change, this ) );
		JLG.core.fireEvent( this.element[ 0 ], "change" );
	},

	on_change: function( p_event ) {

		var index;
		var toggles;

		// Hide existing toggles
		toggles = this.toggle_on;
		if (toggles) { for (index = 0; index < toggles.length; index++) { toggles[ index ].hide(); } }

		// Show new toggles
		toggles = this.toggles[ this.element.val() ];
		if (toggles) { for (index = 0; index < toggles.length; index++) { toggles[ index ].show(); } }
		this.toggle_on = toggles;
	}
} );

/**
 * class ConfigurationChoice
 */
ConfigurationChoice = function( p_id, p_options ) { this.initialize( p_id, p_options ); };
gQuery.extend( ConfigurationChoice.prototype, ConfigurationOption.prototype, {

	highlight: function ()
	{
	    var type = Zukima.Configuration.highlight [this.element.attr( "name" )];

	    // Call the base
	    ConfigurationOption.prototype.highlight.call( this );

	    // Highlight the currently selected element
	    if (type && type != this)
	        {type.highlight ();}

	    // Are we the new selection?
	    if (this.element.attr( "checked" ) )
	        {Zukima.Configuration.highlight [this.element.attr( "name" )] = this;}
	}
} );

/**
 * Spinner styling
 */
if (typeof window.JLG === "undefined") {

	JLG = {};
}
JLG.popup = JLG.popup || {};
JLG.popup.defaults = gQuery.extend( JLG.popup.defaults || {}, {

	alert: {

		layout: "{{?title}}<h3>{{title}}</h3>{{title?}}" +
				"{{?message}}<p>{{message}}</p><br />{{message?}}" +
				"<div class='rex-button-bar'><p>" +
					"<a href='javascript:void(0);' class='rex-btn rex-positive rex-btn-center jlg-ok-button'>{{?okText}}{{okText}}{{okText?}}{{!okText}}Ok{{okText!}}</a>" +
				"</p></div>"
	},


	confirm: {

		layout: "{{?title}}<h3>{{title}}</h3>{{title?}}" +
				"{{?message}}<p>{{message}}</p><br />{{message?}}" +
				"<div class='rex-button-bar'><p>" +
					"<a href='javascript:void(0);' class='rex-btn rex-positive jlg-yes-button'>{{?yesText}}{{yesText}}{{yesText?}}{{!yesText}}Yes{{yesText!}}</a>" +
					"<a href='javascript:void(0);' class='rex-btn rex-negative jlg-no-button'>{{?noText}}{{noText}}{{noText?}}{{!noText}}&nbsp;No{{noText!}}</a>" +
				"</p></div>"
	},

    request: {

        showSpinner: true
    },

    spinner: {

        layout: '<div class="rex-popup-dialog">' +
        			    '<table class="rex-popup-border" cellpadding="0" cellspacing="0">' +
        			        '<tr height="25"><td class="tl"  height="25"></td><td class="tm"  height="25"></td><td class="tr"  height="25" ></td></tr>' +
        			        '<tr><td class="ml"></td><td class="mm"><div class="rex-popup-content"><div class="rex-please-wait">{{jlg-popup-content}}</div></div></td><td class="mr"></td></tr>' +
        			        '<tr><td class="bl"></td><td class="bm"></td><td class="br"></td></tr>' +
        			    '</table>' +
        			'</div>',
        frameEl: ".rex-popup-border",
        displayEl: ".rex-popup-content"
    },

	window: {

        layout: '<div class="rex-popup-dialog">' +
        			    '<table class="rex-popup-border" cellpadding="0" cellspacing="0">' +
        			        '<tr><td class="tl"></td><td class="tm"></td><td class="tr"></td></tr>' +
        			        '<tr><td class="ml"></td><td class="mm"><div class="rex-popup-content">{{jlg-popup-content}}</div></td><td class="mr"></td></tr>' +
        			        '<tr><td class="bl"></td><td class="bm"></td><td class="br"></td></tr>' +
        			    '</table>' +
        			'</div>',
        frameEl: ".rex-popup-border",
        displayEl: ".rex-popup-content"
	}
});

function rex_on_page_ready() {

	// Mark required fields in IE
	if (JLG.core.getIEVersion() > 0 && JLG.core.getIEVersion() < 8) {

		gQuery( ".ui2_field_required:not(:has(.ui2_field_required_before))", this ).prepend( "<span class='ui2_field_required_before'>*</span>" );
	}

	// Focus on the default default form field
	gQuery( ".rex-form-focus" ).attr( "autocomplete", "off" ).focus();
}
gQuery( document ).ready( gQuery.bindFn( rex_on_page_ready, "body" ) );


// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download.
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================

// HISTORY
// ------------------------------------------------------------------
// May 17, 2003: Fixed bug in parseDate() for dates <1970
// March 11, 2003: Added parseDate() function
// March 11, 2003: Added "NNN" formatting option. Doesn't match up
//                 perfectly with SimpleDateFormat formats, but
//                 backwards-compatability was required.

// ------------------------------------------------------------------
// These functions use the same 'format' strings as the
// java.text.SimpleDateFormat class, with minor exceptions.
// The format string consists of the following abbreviations:
//
// Field        | Full Form          | Short Form
// -------------+--------------------+-----------------------
// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
//              | NNN (abbr.)        |
// Day of Month | dd (2 digits)      | d (1 or 2 digits)
// Day of Week  | EE (name)          | E (abbr)
// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
// Minute       | mm (2 digits)      | m (1 or 2 digits)
// Second       | ss (2 digits)      | s (1 or 2 digits)
// AM/PM        | a                  |
//
// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
// Examples:
//  "MMM d, y" matches: January 01, 2000
//                      Dec 1, 1900
//                      Nov 20, 00
//  "M/d/yy"   matches: 01/20/00
//                      9/2/00
//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
// ------------------------------------------------------------------

if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
if( !JLG.date.Parser ) {
	
	JLG.date.Parser = function( p_config ) {

		/**
		 * Member variables
		 */
	     this.m_monthNames = [

	         'January',
	         'February',
	         'March',
	         'April',
	         'May',
	         'June',
	         'July',
	         'August',
	         'September',
	         'October',
	         'November',
	         'December',
	         'Jan',
	         'Feb',
	         'Mar',
	         'Apr',
	         'May',
	         'Jun',
	         'Jul',
	         'Aug',
	         'Sep',
	         'Oct',
	         'Nov',
	         'Dec'
	     ];

	     this.m_dayNames = [

	         'Sunday',
	         'Monday',
	         'Tuesday',
	         'Wednesday',
	         'Thursday',
	         'Friday',
	         'Saturday',
	         'Sun',
	         'Mon',
	         'Tue',
	         'Wed',
	         'Thu',
	         'Fri',
	         'Sat'
	     ];
	};
	gQuery.extend( JLG.date.Parser.prototype, {

	    isDate: function (val,format) {

	    	var date=this.getDateFromFormat(val,format);
	    	if (date===0) { return false; }
	    	return true;
		},

	    compareDates: function (date1,dateformat1,date2,dateformat2) {
	    	var d1=this.getDateFromFormat(date1,dateformat1);
	    	var d2=this.getDateFromFormat(date2,dateformat2);
	    	if (d1===0 || d2===0) {
	    		return -1;
	    		}
	    	else if (d1 > d2) {
	    		return 1;
	    		}
	    	return 0;
	    },

	    formatDate: function (date,format) {

	    	format=format+"";
	    	var result="";
	    	var i_format=0;
	    	var c="";
	    	var token="";
	    	var y=date.getYear()+"";
	    	var M=date.getMonth()+1;
	    	var d=date.getDate();
	    	var E=date.getDay();
	    	var H=date.getHours();
	    	var m=date.getMinutes();
	    	var s=date.getSeconds();
	    	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,KK,K,kk,k;
	    	// Convert real date parts into formatted versions
	    	var value={};
	    	if (y.length < 4) {y=""+(y-0+1900);}
	    	value.y=""+y;
	    	value.yyyy=y;
	    	value.yy=y.substring(2,4);
	    	value.M=M;
	    	value.MM=this._lz(M);
	    	value.MMM=this.m_monthNames[M-1];
	    	value.NNN=this.m_monthNames[M+11];
	    	value.d=d;
	    	value.dd=this._lz(d);
	    	value.E=this.m_dayNames[E+7];
	    	value.EE=this.m_dayNames[E];
	    	value.H=H;
	    	value.HH=this._lz(H);
	    	if (H===0){value.h=12;}
	    	else if (H>12){value.h=H-12;}
	    	else {value.h=H;}
	    	value.hh=this._lz(value.h);
	    	if (H>11){value.K=H-12;} else {value.K=H;}
	    	value.k=H+1;
	    	value.KK=this._lz(value.K);
	    	value.kk=this._lz(value.k);
	    	if (H > 11) { value.a="PM"; }
	    	else { value.a="AM"; }
	    	value.m=m;
	    	value.mm=this._lz(m);
	    	value.s=s;
	    	value.ss=this._lz(s);
	    	while (i_format < format.length) {
	    		c=format.charAt(i_format);
	    		token="";
	    		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
	    			token += format.charAt(i_format++);
	    			}
	    		if (value[token] !== null) { result=result + value[token]; }
	    		else { result=result + token; }
	    		}
	    	return result;
	    },

	    getDateFromFormat: function (val, format) {

	    	val=val+"";
	    	format=format+"";
	    	var fmtLen;
	    	var i_val=0;
	    	var i_format=0;
	    	var c="";
	    	var token="";
	    	var token2="";
	    	var x,y;
	    	var now=new Date();
	    	var year=now.getYear();
	    	var month=now.getMonth()+1;
	    	var date=1;
	    	var hh=0;
	    	var mm=0;
	    	var ss=0;
	    	var ampm="";
	    	var mnLen;
	    	var dnLen;
			var i;
	    	var dayNames = this.m_dayNames;
	    	var monthNames = this.m_monthNames;

	        fmtLen = format.length;
	    	while (i_format < fmtLen) {
	    		// Get next token from format string
	    		c=format.charAt(i_format);
	    		token="";
	    		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
	    			token += format.charAt(i_format++);
	    			}
	    		// Extract contents of value based on format token
	    		if (token=="yyyy" || token=="yy" || token=="y") {
	    			if (token=="yyyy") { x=4;y=4; }
	    			if (token=="yy")   { x=2;y=2; }
	    			if (token=="y")    { x=2;y=4; }
	    			year=this._getInt(val,i_val,x,y);
	    			if (year===null) { return 0; }
	    			i_val += year.length;
	    			if (year.length==2) {
	    				if (year > 70) { year=1900+(year-0); }
	    				else { year=2000+(year-0); }
	    				}
	    			}
	    		else if (token=="MMM"||token=="NNN"){
	    			month=0;
	    			for (i=0, mnLen = monthNames.length; i < mnLen; i++) {
	    				var month_name=monthNames[i];
	    				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
	    					if (token=="MMM"||(token=="NNN"&&i>11)) {
	    						month=i+1;
	    						if (month>12) { month -= 12; }
	    						i_val += month_name.length;
	    						break;
	    						}
	    					}
	    				}
	    			if ((month < 1)||(month>12)){return 0;}
	    			}
	    		else if (token=="EE"||token=="E"){
	    			for (i=0, dnLen = dayNames.length; i<dnLen; i++) {
	    				var day_name=dayNames[i];
	    				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
	    					i_val += day_name.length;
	    					break;
	    					}
	    				}
	    			}
	    		else if (token=="MM"||token=="M") {
	    			month=this._getInt(val,i_val,token.length,2);
	    			if(month===null||(month<1)||(month>12)){return 0;}
	    			i_val+=month.length;}
	    		else if (token=="dd"||token=="d") {
	    			date=this._getInt(val,i_val,token.length,2);
	    			if(date===null||(date<1)||(date>31)){return 0;}
	    			i_val+=date.length;}
	    		else if (token=="hh"||token=="h") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<1)||(hh>12)){return 0;}
	    			i_val+=hh.length;}
	    		else if (token=="HH"||token=="H") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<0)||(hh>23)){return 0;}
	    			i_val+=hh.length;}
	    		else if (token=="KK"||token=="K") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<0)||(hh>11)){return 0;}
	    			i_val+=hh.length;}
	    		else if (token=="kk"||token=="k") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<1)||(hh>24)){return 0;}
	    			i_val+=hh.length;hh--;}
	    		else if (token=="mm"||token=="m") {
	    			mm=this._getInt(val,i_val,token.length,2);
	    			if(mm===null||(mm<0)||(mm>59)){return 0;}
	    			i_val+=mm.length;}
	    		else if (token=="ss"||token=="s") {
	    			ss=this._getInt(val,i_val,token.length,2);
	    			if(ss===null||(ss<0)||(ss>59)){return 0;}
	    			i_val+=ss.length;}
	    		else if (token=="a") {
	    			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
	    			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
	    			else {return 0;}
	    			i_val+=2;}
	    		else {
	    			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
	    			else {i_val+=token.length;}
	    			}
	    		}
	    	// If there are any trailing characters left in the value, it doesn't match
	    	if (i_val != val.length) { return 0; }
	    	// Is date valid for month?
	    	if (month==2) {
	    		// Check for leap year
	    		if ( ( (year%4===0)&&(year%100 !== 0) ) || (year%400===0) ) { // leap year
	    			if (date > 29){ return 0; }
	    			}
	    		else { if (date > 28) { return 0; } }
	    		}
	    	if ((month==4)||(month==6)||(month==9)||(month==11)) {
	    		if (date > 30) { return 0; }
	    		}
	    	// Correct hours value
	    	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
	    	else if (hh>11 && ampm=="AM") { hh-=12; }
	    	var newdate=new Date(year,month-1,date,hh,mm,ss,0);
	    	return newdate.getTime();
		},

	    parse: function (val) {
	    	var preferEuro=(arguments.length==2)?arguments[1]:false;
	    	window.generalFormats=['y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d'];
	    	window.monthFirst=['M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d'];
	    	window.dateFirst =['d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M'];
	    	var checkList=['generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst'];
	    	var d=null;
	    	var len;
	    	var jLen;
			var j;
			var i;
	    	for (i=0, len = checkList.length; i<len; i++) {
	    		var l=window[checkList[i]];
	    		for (j=0, jLen = l.length; j<jLen; j++) {
	    			d=this.getDateFromFormat(val,l[j]);
	    			if (d!==0) { return new Date(d); }
	    			}
	    		}
	    	return null;
		},

		/**
		 * Private methods
		 */
	    _isInteger: function (val) {
	    	var digits="1234567890";
	    	for (var i=0; i < val.length; i++) {
	    		if (digits.indexOf(val.charAt(i))==-1) { return false; }
	    		}
	    	return true;
	    },

	    _getInt: function (str,i,minlength,maxlength) {
	    	for (var x=maxlength; x>=minlength; x--) {
	    		var token=str.substring(i,i+x);
	    		if (token.length < minlength) { return null; }
	    		if (this._isInteger(token)) { return token; }
	    		}
	    	return null;
	    },

	     _lz: function (x) {

	         return(x<0||x>9?"":"0")+x;
	     }
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerCell = function (p_module) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, p_module.Cell.prototype, {

		create: function( p_day, p_parent, p_class ) {

		    // See if this is an available or unavailable cell
			p_module.Cell.prototype.create.call( this, p_day, p_parent, p_class );
			var config = this.getMonth().getCalendar().getConfig();
			this.onValidDatesChanged( config.minDate, config.maxDate );
		},

		onValidDatesChanged: function( p_min, p_max ) {

			this.m_disabled = ((p_min && this.getDate() < p_min) || (p_max && this.getDate() > p_max));
			if (this.m_disabled) {

				this.getCellEl().addClass( "jlg-cal-unavailable" );
				this.getDateEl().addClass( "jlg-cal-unavailable" );

			} else {

				this.getCellEl().removeClass( "jlg-call-unavailable" );
				this.getDateEl().removeClass( "jlg-call-unavailable" );
			}
		},

		onMouseDown: function( p_src ) {

			if (!this.m_disabled) {

				this.getMonth().getCalendar().getConfig().viewer.getConfig().window.unhighlight();
				return p_module.Cell.prototype.onMouseDown.call( this, p_src );
			}
			return true;
		},

		_on_mouse_enter: function( p_src ) {

			if (!this.m_disabled && !this.getMonth().getCalendar().isMouseDown) { this.getMonth().getCalendar().getConfig().viewer.getConfig().window.highlight( this.index ); }
			return true;
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerMonth = function( p_module ) {

	var result = function( p_calendar, p_year, p_month, p_div, p_dim, p_options ) { this.initialize( p_calendar, p_year, p_month, p_div, p_dim, p_options ); };
	gQuery.extend( result.prototype, p_module.Month.prototype, {

		getCellModel: function () {

			if (!this.m_cellModel) {this.m_cellModel = JLG.date.buildDateRangePickerCell (p_module);}
			return this.m_cellModel;
		},

		_alloc_cell: function () {

			return new (this.getCellModel ()) (this);
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerCalendar = function( p_module ) {

 	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, p_module.Calendar.prototype, {

		getMonthModel: function () {

			if (!this.m_monthModel) {this.m_monthModel = JLG.date.buildDateRangePickerMonth (p_module);}
			return this.m_monthModel;
		},

		onValidDatesChanged: function (p_min, p_max) {

			for (var dates = this.getDates (), index = 0, len = dates.length; index < len; ++index) {dates [index].onValidDatesChanged (p_min, p_max);}
		},

		setMaxDate: function (p_value) {

			this.getConfig ().maxDate = p_value;
			this.onValidDatesChanged (this.getConfig ().minDate, p_value);
		},

		setMinDate: function (p_value) {

			this.getConfig ().minDate = p_value;
			this.onValidDatesChanged (p_value, this.getConfig ().maxDate);
		},

		_alloc_month: function (p_year, p_month, p_div, p_dim, p_config) {

			return new (this.getMonthModel ()) (this, p_year, p_month, p_div, p_dim, p_config);
		},

		_on_mouse_up: function( p_event ) {

			var chose = this.isMouseDown;

		    // Call the base class
		    p_module.Calendar.prototype._on_mouse_up.call( this, p_event );

		    // Process the selection
		    if (chose) { this.getConfig().viewer.getConfig().window._select(); }
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerViewer  = function (p_module) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, p_module.Viewer.prototype, {

		initialize: function( p_config ) {

			var config = p_config || {};
			var calConfig = gQuery.extend( { cssClass: "jlg-pac-panel-all" }, config.calendar || {} );

			// Clear modified objects
			delete config.calendar;

			// Call the base class
			p_module.Viewer.prototype.initialize.call( this, gQuery.extend( { calendar: calConfig, cssClass: "jlg-pac-viewer" }, config ) );
		},

		getCalendarModel: function () {

			if (!this.m_calendarModel) {this.m_calendarModel = JLG.date.buildDateRangePickerCalendar (p_module);}
			return this.m_calendarModel;
		},

		setMaxDate: function (p_value) {

			this.getConfig ().calendar.maxDate = p_value;
			if (this.getCalendar ()) { this.getCalendar ().setMaxDate (p_value); }
		},

		setMinDate: function (p_value) {

			this.getConfig ().calendar.minDate = p_value;
			if (this.getCalendar ()) { this.getCalendar ().setMinDate (p_value); }
		},

		/******************************************************
		 * PRIVATE METHODS
		 ******************************************************/
		_alloc_calendar: function( p_config ) {

			return new( this.getCalendarModel() )( gQuery.extend( { viewer: this }, p_config || {} ) );
		},

		/******************************************************
		 * MEMBER VARIABLES
		 ******************************************************/
		m_calendarModel: null
	});
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerWindow = function( p_module ) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, {

		initialize: function( p_config ) {

			// Update the configuration
			p_config = p_config || {};
			this.m_idxHighlight = -1;
			this.m_config = this._mergeConfig( p_config );
			this.m_config.viewer.width = Math.ceil( (1.1 * this.m_config.viewer.height) * this.m_config.monthsDisplayed );
			this.setMaxDate( this.m_config.maxDate );
			this.setMinDate( this.m_config.minDate );

			// Determine proper date format
			this.m_preferEuro = (jgLocale || {}).ukDateFormat ? true : false;
		},

		blur: function( p_element ) {

			var binder = this.getBinder();
			var calendar = binder.getCalendar();

			// Are we allowed to hide?
			if (this.m_holder == p_element && (!calendar || !calendar.isMouseDown)) {

				// Do we have the focus?
				if (this.hasFocus()) {

					// Reset focus
					this.m_hasFocus = false;

				} else {

					// Hide the calendar
					this.hide();
				}
			}
		},

		focus: function() { if (this.m_holder) { this.m_holder.focus(); } },

		getBinder: function() { return this.m_viewer; },

		getConfig: function() { return this.m_config; },

		getElement: function() { return this.m_viewerEl; },

		getPreferEuro: function() { return this.m_preferEuro; },

		getQueuedDate: function() { return this.m_queuedDate; },

		getScrollTimer: function() { return this.m_scrollTimer; },

		getViewer: function() { return this.m_viewer; },

		getViewerModel: function() {

			if (!this.m_viewerModel) { this.m_viewerModel = JLG.date.buildDateRangePickerViewer( p_module ); }
			return this.m_viewerModel;
		},

		hasFocus: function() { return this.m_hasFocus; },

		hide: function( p_element ) {

			var calendar = this.getBinder().getCalendar();
			this.m_hasFocus = false;
			this.unhighlight();
			if (calendar) { calendar._unselect_all(); }
			this.m_popupEl.hide();
			this.m_holder = null;

			// Show elements that might interfere with the popup
			var ieVersion = gQuery.ieVersion();
	        if (ieVersion > 0 && ieVersion < 7) { this.hideElements( "select", "visible" ); }
			this.hideElements( "embed", "visible" );
			this.hideElements( "object", "visible" );
		},

	    hideElements: function( p_elem, p_visibility ){

	        var nodes = gQuery( p_elem ).css( "visibility", p_visibility );
	    },

		highlight: function( p_index ) {

			var calendar = this.getBinder().getCalendar();
			if (calendar) {

				// Highlight the selected date
				this.unhighlight();
				this.m_idxHighlight = p_index;
				calendar.getDates()[ this.m_idxHighlight ].getDateEl().addClass( this.getConfig().highlightDateCssClass );
			}
		},

		_mergeConfig: function (p_config) {

			var config = p_config || {};
			var viewerConfig = config.viewer || {};
			var calConfig = viewerConfig.calendar || {};
			var monthConfig = calConfig.month || {};

			// Clear objects
			delete calConfig.month;
			delete viewerConfig.calendar;
			delete config.viewer;

			// Extend the default config
			monthConfig = gQuery.extend( {}, monthConfig );
			calConfig = gQuery.extend({

				cssClass: "jlg-pac-panel",
				monthBodyCssClass: "jlg-pac-month-body",
				monthDivCssClass: "jlg-pac-month-div",
				monthHeaderCssClass: "jlg-pac-month-header",
				monthNameCssClass: "jlg-pac-month-name",
				monthDayNameCssClass: "jlg-pac-month-day-name",
				monthTableCssClass: "jlg-pac-month-table",
				monthPaddingLeftCssClass: "jlg_paw_padding_left",
				monthPaddingRightCssClass: "jlg_paw_padding_right",
				month: monthConfig

			}, calConfig );

			// Update the viewer options
			viewerConfig = gQuery.extend({ calendar: calConfig, height: 165 }, viewerConfig );

			// Return the total object extension
			config = gQuery.extend({ highlightDateCssClass: 'jlg-pac-date-highlight', monthsDisplayed: 2, viewer: viewerConfig }, config );

			// Return the config
			return config;
		},

		_nextId: gQuery.counter(),

		onClickedNextMonth: function() {

			// Clear the last click
			clearTimeout( this.getScrollTimer() );

			// Initialize
			var oldBinder = this.getBinder();
			var date = this.getQueuedDate() || oldBinder.getDate();
			var newDate = new Date();

			// Determine the new date
			newDate.setDate( 1 );
			if (date.getMonth() == 11) {

				newDate.setMonth( 1 );
				newDate.setFullYear( date.getFullYear() + 1 );

			} else {

				newDate.setMonth( date.getMonth () + 1 );
				newDate.setFullYear( date.getFullYear() );
			}
			this.m_queuedDate = newDate;

			// Delay the update for multiple clicks
			this.m_scrollTimer = setTimeout( gQuery.bindFn( function() {

				// Initialize
				var config = this.getConfig();
				var oldElement = this.getElement();

				// Create a new binder
				this.m_viewer = new( this.getViewerModel() )( gQuery.extend( { window: this }, config.viewer ) );
				this.m_viewerEl = gQuery( document.createElement( 'div' ) );
				var newBinder = this.getBinder();
				var newElement = this.getElement();
				oldElement.after( newElement );
				newBinder.render( gQuery.extend( gQuery.extend( {}, this.m_configRender ), { el: newElement } ) );
				newBinder.setDate( this.m_queuedDate, { count: config.monthsDisplayed } );
				try { oldElement.remove(); } catch (e) {}
				this.m_queuedDate = null;

				// Refresh the navbar
				this.onRefresh();

				// Reset the focus
				this.m_holder.focus();

			}, this ), 0);
		},

		onMouseDown: function() { this.m_hasFocus = true; },

		onClickedPrevMonth: function() {

			// Clear the last click
			clearTimeout( this.getScrollTimer() );

			// Initialize
			var oldBinder = this.getBinder();
			var date = this.getQueuedDate() || oldBinder.getDate();
			var newDate = new Date();

			// Determine the new date
			newDate.setDate( 1 );
			if (date.getMonth() === 0) {

				newDate.setMonth( 11 );
				newDate.setFullYear( date.getFullYear() - 1 );

			} else {

				newDate.setMonth( date.getMonth () - 1 );
				newDate.setFullYear( date.getFullYear() );
			}
			this.m_queuedDate = newDate;

			// Delay the update for multiple clicks
			this.m_scrollTimer = setTimeout( gQuery.bindFn( function() {

				// Initialize
				var config = this.getConfig();
				var oldElement = this.getElement();

				// Create a new viewer
				this.m_viewer = new (this.getViewerModel())( gQuery.extend( { window: this }, config.viewer ) );
				this.m_viewerEl = gQuery( document.createElement( 'div' ) );
				var newBinder = this.getBinder();
				var newElement = this.getElement();
				oldElement.after( newElement );
				newBinder.render( gQuery.extend( gQuery.extend( {}, this.m_configRender ), { el: newElement } ) );
				newBinder.setDate( this.m_queuedDate, { count: config.monthsDisplayed } );
				try { oldElement.remove(); } catch (e) {}
				this.m_queuedDate = null;

				// Refresh the navbar
				this.onRefresh ();

				// Reset the focus
				this.m_holder.focus();

			}, this ), 0);
		},

		onClose: function() {

			this.m_holder.drpwIgnoreFocus = true;
			this.m_holder.focus();
			this.hide();
		},

		onRefresh: function() {

			var binder = this.getBinder();
			var config = this.getConfig();
			var date = binder.getDate();

			// Set the date ranges
			if (config.maxDate) {

				if (date >= config.maxDate) {

					this.m_navLinkFwd.hide();

				} else {

					this.m_navLinkFwd.show();
				}
			} else {

				this.m_navLinkFwd.show();
			}
			if (config.minDate) {

				if (date <= config.minDate) {

					this.m_navLinkBack.hide();

				} else {

					this.m_navLinkBack.show();
				}
			} else {

				this.m_navLinkBack.show();
			}
		},

		render: function( p_config ) {

			var config = this.getConfig();

			// Initialize
			p_config = p_config || {};
			this.m_popupEl = gQuery( document.createElement( "div" ) );
			this.m_viewer = new (this.getViewerModel())( gQuery.extend( { window: this }, config.viewer ) );
			this.m_viewerEl = gQuery( document.createElement( "div" ) );

			// Create the container element
			this.m_popupEl.addClass( config.popupClass || "jlg-pac-popup" );

			// Render the viewer
			this.m_configRender = gQuery.extend( gQuery.extend( {}, config.viewer ), p_config || {} );
			this.m_viewer.render( gQuery.extend( gQuery.extend( {}, this.m_configRender ), { el: this.m_viewerEl } ) );
			this.m_popupEl.append( this.m_viewerEl );

			// Nav container
			var nav = gQuery( document.createElement( "div" ) ).addClass( config.navClass || "jlg-pac-nav" );

			// Backwards
			var navDiv = gQuery( document.createElement( "div" ) ).addClass( "jlg-pac-back" ).css( "float", "left" );
			this.m_navLinkBack = gQuery( document.createElement( "a" ) ).attr( "href", "javascript:void(0);" ).html( "&lt;&lt;" ).appendTo( navDiv );
			navDiv.appendTo( nav );

			// Forward
			navDiv = gQuery( document.createElement( "div" ) ).addClass( "jlg-pac-fwd" ).css( "float", "right" );
			this.m_navLinkFwd = gQuery( document.createElement( "a" ) ).attr( "href", "javascript:void(0);" ).html( "&gt;&gt;" ).appendTo( navDiv );
			navDiv.appendTo( nav );
			nav.appendTo( this.m_popupEl );

			// Close
			var closeDiv = gQuery( document.createElement( "div" ) ).addClass( "jlg-pac-close" );
			var closeLink = gQuery( document.createElement( "a" ) ).attr( "href", "javascript:void(0);" ).html( "Close" ).appendTo( closeDiv );
			closeDiv.appendTo( this.m_popupEl );

			// Append to the root
			var jwindow = gQuery( window );
			var dimBrowser = { w: jwindow.width(), h: jwindow.height() };
			var el = p_config.el ? gQuery( p_config.el ) : gQuery( "body" );
			this.m_popupEl.appendTo( el ).css({ left: dimBrowser.w, top: dimBrowser.h });

			// Connect event handlers
			closeLink.bind( "click", gQuery.bindFn( this.onClose, this ) );
			this.m_navLinkFwd.bind( "click", gQuery.bindFn( this.onClickedNextMonth, this ) );
			this.m_navLinkBack.bind( "click", gQuery.bindFn( this.onClickedPrevMonth, this ) );
			this.m_popupEl.bind( 'mousedown', gQuery.bindFn( this.onMouseDown, this ) );

			// Hide the popup
			this.m_popupEl.hide();

			// Rendered
			this.m_rendered = true;
		},

		setMaxDate: function( p_value ) {

			var binder = this.getBinder();
			var config = this.getConfig();

			// Determine the month ranges
			if (p_value) {

				var months = config.monthsDisplayed;
				var years = Math.floor( months / 12 );
				months = months % 12;
				if (months == 1 && years === 0) {

					months = 0;

				} else if (months > 0) {

					--months;
				}
				if ((p_value.getMonth () + 1) < months) { years += 1; }
				var newDate = new Date( p_value.getFullYear() - years, p_value.getMonth() - months, 1, 0, 0, 0, 0 );
				config.maxDate = newDate;
				p_value = new Date( p_value.getFullYear(), p_value.getMonth(), p_value.getDate (), 0, 0, 0, 0 );
			}
			config.viewer.calendar.maxDate = p_value;
			if (binder) { binder.setMaxDate( p_value ); }
		},

		setMinDate: function( p_value ) {

			var binder = this.getBinder();
			var config = this.getConfig();

			if (p_value) { p_value = new Date( p_value.getFullYear(), p_value.getMonth(), p_value.getDate(), 0, 0, 0, 0); }
			config.minDate = p_value;
			config.viewer.calendar.minDate = p_value;
			if (binder) { binder.setMinDate( p_value ); }
		},

		_select: function() {

		    var selDate = this.m_viewer.getCalendar().getDates()[ this.m_viewer.getCalendar().selFirst ].getDate();
			var month;
			var date;

		    // What dates are selected?
			month = selDate.getMonth() + 1;
			date = selDate.getDate();
			if (this.getPreferEuro()) {

				selDate = ((date < 10) ? '0' : '') + date + "/" + ((month < 10) ? '0' : '') + month + "/" + selDate.getFullYear();

			} else {

				selDate = ((month < 10) ? '0' : '') + month + "/" + ((date < 10) ? '0' : '') + date + "/" + selDate.getFullYear();
			}

			// Populate the holder
			this.m_holder.drpwIgnoreFocus = true;
			this.m_holder.value = selDate;
			this.m_holder.focus();
			this.m_holder.select();
			gQuery( this.m_holder ).fireEvent( "change" );
			this.hide();
		},

		show: function( p_element ) {

			var config = this.getConfig();

			// Render the popup
			if (!this.m_rendered) { this.render (); }

	        // Don't do anything if we are the holder
	        if (p_element == this.m_holder) {

	            return;
	            // NOTREACHED
	        }

			// Hide elements that might interfere with the popup
			p_element = gQuery( p_element );
			var ieVersion = gQuery.ieVersion();
	        if (ieVersion > 0 && ieVersion < 7) { this.hideElements( "select", "hidden" ); }
			this.hideElements( "embed", "hidden" );
			this.hideElements( "object", "hidden" );

			// Show the viewer
			if (!this.m_shown) { this.m_viewer.show( config.monthsDisplayed ); }

			var index;
			var jwindow = gQuery( window );
			var pos = p_element.offset();
			var dim = { w: p_element.outerWidth(), h: p_element.outerHeight() };
	        var elDate = JLG.util.date.parse( p_element.val(), this.getPreferEuro() );
	        if (!pos) {

	            return;
	            // NOTREACHED
	        }

			// Clear variables
			this.m_hasFocus = false;

			// Determine where to place the popup
			pos.top += dim.h;
			if (config.left) { pos.left += config.left; }
			if (config.right) { pos.left = pos.left + dim.w + config.right; }
			this.m_popupEl.absolutize();
			this.m_popupEl.css({ top: String( pos.top ) + "px", left: String( pos.left ) + "px" }).show();
			this.m_holder = p_element[ 0 ];

	        // Highlight the new select
	        if (elDate) {

				// Update the date if necessary
				var date = this.m_viewer.getDate ();
	            var elMonth = new Date( elDate.getFullYear(), elDate.getMonth(), 1 );
				if (config.monthsDisplayed == 1) {

					this.m_viewer.setDate (elMonth);

				} else {

					var offset = (elMonth - date) / (1000 * 60 * 60 * 24);
					var prev = (offset > 0) ? false : true;
					if (Math.abs(offset) > 31) {

						if (!prev) {

							if (elMonth === 0) {

								elMonth.setMonth (11);
								elMonth.setFullYear (elMonth.getFullYear () - 1);

							} else {

								elMonth.setMonth (elMonth.getMonth () - 1);
							}
						}
						this.m_viewer.setDate (elMonth);
					}
				}

	            // Select the new date
				var calendar = this.m_viewer.getCalendar ();
				calendar._unselect_all ();
	            calendar.select (elDate, elDate);

	        } else {

				this.m_viewer.getCalendar ()._unselect_all ();
				if ((config.minDate && this.m_viewer.getDate () < config.minDate) || (config.maxDate && this.m_viewer.getDate () > config.maxDate)) {

					this.m_viewer.setDate (config.minDate ? config.minDate : config.maxDate);
				}
			}

			// Refresh the navbar
			this.onRefresh ();
		},

		unhighlight: function() {

			var calendar = this.getBinder().getCalendar();
			if (calendar && this.m_idxHighlight >= 0) {

				// Unhighlight the last date
				var highlight = calendar.getDates()[ this.m_idxHighlight ];
				highlight.getDateEl().removeClass( this.getConfig().highlightDateCssClass );
				this.m_idxHighlight = -1;
			}
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.date = JLG.date || {};
JLG.date.buildDateRangePicker = function( p_module ) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, {

		initialize: function( p_config ) {

			// Initialize
			this.m_config = gQuery.extend( { min_span: 0 }, p_config || {} );
			this.m_config.min_span *= 1000 * 60 * 60 * 24;
			this.setMaxDate( this.m_config.maxDate );
			this.setMinDate( this.m_config.minDate );

			// Determine the date format
			this.m_preferEuro = (jgLocale || {}).ukDateFormat ? true : false;

			// Create the popup window
			this.m_popup = new( this.getWindowModel() )( this.m_config );

			// Connect signal handlers
			this._connect();
		},

		getPreferEuro: function () {

			return this.m_preferEuro;
		},

		getEndDate: function () {

	        var date = JLG.util.date.parse (this.m_endEl.val(), this.getPreferEuro ());
			return (date && (!this.m_config.minDate || date >= this.m_config.minDate) && (!this.m_config.maxDate || date <= this.m_config.maxDate)) ? date : null;
		},

		getStartDate: function() {

	        var date = JLG.util.date.parse( this.m_startEl.val(), this.getPreferEuro() );
			return (date && (!this.m_config.minDate || date >= this.m_config.minDate) && (!this.m_config.maxDate || date <= this.m_config.maxDate)) ? date : null;
		},

		getWindow: function() { return this.m_popup; },

		getWindowModel: function() {

			if (!this.m_windowModel) {this.m_windowModel = JLG.date.buildDateRangePickerWindow (p_module);}
			return this.m_windowModel;
		},

		onBlur: function( p_event ) { this.m_popup.blur( p_event.target ); },

		onChangeCheckin: function() {

			var start = JLG.util.date.parse( this.m_startEl.val(), this.getPreferEuro() );
			var end = JLG.util.date.parse( this.m_endEl.val(), this.getPreferEuro() );
			var month;
			var date;

			// Do we need to replace the end value?
			if (start && (!end || end.getTime() < (start.getTime() + this.m_config.min_span))) {

				// Update the end date
				end = new Date();
				end.setTime( start.getTime() + this.m_config.min_span );
				month = end.getMonth() + 1;
				date = end.getDate();
				if (this.getPreferEuro()) {

					this.m_endEl.val( ((date < 10) ? '0' : '') + date + "/" + ((month < 10) ? '0' : '') + month + "/" + end.getFullYear() );

				} else {

					this.m_endEl.val( ((month < 10) ? '0' : '') + month + "/" + ((date < 10) ? '0' : '') + date + "/" + end.getFullYear() );
				}
			}
		},

		onFocus: function( p_event ) { 

			var el = p_event.target;
			if( !el.drpwIgnoreFocus ) { this.m_popup.show( el ); }
			el.drpwIgnoreFocus = false;
		},

		reset: function () {

			this._disconnect();
			this._connect();
		},

		setMaxDate: function (p_value) {

			this.m_config.maxDate = p_value ? new Date (p_value.getFullYear (), p_value.getMonth (), p_value.getDate (), 0, 0, 0, 0) : null;
			if (this.m_popup) { this.m_popup.setMaxDate (p_value); }
		},

		setMinDate: function( p_value ) {

			this.m_config.minDate = p_value ? new Date( p_value.getFullYear(), p_value.getMonth(), p_value.getDate(), 0, 0, 0, 0 ) : null;
			if (this.m_popup) { this.m_popup.setMinDate( p_value ); }
		},

		/******************************************************
		 * PRIVATE METHODS
		 ******************************************************/
		_connect: function() {

			// Find the elements
			this.m_startEl = gQuery( this.m_config.startEl );
			this.m_endEl = gQuery( this.m_config.endEl );
			this.m_startEl.bind( "blur", gQuery.bindFn( this.onBlur, this ) ).bind( "change", gQuery.bindFn( this.onChangeCheckin, this ) ).bind( "click focus", gQuery.bindFn( this.onFocus,  this ) );
			this.m_endEl.bind( "blur", gQuery.bindFn( this.onBlur, this ) ).bind( "click focus", gQuery.bindFn( this.onFocus, this ) );
		},

		_disconnect: function() {

			// Clear signal handlers
			this.m_startEl.unbind();
			this.m_endEl.unbind();
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.calendar = JLG.calendar || {};
JLG.calendar.extra = JLG.calendar.extra || {};
if( !JLG.calendar.extra.Bubble ) {

	JLG.calendar.extra.Bubble = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.extra.Bubble.prototype, {

		initialize: function( p_config ) {

		    // Member variables
			this._config = p_config || {};
		    this.locker = null;
		    this.idSignals = [];
		    this.bubble = {};
		    this.bubble.element = gQuery( this.getConfig ().el );
		    this.connector = {};
		    this.connector.element = gQuery( this.getConfig ().el + "-connector" );
		    this.content = gQuery( this.getConfig ().el + "-content" );

		    // Connect our event handler(s)
		    this.connect( "mousemove", this, this._on_mouse_move );
		},

		getConfig: function () {

			return this._config;
		},

		connect: function( p_event, p_object, p_method ) {

		    // Connect the bubble to events
			var pfn = gQuery.bindFn( p_method, p_object );
			this.idSignals.push( pfn );
			this.bubble.element.bind( p_event, pfn );
			this.connector.element.bind( p_event, pfn );
		},

		_destroy: function() {

		    // Remove the signals
		    while( this.idSignals.length ) { 

				var pfn = this.idSignals.pop();
				this.bubble.element.unbind( pfn );
				this.connector.element.unbind( pfn );
			}
		},

		hide: function( p_force ) {

		    // Don't hide if locked
		    if (!p_force && this.locker) {

				return false;
				// NOTREACHED
			}

		    // Don't hide if we happen to appear right under the mouse (firefox)
		    if (p_force || !this.timeShow || (new Date().getTime() - this.timeShow) > 150) {

				var overlay = gQuery( "#rex-lb-overlay" );

		        // Clear the show time
		        this.timeShow = null;

		        // Is the fader visible?
				if( overlay && overlay.css( "display" ) != "none" ) {

					// Hide the overlay
					this._initOverlayStyle( false );
					overlay.css({ opacity: this._overlayStyle.opacity, "-moz-opacity": this._overlayStyle[ "-moz-opacity" ], "-ktml-opacity": this._overlayStyle[ "-khtml-opacity "], filter: this._overlayStyle.filter });
					if (this.m_window) {

						JLG.popup.window().hide( this.m_window, { caller: this } );
						this.m_window = null;
					}
				}

		        // Hide the bubble
				this.bubble.element.hide();
				this.connector.element.hide();
		    }
		},

		lock: function (p_locker) {

		    // Lock the bubble if not already locked
		    if (!this.locker) { this.locker = p_locker; }
		},

		unlock: function (p_locker) {

		    if (this.locker && this.locker == p_locker) { this.locker = null; }
		},

		_on_mouse_move: function (p_event) {

			this.hide();
		},

		show: function( p_pos, p_dim, p_fade ) {

		    var bubDimReal;
			var bubDimWant;
		    var adjust = { h: 0, w: 0 };
		    var bubPos = { left: 0, top: 0 };
		    var temp;
		    var conDisplay = 'block';
			var jwindow = gQuery( window );
		    var screen = { w: jwindow.width(), h: jwindow.height() };
		    var scrollPos = { left: jwindow.scrollLeft(), top: jwindow.scrollTop() };
		    var reset = (p_pos && p_dim);

		    // Are we showing (after being hidden) or repositioning?
			this.bubble.element.absolutize();
			this.connector.element.absolutize();
		    if (reset) {

		        // Don't show if locked
		        if (this.locker) {

					return false;
					// NOTREACHED
				}

		        // Set element positions for initial measurement
				this.bubble.element.css({ top: "0px", left: "0px" });
				this.connector.element.css({ top: "0px", left: "0px" });

		        // How big is the connector, and where should it ideally go?
				this.connector.element.show();
				this.connector.dim = { w: this.connector.element.outerWidth(), h: this.connector.element.outerHeight() };
				this.connector.element.hide();
		        this.connector.pos = { left: Math.round( p_pos.left + (p_dim.w / 2) ), top: Math.round( p_pos.top + (p_dim.h / 2) - this.connector.dim.h ) };
				this.connector.element.css({ top: String( this.connector.pos.top ) + "px", left: String( this.connector.pos.left ) + "px" });
		    }

		    // How big is the bubble?
	        this.bubble.element.show();
		    bubDimWant = { w: this.bubble.element.outerWidth(), h: this.bubble.element.outerHeight() };
	        this.bubble.element.hide();

		    // Calculate the bubble position
		    bubPos.top = 25 + this.connector.pos.top - bubDimWant.h;
		    if ((this.connector.pos.left + this.connector.dim.w + 25 < screen.w) && bubPos.top > 0) {

		        // Bubble with connector
		        bubPos.left = ((bubDimWant.w - this.connector.dim.w) / 2);
		        if (this.connector.pos.left > bubPos.left) {

					bubPos.left = this.connector.pos.left - bubPos.left;

				} else {

					bubPos.left = 0;
				}
		    } else {

		        // Position the bubble without a connector
		        conDisplay = 'none';
		        bubPos.left = Math.round( this.connector.pos.left - bubDimWant.w / 2 );
		        bubPos.top = Math.round( this.connector.pos.top + this.connector.dim.h - bubDimWant.h );
		    }

		    // Make sure we are in scroll position
		    if (bubPos.top < scrollPos.top) {

		        // Get inside the viewable area
		        bubPos.top = scrollPos.top;
		        conDisplay = 'none';
		    }
		    if (bubPos.left < scrollPos.left) {

		        // Get inside the viewable area
		        bubPos.left = scrollPos.left;
		        conDisplay = 'none';
		    }

		    // Are we setting the bubble off the screen?
		    temp = screen.w - bubPos.left - bubDimWant.w;
		    if (temp <= 0) { bubPos.left += temp - 1; }
		    if (bubPos.left <= 0) { bubPos.left = 1; }
		    if (bubPos.top <= 0) { bubPos.top = 1; }

		    // Show the bubble
			this.bubble.element.css({ top: String( bubPos.top ) + "px", left: String( bubPos.left ) + "px" }).show();
			bubDimReal = { w: this.bubble.element.outerWidth(), h: this.bubble.element.outerHeight() };

			// How big is the bubble now?
			if (bubDimReal.w != bubDimWant.w || bubDimReal.h != bubDimWant.h) {

				// Calculate the adjustments to be made
				adjust.w = bubDimWant.w - bubDimReal.w;
				adjust.h = bubDimWant.h - bubDimReal.h;

				if (adjust.w > 0) {

					// Move the bubble over, but not off screen
					bubPos.left -= adjust.w;
					if (bubPos.left <= 0) { bubPos.left = 1; }
				}
				if (adjust.h > 0) { bubPos.top += adjust.h; }

				// Should the connector be displayed?
				if ((bubPos.left + bubDimWant.w) < (this.connector.pos.left + this.connector.dim.w + 25) && conDisplay != "none") {

					// Not showing the connector
					bubPos.top += this.connector.dim.h - 25;
					conDisplay = "none";
				}

				// Check the size again
				this.bubble.element.css({ left: String( bubPos.left ) + "px", top: String( bubPos.top ) + "px" });
			    bubDimReal = { w: this.bubble.element.outerWidth(), h: this.bubble.element.outerHeight() };
			}

			// Show the connector
			this.connector.element.css({ display: conDisplay });

		    // Fade the background
		    if (p_fade && reset) {

				// Don't block as much with the overlay
				this._initOverlayStyle (true);
				gQuery( "#rex-lb-overlay" ).css({ "-moz-opacity": 0.1, opacity: 0.1, filter: "alpha(opacity=10);" });
				this.m_window = JLG.popup.window().show({

					caller: this,
					innerHTML: " ",
					layout: false,
					name: "jlg-calendar-bubble",
					noScroll: true
				});
			}

		    // Make sure the bubble isn't hidden immediately by mouseover
		    this.timeShow = new Date().getTime();
		},

		/**
		 * Private methods
		 */
		_initOverlayStyle: function (p_reset) {

			if (p_reset || !this._overlayStyle) {

				var overlay = gQuery( "#rex-lb-overlay" );
				this._overlayStyle = { opacity: overlay.css( "opacity" ), "-moz-opacity": overlay.css( "-moz-opacity" ), "-khtml-opacity": overlay.css( "-khtml-opacity" ), filter: overlay.css( "filter" ) };
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Cell ) {

	JLG.calendar.core.Cell = function( p_month ) { this.initialize( p_month ); };
	gQuery.extend( JLG.calendar.core.Cell.prototype, {

		initialize: function( p_month ) {

			this.m_month = p_month;
			this.index = -1;
			this.selected = false;
		},

		create: function( p_day, p_parent, p_class ) {

		    var results;
		    var month = this.getMonth();
		    var calendar = month.getCalendar();

			// Member variables
			this.index = calendar.getDates().length;
			this.m_date = new Date( month.year, month.month - 1, p_day, 0, 0, 0, 0 );

			// Add us to the list of dates
			calendar.addDate (this);

			// Create the cell nodes
		    results = month.buildDay( p_parent, p_day, p_class );
		    this.m_cellEl = results.cell;
		    this.m_dateEl = results.date;
			this.bgColor = this.getDateEl().css( "background-color" );
			this.getCellEl()[ 0 ].jlgCalendarCell = this;
		},

		getDate: function() {

			return this.m_date;
		},

		getDateEl: function() {

			return this.m_dateEl;
		},

		getMonth: function() {

			return this.m_month;
		},

		getCellEl: function() {

			return this.m_cellEl;
		},

		_destroy: function() {

		    // Clear the DOM
		    if (this.getDateEl()) { this.getDateEl().remove(); }
		    if (this.getCellEl () && this.getCellEl() != this.getDateEl()) { this.getCellEl().remove(); }

		    // Clear pointers
		    this.m_cellEl = null;
		    this.m_dateEl = null;
		    this.m_month = null;
		},

		onMouseDown: function( p_src ) {

	        var calendar = this.getMonth().getCalendar();

		    // Reset the selections
		    calendar._unselect_all ();
			calendar.selFirst = this.index;
			calendar.selLast = this.index;
			calendar.isMouseDown = true;
			this._select ();
			return true;
		},

		_on_mouse_enter: function (p_src) {

			var index;
			var month = this.getMonth ();
			var calendar = month.getCalendar ();
			var dates = calendar.getDates ();
			var len;

			do {

				if (calendar.selFirst < 0 || !calendar.isMouseDown) {

				    break;
				}
				if (this.index > calendar.selFirst) {

					for (index = calendar.selLast, len = calendar.selFirst; index < len; index++) {

					    dates [index]._unselect ();
					}
					for (index = this.index + 1, len = calendar.selLast; index <= len; index++) {

					    dates [index]._unselect ();
					}
					for (index = calendar.selLast + 1, len = this.index; index <= len; index++) {

					    dates [index]._select ();
					}
				} else {

					for (index = calendar.selFirst + 1, len = calendar.selLast; index <= len; index++) {

					    dates [index]._unselect ();
					}
					for (index = calendar.selLast, len = this.index; index < len; index++) {

					    dates [index]._unselect ();
					}
					for (index = this.index, len = calendar.selFirst; index < len; index++) {

					    dates [index]._select ();
					}
				}
				calendar.selLast = this.index;
			}
			while (0);
			return true;
		},

		_select: function() {

			if (!this.selected) {

			    this.selected = true;
				this.getDateEl().addClass( this.getMonth().getCalendar().getConfig().cellSelectionClass );
			}
		},

		_unselect: function() {

			if (this.selected) {

			    this.selected = false;
				this.getDateEl().removeClass( this.getMonth().getCalendar().getConfig().cellSelectionClass );
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Month ) {

	JLG.calendar.core.Month = function( p_calendar, p_year, p_month, p_div, p_dim, p_options ) { this.initialize( p_calendar, p_year, p_month, p_div, p_dim, p_options ); };
	gQuery.extend( JLG.calendar.core.Month.prototype, {

		initialize: function (p_calendar, p_year, p_month, p_div, p_dim, p_options) {

	    	// Initialize member variables
    		this.m_cellClass = JLG.calendar.core.Cell;
	    	this.m_calendar = p_calendar;
			this.div = p_div;
			this.year = p_year;
			this.month = p_month;
			this.days = [];
			this.id = "month" + p_year + p_month;
			this.options = p_options || {};

	        // Add extra methods
	        this.getNameByIndex = JLG.calendar.core.Month.getNameByIndex;

			// Create the table
			this._create_table (p_year, p_month, p_dim);
		},

		getCalendar: function () {return this.m_calendar;},

		/**
		 * Private methods
		 */
		_alloc_cell: function () {

		    return new this.m_cellClass (this);
		},

		_compute_header_height: function() {

			var display;
			var dim;

			// How big is the header?
			var table = gQuery( document.createElement( "table" ) ).attr({ border: 0, cellPadding: 0, cellSpacing: 0, height: 0, width: 0 }).css({ width: "auto", height: "auto" });
			this._create_table_header( table, 1900, 1 );
			table.appendTo( this.getCalendar ().div );
			display = this.getCalendar().css( 'display' );
			this.getCalendar().div.show();
			dim = { w: table.outerWidth(), h: table.outerHeight() };
			this.getCalendar().div.css( 'display', display );
			table.remove();

			// Return the height
			return dim.h;
		},

		buildDay: function( p_parent, p_day, p_class ) {

		    var row;
		    var cell = null;
		    var date = null;
			var table;

		    // Determine the date alignment
		    if (this.getCalendar().centerDate) {

		        // Create cells with centered information
				cell = p_parent;
				date = p_parent;
		    	p_parent.html( p_day );
				cell.css( 'vertical-align', "middle" );
				if (p_class) { p_parent.addClass( p_class ); }

		    } else {

		    	// Create the nodes
				cell = p_parent;
		        date = gQuery( document.createElement( "div" ) );

				// Update the node contents
		        date.html( p_day + "&nbsp;" );
				date.appendTo( cell );
				cell.css( 'vertical-align', "top" );
		        if (p_class) {

		            // Set the appropriate class
					cell.addClass( p_class );
					date.addClass( p_class );
		        }
		    }

			// Return the elements
			return {cell: cell, date: date};
		},

		_create_table: function( p_year, p_month, p_dim ) {

			// Initialize method variables
			var ieVersion = gQuery.ieVersion();

		    // Create the table
			var table = gQuery( document.createElement( "table" ) ).addClass( this.getCalendar ().getConfig ().monthTableCssClass ).attr({ cellPadding: 0, cellSpacing: 0, id: this.id }).css({ height: p_dim.h + "px", width: p_dim.w + "px" });

			// Create the table body
			var tBody = this._create_table_body( table, p_year, p_month );

			// Create the table header
			var tHeader = this._create_table_header( table, p_year, p_month );

			// Update the tbody class
			if (ieVersion < 0) { gQuery( table[ 0 ].tBodies[ 0 ] ).addClass( this.getCalendar ().getConfig ().monthBodyCssClass );}

			// Need to calculate the header and subtract from the total to get the IE height
			if (ieVersion > 0) {

				// Compute the header size
				table.appendTo( "body" );
				var dimHeader = { w: tHeader.outerWidth(), h: tHeader.outerHeight() };
				tBody.css( "height", p_dim.h - dimHeader.h );
				table.remove();

			} else {

				tBody.css( "height", p_dim.h );
			}

			// Append the table to the div
			table.appendTo( this.div );
		},

		_create_table_body: function( p_table, p_year, p_month ) {

			var cell;
			var data;
			var	day = 1;
			var index;
			var	jDate = new Date( p_year, p_month - 1, 1 );
			var row;
			var skip;
			var tbody;
			var maxDays = this._count_days_in_month( p_year, p_month );
			var maxDaysLast;
			var calendar = this.getCalendar();
			var calCfg = calendar.getConfig();
			var paddingLeftClass = calCfg.monthPaddingLeftCssClass;
			var paddingRightClass = calCfg.monthPaddingRightCssClass;
			var hideOther = this.options.hideOtherDays;
			var days = this.days;

		    // Calculate the number of days in the last month
		    if (p_month == 1) {

		        maxDaysLast = this._count_days_in_month( p_year - 1, 12 );

		    } else {

		        maxDaysLast = this._count_days_in_month( p_year, p_month - 1 );
		    }

		    // Create the table body
			if (gQuery.ieVersion() >= 6) {

				// Use two table for IE
				tbody = gQuery( document.createElement( "table" ) ).addClass( calCfg.monthBodyCssClass ).attr({ id: this.id, cellPadding: 0, cellSpacing: 0, width: "100%" }).css( "width", "100%" );
				cell = gQuery( p_table[ 0 ].insertRow( -1 ).insertCell( -1 ) ).append( tbody );

			} else {

				// Just use one table for everyone else
				tbody = p_table;
			}

		    // What is the first day of the week?
		    skip = jDate.getDay() - calendar.dowFirst;
		    if (skip < 0) { skip += 7; }

			// Cells from the previous month
			row = tbody[ 0 ].insertRow( -1 );
			gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
			for (index = skip; index > 0; index--) {

		    	// Create a blank cell
				cell = gQuery( row.insertCell( -1 ) );
				day = hideOther ? "&nbsp;" : (1 + maxDaysLast - index);
		        this.buildDay( cell, day, "jlg-cal-empty" );
		    }

		    // Current month cells
			day = 1;
	 	    do {

				// Beginning of another week?
		    	if (day > 1) {

			    	// Start a new week
					gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );
					row = tbody[ 0 ].insertRow( -1 );
					gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
				}

		    	// Create the days of the week
		    	for (index = skip + 1; index <= 7 && day <= maxDays; index++, day++) {

		            // Create the next day
					data = gQuery( row.insertCell( -1 ) );
					cell = this._alloc_cell();
					cell.create( day, data );
					days.push( cell );
				}
				skip = 0;
			}
			while (day <= maxDays);

			// Cells from the next month
			for (skip = 1; index <= 7; index++, skip++) {

				// Create a blank cell
				cell = gQuery( row.insertCell( -1 ) );
				day = hideOther ? "&nbsp;" : skip;
		        this.buildDay( cell, day, "jlg-cal-empty" );
			}
			gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );

			// Return the body
			return tbody;
		},

		_create_table_header: function( p_table, p_year, p_month ) {

			var cell;
		    var dow = this.getCalendar()._days_of_week();
			var index;
			var len;
			var pos = this.getCalendar().dowFirst;
			var row;
			var thead;
			var calendar = this.getCalendar();
			var calCfg = calendar.getConfig();
			var paddingLeftClass = calCfg.monthPaddingLeftCssClass;
			var paddingRightClass = calCfg.monthPaddingRightCssClass;

		    // Create the table header
			if (gQuery.ieVersion() >= 6) {

				// Use two table for IE
				thead = gQuery( document.createElement( "table" ) ).addClass( calCfg.monthHeaderCssClass ).attr({ cellPadding: 0, cellSpacing: 0, width: "100%" }).css( "width", "100%" );
				cell = gQuery( p_table[ 0 ].createTHead().insertRow( -1 ).insertCell( -1 ) ).append( thead );

			} else {

				// Just use one table for everyone else
				thead = gQuery( p_table[ 0 ].createTHead() ).addClass( calCfg.monthHeaderCssClass );
			}

		    // Add the Month header
		    row = thead[ 0 ].insertRow( -1 );
			gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
		    cell = gQuery( row.insertCell( -1 ) ).attr( "colSpan", 7 ).addClass( calCfg.monthNameCssClass ).html( this.getNameByIndex( p_month - 1 ) + ' ' + p_year );
			gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );

		    // Create the headers
		    row = thead[ 0 ].insertRow( -1 );
			gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
		    for (index = 0, len = dow.length; index < len; index++) {

				// Insert the next weekday name
		        cell = gQuery( row.insertCell( -1 ) ).addClass( calCfg.monthDayNameCssClass ).html( dow[ pos++ ] );
				if (pos > 6) { pos = 0; }
		    }
			gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );

			// Return the header
			return thead;
		},

		_count_days_in_month: function (p_year, p_month) {

			var m = [31,28,31,30,31,30,31,31,30,31,30,31];
			if (p_month != 2) {return m[p_month - 1];}
			if (p_year%4 !== 0) {return m[1];}
			if (p_year%100 === 0 && p_year%400 !== 0) {return m[1];}
			return m[1] + 1;
		},

		_destroy: function() {

		    // Clear the cells
			if (gQuery.ieVersion() < 0) {

				var index;
				var len;
				var days = this.days;
				var pfnPopDay = days.pop;
				for (index = 0, len = days.length; index < len; index++) { pfnPopDay.call (days)._destroy (); }
			}

		    // Clear the DOM
		    if (this.div) { this.div.remove(); }

		    // Clear pointers
		    this.m_calendar = null;
		    this.div = null;
		}
	} );
}

/**
 * Add class methods
 */
JLG.calendar.core.Month.getNameByIndex = function (p_index) {

	return JLG.calendar.core.Month._monthNames [p_index];
};

JLG.calendar.core.Month._monthNames = [

	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December"
];


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Calendar ) {

	JLG.calendar.core.Calendar = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.core.Calendar.prototype, {

		initialize: function( p_config ) {

		   	// Member variables
			this.m_daysOfWeekLong = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
			this.m_daysOfWeekShort = ["S", "M", "T", "W", "T", "F", "S"];
		    this.brTerm = null;
		    this.m_dates = null;
		    this.daysOfWeekArray = null;
		    this.div = null;
		    this.firstDate = null;
		    this.id = null;
		    this.m_months = null;
		    this.selFirst = -1;
		    this.selLast = -1;
		    this.sizeMonth = 0;
			this.m_config = gQuery.extend({

				cellSelectionClass: "jlg-cell-selected",
				cssClass: "jlg-cal-panel",
				monthBodyCssClass: "jlg-cal-month-body",
				monthDivCssClass: "jlg-cal-month-div",
				monthHeaderCssClass: "jlg-cal-month-header",
				monthNameCssClass: "jlg-cal-month-name",
				monthDayNameCssClass: "jlg-cal-month-day-name",
				monthTableCssClass: "jlg-cal-month-table",
				monthPaddingLeftCssClass: "jlg-calendar-padding-left",
				monthPaddingRightCssClass: "jlg-calendar-padding-right"

			}, p_config || {});
		},

	    addDate: function (p_date) {

	        this.m_dates.push (p_date);
	    },

		create: function( p_date, p_dow, p_parent ) {

		    // Member variables
		    this.m_dates = [];
		    this.dowFirst = p_dow;
		    this.firstDate = new Date( p_date.getFullYear(), p_date.getMonth(), 1, 0, 0, 0, 0 );
		    this.id = "calendar" + this._nextId();
		    this.m_months = [];

		    // Create the div
			var divHtml = "<div class='" + this.m_config.cssClass + "' style='display: none;'  id='" + this.id + "'></div>";
		    if( p_parent ) {

				p_parent.append( divHtml );

		    } else {

		        document.write( divHtml );
		    }
			this.div = p_parent.find( "#" + this.id );

		    // Disable text selection inside the div
			this.div[ 0 ].onselectstart = function() { return false; };
			this.div[ 0 ].unselectable = "on";
			this.div.css( '-moz-user-select', "none" );
 
		    // Connect event handlers
			this.div.bind( "mousedown", gQuery.bindFn( this.onMouseDown, this ) ).bind( "mouseover", gQuery.bindFn( this._on_mouse_enter, this ) ).bind( "mouseup", gQuery.bindFn( this._on_mouse_up, this ) );
		},

		empty: function () {

	        var index;
	        var len;
	        var dates = this.m_dates || {};
	        var months = this.m_months || {};
	        var pfnPopDate = dates.pop;
	        var pfnPopMonth = months.pop;

		    // Clear the dates array
		    for (index = 0, len = (dates.length || 0); index < len; index++) { pfnPopDate.call (dates); }

		    // Destroy the month objects
		    for (index = 0, len = (months.length || 0); index < len; index++) { pfnPopMonth.call (months)._destroy (); }

		    // clear the DOM
		    if (this.brTerm) { this.brTerm.remove(); }

		    // Clear members
		    this.brTerm = null;
		    this.daysOfWeekArray = null;
		    this.selFirst = -1;
		    this.selLast = -1;
		},

		getConfig: function () {

			return this.m_config;
		},

	    getDates: function () {

	        return this.m_dates;
	    },

		getMonths: function () {

			return this.m_months;
		},

	    select: function (p_start, p_end) {

	        if ((p_start || p_end) && this.firstDate) {

	            var offStart = Math.max (Math.floor (((p_start || p_end) - this.firstDate) / 86400000), 0);
	            var offEnd = Math.min (Math.floor (((p_end || p_start) - this.firstDate) / 86400000), this.getDates ().length - 1);
	            var dates = this.getDates ();
	            var index;
	            var len = dates.length;

	            // Make sure the dates are visible
	            if (offEnd >= 0 && offStart < len) {

	                // Select all of the dates
	                for (index = offStart; index <= offEnd; index++) {

	                    dates [index]._select ();
	                }

	                // Store the selection
	                this.selFirst = offStart;
	                this.selLast = offEnd;
	            }
	        }
	    },

		show: function (p_count, p_dim, p_max) {

		    // All-in-one show
		    this._before_show (p_count, p_dim, p_max);
		    this._do_show ();
		},

	    /**
	     * Private methods
	     */
		_alloc_month: function (p_year, p_month, p_div, p_dim, p_config) {

			return new this.m_monthClass (this, p_year, p_month, p_div, p_dim, p_config);
		},

		_before_show: function (p_want, p_dim, p_max) {

			var results = this._compute_size (p_want, p_dim, null, p_max);

		    // How many months did we end up with?
		    this.centerDate = results.center || (this.m_config.centerDate ? true : false);
		    this.countMonths = results.months;
		    this.sizeMonth = results.month_size;
		    this.maxDim.w = results.total_width;
			this.maxDim.h = results.total_height;
	    },

		_compute_size: function (p_want, p_dim, p_min, p_max) {

		    var cols;
			var jwindow = gQuery( window );
		    var dimView = { w: jwindow.width(), h: jwindow.height() };
		    var height;
		    var margins = this._month_margins ();
		    var rows;
		    var size;
			var orig_want = p_want;
			var twant = Math.abs (p_want);
			var width;
			var minSize = p_min || 165;
			p_want = twant;

			// Adjust margins
			if ( margins.w < 0 ) { margins.w = 0; }
			if ( margins.h < 0 ) { margins.h = 0; }

		    // What is our max width?
		    this.maxDim = gQuery.extend( {}, p_dim );
		    if ( p_dim.h <= 0 ) { p_dim.h = dimView.h; }
		    if ( p_dim.w <= 0 ) { p_dim.w = dimView.w; }

		    do {

		        // Determine the largest allowable calendar size
		        if (p_want == 1) {

		            // Easy for only one calendar
		            cols = 1;
					size = ( p_dim.w < p_dim.h ) ? ( p_dim.w - margins.w ) : ( p_dim.h - margins.h );

		        } else {

		            // How many calendars will fit?
		            for (cols = 1; cols <= p_want; cols++) {

						// How many rows?
		                rows = Math.ceil (p_want / cols);

		                // Calculate the height with cols number of items
		                width = Math.min (Math.floor ((p_dim.w - (cols * margins.w)) / cols), p_dim.h - margins.h);
		                height = (width + margins.h) * rows;
		                if (height < p_dim.h) {

							// Calendar fits without adjustment
							size = width;
							break;
							// NOTREACHED
						}

						// How big should it be for the rows to fit?
		                height = Math.min (Math.floor ((p_dim.h - (rows * margins.h)) / rows), p_dim.w - margins.w);
		                width = (height + margins.w)  * cols;
						if ((width + height + margins.w) > p_dim.w || cols == p_want) {

							// Found our best bet
							size = height;
							break;
							// NOTREACHED
						}
		            }
		        }

		        // The calendars should never be smaller than the min size
		        if (size < minSize) {

		            // Adjust the number of columns as well
		            size = minSize;
		            cols = Math.floor (p_dim.w / (size + margins.w));
	                rows = Math.ceil (p_want / cols);
		        }

		        // Limit rows if necessary
		        if (this.maxDim.h > 0) {

		            // Enough calendars to fill the space
					twant = Math.floor (this.maxDim.h / (size + margins.h)) * cols;

		        } else {

		            // Can we keep adding more?
		            if (p_max) {

		                // Can we fit more calendars now that we know the max size?
		                for (; p_max < 0 || twant < p_max; twant++) {

		                    // Calculate the height with index number of items
		                    rows = Math.ceil (twant / cols);
		                    height = (size * rows) + ((rows - 1) * margins.h);
		                    if (height > p_dim.h) {

		                        break;
		                    }
		                }

		                // Go back by 1
		                --twant;
		            }

		            // Even out based on the number of columns
		            twant = Math.ceil (twant / cols) * cols;
		        }

				// What number to use?
				if (twant < p_want && orig_want > 0) {

					// Going to have to scroll
					size = minSize;
		            cols = Math.floor (p_dim.w / (size + margins.w));
	                rows = Math.ceil (p_want / cols);
					p_want = cols * rows;

				} else {

					// Reset in case we added some
					p_want = twant;
		            cols = Math.floor (p_dim.w / (size + margins.w));
					if (cols > p_want) {

						cols = p_want;
					}
	                rows = Math.ceil (p_want / cols);
				}
		    }
		    while (0);

	        var div;
	        var month;
	        var dim = { w: size - margins.w, h: size - margins.h - (gQuery.ieVersion() > 0 ? 15 : 0) };

			// Move to the next array
			div = gQuery( document.createElement( "div" ) ).addClass( this.m_config.monthDivCssClass ).appendTo( "body" );
			month = this._alloc_month (2008, 11, div, dim, this.m_config.month || {});
			dim = { w: div.outerWidth(), h: div.outerHeight() };
			div.remove();
			if ((dim.w > size || dim.h > size) && (!p_min || gQuery.ieVersion() < 0)) {

				return this._compute_size (p_want, p_dim, Math.max(dim.w, dim.h), p_max);
			}

			// Return the results
			return {

				center: (size < 250),
				months: p_want,
				month_size: size,
				total_width: (size + margins.w) * cols,
				total_height: (size + margins.h) * rows
			};
		},

		_days_of_week: function () {

		    // Return the appropriate list
		    if (!this.daysOfWeekArray) {

		        var div;
		        var month;
		        var dim = { w: this.sizeMonth, h: this.sizeMonth };
			    var dimPos = { x: 0, y: 0 };

		        // Iterate through the arrays
		        this.daysOfWeekArray = this.m_daysOfWeekLong;
		        while (this.daysOfWeekArray != this.m_daysOfWeekShort) {

		            // Move to the next array
		            div = gQuery( document.createElement( "div" ) ).addClass( this.m_config.monthDivCssClass );
		            month = this._alloc_month (1900, 1, div, dim, this.m_config.month || {});
					div.appendTo( "body" ).css({ top: dimPos.y, left: dimPos.x });
		            dim = { w: div.outerWidth(), h: div.outerHeight() };
		            div.remove();

		            // Done?
		            if (dim.w <= this.sizeMonth) {

		                break;
		            }
		            else if (this.daysOfWeekArray == this.m_daysOfWeekLong) {

		                this.daysOfWeekArray = this.m_daysOfWeekShort;
		            }
		        }
		    }

		    // Return the array of days
		    return this.daysOfWeekArray;
		},

		_destroy: function () {

	        var index;
	        var len;
	        var dates = this.m_dates || {};
	        var months = this.m_months || {};
	        var pfnPopDate = dates.pop;
	        var pfnPopMonth = months.pop;

		    // Clear the dates array
		    for (index = 0, len = (dates.length || 0); index < len; index++) { pfnPopDate.call (dates); }

		    // Destroy the month objects
		    for (index = 0, len = (months.length || 0); index < len; index++) { pfnPopMonth.call (months)._destroy (); }

		    // Disconnect signals
		    if (this.div) { this.div.unbind(); }

		    // Clear the DOM
		    if (this.div) { this.div.remove(); }
		    if (this.brTerm) { this.brTerm.remove(); }

		    // Clear pointer
		    this.brTerm = null;
		    this.daysOfWeekArray = null;
		    this.div = null;
		},

		_do_show: function () {

			var year;
			var index;
			var obj;
			var node;
			var month;
			var monthCount = this.countMonths;
			var months = this.m_months;
			var dim = { w: this.sizeMonth, h: this.sizeMonth };
			var divClass = this.m_config.monthDivCssClass;
			var pfnPushMonth = months.push;

		    // Determine the days of week to be used
		    this.daysOfWeekArray = null;
		    this._days_of_week();
		    obj = this.daysOfWeekArray;
		    this.empty();
		    this.daysOfWeekArray = obj;

		    // Create the months
		    for (index = 0, year = this.firstDate.getFullYear(), month = this.firstDate.getMonth() + 1; index < monthCount; index++, month++) {

				node = gQuery( document.createElement( "div" ) ).addClass( divClass ).attr({ height: dim.h, width: dim.w }).css({ width: String( dim.w ) + "px", height: "" + String( dim.h ) + "px" });
		        obj = this._alloc_month( year, month, node, dim, this.m_config.month || {} );
		        pfnPushMonth.call( months, obj );
				node.appendTo( this.div );
		        if (month == 12) {

		            // Round to next year
		            month = 0;
		            year++;
		        }
		    }

		    // Set the container width & height
		    this.brTerm = gQuery( document.createElement( "br" ) );
			this.div.css({ width: String( this.maxDim.w ) + "px", height: String( this.maxDim.h ) + "px" });
			this.brTerm.appendTo( this.div );

			// Show the div
			this.div.show();
		},

		_month_margins: function () {

		    var month;
		    var margins;

		    // Calculate the margins of a month object
		    month = gQuery( document.createElement( "div" ) ).addClass( this.m_config.monthDivCssClass ).attr({ height: "1px", width: "1px" }).css({ height: "1px", width: "1px" }).appendTo( "body" );
		    margins = { w: month.outerWidth(), h: month.outerHeight() };
			month.remove();
		    margins.w--;
		    margins.h--;

		    // Return the margins
		    return margins;
		},

		_on_mouse_up: function (p_event) {

			this.isMouseDown = false;
		},

		_on_mouse_enter: function( p_event ) {

			var jlgCell = p_event.target.jlgCalendarCell;
			if( jlgCell ) { jlgCell._on_mouse_enter( p_event ); }
		},

		onMouseDown: function( p_event ) {

			var jlgCell = p_event.target.jlgCalendarCell;
			if( jlgCell ) { jlgCell.onMouseDown( p_event ); }
		},

		_unselect_all: function () {

			var	index;
			var dates = this.getDates ();
			var len;

			// Unselect existing selections
			if (this.selFirst >= 0 && this.selLast >= 0) {

				for (index = this.selFirst, len = this.selLast; index <= len; index++) { dates [index]._unselect (); }
				for (index = this.selLast, len = this.selFirst; index <= len; index++) { dates [index]._unselect (); }
			}

		    // Nothing selected
			this.selFirst = -1;
			this.selLast = -1;
		},

		/**
		 * Private methods
		 */
		_nextId: gQuery.counter()
	} );
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Viewer ) {

	JLG.calendar.core.Viewer = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.core.Viewer.prototype, {

		initialize: function( p_config ) {

			this.m_asyncRequestCount = 0;
			this.m_asyncShowCalendar = false;
			var config = p_config || {};
			var calConfig = gQuery.extend( { cssClass: "jlg-calendar-all" }, config.calendar || {} );

			// Delete modified objects
			delete config.calendar;

	        // Initialize members
	        this.m_config = gQuery.extend( { calendar: calConfig, cssClass: "jlg-calendar-viewer", weekStartsOn: 0 }, config );

	        // Grab the div elements
	        this.m_date = new Date();
			if (this.getConfig().href_base) {

				this.urlAsync = this.getConfig().href_base + this.getConfig().asyncUrl;

			} else {

				this.urlAsync = this.getConfig().asyncUrl;
			}
		},

		getCalendar: function () {

			return this.m_calendar;
		},

		getConfig: function () {

			return this.m_config;
		},

		getDate: function () {

			return this.m_date;
		},

		getProperty: function () {

			return this.m_config.propertyId;
		},

		hasOutstandingRequest: function () {

			return (this.m_asyncRequestCount > 0);
		},

		render: function( p_config ) {

			// Make sure we have a config
			p_config = p_config || {};

	        // Container div
			var cssViewer = this.getConfig().cssClass;
			this.container = gQuery( document.createElement( "div" ) );
			if( cssViewer ) { this.container.addClass( cssViewer ); }

			// All inclusive calendar
			var cssCalendar = (this.getConfig().calendar || {}).cssClass;
	        this.div = gQuery( document.createElement( "div" ) );
			if( cssCalendar ) { this.div.addClass( cssCalendar ); }
			this.div.appendTo( this.container );

			// Break floats
			var br = gQuery( document.createElement( "br" ) ).css( "clear", "both" ).appendTo( this.container );

			// Append to the body
			this.container.appendTo( gQuery( p_config.el || this.getConfig().el || "body" ) );
		},

		show: function (p_count) {

		    var dimDiv;
		    var dimView;
			var index;
			var height;
			var width;
			var jwindow = gQuery( window );

		    // Store the count
		    this.count = p_count || this.getConfig ().monthCount || 1;

		    // Destroy the calendars
		    if (this.getCalendar ()) {

				this.getCalendar ()._destroy ();
				this.m_calendar = null;
			}

		    // Reset calendar variables
	        this.div.show();

		    // Calculate dimensions
		    dimDiv = { w: this.div.innerWidth(), h: this.div.innerHeight() };
		    dimView = { w: jwindow.width(), h: jwindow.height() };

			// Calculate the height
			height = this.getConfig().height;
		    if (height) {

				// Did we get passed a string?
				if (typeof(height) == "string") {

					// Is this a percentage or just a straight number?
					index = height.indexOf ('%');
					if (index >= 0)
						{height = parseInt (dimView.h * (parseInt (height.slice (0, index), 10) / 100), 10);}
					else
						{height = parseInt (height, 10);}
				}

				// Calculate the height
				dimDiv.h = (height < 0) ? dimView.h + height : height;

			} else {

				// Height is negotiable
				dimDiv.h = -1;
			}

			// Calculate the width
			width = this.getConfig ().width;
		    if (width) {

				// Did we get passed a string?
				if (typeof(width) == "string") {

					// Is this a percentage or just a straight number?
					index = width.indexOf ('%');
					if (index >= 0) {
					
						width = parseInt (dimView.w * (parseInt (width.slice (0, index), 10) / 100), 10);

					} else {

						width = parseInt (width, 10);
					}
				}
				dimDiv.w = (width < 0) ? dimView.w + width : width;

		    } else {

				// Width is negotiable
				// dimDiv.w = -1;
			}

		    // Create the calendar
		    this.m_calendar = this._alloc_calendar (this.getConfig ().calendar || {});
		    this.getCalendar ().create (this.m_date, this.getConfig ().weekStartsOn, this.div);
		    this.getCalendar ()._before_show (this.count, dimDiv, (this.count == 1) ? 1 : -1);
		    if (this._beforeShowCalendar ()) {

		        // Show now
		        this.m_asyncShowCalendar = false;
		        this.getCalendar ()._do_show ();
		        this._afterShowCalendar ();

		    } else {

		        // Show when the async call completes
		        this.m_asyncShowCalendar = true;
		    }
		},

		setMonthCount: function (p_count) {

		    // Change properties and refresh calendars
		    this.count = p_count;
		    this.show (this.count);
		},

		setDate: function (p_date, p_options) {

		    // Change properties and refresh calendars
			p_options = p_options || {};
		    this.m_date = p_date;
		    this.show (p_options.count || this.count);
		},

		setProperty: function (p_property, p_config) {

		    this.m_config.propertyId = p_property;
		    this.show ((p_config || {}).monthCount || this.count);
		},

		/**
		 * Private methods
		 */
		_adjust_date: function (p_date, p_offset)
		{
		    // Adjust the calendar date offset
		    if (p_offset > 0 && p_date.getMonth () == 11)
		    {
		        // Rollover to the next year
		        p_date.setMonth (0);
		        p_date.setYear (p_date.getFullYear () + 1);
		    }
		    else if (p_offset < 0 && p_date.getMonth () === 0)
		    {
		        // Rollover to the previous year
		        p_date.setMonth (11);
		        p_date.setYear (p_date.getFullYear () - 1);
		    }
		    else
		    {
		        // Roll to the next/prev month
		        p_date.setMonth (p_date.getMonth () + ((p_offset > 0) ? 1 : -1));
		    }

		    // Return the date
		    return p_date;
		},

		_afterShowCalendar: function () {
		},

		_alloc_calendar: function( p_config ) {

			return new JLG.calendar.core.Calendar( gQuery.extend( { viewer: this }, p_config || {} ) );
		},

		_asyncShow: function() {

		    if (this.m_asyncShowCalendar) {

		        // Clear the flags and show
		        this.m_asyncShowCalendar = false;
		        this.getCalendar()._do_show ();
		        this._afterShowCalendar();
		    }
		},

		_afterAsyncUpdate: function() {

			// Unlock and show the calendars
			--this.m_asyncRequestCount;
			this.unlock();
			this._asyncShow();
		},

		_asyncUpdate: function( p_startsOn, p_count, p_method ) {

			if( this.getConfig().propertyId ) {

			    var startsOn = p_startsOn.getFullYear() + "-" + (p_startsOn.getMonth() + 1) + "-" + p_startsOn.getDate();
			    var query = ".js?starts_on=" + escape( startsOn ) + "&count_months=" + escape( p_count ) + "&json_callback=" + escape( p_method );

				// Log the fact that there is an outstanding update
				++this.m_asyncRequestCount;

			    // Asynchronously get the calendar information
				gQuery.getScript( 'http://owner.rentexpert.com/property/' + this.getConfig().propertyId + this.urlAsync + query, gQuery.bindFn( this._afterAsyncUpdate, this ), true );
			}
		},

		_beforeShowCalendar: function() { return true; },

		unlock: function () {},

		/**
		 * Private methods
		 */
		_nextId: gQuery.counter()
	} );
}

// Create a default picker
JLG.date.DateRangePicker = JLG.date.buildDateRangePicker( JLG.calendar.core );


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
JLG.calendar.availability.Range = JLG.calendar.availability.Range || function (p_startsOn, p_endsOn) {

    // Member variables
    this.starts_on = p_startsOn;
    this.ends_on = p_endsOn;
    this.start_time = p_startsOn.getTime ();
    this.end_time = p_endsOn.getTime ();
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Cell ) {

	JLG.calendar.availability.Cell = function( p_month ) { this.initialize( p_month ); };
	gQuery.extend( JLG.calendar.availability.Cell.prototype, JLG.calendar.core.Cell.prototype, {

		initialize: function( p_month ) {

    		this.m_availAvailable = true;
			JLG.calendar.core.Cell.prototype.initialize.call( this, p_month );
		},

		create: function (p_day, p_parent) {

		    // See if this is an available or unavailable cell
		    this.m_availAvailable = this.getMonth ().getCalendar ().isAvailable (new Date (this.getMonth ().year, this.getMonth ().month - 1, p_day));
			JLG.calendar.core.Cell.prototype.create.call (this, p_day, p_parent, this.m_availAvailable ? null : "jlg-cal-unavailable");
		},

	    /**
	     * Private Methods
	     */
	 	onMouseDown: function (p_src) {

	        return this.m_availAvailable ? JLG.calendar.core.Cell.prototype.onMouseDown.call (this, p_src) : true;
	 	},

	 	_on_mouse_enter: function (p_src) {

	        return this.m_availAvailable ? JLG.calendar.core.Cell.prototype._on_mouse_enter.call (this, p_src) : true;
	 	},

	 	_select: function () {

	        if (this.m_availAvailable) { JLG.calendar.core.Cell.prototype._select.call (this); }
	 	},

	 	_unselect: function () {

	        if (this.m_availAvailable) { JLG.calendar.core.Cell.prototype._unselect.call (this); }
	 	}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Month ) {

	JLG.calendar.availability.Month = function( p_calendar, p_year, p_month, p_div, p_dim, p_options ) { this.initialize( p_calendar, p_year, p_month, p_div, p_dim, p_options ); };
	gQuery.extend( JLG.calendar.availability.Month.prototype, JLG.calendar.core.Month.prototype, {

		_alloc_cell: function () {

			return new JLG.calendar.availability.Cell (this);
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Calendar ) {

	JLG.calendar.availability.Calendar = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.availability.Calendar.prototype, JLG.calendar.core.Calendar.prototype, {

		initialize: function( p_config ) {

			this._availabilities = [];
			JLG.calendar.core.Calendar.prototype.initialize.call( this, p_config );
		},

		setAvailability: function( p_availability ) {

			this._availabilities = p_availability || [];
		},

		_alloc_month: function (p_year, p_month, p_div, p_dim, p_options) {

			return new JLG.calendar.availability.Month (this, p_year, p_month, p_div, p_dim, p_options);
		},

		findReservation: function() {

			var query;
		    var starts_on = this.getDates()[ this.selFirst ].getDate();
		    var ends_on = this.getDates()[ this.selLast ].getDate();
			var temp;

		    // What dates are selected?
		    if (starts_on.getTime() > ends_on.getTime()) {

		        // Swap dates
		        temp = starts_on;
		        starts_on = ends_on;
		        ends_on = temp;
		    }
			if (starts_on.getTime() == ends_on.getTime()) { ends_on.setTime( ends_on.getTime() + 86400000 ); }

			// Make sure the dates are property formatted
			starts_on = starts_on.getFullYear() + "-" + (starts_on.getMonth() + 1) + "-" + starts_on.getDate();
			ends_on = ends_on.getFullYear() + "-" + (ends_on.getMonth() + 1) + "-" + ends_on.getDate();

		    // Asynchronously get the calendar information
		    query = "?checkin=" + escape( starts_on ) + "&checkout=" + escape( ends_on );
			gQuery.loadScript( 'http://owner.rentexpert.com/property/' + this.getConfig ().viewer.getConfig().propertyId + '/booking/popup_find' + query, null, true );
		},

		isAvailable: function( p_date ) {

			var result = false;
		    var testTime = p_date.getTime();

		    // Scoot past all rnages (in case there are extra)
			for (var avail = this._availabilities, index = 0, len = avail.length; index < len; ++index) {

				var range = avail[ index ];

		        // Haven't yet reached the next availability
		        if (range.start_time > testTime) {

					break;
					// NOTREACHED
				}

		        // Haven't reached the end of this availability range
		        if (testTime <= range.end_time) {

					result = true;
					break;
					// NOTREACHED
				}
		    }

		    // Not available
		    return result;
		},

		_on_mouse_up: function( p_event ) {

		    // Process the selection
		    if (!this.getConfig().disableBooking && this.isMouseDown) { this.findReservation(); }

		    // Call the base class
		    JLG.calendar.core.Calendar.prototype._on_mouse_up.call( this, p_event );
		}
	} );
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Viewer ) {

	JLG.calendar.availability.Viewer = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.availability.Viewer.prototype, JLG.calendar.core.Viewer.prototype, {

		initialize: function( p_config ) {

			// Call the base class
			JLG.calendar.core.Viewer.prototype.initialize.call( this, gQuery.extend( { asyncUrl: '/calendar/availability' }, p_config || {} ) );

			// Initialize custom options
			this.m_availabilityCallback = this.getConfig().availabilityCallback || "JLG.calendar.availability.viewer.setCentralAvailabilityInPlace";

			// Store ourself as the instance
			JLG.calendar.availability.viewer = this;
		},

		setCentralAvailabilityInPlace: function( p_json ) {

			// Should we store to a window variable?
			var availVar = this.getConfig().availabilityVar;
			if( availVar ) { window[ availVar ] = p_json; }

			// Determine the availability
			var availability = [];
			for( var index = 0, len = p_json.length; index < len; index++ ) { availability.push( new JLG.calendar.availability.Range( p_json[ index ].starts_on, p_json[ index ].ends_on ) ); }
			this.getCalendar().setAvailability( availability );
		},

		_alloc_calendar: function( p_config ) {

			return new JLG.calendar.availability.Calendar( gQuery.extend( { viewer: this }, p_config || {} ) );
		},

		_beforeShowCalendar: function() {

			// Do we have a property defined
			if( this.getProperty() ) {

				// See if we have availability information already defined
				var availability = this.getConfig().availabilityVar;
				if( availability ) {

					var value = eval( "window." + availability );
					if( value ) {

						eval( this.m_availabilityCallback +"(window. " + availability + ")" );
						return true;
						// NOTREACHED
					}
				}

			    // Don't show until we have the data
			    this._asyncUpdate( this.getCalendar().firstDate, this.getCalendar().countMonths, this.m_availabilityCallback );
		    	return false;
				// NOTREACHED

			} else {

				return true;
				// NOTREACHED
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.util = JLG.util || {};
JLG.util.date = JLG.util.date || new JLG.date.Parser ();


if (typeof window.REX === "undefined" || !window.REX) { window.REX = {}; }
REX.views = REX.util || {};
REX.views.personas = REX.views.personas || {};
if( !REX.views.personas.Selector ) {

	REX.views.personas.Selector = function( p_user_id, p_options ) { this.initialize( p_user_id, p_options ); };
	gQuery.extend( REX.views.personas.Selector.prototype, {

		initialize: function( p_user_id, p_options ) {

			gQuery( document ).ready( gQuery.bindFn( function() {

				// Initialize
				this.id = gQuery( '#persona_id' );
				if( this.id && this.id.length > 0 ) {

					// Nothing else matters if the id field doesn't exist
					this.user_id = p_user_id;
					this.m_fieldset = gQuery( '#persona_fieldset' );
					this.m_lastValue = parseInt( String( this.id.val() ), 10 );
					if( this.m_lastValue < -1 ) {

						this.m_lastValue = 0;
						this.id.val( 0 );
					}

					// Pull optional information
					p_options = p_options || {};
					this.m_config = p_options;
					if( p_options ) { this.m_sessionId = p_options.sessionId; }

					// Connect signal handlers
					this.id.bind( "change", gQuery.bindFn( this.onChange, this ) );
					if( this.id.val() < -1 ) { this.id.val(-1 ); }
					this.onChange ();
				}
			}, this ) );
		},

		onChange: function() {

			var new_value = parseInt( String( this.id.val() ), 10 );

			// Don't allow line selection
			if( new_value != -1 && this.m_lastValue == new_value ) {

				// NOOP

			} else if( new_value === -3 ) {

				// Goto the address management page
				if( this.m_config.urlForManage ) {

					JLG.popup.showSpinner ();
					window.location.href = this.m_config.urlForManage;
				}
			} else if( new_value < -1 ) {

				// Can't select the line
				this.id.val( this.m_lastValue );

			} else {

				// Delete the "Please select" if that was the last selection
				if( parseInt( String( this.id[ 0 ].options[ 0 ].value ), 10 ) === 0) { this.id[ 0 ].remove( 0 ); }

				// See if we are creating a new contact or using an existing one
				this.m_lastValue = new_value;
				if( new_value > 0 ) {

					// Show the editing form
					this.m_fieldset.hide();

				} else {

					// Show the editing form
					this.m_fieldset.show();

					// Create a new contact
					gQuery( this.id[ 0 ].form ).find( "input" ).each( function() { 

						var jthis = gQuery( this );
						if( jthis.attr( "id" ).indexOf( 'persona_' ) === 0 ) { jthis.val( "" ); }
					} );
				}
			}
		}
	} );
}


if (typeof window.REX === "undefined" || !window.REX) {window.REX = {};}
REX.views = REX.views || {};
REX.views.booking = REX.views.booking || {};
REX.views.booking.bookit = REX.views.booking.bookit || {};
if( !REX.views.booking.bookit.Page ) {

	REX.views.booking.bookit.Page = function() { this.initialize(); };
	gQuery.extend( REX.views.booking.bookit.Page.prototype, {

		initialize: function () {

			this.m_amount = 0;
			REX.views.booking.bookit.page = this;
		},

		chooseOption: function (p_check, p_id, p_amount) {

			if (p_check.checked) {

				this.m_amount += p_amount;
				gQuery( "#rex-item-row-" + p_id ).removeClass( "rex-unselected-item" );

			} else {

				this.m_amount -= p_amount;
				gQuery( "#rex-item-row-" + p_id ).addClass( "rex-unselected-item" );
			}
			this.m_el.innerHTML = this.m_amount.toFixed(2);
			return true;
		},

		show: function (p_amount) {

			this.m_amount = p_amount;
			this.m_el = gQuery( "#rex-total-bill" )[ 0 ];
		}
	} );
}


if (typeof window.REX === "undefined" || !window.REX) {window.REX = {};}
REX.views = REX.views || {};
REX.views.booking = REX.views.booking || {};
REX.views.booking.choose_payment = REX.views.booking.choose_payment || {};
if( !REX.views.booking.choose_payment.Page ) {

	REX.views.booking.choose_payment.Page = function() { this.initialize(); };
	gQuery.extend( REX.views.booking.choose_payment.Page.prototype, {

		initialize: function () {

			this.m_methods = [

				'ach',
				'cash',
				'check',
				'moneyorder',
				'other_cc',
				'paypal_wps'
			];
			REX.views.booking.choose_payment.page = this;
		},

		show: function () {

			var tmp;
			var jbtn = gQuery( '#pay-button' );

			// Initialize members
			this.m_button = jbtn[ 0 ];
			jbtn.bind( "click", gQuery.bindFn( this._onPay, this ) );

			// Initialize the field to show/hide account information
			if ($("paypal_wps-radio")) {

				tmp = new ConfigurationChoice ("#paypal_wps-radio");
			}
		},

		/**
		 * Private methods
		 */
		_onPay: function () {

			var ipt;
			var index;
			var method;
			var form = null;

			// Loop through the radios to determine which one is checked
			for (index = 0; index < this.m_methods.length; index++) {
				method = this.m_methods[index];
				ipt = $(method + "-radio");
				if (ipt && ipt.checked === true) {

					// We found our guy
					switch (method) {

						case 'paypal_wps':
							form = $('paypal_wps_form');
							break;
							// NOTREACHED

						default:
							form = $('manual_form');
							$("payment_method").value = method;
							break;
							// NOTREACHED
					}
					break;
					// NOTREACHED
				}
			}

			// Submit any form found
			if (form && (!form.onsubmit || form.onsubmit ())) {

				form.submit ();
			}
		}
	} );
}


if (typeof window.REX === "undefined" || !window.REX) {window.REX = {};}
REX.views = REX.views || {};
REX.views.booking = REX.views.booking || {};
REX.views.booking.find = REX.views.booking.find || {};
if( !REX.views.booking.find.Page ) {
	
	REX.views.booking.find.Page = function() { this.initialize(); };
	gQuery.extend( REX.views.booking.find.Page.prototype, {

		initialize: function () {

			// Allow global access
			REX.views.booking.find.page = this;

			// Initialize members
			this.m_picker = null;
		},

		show: function (p_property, p_checkin, p_checkout) {

			// Do we have an existing picker?
			if (this.m_picker) {

				this.m_picker.reset ();

			} else {

				// Create the date picker
				this.m_picker = new JLG.calendar.availability.DateRangePicker ({

					startEl: p_checkin,
					endEl: p_checkout,
					min_span: 1,
					propertyId: p_property,
					viewer: { availabilityCallback: 'REX.views.booking.find.page._setPopupAvailability', availabilityVar: "rentalAvailability" }
				});
			}
		},

		/**
		 * Private methods
		 */
		_setPopupAvailability: function (p_json) {

			this.m_picker.setAvailability (p_json);
		}
	} );
}


if (typeof window.REX === "undefined" || !window.REX) {window.REX = {};}
REX.views = REX.views || {};
REX.views.calendar = REX.views.calendar || {};
REX.views.calendar.reservation = REX.views.calendar.reservation || {};
if( !REX.views.calendar.reservation.Controls ) {

	REX.views.calendar.reservation.Controls = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( REX.views.calendar.reservation.Controls.prototype, {

		initialize: function( p_months ) {

		    // Member variables
		    this.count = $( "rex_count" );
		    this.month = $( "date_rex_month" );
		    this.year = $( "date_rex_year" );

		    // Set up event handlers
		    Event.observe( this.year, "change", this.onChangeDate.bind( this ) );
		    Event.observe( this.month, "change", this.onChangeDate.bind( this ) );
		    Event.observe( this.count, "change", this.onChangeCount.bind( this ) );

			// Signal an onchange event
			this.count.value = p_months;
		},

		onChangeCount: function( p_event ) {

			try { delete window.rentalEvents; } catch( e ) { window.rentalEvents = undefined; }
			JLG.calendar.reservation.viewer.setMonthCount( this.count.value );
		},

		onChangeDate: function( p_event ) {

			try { delete window.rentalEvents; } catch( e ) { window.rentalEvents = undefined; }
			JLG.calendar.reservation.viewer.setDate( new Date( this.year.value, this.month.value - 1, 1 ) );
		},

		/**
	     * Private methods
	     */
		_disable: function() {

			this.count.disabled = true;
			this.month.disabled = true;
			this.year.disabled = true;
	    },

		_enable: function() {

			this.count.disabled = false;
			this.month.disabled = false;
			this.year.disabled = false;
	    }
	} );
}


if (typeof window.REX === "undefined" || !window.REX) {window.REX = {};}
REX.views = REX.views || {};
REX.views.calendar = REX.views.calendar || {};
REX.views.calendar.reservation = REX.views.calendar.reservation || {};
if( !REX.views.calendar.reservation.Page ) {

	REX.views.calendar.reservation.Page = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( REX.views.calendar.reservation.Page.prototype, {

		initialize: function( p_config ) {

			// We are the current page
			REX.currentPage = this;

			// Store the config
			this.m_config = p_config || {};
			this.m_propertyId = p_config.propertyId;

			// Create the date picker
			this.m_picker = new JLG.calendar.availability.DateRangePicker({

				startEl: '#cal-popup-checkin',
				endEl: '#cal-popup-checkout',
				min_span: 1,
				propertyId: this.m_propertyId,
				viewer: { availabilityVar: "rentalAvailability" }
			});

			// Create the calendar viewer
			this.m_viewer = new JLG.calendar.reservation.Viewer( gQuery.extend({

				eventsVar: "rentalEvents",
				calendar: { month: { hideOtherDays: true } },
				height: -280,
				monthCount: 6

			}, p_config || {} ) );

			// Render the viewer
			this.m_viewer.render();

			// Bookmark the page
			JLG.popup.history().add( "/" );
		},

		show: function (p_config) {

			// Initialize the calendar controls
			var controls = new REX.views.calendar.reservation.Controls (this.m_viewer.getConfig ().monthCount);

			// Update the calendar
			JLG.calendar.reservation.viewer.setProperty (this.m_propertyId);

			// Reset the picker
			this.m_picker.reset ();
		},

		/**
		 * Private methods
		 */
		_setPopupAvailability: function (p_json) {

			this.m_picker.setAvailability (p_json);
		}
	} );
}


if (typeof window.REX == "undefined" || !window.REX) { window.REX = {}; }
REX.views = REX.views || {};
REX.views.spotlight = REX.views.spotlight || {};
if( !REX.views.spotlight.Autocompleter ) {

	// Create the class, then extend it
	REX.views.spotlight.Autocompleter = function( p_element, p_update, p_url, p_options ) { this.initialize( p_element, p_update, p_url, p_options ); };
	gQuery.extend( REX.views.spotlight.Autocompleter.prototype, Autocompleter.Local.prototype, {

		initialize: function( p_element, p_update, p_url, p_options ) {

			this.m_currentArray = [];
			this.m_options = {};
			p_options = { fullSearch: false, frequency: 0, ignoreCase: true, minChars: 3 };
		    this.m_options.asynchronous = true;
			this.m_options.evalJSON = true;
		    this.m_options.onComplete = this.onComplete.bind( this );
			this.m_url = p_url;
			Autocompleter.Local.prototype.initialize.call( this, p_element, p_update, [], p_options );
			this.options.frequency = 0;
			this.options.selector = this.selector.bind( this );
		},

		getEntry: function( index ) {

			try { return this.update.firstChild.childNodes[ index ]; } catch( e ) { return null; }
		},

		getUpdatedChoices: function() {

			// Only use text and return if there aren't at least three text characters
			prefix = this.getToken().replace( /[^a-zA-Z0-9]/i, '' );
			if (prefix.length < 3) { return; }
			prefix = prefix.slice( 0, 3 ).toLowerCase();

			// Request the javascript if we don't already have it
			if (prefix != this.m_currentPrefix) {

				this.m_currentPrefix = prefix;
				var ar = new Ajax.Request( gQuery.joinUrl([ this.m_url, prefix + ".js" ]), this.m_options );

			} else {

				var results = this.options.selector();
				if (results) { this.updateChoices( results ); }
			}
		},

		markNext: function() {

			if (this.index < this.entryCount) {

				this.index++;
				if (this.index == this.entryCount) { this.index = -1; }

			} else {

				this.index = 0;
			}
		},

		markPrevious: function() {

			if (this.index >= 0) {

				this.index--;

			} else {

				this.index = this.entryCount - 1;
			}
		},

		onComplete: function( p_request ) {

			try {

				this.m_currentArray = eval( p_request.responseText );
				var results = this.options.selector();
				if (results) { this.updateChoices( results ); }

			} catch (e) {

				this.m_currentArray = [];
			}
		},

	    selector: function() {

			var array = this.m_currentArray;
			var instance = this;
	        var ret = []; // Beginning matches
	        var entry = instance.getToken().toLowerCase();
	        var count = 0;

	        for (var index = 0, len = array.length; index < len && ret.length < instance.options.choices ; index++) {

				var elem = array[ index ];
				var foundPos = elem[ 0 ].indexOf( entry );
				while (foundPos != -1) {

					if (foundPos == 0) {

						ret.push( "<li><div class='formal'><strong>" + elem[ 1 ].substr( 0, entry.length ) + "</strong>" + elem[ 1 ].substr( entry.length ) + ", " +
									elem[ 2 ] + "</div><div class='informal'> (" + elem[ 3 ] +")</div><br style='clear:both;height:0px;padding:0;margin:0;' /></li>" );
						break;
					}
					foundPos = elem[ 0 ].indexOf( entry, foundPos + 1 );
				}
	        }
	       	return "<ul>" + ret.join( '' ) + "</ul>";
	    },

		updateChoices: function( choices ) {

			if(!this.changed && this.hasFocus) {

				this.update.innerHTML = choices;
				Element.cleanWhitespace(this.update);
				Element.cleanWhitespace(this.update.firstChild);

				if (this.update.firstChild && this.update.firstChild.childNodes) {

					this.entryCount = this.update.firstChild.childNodes.length;
					for (var i = 0; i < this.entryCount; i++) {

						var entry = this.getEntry(i);
						entry.autocompleteIndex = i;
						this.addObservers(entry);
					}
				} else {

					this.entryCount = 0;
				}
				this.stopIndicator();
				this.index = -1;
				this.render();
			}
		},

	  	updateElement: function( selectedElement ) {

			if (selectedElement) { Autocompleter.Local.prototype.updateElement.call( this, selectedElement ); }
		}
	} );
}


(function() {

	if( typeof window.REX == "undefined" || !window.REX ) { window.REX = {}; }
	REX.views = REX.views || {};
	REX.views.spotlight = REX.views.spotlight || {};
	REX.views.spotlight.Page = function() {

		this.m_datePickers = {};
		this.m_querySampleSize = 3;
		this.m_slideIndex = 0;
		this.m_slidePhotos = [];
	};
	gQuery.extend( REX.views.spotlight.Page.prototype, {

		fbShare: function( p_id ) {

			u = "http://www.rentexpert.com/" + p_id;
			window.open( 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent( u ), 'sharer', 'toolbar=0,status=0,width=626,height=350' );
			return false;
		},

		initDateRangePicker: function( p_name, p_checkin, p_checkout ) {

			// Do we have an existing picker?
			var picker = this.m_datePickers[ p_name ];
			if (picker) {

				picker.reset();

			} else {

				// Find the DOM elements
				p_checkin = gQuery( p_checkin );
				p_checkout = gQuery( p_checkout );
				if (p_checkin && p_checkout) {

					// Initialize the elements
					if ((p_checkin.val() || '').length === 0) { p_checkin.val( jgLocale.dateFormat ); }
					if ((p_checkout.val() || '').length === 0) { p_checkout.val( jgLocale.dateFormat ); }

					// Create the date picker
					picker = new JLG.date.DateRangePicker({

						left: 0,
						startEl: p_checkin,
						endEl: p_checkout,
						min_span: 3,
						minDate: new Date()
					});
					this.m_datePickers[ p_name ] = picker;
				}
			}
		},

		slideClickThumb: function( p_index ) {

			if (this.m_slideTimer !== null) {

				clearInterval( this.m_slideTimer );
				this.m_slideTimer = null;
			}
			this.m_slideIndex = p_index;
			this.slideScroll();
		},

		slideNext: function( p_stop ) {

			// Stop the timer if desired
			if (p_stop && this.m_slideTimer !== null) {

				clearInterval( this.m_slideTimer );
				this.m_slideTimer = null;
			}

			// Move to the next slide
			this.m_slideIndex += 1;
			if (this.m_slideIndex >= this.m_slidePhotos.length) { this.m_slideIndex = 0; }
			this.slideScroll();
		},

		slidePrev: function( p_stop ) {

			// Stop the timer if desired
			if (p_stop && this.m_slideTimer !== null) {

				clearInterval( this.m_slideTimer );
				this.m_slideTimer = null;
			}

			// Move to the next slide
			this.m_slideIndex -= 1;
			if (this.m_slideIndex < 0) { this.m_slideIndex = this.m_slidePhotos.length - 1; }
			this.slideScroll();
		},

		slideScroll: function() {

			var photoLeft = (this.m_slideIndex - 1) * 65;
			if (this.m_slideSelected) { this.m_slideSelected.removeClass( "rex-selected" ); }
			this.m_slideSelected = gQuery( "#rex-thumb-" + this.m_slideIndex );
			this.m_slideSelected.addClass( "rex-selected" );
			if (photoLeft < 0) { photoLeft = 0; }
			this.m_slideThumbs.scrollLeft( photoLeft );
			var photo = this.m_slidePhotos[ this.m_slideIndex ];
			var scale = Math.max( photo.width, photo.height );
			var height = Math.ceil( (photo.height * 272) / scale );
			var width = Math.ceil( (photo.width * 272) / scale );
			this.m_slideLarge.attr({ src: photo.src, alt: photo.title, width: width, height: height });
		},

		slideStart: function( p_photos ) {

			// Initialize
			var left = gQuery( "#sl-ss-prev" );
			var right = gQuery( "#sl-ss-next" );
			this.m_slidePhotos = p_photos;
			this.m_slideLarge = gQuery( "#sl-ss-photo" );
			this.m_slideThumbs = gQuery( "#sl-ss-thumbs" );

			// Setup event handlers
			left.bind( "click", gQuery.bindFn( function() { this.slidePrev( true ); }, this ) );
			right.bind( "click", gQuery.bindFn( function() { this.slideNext( true ); }, this ) );

			// Setup the timer
			if( this.m_slideLarge && this.m_slideThumbs ) {

				this.slideScroll();
				this.m_slideTimer = setInterval( gQuery.bindFn( function() { this.slideNext(); }, this ), 5000 );
			}
		}
	} );

	// Create the page
	window.page = new REX.views.spotlight.Page();
	gQuery( document ).ready( function () {

		CoolLightbox.initialize();
		VacationJournal.ifRentalsChanged( function( p_rentals ) {

			gQuery( ".add_favorite" ).each( function() {

				var el = gQuery( this );
				var id = el.attr( "class" ).match( /add_favorite_(\d+)/ );
				id = (id.length === 0) ? null : id[ id.length - 1 ];
				if( id && gQuery.inArray( id, p_rentals ) >= 0 ) {

					el.children().hide().filter( ".i_favorite" ).css({ display: "block" });

				} else {

					el.children().show().filter( ".i_favorite,.i_favpend" ).hide();
				}
			} );
		} );

		VacationJournal.ifJournalChanged( function( p_juid ) {

			if( p_juid ) {

				var url = "http://www.rentexpert.com/$" + p_juid;
				gQuery( '#journal_url' ).html( "<a href=\"" + url + "\">" + url + "</a>" );

			} else {

				gQuery( '#journal_url' ).html( "" );
			}
		} );

		if( JgUser.flag( "a" ) ) {

			gQuery( ".agent_bar" ).each( function() {

				var jel = gQuery( this );
				var id = /^ab_([0-9]+)$/.exec( jel.attr( "id" ) );
				if( id && id.length > 1 ) {

			 		gQuery.getScript( "/spotlight/agent_bar/" + id[ 1 ] + ".js", null, true );
				}
			} );
		}
	} );
})();


/*
 * gQuery Easing v1.3 - http://gsgd.co.uk/sandbox/gquery/easing/
 *
 * Uses the built in easing capabilities added In gQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - gQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
gQuery.easing['jswing'] = gQuery.easing['swing'];

gQuery.extend( gQuery.easing,
{
	def: 'easeOutQuad',
	swing: function (x, t, b, c, d) {
		//alert(gQuery.easing.default);
		return gQuery.easing[gQuery.easing.def](x, t, b, c, d);
	},
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - gQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return gQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return gQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

/*
 *
 * TERMS OF USE - EASING EQUATIONS
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2001 Robert Penner
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
 */

/**
 * Cool LightBox - for gQuery 1.3
 * @name coollightbox.v2.2.js
 * @author Eduardo D. Sada - http://www.coders.me/web-html-js-css/javascript/cool-lightbox-2
 * @version 2.2
 * @date 1-Jun-2009
 * @copyright (c) 2009 Eduardo D. Sada (www.coders.me)
 * @license MIT - http://es.wikipedia.org/wiki/Licencia_MIT
 * @example http://www.coders.me/ejemplos/cool-lightbox-2/
*/

gQuery.bind = function(object, method){
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    var args2 = [this].concat(args, gQuery.makeArray( arguments ));
    return method.apply(object, args2);
  };
};

(function() {

  CoolLightbox = {
    getOptions: function() {
      return {
        name          : 'SLB',
        zIndex        : 400000,
        color         : 'blue',
        find          : 'coollightbox',
        imagesdir     : '/javascripts/lib/cool_lightbox/coolimages',
        background    : 'bgCool.png',
        backgroundIE  : 'bgCool.gif',
        closeButton   : 'CoolClose.png',
        displayed     : 0,
        modal         : 0,
        showDuration  : 200,
        showEffect    : 'linear',
        closeDuration : 400,
        closeEffect   : 'linear',
        moveDuration  : 800,
        moveEffect    : 'easeOutBack',
        resizeDuration: 800,
        resizeEffect  : 'easeOutBack',
        shake         : { distance: 10,
                          duration: 100,
                          transition: 'easeInOutBack',
                          loops: 2
                        },
        BoxStyles     : { 'width' : 486, 'height': 320 },
        Skin          : { 'white' : { 'hexcolor': '#FFFFFF', 'captionColor': '#000000', 'background-color': '#000', 'opacity': 0.6 },
                          'black' : { 'hexcolor': '#000000', 'captionColor': '#FFFFFF', 'background-color': '#fff', 'opacity': 0.6 },
					 	  'blue'  : { 'hexcolor': '#2D76B7', 'captionColor': '#FFFFFF', 'background-color': '#fff', 'opacity': 0.6 }}
      };
    },

    initialize: function(options) {

      this.options = gQuery.extend(this.getOptions(), options);
      this.options.OverlayStyles = gQuery.extend(this.options.Skin[this.options.color], this.options.OverlayStyles || {});

      var strBG = this.options.imagesdir+'/'+this.options.color+'/'+((((window.XMLHttpRequest == undefined) && (ActiveXObject != undefined)))?this.options.backgroundIE:this.options.background);
      var name  = this.options.name;

// BEGIN JLG: Introduce IE8 hack & history support
	// var ieVersion = gQuery.ieVersion();
	// var ieStyle = (ieVersion == 8) ? ";top:-17px;" : "";
	var ieStyle = "";

	// Add the history elements
	var code = "<input type='hidden' id='clbh_f' />";
	if( gQuery.ieVersion() >= 0 ) {

		code += "<iframe id='clbh_if' src='/blank.html' style='position:absolute; top:0; left:0; width:1px; height:1px; visibility:hidden;'></iframe>";
	}
	gQuery( "body" ).prepend( code );

	// Call the yahoo initializer
	var clb_self = this;
	try {

		// Make sure we have a history object
		var hClass = function() { this.initialize(); };
		gQuery.extend( hClass.prototype, {

			initialize: function() {

				// Initialize member variables
				this._bookmarked = "/#_clbh_=0";
				this.m_count = 0;
				this._hrefBase = "";
				this._initial = "/#_clbh_=0";
				this._is_active = false;
				this._module = "";
				this._hrefBase = "";
				this._module = "clb";
				this._bookmarked = JLG.util.History.getBookmarkedState( this._module ) || "/#_clbh_=0";
				this._initial = this._bookmarked || "/#_clbh_=0";

				// Register the history system
				JLG.util.History.register( this._module, this._initial, gQuery.bindFn( this.go, this ) );

				// Initialize oursef when the page loads
				JLG.util.History.onReady( gQuery.bindFn( this._onReady, this ) );
			},

			add: function( p_token ) {

				// Ignore further history items once we reach the beginning
				if( p_token != "/" ) { this._is_active = true; }
				p_token += "#_clbh_=" + (++this.m_count);
				this._bookmarked = p_token;
				try { JLG.util.History.navigate( this._module, p_token ); } catch( e ) {}
			},

			go: function( p_token ) {

				// Setup an easy exit
				do {

					// History tag present?
					var idxCount = p_token.indexOf( "#_clbh_" );
					if( idxCount < 0 ) {

						// Invalid Token
						break;
						// NOTREACHED
					}

					// Are we going home?
					var pos = parseInt( p_token.substring( idxCount + 11 ), 10 ) - 1;
					if( idxCount == 1 && p_token.charAt( 0 ) == "/" ) {

						// Hide the popup
						this._is_active = false;
						clb_self.close();
						break;
						// NOTREACHED
					}

					// Don't do anything if we are already on the page
					if( p_token == this._bookmarked ) {

						// Don't do anything if we are already on the page
						break;
						// NOTREACHED
					}

					// Is the history object disabled?
					if( !this._is_active ) {

						// Go home if we are inactive and the user is moving around in history
						var dist = this.m_count + clb_self.ifCount - clb_self.ifOrigCount - 1;
						if( this.m_count > 0 ) { history.go( -dist ); }
						break;
						// NOTREACHED
					}

					// Pull information from the token
					this.m_count = pos;
					p_token = p_token.substring( 0, idxCount );
				}
				while( 0 );
			},

			/**
			 * Protected methods
			 */
			_onReady: function() {

				var token = JLG.util.History.getCurrentState( this._module );
				if( window.location.hash.substr( 1 ).length > 0 ) { this.go( token ); }
			}
		} );
		this.history = new hClass();

		// Initialize the Yahoo history manager
		JLG.util.History.initialize( "clbh_f", "clbh_if" );

	} catch (e) {
	}
// END JLG:

			gQuery('body').append('<div id="'+name+'-Overlay"></div><div id="'+name+'-Wrapper"><div id="'+name+'-Background"></div><div id="'+name+'-Contenedor"><div id="'+name+'-Top" style="background-image: url('+strBG+')"><a id="'+name+'-CloseButton" href="#"><img src="'+this.options.imagesdir+'/'+this.options.color+'/'+this.options.closeButton+'" alt="Close"></a><div id="'+name+'-TopLeft" style="background-image: url('+strBG+')' + ieStyle + '"></div></div><div id="'+name+'-Contenido"></div><div id="'+name+'-Bottom" style="background-image: url('+strBG+')"><div id="'+name+'-BottomRight" style="background-image: url('+strBG+')"><div id="'+name+'-Navegador"><strong id="'+name+'-Caption"></strong></div></div></div></div></div>');

      this.Overlay      = gQuery('#'+name+'-Overlay');
      this.Wrapper      = gQuery('#'+name+'-Wrapper');
      this.Background   = gQuery('#'+name+'-Background');
      this.Contenedor   = gQuery('#'+name+'-Contenedor');
      this.Top          = gQuery('#'+name+'-Top');
      this.CloseButton  = gQuery('#'+name+'-CloseButton');
      this.Contenido    = gQuery('#'+name+'-Contenido');
      this.bb           = gQuery('#'+name+'-Bottom');
      this.innerbb      = gQuery('#'+name+'-BottomRight');
      this.Nav          = gQuery('#'+name+'-Navegador');
      this.Descripcion  = gQuery('#'+name+'-Caption');

      this.Overlay.css({
        'position'  : 'absolute',
        'top'       : 0,
        'left'      : 0,
        'opacity'   : this.options.OverlayStyles['opacity'],
        'height'    : gQuery(document).height(),
        'width'     : gQuery(document).width(),
        'z-index'   : this.options.zIndex,
        'background-color': this.options.OverlayStyles['background-color']
      }).hide();

      this.Wrapper.css({
        'z-index'   : this.options.zIndex,
        'top'       : (-this.options.BoxStyles['height']-280)+'px',
        'left'      : ( (gQuery(document).width() - this.options.BoxStyles['width']) / 2)
      }).hide();

      this.Background.css({
        'z-index'   : this.options.zIndex + 1
      });

      this.Contenedor.css({
        'position'  : 'absolute',
        'width'     : this.options.BoxStyles['width'] + 'px',
        'z-index'   : this.options.zIndex + 2
      });

      this.Contenido.css({
        'height'            : this.options.BoxStyles['height'] + 'px',
        'border-left-color' : this.options.Skin[this.options.color].hexcolor,
        'border-right-color': this.options.Skin[this.options.color].hexcolor
      });

      this.Nav.css({
        'color'     : this.options.Skin[this.options.color].captionColor
      });

      this.Descripcion.css({
        'color'     : this.options.Skin[this.options.color].captionColor
      });



      /**
       * AGREGAMOS LOS EVENTOS
       ************************/

      this.CloseButton.bind('click', gQuery.bindFn( function(){
        this.close();
        return false;
      }, this ));

      this.Overlay.bind('click', gQuery.bindFn( function(){
        if (!this.options.modal) {
          this.close();
        }
      },this));


       gQuery(document).bind('keydown', gQuery.bindFn( function(obj, event){
        if (this.options.displayed == 1) {
          if (event.keyCode == 27){
            this.close();
          }

          if (event.keyCode == 37){
            if (this.prev) {
              this.prev.trigger('click', event);
            }
          }

          if (event.keyCode == 39){
            if (this.next) {
              this.next.trigger('click', event);
            }
          }
        }
      },this));

      gQuery(window).bind('resize', gQuery.bindFn( function(){
        if(this.options.displayed == 1) {
          this.replaceBox();
        } else {
          this.Overlay.css({'height': '0px', 'width': '0px'});
        }
      }, this));

      gQuery(window).bind('scroll', gQuery.bindFn( function(){

		// BEGIN JLG:
        // if(this.options.displayed == 1) {
        if(this.options.displayed == 1 && gQuery( window ).width() > this.ajustarWidth && gQuery( window ).height() > this.ajustarHeight) {
		// END JLG:
          this.replaceBox();
        }
      }, this));

      this.refresh();

    },

    hook: function(enlace) {
      enlace = gQuery(enlace);
      enlace.blur();
      this.show((enlace.attr("title") || enlace.attr("name") || ""), enlace.attr("href"), (enlace.attr('rel') || false));
    },

    close: function() {
      this.display(0);
      this.modal = 0;
		// BEGIN JLG:
		if( this.history && this.history._is_active ) { this.history.add( "/" ); }
		// END JLG:
    },

    refresh: function() {

// BEGIN JLG: Updated to be faster
		var self = this;
		var regex = new RegExp( "^" + this.options.find + "_hidden" );
		this.anchors = [];
		gQuery( "a[rel*=coollightbox], area[rel*=coollightbox]" ).each( function() {

			var jel = gQuery( this );
			var rel = jel.attr('rel');
			jel.click( function( event ) {

				event.preventDefault();
				event.stopImmediatePropagation();
				self.hook( this );
			} );

			if( !(jel.attr( 'id'  ) == self.options.name + "Left" || jel.attr( 'id' ) == self.options.name + "Right") ) { self.anchors.push( this ); }

			if( regex.test( rel ) ) { jel.show(); }
		} );
// END JLG:
    },

    display: function(option) {
      if(this.options.displayed == 0 && option != 0 || option == 1) {

        gQuery('embed, object, select').css({ 'visibility' : 'hidden' });

        if (this.options.displayed == 0) {
          this.Wrapper.css({
            'top'     : (-this.options.BoxStyles['height']-280)+'px',
            'height'  : (this.options.BoxStyles['height']-80)+'px',
            'width'   : this.options.BoxStyles['width']+'px'
          }).hide();
        }

        this.options.displayed = 1;
        this.Overlay.stop();
        this.Overlay.fadeIn(this.options.showDuration, gQuery.bindFn( function(){
          this.Wrapper.show();
          this.Overlay.css({
            'opacity'   : this.options.OverlayStyles['opacity']
          });
        }, this));

      }
       //Cerrar el Lightbox
      else
      {

        gQuery('embed, object, select').css({ 'visibility' : 'visible' });

        this.Wrapper.css({
          'top'     : (-this.options.BoxStyles['height']-280)+'px',
          'height'  : (this.options.BoxStyles['height']-80)+'px',
          'width'   : this.options.BoxStyles['width']+'px'
        }).hide();

        this.options.displayed = 0;

        this.Overlay.stop();
        this.Overlay.fadeOut(this.options.closeDuration, gQuery.bindFn( function(){
          if (this.Image)
            this.Image.remove();
          this.Overlay.css({'height': 0, 'width': 0 });
        }, this));
      }
    },

    replaceBox: function(data) {

      data = gQuery.extend({
        'width'  : this.ajustarWidth,
        'height' : this.ajustarHeight,
        'resize' : 0
      }, data || {});

      if (this.MoveBox)
        this.MoveBox.stop();

      this.MoveBox = this.Wrapper.animate({
        left  : ( gQuery(window).scrollLeft()  + ((gQuery(window).width()  - data.width) / 2)),
        top   : ( gQuery(window).scrollTop()   + (gQuery(window).height() - (data.height + ((this.MostrarNav)?80:48))) / 2 )
      }, {
        duration  : this.options.moveDuration,
        easing    : this.options.moveEffect
      });

      if (data.resize) {

        if (this.ResizeBox2)
          this.ResizeBox2.stop();
        this.ResizeBox2 = this.Contenido.animate({
          height   : data.height
        }, {
          duration  : this.options.resizeDuration,
          easing    : this.options.resizeEffect
        });

        if (this.ResizeBox)
          this.ResizeBox.stop();

        this.ResizeBox = this.Contenedor.animate({
          width     : data.width
        }, {
          duration  : this.options.resizeDuration,
          easing    : this.options.resizeEffect,
          complete  : gQuery.bindFn( function(){
            this.Wrapper.css({'width' : data.width});
            this.ResizeBox.trigger('onComplete');
          }, this)
        });
      }

      if (window.opera) { //Opera Bug :(
        this.Overlay.css({'height': 0, 'width': 0 });
      }

      this.Overlay.css({
        'height'    : gQuery(document).height(),
        'width'     : gQuery(window).width()
      });
    },

    getInfo: function (image, id) {
      image=gQuery(image);
      IEuta = gQuery('<a id="'+this.options.name+id+'" title="'+image.attr('title')+'" rel="'+image.attr('rel')+'"><img class="bt'+id+'" src="'+this.options.imagesdir+'/'+this.options.color+'/CoolBt'+id+'.png'+'" /></a>');
      IEuta.attr('href', image.attr('href')); //IE fix
      return IEuta;
    },

    show: function(caption, url, rel) {
      this.MostrarNav = false;
      this.showLoading();

      var baseURL = url.match(/(.+)?/)[1] || url;

      var imageURL = /\.(jpe?g|png|gif|bmp)/gi;

      if (this.ResizeBox) {
        this.ResizeBox.unbind('onComplete'); //fix for gQuery
      }

      if (caption) {
        this.MostrarNav = true;
      }
      // check for images
      if ( baseURL.match(imageURL) ) {
          /**
           * Cargar Imagen.
           *****************/
          this.imgPreloader = new Image();
          this.imgPreloader.onload = gQuery.bindFn( function(){
              this.imgPreloader.onload=function(){};

              //Resizing large images
              var x = gQuery(window).width() - 100;
              var y = gQuery(window).height() - 100;

              var imageWidth = this.imgPreloader.width;
              var imageHeight = this.imgPreloader.height;

              if (imageWidth > x)
              {
                imageHeight = imageHeight * (x / imageWidth);
                imageWidth = x;
                if (imageHeight > y)
                {
                  imageWidth = imageWidth * (y / imageHeight);
                  imageHeight = y;
                }
              }
              else if (imageHeight > y)
              {
                imageWidth = imageWidth * (y / imageHeight);
                imageHeight = y;
                if (imageWidth > x)
                {
                  imageHeight = imageHeight * (x / imageWidth);
                  imageWidth = x;
                }
              }
              //End Resizing

              //Ajustar el tamaño del lightbox
              if (this.MostrarNav || caption){
                this.ajustarHeight = (imageHeight-21);
              }else{
                this.ajustarHeight = (imageHeight-35);
              };

              this.ajustarWidth = (imageWidth+14);

              this.replaceBox({
                'width'  :this.ajustarWidth,
                'height' :this.ajustarHeight,
                'resize' : 1
              });

              //Mostrar la imagen, solo cuando la animacion de resizado se ha completado
              this.ResizeBox.bind('onComplete', gQuery.bind(this, function(){
                this.showImage(this.imgPreloader.src, {'width':imageWidth, 'height': imageHeight});
              }));
          }, this);

          this.imgPreloader.onerror = gQuery.bindFn( function(){
            this.show('', this.options.imagesdir+'/'+this.options.color+'/404.png', this.options.find);
          }, this);

          this.imgPreloader.src = url;

      } else { //code to show html pages
          var queryString = url.match(/\?(.+)/)[1];
          var params = this.parseQuery( queryString );
			// BEGIN JLG:
          // params['width']   = parseInt(params['width']);
          // params['height']  = parseInt(params['height']);
          params['width']   = parseInt(params['width'] || (gQuery(window).width() - 100), 10);
          params['height']  = parseInt(params['height'] || (gQuery(window).height() - 100), 10);
			// END JLG:
          params['modal']   = params['modal'];

          this.options.modal = params['modal'];

          this.ajustarHeight = parseInt(params['height'], 10)+(window.opera?2:0);
          this.ajustarWidth  = parseInt(params['width'], 10)+14;

          this.replaceBox({
            'width'  : this.ajustarWidth,
            'height' : this.ajustarHeight,
            'resize' : 1
          });


          if (url.indexOf('TB_inline') != -1) //INLINE ID
          {
            this.ResizeBox.bind('onComplete', gQuery.bindFn( function(){
              this.showContent(gQuery('#'+params['inlineId']).html(), {'width': params['width']+14, 'height': this.ajustarHeight}, params['background']);
            }, this));
          }
          else if(url.indexOf('TB_iframe') != -1) //IFRAME
          {
            var urlNoQuery = url.split('TB_');
            this.ResizeBox.bind('onComplete', gQuery.bindFn( function(){
              this.showIframe(urlNoQuery[0], {'width': params['width']+14, 'height': params['height']}, params['background']);
            }, this));
          }
          else //AJAX
          {
            this.ResizeBox.bind('onComplete', gQuery.bindFn( function(){
              gQuery.ajax({
                url: url,
                type: "GET",
                cache: false,
                error: gQuery.bindFn( function(){this.show('', this.options.imagesdir+'/'+this.options.color+'/404html.png', this.options.find);}, this),
                success: gQuery.bindFn( this.handlerFunc, this)
              });
            }, this));
          }

      }


      this.next       = false;
      this.prev       = false;
       //Si la imagen pertenece a un grupo
// BEGIN JLG:
      // if (rel.length > this.options.find.length)
	var regex = new RegExp( "^" + this.options.find + "\\[" );
	if( regex.test( rel ) )
// END JLG:
      {
          this.MostrarNav = true;
          var foundSelf   = false;
          var exit        = false;
          var self        = this;

          gQuery.each(this.anchors, function(index){
            if (gQuery(this).attr('rel') == rel && !exit) {
              if (gQuery(this).attr('href') == url) {
                  foundSelf = true;
              } else {
                  if (foundSelf) {
                      self.next = self.getInfo(this, "Right");
                       //stop searching
                      exit = true;
                  } else {
                      self.prev = self.getInfo(this, "Left");
                  }
              }
            }
          });
      }

      this.addButtons();
      this.showNav(caption);
      this.display(1);
    },// end function

    handlerFunc: function(obj, html) {
      this.showContent(html, {'width':this.ajustarWidth, 'height': this.ajustarHeight});
    },

    showLoading: function() {
      this.Background.empty().removeAttr('style').css({'width':'auto', 'height':'auto'});
      this.Contenido.empty().css({
        'background-color'  : 'transparent',
        'padding'           : '0px',
        'width'             : 'auto'
      });

      this.Contenedor.css({
        'background' : 'url('+this.options.imagesdir+'/'+this.options.color+'/loading.gif) no-repeat 50% 50%'
      });

      this.Contenido.empty().css({
          'background-color': 'transparent',
          'padding'         : '0px',
          'width'           : 'auto'
      });

      this.replaceBox({
        'width'  : this.options.BoxStyles.width,
        'height' : this.options.BoxStyles.height,
        'resize' : 1
      });

    },

    addButtons: function(){
        if(this.prev) this.prev.bind('click', gQuery.bindFn( function(event) {event.preventDefault();this.hook(this.prev);}, this));
        if(this.next) this.next.bind('click', gQuery.bindFn( function(event) {event.preventDefault();this.hook(this.next);}, this));
    },

	/**********************
	 * BEGIN JLG:
	 *********************/
	resize: function( p_options ) {

		p_options = gQuery.extend( { width: this.ajustarWidth - 14, height: this.ajustarHeight -  (window.opera ? 2 : 0) }, p_options || {} );
		this.ajustarWidth = p_options.width + 14;
		this.ajustarHeight = p_options.height + (window.opera ? 2 : 0);
		if( this.MoveBox ) { this.MoveBox.stop(); }
		this.MoveBox = this.Wrapper.animate(
			{

				left: (gQuery( window ).scrollLeft() + ((gQuery( window ).width()  - this.ajustarWidth) / 2)),
				top: (gQuery( window ).scrollTop() + (gQuery( window ).height() - (this.ajustarHeight + (this.MostrarNav ? 80 : 48))) / 2 )
			}, {

				duration: this.options.moveDuration,
				easing: this.options.moveEffect
			}
		);
		if( this.ResizeBox2 ) { this.ResizeBox2.stop(); }
		this.ResizeBox2 = this.Contenido.animate(

			{ height: this.ajustarHeight, width: p_options.width }, {

				duration: this.options.resizeDuration,
				easing: this.options.resizeEffect
        	}
		);
		if( this.ResizeBox ) { this.ResizeBox.stop(); }
		this.ResizeBox = this.Contenedor.animate(

			{ width: this.ajustarWidth }, {

				duration: this.options.resizeDuration,
				easing: this.options.resizeEffect,
				complete: gQuery.bindFn( function() { this.Wrapper.css({ 'width': this.ajustarWidth }); }, this )
			}
		);
		this.Image.css({ width: this.ajustarWidth, height: this.ajustarHeight });
		this.Background.empty().css({ 'height': this.ajustarHeight + 35, 'width': p_options.width });
	},

   /**
    * Mostrar navegacion.
    *****************/
    showNav: function(caption) {
        if (this.MostrarNav || caption) {
          this.bb.addClass("SLB-bbnav");
          this.Nav.empty();
          this.innerbb.empty();
          this.innerbb.append(this.Nav);
          this.Descripcion.html(caption);
          this.Nav.append(this.prev);
          this.Nav.append(this.next);
          this.Nav.append(this.Descripcion);
        }
        else
        {
          this.bb.removeClass("SLB-bbnav");
          this.innerbb.empty();
        }
    },

    showImage: function(image, size) {
      this.Background.empty().removeAttr('style').css({'width':'auto', 'height':'auto'}).append('<img id="'+this.options.name+'-Image"/>');
      this.Image = gQuery('#'+this.options.name+'-Image');
      this.Image.attr('src', image).css({
        'width'  : size['width'],
        'height' : size['height']
      });

      this.Contenedor.css({
        'background' : 'none'
      });

      this.Contenido.empty().css({
          'background-color': 'transparent',
          'padding'         : '0px',
          'width'           : 'auto'
      });
    },

    showContent: function(html, size, bg) {
      this.Background.empty().css({
        'width'            : size['width']-14,
        'height'           : size['height']+35,
        'background-color' : bg || '#ffffff'
      });

      this.Contenido.empty().css({
        'width'             : size['width']-14,
        'background-color'  : bg || '#ffffff'
      }).append('<div id="'+this.options.name+'-Image"/>');

      this.Image = gQuery('#'+this.options.name+'-Image');
      this.Image.css({
        'width'       : size['width']-14,
        'height'      : size['height'],
        'overflow'    : 'auto',
        'background'  : bg || '#ffffff'
      }).append(html);

      this.Contenedor.css({
        'background': 'none'
      });
      var wId = gQuery(this.Wrapper).attr('id');
      gQuery('#'+wId+' select, #'+wId+' object, #'+wId+' embed').css({ 'visibility' : 'visible' });
    },

    showIframe: function(src, size, bg) {
      this.Background.empty().css({
        'width'           : size['width']-14,
        'height'          : size['height']+35,
        'background-color': bg || '#ffffff'
      });

      var id = "if_"+new Date().getTime()+"-Image";
		this.iframeId = id;

      this.Contenido.empty().css({
        'width'             : size['width']-14,
        'background-color'  : bg || '#ffffff',
        'padding'           : '0px'
      }).append('<iframe id="'+id+'" frameborder="0"></iframe>');

      this.Image = gQuery('#'+id);
      this.Image.css({
          'width'       : size['width']-14,
          'height'      : size['height'],
          'background'  : bg || '#ffffff'
      }).attr('src', src);

      this.Contenedor.css({
        'background' : 'none'
      });

		// BEGIN JLG:
		if( this.history ) {

			this.history.add( src );
			this.ifOrigCount = history.length;
			this.ifCount = this.ifOrigCount;
			setInterval( gQuery.bindFn( function() {

				if( this.ifCount != history.length ) {

					this.ifCount = history.length;
					if( this.ifCount == this.ifOrigCount ) { this.Image.attr( "src", src ); }
				}
			}, this ), 50 );
		}
		// END JLG:
    },

    parseQuery: function (query) {
      if( !query )
        return {};
      var params = {};

      var pairs = query.split(/[;&]/);
      for ( var i = 0; i < pairs.length; i++ ) {
        var pair = pairs[i].split('=');
        if ( !pair || pair.length != 2 )
          continue;
        params[unescape(pair[0])] = unescape(pair[1]).replace(/\+/g, ' ');
       }
       return params;
    },

    shake: function() {
      var d=this.options.shake.distance;
      var l=this.Wrapper.position();
      l=l.left;
      for(x=0;x<this.options.shake.loops;x++) {
       this.Wrapper.animate({left: l+d}, this.options.shake.duration, this.options.shake.transition)
       .animate({left: l-d}, this.options.shake.duration, this.options.shake.transition);
      }
       this.Wrapper.animate({"left": l+d}, this.options.shake.duration, this.options.shake.transition)
       .animate({"left": l}, this.options.shake.duration, this.options.shake.transition);
    }

  };
})();