/*  Prototype JavaScript framework, version 1.5.0
 *  (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://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.5.0',
  BrowserFeatures: {
	XPath: !!document.evaluate
  },

  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
  emptyFunction: function() {},
  K: function(x) { return x }
}

var Class = {
  create: function() {
	return function() {
	  this.initialize.apply(this, arguments);
	}
  }
}

var Abstract = new Object();

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 === undefined) return 'undefined';
	  if (object === null) return 'null';
	  return object.inspect ? object.inspect() : object.toString();
	} catch (e) {
	  if (e instanceof RangeError) return '...';
	  throw e;
	}
  },

  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);
  }
});

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
	return __method.apply(object, args.concat($A(arguments)));
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this, args = $A(arguments), object = args.shift();
  return function(event) {
	return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  }
}

Object.extend(Number.prototype, {
  toColorPart: function() {
	var digits = this.toString(16);
	if (this < 16) return '0' + digits;
	return digits;
  },

  succ: function() {
	return this + 1;
  },

  times: function(iterator) {
	$R(0, this, true).each(iterator);
	return this;
  }
});

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;
  }
}

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
  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);
  },

  stop: function() {
	if (!this.timer) return;
	clearInterval(this.timer);
	this.timer = null;
  },

  onTimerEvent: function() {
	if (!this.currentlyExecuting) {
	  try {
		this.currentlyExecuting = true;
		this.callback(this);
	  } finally {
		this.currentlyExecuting = false;
	  }
	}
  }
}
String.interpret = function(value){
  return value == null ? '' : String(value);
}

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
	var result = '', source = this, match;
	replacement = arguments.callee.prepareReplacement(replacement);

	while (source.length > 0) {
	  if (match = source.match(pattern)) {
		result += source.slice(0, match.index);
		result += String.interpret(replacement(match));
		source  = source.slice(match.index + match[0].length);
	  } else {
		result += source, source = '';
	  }
	}
	return result;
  },

  sub: function(pattern, replacement, count) {
	replacement = this.gsub.prepareReplacement(replacement);
	count = count === undefined ? 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 this;
  },

  truncate: function(length, truncation) {
	length = length || 30;
	truncation = truncation === undefined ? '...' : truncation;
	return this.length > length ?
	  this.slice(0, length - truncation.length) + truncation : 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 div = document.createElement('div');
	var text = document.createTextNode(this);
	div.appendChild(text);
	return div.innerHTML;
  },

  unescapeHTML: function() {
	var div = document.createElement('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 name = decodeURIComponent(pair[0]);
		var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;

		if (hash[name] !== undefined) {
		  if (hash[name].constructor != Array)
			hash[name] = [hash[name]];
		  if (value) hash[name].push(value);
		}
		else hash[name] = 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);
  },

  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.replace(/\\/g, '\\\\');
	if (useDoubleQuotes)
	  return '"' + escapedString.replace(/"/g, '\\"') + '"';
	else
	  return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (typeof replacement == 'function') return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
}

String.prototype.parseQuery = String.prototype.toQueryParams;

var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
  initialize: function(template, pattern) {
	this.template = template.toString();
	this.pattern  = pattern || Template.Pattern;
  },

  evaluate: function(object) {
	return this.template.gsub(this.pattern, function(match) {
	  var before = match[1];
	  if (before == '\\') return match[2];
	  return before + String.interpret(object[match[3]]);
	});
  }
}

var $break    = new Object();
var $continue = new Object();

var Enumerable = {
  each: function(iterator) {
	var index = 0;
	try {
	  this._each(function(value) {
		try {
		  iterator(value, index++);
		} catch (e) {
		  if (e != $continue) throw e;
		}
	  });
	} catch (e) {
	  if (e != $break) throw e;
	}
	return this;
  },

  eachSlice: function(number, iterator) {
	var index = -number, slices = [], array = this.toArray();
	while ((index += number) < array.length)
	  slices.push(array.slice(index, index+number));
	return slices.map(iterator);
  },

  all: function(iterator) {
	var result = true;
	this.each(function(value, index) {
	  result = result && !!(iterator || Prototype.K)(value, index);
	  if (!result) throw $break;
	});
	return result;
  },

  any: function(iterator) {
	var result = false;
	this.each(function(value, index) {
	  if (result = !!(iterator || Prototype.K)(value, index))
		throw $break;
	});
	return result;
  },

  collect: function(iterator) {
	var results = [];
	this.each(function(value, index) {
	  results.push((iterator || Prototype.K)(value, index));
	});
	return results;
  },

  detect: function(iterator) {
	var result;
	this.each(function(value, index) {
	  if (iterator(value, index)) {
		result = value;
		throw $break;
	  }
	});
	return result;
  },

  findAll: function(iterator) {
	var results = [];
	this.each(function(value, index) {
	  if (iterator(value, index))
		results.push(value);
	});
	return results;
  },

  grep: function(pattern, iterator) {
	var results = [];
	this.each(function(value, index) {
	  var stringValue = value.toString();
	  if (stringValue.match(pattern))
		results.push((iterator || Prototype.K)(value, index));
	})
	return results;
  },

  include: function(object) {
	var found = false;
	this.each(function(value) {
	  if (value == object) {
		found = true;
		throw $break;
	  }
	});
	return found;
  },

  inGroupsOf: function(number, fillWith) {
	fillWith = fillWith === undefined ? null : fillWith;
	return this.eachSlice(number, function(slice) {
	  while(slice.length < number) slice.push(fillWith);
	  return slice;
	});
  },

  inject: function(memo, iterator) {
	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) {
	var result;
	this.each(function(value, index) {
	  value = (iterator || Prototype.K)(value, index);
	  if (result == undefined || value >= result)
		result = value;
	});
	return result;
  },

  min: function(iterator) {
	var result;
	this.each(function(value, index) {
	  value = (iterator || Prototype.K)(value, index);
	  if (result == undefined || value < result)
		result = value;
	});
	return result;
  },

  partition: function(iterator) {
	var trues = [], falses = [];
	this.each(function(value, index) {
	  ((iterator || Prototype.K)(value, index) ?
		trues : falses).push(value);
	});
	return [trues, falses];
  },

  pluck: function(property) {
	var results = [];
	this.each(function(value, index) {
	  results.push(value[property]);
	});
	return results;
  },

  reject: function(iterator) {
	var results = [];
	this.each(function(value, index) {
	  if (!iterator(value, index))
		results.push(value);
	});
	return results;
  },

  sortBy: function(iterator) {
	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 (typeof args.last() == 'function')
	  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,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
	return iterable.toArray();
  } else {
	var results = [];
	for (var i = 0, length = iterable.length; i < length; i++)
	  results.push(iterable[i]);
	return results;
  }
}

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(value && value.constructor == Array ?
		value.flatten() : [value]);
	});
  },

  without: function() {
	var values = $A(arguments);
	return this.select(function(value) {
	  return !values.include(value);
	});
  },

  indexOf: function(object) {
	for (var i = 0, length = this.length; i < length; i++)
	  if (this[i] == object) return i;
	return -1;
  },

  reverse: function(inline) {
	return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
	return this.length > 1 ? this : this[0];
  },

  uniq: function() {
	return this.inject([], function(array, value) {
	  return array.include(value) ? array : array.concat([value]);
	});
  },

  clone: function() {
	return [].concat(this);
  },

  size: function() {
	return this.length;
  },

  inspect: function() {
	return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});

Array.prototype.toArray = Array.prototype.clone;

function $w(string){
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if(window.opera){
  Array.prototype.concat = function(){
	var array = [];
	for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
	for(var i = 0, length = arguments.length; i < length; i++) {
	  if(arguments[i].constructor == Array) {
		for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
		  array.push(arguments[i][j]);
	  } else {
		array.push(arguments[i]);
	  }
	}
	return array;
  }
}
var Hash = function(obj) {
  Object.extend(this, obj || {});
};

Object.extend(Hash, {
  toQueryString: function(obj) {
	var parts = [];

	  this.prototype._each.call(obj, function(pair) {
	  if (!pair.key) return;

	  if (pair.value && pair.value.constructor == Array) {
		var values = pair.value.compact();
		if (values.length < 2) pair.value = values.reduce();
		else {
			key = encodeURIComponent(pair.key);
		  values.each(function(value) {
			value = value != undefined ? encodeURIComponent(value) : '';
			parts.push(key + '=' + encodeURIComponent(value));
		  });
		  return;
		}
	  }
	  if (pair.value == undefined) pair[1] = '';
	  parts.push(pair.map(encodeURIComponent).join('='));
	  });

	return parts.join('&');
  }
});

Object.extend(Hash.prototype, Enumerable);
Object.extend(Hash.prototype, {
  _each: function(iterator) {
	for (var key in this) {
	  var value = this[key];
	  if (value && value == Hash.prototype[key]) continue;

	  var pair = [key, value];
	  pair.key = key;
	  pair.value = value;
	  iterator(pair);
	}
  },

  keys: function() {
	return this.pluck('key');
  },

  values: function() {
	return this.pluck('value');
  },

  merge: function(hash) {
	return $H(hash).inject(this, function(mergedHash, pair) {
	  mergedHash[pair.key] = pair.value;
	  return mergedHash;
	});
  },

  remove: function() {
	var result;
	for(var i = 0, length = arguments.length; i < length; i++) {
	  var value = this[arguments[i]];
	  if (value !== undefined){
		if (result === undefined) result = value;
		else {
		  if (result.constructor != Array) result = [result];
		  result.push(value)
		}
	  }
	  delete this[arguments[i]];
	}
	return result;
  },

  toQueryString: function() {
	return Hash.toQueryString(this);
  },

  inspect: function() {
	return '#<Hash:{' + this.map(function(pair) {
	  return pair.map(Object.inspect).join(': ');
	}).join(', ') + '}>';
  }
});

function $H(object) {
  if (object && object.constructor == Hash) return object;
  return new Hash(object);
};
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
  initialize: function(start, end, exclusive) {
	this.start = start;
	this.end = end;
	this.exclusive = exclusive;
  },

  _each: function(iterator) {
	var value = this.start;
	while (this.include(value)) {
	  iterator(value);
	  value = value.succ();
	}
  },

  include: function(value) {
	if (value < this.start)
	  return false;
	if (this.exclusive)
	  return value < this.end;
	return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}

var Ajax = {
  getTransport: function() {
	return Try.these(
	  function() {return new XMLHttpRequest()},
	  function() {return new ActiveXObject('Msxml2.XMLHTTP')},
	  function() {return new ActiveXObject('Microsoft.XMLHTTP')}
	) || false;
  },

  activeRequestCount: 0
}

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
	this.responders._each(iterator);
  },

  register: function(responder) {
	if (!this.include(responder))
	  this.responders.push(responder);
  },

  unregister: function(responder) {
	this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
	this.each(function(responder) {
	  if (typeof responder[callback] == 'function') {
		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 = function() {};
Ajax.Base.prototype = {
  setOptions: function(options) {
	this.options = {
	  method:       'post',
	  asynchronous: true,
	  contentType:  'application/x-www-form-urlencoded',
	  encoding:     'UTF-8',
	  parameters:   ''
	}
	Object.extend(this.options, options || {});

	this.options.method = this.options.method.toLowerCase();
	if (typeof this.options.parameters == 'string')
	  this.options.parameters = this.options.parameters.toQueryParams();
  }
}

Ajax.Request = Class.create();
Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  _complete: false,

  initialize: function(url, options) {
	this.transport = Ajax.getTransport();
	this.setOptions(options);
	this.request(url);
  },

  request: function(url) {
	this.url = url;
	this.method = this.options.method;
	var params = this.options.parameters;

	if (!['get', 'post'].include(this.method)) {
	  // simulate other verbs over post
	  params['_method'] = this.method;
	  this.method = 'post';
	}

	params = Hash.toQueryString(params);
	if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='

	// when GET, append parameters to URL
	if (this.method == 'get' && params)
	  this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;

	try {
	  Ajax.Responders.dispatch('onCreate', this, this.transport);

	  this.transport.open(this.method.toUpperCase(), this.url,
		this.options.asynchronous);

	  if (this.options.asynchronous)
		setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);

	  this.transport.onreadystatechange = this.onStateChange.bind(this);
	  this.setRequestHeaders();

	  var body = this.method == 'post' ? (this.options.postBody || params) : null;

	  this.transport.send(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 (typeof extras.push == 'function')
		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() {
	return !this.transport.status
		|| (this.transport.status >= 200 && this.transport.status < 300);
  },

  respondToReadyState: function(readyState) {
	var state = Ajax.Request.Events[readyState];
	var transport = this.transport, json = this.evalJSON();

	if (state == 'Complete') {
	  try {
		this._complete = true;
		(this.options['on' + this.transport.status]
		 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
		 || Prototype.emptyFunction)(transport, json);
	  } catch (e) {
		this.dispatchException(e);
	  }

	  if ((this.getHeader('Content-type') || 'text/javascript').strip().
		match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
		  this.evalResponse();
	}

	try {
	  (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
	  Ajax.Responders.dispatch('on' + state, this, transport, json);
	} catch (e) {
	  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 }
  },

  evalJSON: function() {
	try {
	  var json = this.getHeader('X-JSON');
	  return json ? eval('(' + json + ')') : null;
	} catch (e) { return null }
  },

  evalResponse: function() {
	try {
	  return eval(this.transport.responseText);
	} catch (e) {
	  this.dispatchException(e);
	}
  },

  dispatchException: function(exception) {
	(this.options.onException || Prototype.emptyFunction)(this, exception);
	Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  initialize: function(container, url, options) {
	this.container = {
	  success: (container.success || container),
	  failure: (container.failure || (container.success ? null : container))
	}

	this.transport = Ajax.getTransport();
	this.setOptions(options);

	var onComplete = this.options.onComplete || Prototype.emptyFunction;
	this.options.onComplete = (function(transport, param) {
	  this.updateContent();
	  onComplete(transport, param);
	}).bind(this);

	this.request(url);
  },

  updateContent: function() {
	var receiver = this.container[this.success() ? 'success' : 'failure'];
	var response = this.transport.responseText;

	if (!this.options.evalScripts) response = response.stripScripts();

	if (receiver = $(receiver)) {
	  if (this.options.insertion)
		new this.options.insertion(receiver, response);
	  else
		receiver.update(response);
	}

	if (this.success()) {
	  if (this.onComplete)
		setTimeout(this.onComplete.bind(this), 10);
	}
  }
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(container, url, options) {
	this.setOptions(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(request) {
	if (this.options.decay) {
	  this.decay = (request.responseText == this.lastText ?
		this.decay * this.options.decay : 1);

	  this.lastText = request.responseText;
	}
	this.timer = setTimeout(this.onTimerEvent.bind(this),
	  this.decay * this.frequency * 1000);
  },

  onTimerEvent: function() {
	this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
	for (var i = 0, elements = [], length = arguments.length; i < length; i++)
	  elements.push($(arguments[i]));
	return elements;
  }
  if (typeof element == 'string')
	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(query.snapshotItem(i));
	return results;
  };
}

document.getElementsByClassName = function(className, parentElement) {
  if (Prototype.BrowserFeatures.XPath) {
	var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
	return document._getElementsByXPath(q, parentElement);
  } else {
	var children = ($(parentElement) || document.body).getElementsByTagName('*');
	var elements = [], child;
	for (var i = 0, length = children.length; i < length; i++) {
	  child = children[i];
	  if (Element.hasClassName(child, className))
		elements.push(Element.extend(child));
	}
	return elements;
  }
};

/*--------------------------------------------------------------------------*/

if (!window.Element)
  var Element = new Object();

Element.extend = function(element) {
  if (!element || _nativeExtensions || element.nodeType == 3) return element;

  if (!element._extended && element.tagName && element != window) {
	var methods = Object.clone(Element.Methods), cache = Element.extend.cache;

	if (element.tagName == 'FORM')
	  Object.extend(methods, Form.Methods);
	if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
	  Object.extend(methods, Form.Element.Methods);

	Object.extend(methods, Element.Methods.Simulated);

	for (var property in methods) {
	  var value = methods[property];
	  if (typeof value == 'function' && !(property in element))
		element[property] = cache.findOrStore(value);
	}
  }

  element._extended = true;
  return element;
};

Element.extend.cache = {
  findOrStore: function(value) {
	return this[value] = this[value] || function() {
	  return value.apply(null, [this].concat($A(arguments)));
	}
  }
};

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, html) {
	html = typeof html == 'undefined' ? '' : html.toString();
	$(element).innerHTML = html.stripScripts();
	setTimeout(function() {html.evalScripts()}, 10);
	return element;
  },

  replace: function(element, html) {
	element = $(element);
	html = typeof html == 'undefined' ? '' : html.toString();
	if (element.outerHTML) {
	  element.outerHTML = html.stripScripts();
	} else {
	  var range = element.ownerDocument.createRange();
	  range.selectNodeContents(element);
	  element.parentNode.replaceChild(
		range.createContextualFragment(html.stripScripts()), element);
	}
	setTimeout(function() {html.evalScripts()}, 10);
	return element;
  },

  inspect: function(element) {
	element = $(element);
	var result = '<' + element.tagName.toLowerCase();
	$H({'id': 'id', 'className': 'class'}).each(function(pair) {
	  var property = pair.first(), attribute = pair.last();
	  var value = (element[property] || '').toString();
	  if (value) result += ' ' + attribute + '=' + value.inspect(true);
	});
	return result + '>';
  },

  recursivelyCollect: function(element, property) {
	element = $(element);
	var elements = [];
	while (element = element[property])
	  if (element.nodeType == 1)
		elements.push(Element.extend(element));
	return elements;
  },

  ancestors: function(element) {
	return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
	return $A($(element).getElementsByTagName('*'));
  },

  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 (typeof selector == 'string')
	  selector = new Selector(selector);
	return selector.match($(element));
  },

  up: function(element, expression, index) {
	return Selector.findElement($(element).ancestors(), expression, index);
  },

  down: function(element, expression, index) {
	return Selector.findElement($(element).descendants(), expression, index);
  },

  previous: function(element, expression, index) {
	return Selector.findElement($(element).previousSiblings(), expression, index);
  },

  next: function(element, expression, index) {
	return Selector.findElement($(element).nextSiblings(), expression, index);
  },

  getElementsBySelector: function() {
	var args = $A(arguments), element = $(args.shift());
	return Selector.findChildElements(element, args);
  },

  getElementsByClassName: function(element, className) {
	return document.getElementsByClassName(className, element);
  },

  readAttribute: function(element, name) {
	element = $(element);
	if (document.all && !window.opera) {
	  var t = Element._attributeTranslations;
	  if (t.values[name]) return t.values[name](element, name);
	  if (t.names[name])  name = t.names[name];
	  var attribute = element.attributes[name];
	  if(attribute) return attribute.nodeValue;
	}
	return element.getAttribute(name);
  },

  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;
	if (elementClassName.length == 0) return false;
	if (elementClassName == className ||
		elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
	  return true;
	return false;
  },

  addClassName: function(element, className) {
	if (!(element = $(element))) return;
	Element.classNames(element).add(className);
	return element;
  },

  removeClassName: function(element, className) {
	if (!(element = $(element))) return;
	Element.classNames(element).remove(className);
	return element;
  },

  toggleClassName: function(element, className) {
	if (!(element = $(element))) return;
	Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
	return element;
  },

  observe: function() {
	Event.observe.apply(Event, arguments);
	return $A(arguments).first();
  },

  stopObserving: function() {
	Event.stopObserving.apply(Event, arguments);
	return $A(arguments).first();
  },

  // 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.match(/^\s*$/);
  },

  descendantOf: function(element, ancestor) {
	element = $(element), ancestor = $(ancestor);
	while (element = element.parentNode)
	  if (element == ancestor) return true;
	return false;
  },

  scrollTo: function(element) {
	element = $(element);
	var pos = Position.cumulativeOffset(element);
	window.scrollTo(pos[0], pos[1]);
	return element;
  },

  getStyle: function(element, style) {
	element = $(element);
	if (['float','cssFloat'].include(style))
	  style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
	style = style.camelize();
	var value = element.style[style];
	if (!value) {
	  if (document.defaultView && document.defaultView.getComputedStyle) {
		var css = document.defaultView.getComputedStyle(element, null);
		value = css ? css[style] : null;
	  } else if (element.currentStyle) {
		value = element.currentStyle[style];
	  }
	}

	if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
	  value = element['offset'+style.capitalize()] + 'px';

	if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
	  if (Element.getStyle(element, 'position') == 'static') value = 'auto';
	if(style == 'opacity') {
	  if(value) return parseFloat(value);
	  if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
		if(value[1]) return parseFloat(value[1]) / 100;
	  return 1.0;
	}
	return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
	element = $(element);
	for (var name in style) {
	  var value = style[name];
	  if(name == 'opacity') {
		if (value == 1) {
		  value = (/Gecko/.test(navigator.userAgent) &&
			!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
		  if(/MSIE/.test(navigator.userAgent) && !window.opera)
			element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
		} else if(value === '') {
		  if(/MSIE/.test(navigator.userAgent) && !window.opera)
			element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
		} else {
		  if(value < 0.00001) value = 0;
		  if(/MSIE/.test(navigator.userAgent) && !window.opera)
			element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
			  'alpha(opacity='+value*100+')';
		}
	  } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
	  element.style[name.camelize()] = 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.style.overflow || 'auto';
	if ((Element.getStyle(element, 'overflow') || 'visible') != '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;
  }
};

Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});

Element._attributeTranslations = {};

Element._attributeTranslations.names = {
  colspan:   "colSpan",
  rowspan:   "rowSpan",
  valign:    "vAlign",
  datetime:  "dateTime",
  accesskey: "accessKey",
  tabindex:  "tabIndex",
  enctype:   "encType",
  maxlength: "maxLength",
  readonly:  "readOnly",
  longdesc:  "longDesc"
};

Element._attributeTranslations.values = {
  _getAttr: function(element, attribute) {
	return element.getAttribute(attribute, 2);
  },

  _flag: function(element, attribute) {
	return $(element).hasAttribute(attribute) ? attribute : null;
  },

  style: function(element) {
	return element.style.cssText.toLowerCase();
  },

  title: function(element) {
	var node = element.getAttributeNode('title');
	return node.specified ? node.nodeValue : null;
  }
};

Object.extend(Element._attributeTranslations.values, {
  href: Element._attributeTranslations.values._getAttr,
  src:  Element._attributeTranslations.values._getAttr,
  disabled: Element._attributeTranslations.values._flag,
  checked:  Element._attributeTranslations.values._flag,
  readonly: Element._attributeTranslations.values._flag,
  multiple: Element._attributeTranslations.values._flag
});

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
	var t = Element._attributeTranslations;
	attribute = t.names[attribute] || attribute;
	return $(element).getAttributeNode(attribute).specified;
  }
};

// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
  Element.Methods.update = function(element, html) {
	element = $(element);
	html = typeof html == 'undefined' ? '' : html.toString();
	var tagName = element.tagName.toUpperCase();
	if (['THEAD','TBODY','TR','TD'].include(tagName)) {
	  var div = document.createElement('div');
	  switch (tagName) {
		case 'THEAD':
		case 'TBODY':
		  div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
		  depth = 2;
		  break;
		case 'TR':
		  div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
		  depth = 3;
		  break;
		case 'TD':
		  div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
		  depth = 4;
	  }
	  $A(element.childNodes).each(function(node){
		element.removeChild(node)
	  });
	  depth.times(function(){ div = div.firstChild });

	  $A(div.childNodes).each(
		function(node){ element.appendChild(node) });
	} else {
	  element.innerHTML = html.stripScripts();
	}
	setTimeout(function() {html.evalScripts()}, 10);
	return element;
  }
};

Object.extend(Element, Element.Methods);

var _nativeExtensions = false;

if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
	var className = 'HTML' + tag + 'Element';
	if(window[className]) return;
	var klass = window[className] = {};
	klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
  });

Element.addMethods = function(methods) {
  Object.extend(Element.Methods, methods || {});

  function copy(methods, destination, onlyIfAbsent) {
	onlyIfAbsent = onlyIfAbsent || false;
	var cache = Element.extend.cache;
	for (var property in methods) {
	  var value = methods[property];
	  if (!onlyIfAbsent || !(property in destination))
		destination[property] = cache.findOrStore(value);
	}
  }

  if (typeof HTMLElement != 'undefined') {
	copy(Element.Methods, HTMLElement.prototype);
	copy(Element.Methods.Simulated, HTMLElement.prototype, true);
	copy(Form.Methods, HTMLFormElement.prototype);
	[HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
	  copy(Form.Element.Methods, klass.prototype);
	});
	_nativeExtensions = true;
  }
}

var Toggle = new Object();
Toggle.display = Element.toggle;

/*--------------------------------------------------------------------------*/

Abstract.Insertion = function(adjacency) {
  this.adjacency = adjacency;
}

Abstract.Insertion.prototype = {
  initialize: function(element, content) {
	this.element = $(element);
	this.content = content.stripScripts();

	if (this.adjacency && this.element.insertAdjacentHTML) {
	  try {
		this.element.insertAdjacentHTML(this.adjacency, this.content);
	  } catch (e) {
		var tagName = this.element.tagName.toUpperCase();
		if (['TBODY', 'TR'].include(tagName)) {
		  this.insertContent(this.contentFromAnonymousTable());
		} else {
		  throw e;
		}
	  }
	} else {
	  this.range = this.element.ownerDocument.createRange();
	  if (this.initializeRange) this.initializeRange();
	  this.insertContent([this.range.createContextualFragment(this.content)]);
	}

	setTimeout(function() {content.evalScripts()}, 10);
  },

  contentFromAnonymousTable: function() {
	var div = document.createElement('div');
	div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
	return $A(div.childNodes[0].childNodes[0].childNodes);
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  initializeRange: function() {
	this.range.setStartBefore(this.element);
  },

  insertContent: function(fragments) {
	fragments.each((function(fragment) {
	  this.element.parentNode.insertBefore(fragment, this.element);
	}).bind(this));
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  initializeRange: function() {
	this.range.selectNodeContents(this.element);
	this.range.collapse(true);
  },

  insertContent: function(fragments) {
	fragments.reverse(false).each((function(fragment) {
	  this.element.insertBefore(fragment, this.element.firstChild);
	}).bind(this));
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  initializeRange: function() {
	this.range.selectNodeContents(this.element);
	this.range.collapse(this.element);
  },

  insertContent: function(fragments) {
	fragments.each((function(fragment) {
	  this.element.appendChild(fragment);
	}).bind(this));
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  initializeRange: function() {
	this.range.setStartAfter(this.element);
  },

  insertContent: function(fragments) {
	fragments.each((function(fragment) {
	  this.element.parentNode.insertBefore(fragment,
		this.element.nextSibling);
	}).bind(this));
  }
});

/*--------------------------------------------------------------------------*/

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);
var Selector = Class.create();
Selector.prototype = {
  initialize: function(expression) {
	this.params = {classNames: []};
	this.expression = expression.toString().strip();
	this.parseExpression();
	this.compileMatcher();
  },

  parseExpression: function() {
	function abort(message) { throw 'Parse error in selector: ' + message; }

	if (this.expression == '')  abort('empty expression');

	var params = this.params, expr = this.expression, match, modifier, clause, rest;
	while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
	  params.attributes = params.attributes || [];
	  params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
	  expr = match[1];
	}

	if (expr == '*') return this.params.wildcard = true;

	while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
	  modifier = match[1], clause = match[2], rest = match[3];
	  switch (modifier) {
		case '#':       params.id = clause; break;
		case '.':       params.classNames.push(clause); break;
		case '':
		case undefined: params.tagName = clause.toUpperCase(); break;
		default:        abort(expr.inspect());
	  }
	  expr = rest;
	}

	if (expr.length > 0) abort(expr.inspect());
  },

  buildMatchExpression: function() {
	var params = this.params, conditions = [], clause;

	if (params.wildcard)
	  conditions.push('true');
	if (clause = params.id)
	  conditions.push('element.readAttribute("id") == ' + clause.inspect());
	if (clause = params.tagName)
	  conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
	if ((clause = params.classNames).length > 0)
	  for (var i = 0, length = clause.length; i < length; i++)
		conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
	if (clause = params.attributes) {
	  clause.each(function(attribute) {
		var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
		var splitValueBy = function(delimiter) {
		  return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
		}

		switch (attribute.operator) {
		  case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
		  case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
		  case '|=':      conditions.push(
							splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
						  ); break;
		  case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
		  case '':
		  case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
		  default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
		}
	  });
	}

	return conditions.join(' && ');
  },

  compileMatcher: function() {
	this.match = new Function('element', 'if (!element.tagName) return false; \
	  element = $(element); \
	  return ' + this.buildMatchExpression());
  },

  findElements: function(scope) {
	var element;

	if (element = $(this.params.id))
	  if (this.match(element))
		if (!scope || Element.childOf(element, scope))
		  return [element];

	scope = (scope || document).getElementsByTagName(this.params.tagName || '*');

	var results = [];
	for (var i = 0, length = scope.length; i < length; i++)
	  if (this.match(element = scope[i]))
		results.push(Element.extend(element));

	return results;
  },

  toString: function() {
	return this.expression;
  }
}

Object.extend(Selector, {
  matchElements: function(elements, expression) {
	var selector = new Selector(expression);
	return elements.select(selector.match.bind(selector)).map(Element.extend);
  },

  findElement: function(elements, expression, index) {
	if (typeof expression == 'number') index = expression, expression = false;
	return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
	return expressions.map(function(expression) {
	  return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
		var selector = new Selector(expr);
		return results.inject([], function(elements, result) {
		  return elements.concat(selector.findElements(result || element));
		});
	  });
	}).flatten();
  }
});

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
	$(form).reset();
	return form;
  },

  serializeElements: function(elements, getHash) {
	var data = elements.inject({}, function(result, element) {
	  if (!element.disabled && element.name) {
		var key = element.name, value = $(element).getValue();
		if (value != undefined) {
		  if (result[key]) {
			if (result[key].constructor != Array) result[key] = [result[key]];
			result[key].push(value);
		  }
		  else result[key] = value;
		}
	  }
	  return result;
	});

	return getHash ? data : Hash.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, getHash) {
	return Form.serializeElements(Form.getElements(form), getHash);
  },

  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().each(function(element) {
	  element.blur();
	  element.disabled = 'true';
	});
	return form;
  },

  enable: function(form) {
	form = $(form);
	form.getElements().each(function(element) {
	  element.disabled = '';
	});
	return form;
  },

  findFirstElement: function(form) {
	return $(form).getElements().find(function(element) {
	  return element.type != 'hidden' && !element.disabled &&
		['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
	});
  },

  focusFirstElement: function(form) {
	form = $(form);
	form.findFirstElement().activate();
	return form;
  }
}

Object.extend(Form, Form.Methods);

/*--------------------------------------------------------------------------*/

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 Hash.toQueryString(pair);
	  }
	}
	return '';
  },

  getValue: function(element) {
	element = $(element);
	var method = element.tagName.toLowerCase();
	return Form.Element.Serializers[method](element);
  },

  clear: function(element) {
	$(element).value = '';
	return element;
  },

  present: function(element) {
	return $(element).value != '';
  },

  activate: function(element) {
	element = $(element);
	element.focus();
	if (element.select && ( element.tagName.toLowerCase() != 'input' ||
	  !['button', 'reset', 'submit'].include(element.type) ) )
	  element.select();
	return element;
  },

  disable: function(element) {
	element = $(element);
	element.disabled = true;
	return element;
  },

  enable: function(element) {
	element = $(element);
	element.blur();
	element.disabled = false;
	return element;
  }
}

Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;
var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element) {
	switch (element.type.toLowerCase()) {
	  case 'checkbox':
	  case 'radio':
		return Form.Element.Serializers.inputSelector(element);
	  default:
		return Form.Element.Serializers.textarea(element);
	}
  },

  inputSelector: function(element) {
	return element.checked ? element.value : null;
  },

  textarea: function(element) {
	return element.value;
  },

  select: function(element) {
	return this[element.type == 'select-one' ?
	  'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
	var index = element.selectedIndex;
	return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
	var values, length = element.length;
	if (!length) return null;

	for (var i = 0, values = []; i < length; i++) {
	  var opt = element.options[i];
	  if (opt.selected) values.push(this.optionValue(opt));
	}
	return values;
  },

  optionValue: function(opt) {
	// extend element because hasAttribute may not be native
	return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
}

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
  initialize: function(element, frequency, callback) {
	this.frequency = frequency;
	this.element   = $(element);
	this.callback  = callback;

	this.lastValue = this.getValue();
	this.registerCallback();
  },

  registerCallback: function() {
	setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  onTimerEvent: function() {
	var value = this.getValue();
	var changed = ('string' == typeof this.lastValue && 'string' == typeof value
	  ? this.lastValue != value : String(this.lastValue) != String(value));
	if (changed) {
	  this.callback(this.element, value);
	  this.lastValue = value;
	}
  }
}

Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
	return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
	return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
  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.bind(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();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
	return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
	return Form.serialize(this.element);
  }
});
if (!window.Event) {
  var Event = new Object();
}

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,

  element: function(event) {
	return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
	return (((event.which) && (event.which == 1)) ||
			((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
	return event.pageX || (event.clientX +
	  (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
	return event.pageY || (event.clientY +
	  (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
	if (event.preventDefault) {
	  event.preventDefault();
	  event.stopPropagation();
	} else {
	  event.returnValue = false;
	  event.cancelBubble = true;
	}
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
	var element = Event.element(event);
	while (element.parentNode && (!element.tagName ||
		(element.tagName.toUpperCase() != tagName.toUpperCase())))
	  element = element.parentNode;
	return element;
  },

  observers: false,

  _observeAndCache: function(element, name, observer, useCapture) {
	if (!this.observers) this.observers = [];
	if (element.addEventListener) {
	  this.observers.push([element, name, observer, useCapture]);
	  element.addEventListener(name, observer, useCapture);
	} else if (element.attachEvent) {
	  this.observers.push([element, name, observer, useCapture]);
	  element.attachEvent('on' + name, observer);
	}
  },

  unloadCache: function() {
	if (!Event.observers) return;
	for (var i = 0, length = Event.observers.length; i < length; i++) {
	  Event.stopObserving.apply(this, Event.observers[i]);
	  Event.observers[i][0] = null;
	}
	Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
	element = $(element);
	useCapture = useCapture || false;

	if (name == 'keypress' &&
		(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
		|| element.attachEvent))
	  name = 'keydown';

	Event._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
	element = $(element);
	useCapture = useCapture || false;

	if (name == 'keypress' &&
		(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
		|| element.detachEvent))
	  name = 'keydown';

	if (element.removeEventListener) {
	  element.removeEventListener(name, observer, useCapture);
	} else if (element.detachEvent) {
	  try {
		element.detachEvent('on' + name, observer);
	  } catch (e) {}
	}
  }
});

/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
  Event.observe(window, 'unload', Event.unloadCache, false);
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;
  },

  realOffset: function(element) {
	var valueT = 0, valueL = 0;
	do {
	  valueT += element.scrollTop  || 0;
	  valueL += element.scrollLeft || 0;
	  element = element.parentNode;
	} while (element);
	return [valueL, valueT];
  },

  cumulativeOffset: function(element) {
	var valueT = 0, valueL = 0;
	do {
	  valueT += element.offsetTop  || 0;
	  valueL += element.offsetLeft || 0;
	  element = element.offsetParent;
	} while (element);
	return [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 [valueL, valueT];
  },

  offsetParent: function(element) {
	if (element.offsetParent) return element.offsetParent;
	if (element == document.body) return element;

	while ((element = element.parentNode) && element != document.body)
	  if (Element.getStyle(element, 'position') != 'static')
		return element;

	return document.body;
  },

  // 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 = this.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 = this.realOffset(element);

	this.xcomp = x + offsetcache[0] - this.deltaX;
	this.ycomp = y + offsetcache[1] - this.deltaY;
	this.offset = this.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;
  },

  page: 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)
		if (Element.getStyle(element,'position')=='absolute') break;

	} while (element = element.offsetParent);

	element = forElement;
	do {
	  if (!window.opera || element.tagName=='BODY') {
		valueT -= element.scrollTop  || 0;
		valueL -= element.scrollLeft || 0;
	  }
	} while (element = element.parentNode);

	return [valueL, valueT];
  },

  clone: function(source, target) {
	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 = Position.page(source);

	// find coordinate system to use
	target = $(target);
	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(target,'position') == 'absolute') {
	  parent = Position.offsetParent(target);
	  delta = Position.page(parent);
	}

	// 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)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
	if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
	if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
	if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  },

  absolutize: function(element) {
	element = $(element);
	if (element.style.position == 'absolute') return;
	Position.prepare();

	var offsets = Position.positionedOffset(element);
	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';
  },

  relativize: function(element) {
	element = $(element);
	if (element.style.position == 'relative') return;
	Position.prepare();

	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;
  }
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  Position.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 [valueL, valueT];
  }
}

Element.addMethods();
var Scriptaculous = {
  Version: '1.6.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>');
  },
  load: function() {
	if((typeof Prototype=='undefined') || 
	   (typeof Element == 'undefined') || 
	   (typeof Element.Methods=='undefined') ||
	   parseFloat(Prototype.Version.split(".")[0] + "." +
				  Prototype.Version.split(".")[1]) < 1.5)
	   throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
/*
	USING compine.php instead 
		
	$A(document.getElementsByTagName("script")).findAll( function(s) {
	  return (s.src && s.src.match(/scripts\.js(\?.*)?$/))
	}).each( function(s) {
	  var path = s.src.replace(/scripts\.js(\?.*)?$/,'');
	  var includes = s.src.match(/\?.*load=([a-zA-Z0-9_,]*)/);
	  (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,ajax,autoAjax,dhtmlHistoryjson,json,swfobject').split(',').each(
	   function(include) { Scriptaculous.require(path+include+'.js') });
	});
*/    
  }
}

Scriptaculous.load();

// JavaScript Document
// taille de la fenetre
var screenSizeX = 0;
var screenSizeY = 0;
function getScreenSize () {
	if (self.innerWidth) {
		screenSizeX = self.innerWidth;
		screenSizeY = self.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientWidth) {
		screenSizeX = document.documentElement.clientWidth;
		screenSizeY = document.documentElement.clientHeight;
	}
	else if (document.body) {
		screenSizeX = document.body.clientWidth;
		screenSizeY = document.body.clientHeight;
	}
}
getScreenSize ();

function findPosX(obj) {
	var curleft = 0;
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
		
//	if (document.documentElement)
//		curleft -= document.documentElement.scrollLeft;
		
	return curleft;
}

function findPosY(obj) {
	var curtop = 0;
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		curtop += obj.y;

//	if (document.documentElement)
//		curtop -= document.documentElement.scrollTop;

	return curtop;
}

Tab = Class.create();
Tab.prototype = {
	setOptions: function(options) {
		this.options = {
			delay: 0.1
		}
		Object.extend(this.options, options || {});
	},
	
	initialize: function(element, options, show) {
		this.element = element;
		this.setOptions(options);

		if (show == false)
		{
			element.style.display = 'none';
			
			if (this.options.opacity == true)
				element.setOpacity (0);
		}
	},

	close: function(){
		if (this.element.style.display != 'none')
			this.toggle ();
	},
	show: function(){
		if (this.element.style.display == 'none')
			this.toggle ();
	},
	toggle: function(){
		setTimeout(function(){
			if (this.element.style.display == 'none') {
				new Effect.BlindDown  (this.element, { duration:this.options.duration,afterFinish:this.options.onComplete });
				if (this.options.opacity == true)
					new Effect.Opacity    (this.element, { duration:this.options.duration });
			} else  {
				new Effect.BlindUp	  (this.element, { duration:this.options.duration,afterFinish:this.options.onComplete });
				if (this.options.opacity == true)
					new Effect.Opacity	  (this.element, { duration:this.options.duration,from:1,to:0 });
			}
		}.bind(this), this.options.delay*1000);
	}
}

function createPng (parent, id, src, w, h, options) {
	var s = '<img id='+id;
	if (browser.name == "explorer" && browser.version < 7) {
		s += " style=\"FILTER: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='scale')";
		if (w)
			s += "width:"+w+"px;";
		if (h)
			s += "height:"+h+"px;";
		s +="\"";
		s += ' src="'+img_server+'i/null.png"';
	} else
		s += ' src="'+src+'"';
		
	if (w)
		s+= ' width='+w;
	if (h)
		s+= ' height='+h;
	if (options)
		s+= ' '+options;
	s += '>';

	$(parent).innerHTML += s;
}

function escape2 (text) {
	return escape (text).replace(/\+/g, "%2B");
}

// Custom dropCheckList
var dl_opened = null;
var dl_timeout = null;
function dl_openContainer(elem) {
	elem = $(elem);
	
	if (dl_opened == elem)
		elem = null;		
	
	if (dl_opened)
		dl_opened.hide ();

	if (elem)		
		elem.show ();
		
	dl_opened = elem;
}
function dl_closeContainer() {
	dl_timeout = setTimeout ("dl_openContainer(null)", 2000);
}
function dl_click(id) {
	var str = '';
	var elem;
	var value = 0;
	
	for (var i=1; i<100; i++) {
		elem = $(id+'_'+i);
		if (elem == null)
			break;
			
		if (elem.checked == false)
			continue;
			
		value |= Math.pow (2, i);
					
		if (str != '')
			str += ', ';
			
		str += $(id+'_label'+i).innerHTML;
	}
	
	if (str == '')
		str = TEXT_CHOOSE != null ? TEXT_CHOOSE : 'choisir dans la liste ...';
		
	$(id).value = value;
	$(id+'_disp').innerHTML = str;
}

function customCheckClick (img) {
	if (img.disabled == true)
		return;

	var field = $('f'+img.id);
	var info = img.id.split ('_');
	
	if (field.value == '') {
		img.src = img_server+"i/check1.png";
		field.value = info[info.length-1];
	} else {
		img.src = img_server+"i/check0.png";
		field.value = '';
	}
	
	try { field.onchange (); } catch (e) { }
}

function emailValidate (emailStr) {
	var emailPat=/^(.+)@(.+)$/;
	var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
	var validChars="\[^\\s" + specialChars + "\]";
	var quotedUser="(\"[^\"]*\")";
	var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
	var atom=validChars + '+';
	var word="(" + atom + "|" + quotedUser + ")";
	var userPat=new RegExp("^" + word + "(\\." + word + ")*$");
	var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");

	var matchArray=emailStr.match(emailPat);

	if (matchArray==null)
		return false;

	var user=matchArray[1];
	var domain=matchArray[2];

	for (i=0; i<user.length; i++)
		if (user.charCodeAt(i)>127)
			return false;

	for (i=0; i<domain.length; i++)
		if (domain.charCodeAt(i)>127)
			return false;

	if (user.match(userPat)==null)
		return false;

	var IPArray=domain.match(ipDomainPat);
	if (IPArray!=null) {
		// this is an IP address
		for (var i=1;i<=4;i++)
			if (IPArray[i]>255)
				return false;

		return true;
	}

// Domain is symbolic name.  Check if it's valid.
	var atomPat=new RegExp("^" + atom + "$");
	var domArr=domain.split(".");
	var len=domArr.length;
	for (i=0;i<len;i++)
		if (domArr[i].search(atomPat)==-1) 
			return false;

// Make sure there's a host name preceding the domain.
	if (len<2)
		return false;

	return true;
}
// script.aculo.us builder.js v1.7.0, Fri Jan 19 19:16:36 CET 2007

// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// 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/

var Builder = {
  NODEMAP: {
	AREA: 'map',
	CAPTION: 'table',
	COL: 'table',
	COLGROUP: 'table',
	LEGEND: 'fieldset',
	OPTGROUP: 'select',
	OPTION: 'select',
	PARAM: 'object',
	TBODY: 'table',
	TD: 'table',
	TFOOT: 'table',
	TH: 'table',
	THEAD: 'table',
	TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
	elementName = elementName.toUpperCase();
	
	// try innerHTML approach
	var parentTag = this.NODEMAP[elementName] || 'div';
	var parentElement = document.createElement(parentTag);
	try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
	  parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
	} catch(e) {}
	var element = parentElement.firstChild || null;
	  
	// see if browser added wrapping tags
	if(element && (element.tagName.toUpperCase() != elementName))
	  element = element.getElementsByTagName(elementName)[0];
	
	// fallback to createElement approach
	if(!element) element = document.createElement(elementName);
	
	// abort if nothing could be created
	if(!element) return;

	// attributes (or text)
	if(arguments[1])
	  if(this._isStringOrNumber(arguments[1]) ||
		(arguments[1] instanceof Array)) {
		  this._children(element, arguments[1]);
		} else {
		  var attrs = this._attributes(arguments[1]);
		  if(attrs.length) {
			try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
			  parentElement.innerHTML = "<" +elementName + " " +
				attrs + "></" + elementName + ">";
			} catch(e) {}
			element = parentElement.firstChild || null;
			// workaround firefox 1.0.X bug
			if(!element) {
			  element = document.createElement(elementName);
			  for(attr in arguments[1]) 
				element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
			}
			if(element.tagName.toUpperCase() != elementName)
			  element = parentElement.getElementsByTagName(elementName)[0];
			}
		} 

	// text, or array of children
	if(arguments[2])
	  this._children(element, arguments[2]);

	 return element;
  },
  _text: function(text) {
	 return document.createTextNode(text);
  },

  ATTR_MAP: {
	'className': 'class',
	'htmlFor': 'for'
  },

  _attributes: function(attributes) {
	var attrs = [];
	for(attribute in attributes)
	  attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
		  '="' + attributes[attribute].toString().escapeHTML() + '"');
	return attrs.join(" ");
  },
  _children: function(element, children) {
	if(typeof children=='object') { // array can hold nodes and text
	  children.flatten().each( function(e) {
		if(typeof e=='object')
		  element.appendChild(e)
		else
		  if(Builder._isStringOrNumber(e))
			element.appendChild(Builder._text(e));
	  });
	} else
	  if(Builder._isStringOrNumber(children)) 
		 element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
	return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
	var element = this.node('div');
	$(element).update(html.strip());
	return element.down();
  },
  dump: function(scope) { 
	if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
  
	var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
	  "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
	  "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
	  "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
	  "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
	  "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
  
	tags.each( function(tag){ 
	  scope[tag] = function() { 
		return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
	  } 
	});
  }
}

var builder_loaded = true;// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007

// Copyright (c) 2005, 2006 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 = '#';
  if(this.slice(0,4) == 'rgb(') {  
	var cols = this.slice(4,this.length-1).split(',');  
	var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
	if(this.slice(0,1) == '#') {  
	  if(this.length==4) for(var 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(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
  return element;
}

Element.getOpacity = function(element){
  return $(element).getStyle('opacity');
}

Element.setOpacity = function(element, value){
  return $(element).setStyle({opacity:value});
}

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) { }
};

/*--------------------------------------------------------------------------*/

Array.prototype.call = function() {
  var args = arguments;
  this.each(function(f){ f.apply(this, args) });
}

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
	name: 'ElementDoesNotExistError',
	message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  tagifyText: function(element) {
	if(typeof Builder == 'undefined')
	  throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
	  
	var tagifyStyle = 'position:relative';
	if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
	
	element = $(element);
	$A(element.childNodes).each( function(child) {
	  if(child.nodeType==3) {
		child.nodeValue.toArray().each( function(character) {
		  element.insertBefore(
			Builder.node('span',{style: tagifyStyle},
			  character == ' ' ? String.fromCharCode(160) : character), 
			  child);
		});
		Element.remove(child);
	  }
	});
  },
  multiple: function(element, effect) {
	var elements;
	if(((typeof element == 'object') || 
		(typeof element == 'function')) && 
	   (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) {
	  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);
  }
};

var Effect2 = Effect; // deprecated

/* ------------- transitions ------------- */

Effect.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) {
	return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
  },
  wobble: function(pos) {
	return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
  },
  pulse: function(pos, pulses) { 
	pulses = pulses || 5; 
	return (
	  Math.round((pos % (1/pulses)) * pulses) == 0 ? 
			((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
		1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
	  );
  },
  none: function(pos) {
	return 0;
  },
  full: function(pos) {
	return 1;
  }
};

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, 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 = (typeof effect.options.queue == 'string') ? 
	  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++) 
	  if(this.effects[i]) this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
	if(typeof queueName != 'string') return queueName;
	
	if(!this.instances[queueName])
	  this.instances[queueName] = new Effect.ScopedQueue();
	  
	return this.instances[queueName];
  }
}
Effect.Queue = Effect.Queues.get('global');

Effect.DefaultOptions = {
  transition: Effect.Transitions.sinoidal,
  duration:   1.0,   // seconds
  fps:        60.0,  // max. 60fps due to Effect.Queue implementation
  sync:       false, // true for combining
  from:       0.0,
  to:         1.0,
  delay:      0.0,
  queue:      'parallel'
}

Effect.Base = function() {};
Effect.Base.prototype = {
  position: null,
  start: function(options) {
	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.event('beforeStart');
	if(!this.options.sync)
	  Effect.Queues.get(typeof this.options.queue == 'string' ? 
		'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.finishOn - this.startOn);
	  var frame = Math.round(pos * this.options.fps * this.options.duration);
	  if(frame > this.currentFrame) {
		this.render(pos);
		this.currentFrame = frame;
	  }
	}
  },
  render: function(pos) {
	if(this.state == 'idle') {
	  this.state = 'running';
	  this.event('beforeSetup');
	  if(this.setup) this.setup();
	  this.event('afterSetup');
	}
	if(this.state == 'running') {
	  if(this.options.transition) pos = this.options.transition(pos);
	  pos *= (this.options.to-this.options.from);
	  pos += this.options.from;
	  this.position = pos;
	  this.event('beforeUpdate');
	  if(this.update) this.update(pos);
	  this.event('afterUpdate');
	}
  },
  cancel: function() {
	if(!this.options.sync)
	  Effect.Queues.get(typeof this.options.queue == 'string' ? 
		'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(property in this)
	  if(typeof this[property] != 'function') data[property] = this[property];
	return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
}

Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
  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.Event = Class.create();
Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
  initialize: function() {
	var options = Object.extend({
	  duration: 0
	}, arguments[0] || {});
	this.start(options);
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
  initialize: function(element) {
	this.element = $(element);
	if(!this.element) throw(Effect._elementDoesNotExistError);
	// make this work on IE on elements without 'layout'
	if(/MSIE/.test(navigator.userAgent) && !window.opera && (!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();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
  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() {
	// Bug in Opera: Opera returns the "real" position of a static element or
	// relative element that does not have top/left explicitly set.
	// ==> Always set top and left for position relative elements in your stylesheets 
	// (to 0 if you do not need them) 
	this.element.makePositioned();
	this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
	this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
	if(this.options.mode == 'absolute') {
	  // absolute movement, so we need to calc deltaX and deltaY
	  this.options.x = this.options.x - this.originalLeft;
	  this.options.y = this.options.y - this.originalTop;
	}
  },
  update: function(position) {
	this.element.setStyle({
	  left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
	  top:  Math.round(this.options.y  * position + this.originalTop)  + '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();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
  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 = Math.round(width) + 'px';
	if(this.options.scaleY) d.height = Math.round(height) + '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();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
  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+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
  },
  finish: function() {
	this.element.setStyle(Object.extend(this.oldStyle, {
	  backgroundColor: this.options.restorecolor
	}));
  }
});

Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
  initialize: function(element) {
	this.element = $(element);
	this.start(arguments[1] || {});
  },
  setup: function() {
	Position.prepare();
	var offsets = Position.cumulativeOffset(this.element);
	if(this.options.offset) offsets[1] += this.options.offset;
	var max = window.innerHeight ? 
	  window.height - window.innerHeight :
	  document.body.scrollHeight - 
		(document.documentElement.clientHeight ? 
		  document.documentElement.clientHeight : document.body.clientHeight);
	this.scrollStart = Position.deltaY;
	this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
  },
  update: function(position) {
	Position.prepare();
	window.scrollTo(Position.deltaX, 
	  this.scrollStart + (position*this.delta));
  }
});

/* ------------- 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) {
	  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 oldStyle = {
	top: element.getStyle('top'),
	left: element.getStyle('left') };
	return new Effect.Move(element, 
	  { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
	new Effect.Move(effect.element,
	  { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	new Effect.Move(effect.element,
	  { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	new Effect.Move(effect.element,
	  { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	new Effect.Move(effect.element,
	  { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	new Effect.Move(effect.element,
	  { x: -20, y: 0, duration: 0.05, 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');
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
	scaleX: false, 
	scaleMode: 'box',
	scaleFrom: 100,
	restoreAfterFinish: true,
	beforeStartInternal: 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().setStyle({bottom: oldInnerBottom});
	  effect.element.down().undoPositioned();
	}
   }, 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) {
	  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) {
	new Effect.Scale(element, 1, { 
	  scaleContent: false, 
	  scaleY: false,
	  afterFinishInternal: function(effect) {
		effect.element.hide().undoClipping().setStyle(oldStyle);
	  } });
  }}, arguments[1] || {}));
};

Effect.Morph = Class.create();
Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
  initialize: function(element) {
	this.element = $(element);
	if(!this.element) throw(Effect._elementDoesNotExistError);
	var options = Object.extend({
	  style: {}
	}, arguments[1] || {});
	if (typeof options.style == 'string') {
	  if(options.style.indexOf(':') == -1) {
		var cssText = '', selector = '.' + options.style;
		$A(document.styleSheets).reverse().each(function(styleSheet) {
		  if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
		  else if (styleSheet.rules) cssRules = styleSheet.rules;
		  $A(cssRules).reverse().each(function(rule) {
			if (selector == rule.selectorText) {
			  cssText = rule.style.cssText;
			  throw $break;
			}
		  });
		  if (cssText) throw $break;
		});
		this.style = cssText.parseStyle();
		options.afterFinishInternal = function(effect){
		  effect.element.addClassName(effect.options.style);
		  effect.transforms.each(function(transform) {
			if(transform.style != 'opacity')
			  effect.element.style[transform.style.camelize()] = '';
		  });
		}
	  } else this.style = options.style.parseStyle();
	} else this.style = $H(options.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].underscore().dasherize(), value = pair[1], unit = null;

	  if(value.parseColor('#zzzzzz') != '#zzzzzz') {
		value = value.parseColor();
		unit  = 'color';
	  } else if(property == 'opacity') {
		value = parseFloat(value);
		if(/MSIE/.test(navigator.userAgent) && !window.opera && (!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 $H({ 
		style: property, 
		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 = $H(), value = null;
	this.transforms.each(function(transform){
	  value = transform.unit=='color' ?
		$R(0,2).inject('#',function(m,v,i){
		  return m+(Math.round(transform.originalValue[i]+
			(transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
		transform.originalValue + Math.round(
		  ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
	  style[transform.style] = value;
	});
	this.element.setStyle(style);
  }
});

Effect.Transform = Class.create();
Object.extend(Effect.Transform.prototype, {
  initialize: function(tracks){
	this.tracks  = [];
	this.options = arguments[1] || {};
	this.addTracks(tracks);
  },
  addTracks: function(tracks){
	tracks.each(function(track){
	  var data = $H(track).values().first();
	  this.tracks.push($H({
		ids:     $H(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 elements = [$(track.ids) || $$(track.ids)].flatten();
		return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.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.prototype.parseStyle = function(){
  var element = Element.extend(document.createElement('div'));
  element.innerHTML = '<div style="' + this + '"></div>';
  var style = element.down().style, styleRules = $H();
  
  Element.CSS_PROPERTIES.each(function(property){
	if(style[property]) styleRules[property] = style[property]; 
  });
  if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) {
	styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
  }
  return styleRules;
};

Element.morph = function(element, style) {
  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
  return element;
};

['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
  function(f) { Element.Methods[f] = Element[f]; }
);

Element.Methods.visualEffect = function(element, effect, options) {
  s = effect.gsub(/_/, '-').camelize();
  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
  new Effect[effect_class](element, options);
  return $(element);
};

Element.addMethods();

var effects_loaded = true;// script.aculo.us dragdrop.js v1.7.0, Fri Jan 19 19:16:36 CET 2007

// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
// 
// 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/

if(typeof Effect == 'undefined')
  throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
  drops: [],

  remove: function(element) {
	this.drops = this.drops.reject(function(d) { return d.element==$(element) });
  },

  add: function(element) {
	element = $(element);
	var options = Object.extend({
	  greedy:     true,
	  hoverclass: null,
	  tree:       false
	}, arguments[1] || {});

	// cache containers
	if(options.containment) {
	  options._containers = [];
	  var containment = options.containment;
	  if((typeof containment == 'object') && 
		(containment.constructor == Array)) {
		containment.each( function(c) { options._containers.push($(c)) });
	  } else {
		options._containers.push($(containment));
	  }
	}
	
	if(options.accept) options.accept = [options.accept].flatten();

	Element.makePositioned(element); // fix IE
	options.element = element;

	this.drops.push(options);
  },
  
  findDeepestChild: function(drops) {
	deepest = drops[0];
	  
	for (i = 1; i < drops.length; ++i)
	  if (Element.isParent(drops[i].element, deepest.element))
		deepest = drops[i];
	
	return deepest;
  },

  isContained: function(element, drop) {
	var containmentNode;
	if(drop.tree) {
	  containmentNode = element.treeNode; 
	} else {
	  containmentNode = element.parentNode;
	}
	return drop._containers.detect(function(c) { return containmentNode == c });
  },
  
  isAffected: function(point, element, drop) {
	return (
	  (drop.element!=element) &&
	  ((!drop._containers) ||
		this.isContained(element, drop)) &&
	  ((!drop.accept) ||
		(Element.classNames(element).detect( 
		  function(v) { return drop.accept.include(v) } ) )) &&
	  Position.within(drop.element, point[0], point[1]) );
  },

  deactivate: function(drop) {
	if(drop.hoverclass)
	  Element.removeClassName(drop.element, drop.hoverclass);
	this.last_active = null;
  },

  activate: function(drop) {
	if(drop.hoverclass)
	  Element.addClassName(drop.element, drop.hoverclass);
	this.last_active = drop;
  },

  show: function(point, element) {
	if(!this.drops.length) return;
	var affected = [];
	
	if(this.last_active) this.deactivate(this.last_active);
	this.drops.each( function(drop) {
	  if(Droppables.isAffected(point, element, drop))
		affected.push(drop);
	});
		
	if(affected.length>0) {
	  drop = Droppables.findDeepestChild(affected);
	  Position.within(drop.element, point[0], point[1]);
	  if(drop.onHover)
		drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
	  
	  Droppables.activate(drop);
	}
  },

  fire: function(event, element) {
	if(!this.last_active) return;
	Position.prepare();

	if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
	  if (this.last_active.onDrop) 
		this.last_active.onDrop(element, this.last_active.element, event);
  },

  reset: function() {
	if(this.last_active)
	  this.deactivate(this.last_active);
  }
}

var Draggables = {
  drags: [],
  observers: [],
  
  register: function(draggable) {
	if(this.drags.length == 0) {
	  this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
	  this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
	  this.eventKeypress  = this.keyPress.bindAsEventListener(this);
	  
	  Event.observe(document, "mouseup", this.eventMouseUp);
	  Event.observe(document, "mousemove", this.eventMouseMove);
	  Event.observe(document, "keypress", this.eventKeypress);
	}
	this.drags.push(draggable);
  },
  
  unregister: function(draggable) {
	this.drags = this.drags.reject(function(d) { return d==draggable });
	if(this.drags.length == 0) {
	  Event.stopObserving(document, "mouseup", this.eventMouseUp);
	  Event.stopObserving(document, "mousemove", this.eventMouseMove);
	  Event.stopObserving(document, "keypress", this.eventKeypress);
	}
  },
  
  activate: function(draggable) {
	if(draggable.options.delay) { 
	  this._timeout = setTimeout(function() { 
		Draggables._timeout = null; 
		window.focus(); 
		Draggables.activeDraggable = draggable; 
	  }.bind(this), draggable.options.delay); 
	} else {
	  window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
	  this.activeDraggable = draggable;
	}
  },
  
  deactivate: function() {
	this.activeDraggable = null;
  },
  
  updateDrag: function(event) {
	if(!this.activeDraggable) return;
	var pointer = [Event.pointerX(event), Event.pointerY(event)];
	// Mozilla-based browsers fire successive mousemove events with
	// the same coordinates, prevent needless redrawing (moz bug?)
	if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
	this._lastPointer = pointer;
	
	this.activeDraggable.updateDrag(event, pointer);
  },
  
  endDrag: function(event) {
	if(this._timeout) { 
	  clearTimeout(this._timeout); 
	  this._timeout = null; 
	}
	if(!this.activeDraggable) return;
	this._lastPointer = null;
	this.activeDraggable.endDrag(event);
	this.activeDraggable = null;
  },
  
  keyPress: function(event) {
	if(this.activeDraggable)
	  this.activeDraggable.keyPress(event);
  },
  
  addObserver: function(observer) {
	this.observers.push(observer);
	this._cacheObserverCallbacks();
  },
  
  removeObserver: function(element) {  // element instead of observer fixes mem leaks
	this.observers = this.observers.reject( function(o) { return o.element==element });
	this._cacheObserverCallbacks();
  },
  
  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
	if(this[eventName+'Count'] > 0)
	  this.observers.each( function(o) {
		if(o[eventName]) o[eventName](eventName, draggable, event);
	  });
	if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
  },
  
  _cacheObserverCallbacks: function() {
	['onStart','onEnd','onDrag'].each( function(eventName) {
	  Draggables[eventName+'Count'] = Draggables.observers.select(
		function(o) { return o[eventName]; }
	  ).length;
	});
  }
}

/*--------------------------------------------------------------------------*/

var Draggable = Class.create();
Draggable._dragging    = {};

Draggable.prototype = {
  initialize: function(element) {
	var defaults = {
	  handle: false,
	  reverteffect: function(element, top_offset, left_offset) {
		var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
		new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
		  queue: {scope:'_draggable', position:'end'}
		});
	  },
	  endeffect: function(element) {
		var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
		new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
		  queue: {scope:'_draggable', position:'end'},
		  afterFinish: function(){ 
			Draggable._dragging[element] = false 
		  }
		}); 
	  },
	  zindex: 1000,
	  revert: false,
	  scroll: false,
	  scrollSensitivity: 20,
	  scrollSpeed: 15,
	  snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
	  delay: 0
	};
	
	if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
	  Object.extend(defaults, {
		starteffect: function(element) {
		  element._opacity = Element.getOpacity(element);
		  Draggable._dragging[element] = true;
		  new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
		}
	  });
	
	var options = Object.extend(defaults, arguments[1] || {});

	this.element = $(element);
	
	if(options.handle && (typeof options.handle == 'string'))
	  this.handle = this.element.down('.'+options.handle, 0);
	
	if(!this.handle) this.handle = $(options.handle);
	if(!this.handle) this.handle = this.element;
	
	if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
	  options.scroll = $(options.scroll);
	  this._isScrollChild = Element.childOf(this.element, options.scroll);
	}

	Element.makePositioned(this.element); // fix IE    

	this.delta    = this.currentDelta();
	this.options  = options;
	this.dragging = false;   

	this.eventMouseDown = this.initDrag.bindAsEventListener(this);
	Event.observe(this.handle, "mousedown", this.eventMouseDown);
	
	Draggables.register(this);
  },
  
  destroy: function() {
	Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
	Draggables.unregister(this);
  },
  
  currentDelta: function() {
	return([
	  parseInt(Element.getStyle(this.element,'left') || '0'),
	  parseInt(Element.getStyle(this.element,'top') || '0')]);
  },
  
  initDrag: function(event) {
	if(typeof Draggable._dragging[this.element] != 'undefined' &&
	  Draggable._dragging[this.element]) return;
	if(Event.isLeftClick(event)) {    
	  // abort on form elements, fixes a Firefox issue
	  var src = Event.element(event);
	  if((tag_name = src.tagName.toUpperCase()) && (
		tag_name=='INPUT' ||
		tag_name=='SELECT' ||
		tag_name=='OPTION' ||
		tag_name=='BUTTON' ||
		tag_name=='TEXTAREA')) return;
		
	  var pointer = [Event.pointerX(event), Event.pointerY(event)];
	  var pos     = Position.cumulativeOffset(this.element);
	  this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
	  
	  Draggables.activate(this);
	  Event.stop(event);
	}
  },
  
  startDrag: function(event) {
	this.dragging = true;
	
	if(this.options.zindex) {
	  this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
	  this.element.style.zIndex = this.options.zindex;
	}
	
	if(this.options.ghosting) {
	  this._clone = this.element.cloneNode(true);
	  Position.absolutize(this.element);
	  this.element.parentNode.insertBefore(this._clone, this.element);
	}
	
	if(this.options.scroll) {
	  if (this.options.scroll == window) {
		var where = this._getWindowScroll(this.options.scroll);
		this.originalScrollLeft = where.left;
		this.originalScrollTop = where.top;
	  } else {
		this.originalScrollLeft = this.options.scroll.scrollLeft;
		this.originalScrollTop = this.options.scroll.scrollTop;
	  }
	}
	
	Draggables.notify('onStart', this, event);
		
	if(this.options.starteffect) this.options.starteffect(this.element);
  },
  
  updateDrag: function(event, pointer) {
	if(!this.dragging) this.startDrag(event);
	Position.prepare();
	Droppables.show(pointer, this.element);
	Draggables.notify('onDrag', this, event);
	
	this.draw(pointer);
	if(this.options.change) this.options.change(this);
	
	if(this.options.scroll) {
	  this.stopScrolling();
	  
	  var p;
	  if (this.options.scroll == window) {
		with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
	  } else {
		p = Position.page(this.options.scroll);
		p[0] += this.options.scroll.scrollLeft + Position.deltaX;
		p[1] += this.options.scroll.scrollTop + Position.deltaY;
		p.push(p[0]+this.options.scroll.offsetWidth);
		p.push(p[1]+this.options.scroll.offsetHeight);
	  }
	  var speed = [0,0];
	  if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
	  if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
	  if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
	  if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
	  this.startScrolling(speed);
	}
	
	// fix AppleWebKit rendering
	if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
	
	Event.stop(event);
  },
  
  finishDrag: function(event, success) {
	this.dragging = false;

	if(this.options.ghosting) {
	  Position.relativize(this.element);
	  Element.remove(this._clone);
	  this._clone = null;
	}

	if(success) Droppables.fire(event, this.element);
	Draggables.notify('onEnd', this, event);

	var revert = this.options.revert;
	if(revert && typeof revert == 'function') revert = revert(this.element);
	
	var d = this.currentDelta();
	if(revert && this.options.reverteffect) {
	  this.options.reverteffect(this.element, 
		d[1]-this.delta[1], d[0]-this.delta[0]);
	} else {
	  this.delta = d;
	}

	if(this.options.zindex)
	  this.element.style.zIndex = this.originalZ;

	if(this.options.endeffect) 
	  this.options.endeffect(this.element);
	  
	Draggables.deactivate(this);
	Droppables.reset();
  },
  
  keyPress: function(event) {
	if(event.keyCode!=Event.KEY_ESC) return;
	this.finishDrag(event, false);
	Event.stop(event);
  },
  
  endDrag: function(event) {
	if(!this.dragging) return;
	this.stopScrolling();
	this.finishDrag(event, true);
	Event.stop(event);
  },
  
  draw: function(point) {
	var pos = Position.cumulativeOffset(this.element);
	if(this.options.ghosting) {
	  var r   = Position.realOffset(this.element);
	  pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
	}
	
	var d = this.currentDelta();
	pos[0] -= d[0]; pos[1] -= d[1];
	
	if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
	  pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
	  pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
	}
	
	var p = [0,1].map(function(i){ 
	  return (point[i]-pos[i]-this.offset[i]) 
	}.bind(this));
	
	if(this.options.snap) {
	  if(typeof this.options.snap == 'function') {
		p = this.options.snap(p[0],p[1],this);
	  } else {
	  if(this.options.snap instanceof Array) {
		p = p.map( function(v, i) {
		  return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
	  } else {
		p = p.map( function(v) {
		  return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
	  }
	}}
	
	var style = this.element.style;
	if((!this.options.constraint) || (this.options.constraint=='horizontal'))
	  style.left = p[0] + "px";
	if((!this.options.constraint) || (this.options.constraint=='vertical'))
	  style.top  = p[1] + "px";
	
	if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },
  
  stopScrolling: function() {
	if(this.scrollInterval) {
	  clearInterval(this.scrollInterval);
	  this.scrollInterval = null;
	  Draggables._lastScrollPointer = null;
	}
  },
  
  startScrolling: function(speed) {
	if(!(speed[0] || speed[1])) return;
	this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
	this.lastScrolled = new Date();
	this.scrollInterval = setInterval(this.scroll.bind(this), 10);
  },
  
  scroll: function() {
	var current = new Date();
	var delta = current - this.lastScrolled;
	this.lastScrolled = current;
	if(this.options.scroll == window) {
	  with (this._getWindowScroll(this.options.scroll)) {
		if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
		  var d = delta / 1000;
		  this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
		}
	  }
	} else {
	  this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
	  this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
	}
	
	Position.prepare();
	Droppables.show(Draggables._lastPointer, this.element);
	Draggables.notify('onDrag', this);
	if (this._isScrollChild) {
	  Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
	  Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
	  Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
	  if (Draggables._lastScrollPointer[0] < 0)
		Draggables._lastScrollPointer[0] = 0;
	  if (Draggables._lastScrollPointer[1] < 0)
		Draggables._lastScrollPointer[1] = 0;
	  this.draw(Draggables._lastScrollPointer);
	}
	
	if(this.options.change) this.options.change(this);
  },
  
  _getWindowScroll: function(w) {
	var T, L, W, H;
	with (w.document) {
	  if (w.document.documentElement && documentElement.scrollTop) {
		T = documentElement.scrollTop;
		L = documentElement.scrollLeft;
	  } else if (w.document.body) {
		T = body.scrollTop;
		L = body.scrollLeft;
	  }
	  if (w.innerWidth) {
		W = w.innerWidth;
		H = w.innerHeight;
	  } else if (w.document.documentElement && documentElement.clientWidth) {
		W = documentElement.clientWidth;
		H = documentElement.clientHeight;
	  } else {
		W = body.offsetWidth;
		H = body.offsetHeight
	  }
	}
	return { top: T, left: L, width: W, height: H };
  }
}

/*--------------------------------------------------------------------------*/

var SortableObserver = Class.create();
SortableObserver.prototype = {
  initialize: function(element, observer) {
	this.element   = $(element);
	this.observer  = observer;
	this.lastValue = Sortable.serialize(this.element);
  },
  
  onStart: function() {
	this.lastValue = Sortable.serialize(this.element);
  },
  
  onEnd: function() {
	Sortable.unmark();
	if(this.lastValue != Sortable.serialize(this.element))
	  this.observer(this.element)
  }
}

var Sortable = {
  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
  
  sortables: {},
  
  _findRootElement: function(element) {
	while (element.tagName.toUpperCase() != "BODY") {  
	  if(element.id && Sortable.sortables[element.id]) return element;
	  element = element.parentNode;
	}
  },

  options: function(element) {
	element = Sortable._findRootElement($(element));
	if(!element) return;
	return Sortable.sortables[element.id];
  },
  
  destroy: function(element){
	var s = Sortable.options(element);
	
	if(s) {
	  Draggables.removeObserver(s.element);
	  s.droppables.each(function(d){ Droppables.remove(d) });
	  s.draggables.invoke('destroy');
	  
	  delete Sortable.sortables[s.element.id];
	}
  },

  create: function(element) {
	element = $(element);
	var options = Object.extend({ 
	  element:     element,
	  tag:         'li',       // assumes li children, override with tag: 'tagname'
	  dropOnEmpty: false,
	  tree:        false,
	  treeTag:     'ul',
	  overlap:     'vertical', // one of 'vertical', 'horizontal'
	  constraint:  'vertical', // one of 'vertical', 'horizontal', false
	  containment: element,    // also takes array of elements (or id's); or false
	  handle:      false,      // or a CSS class
	  only:        false,
	  delay:       0,
	  hoverclass:  null,
	  ghosting:    false,
	  scroll:      false,
	  scrollSensitivity: 20,
	  scrollSpeed: 15,
	  format:      this.SERIALIZE_RULE,
	  onChange:    Prototype.emptyFunction,
	  onUpdate:    Prototype.emptyFunction
	}, arguments[1] || {});

	// clear any old sortable with same element
	this.destroy(element);

	// build options for the draggables
	var options_for_draggable = {
	  revert:      true,
	  scroll:      options.scroll,
	  scrollSpeed: options.scrollSpeed,
	  scrollSensitivity: options.scrollSensitivity,
	  delay:       options.delay,
	  ghosting:    options.ghosting,
	  constraint:  options.constraint,
	  handle:      options.handle };

	if(options.starteffect)
	  options_for_draggable.starteffect = options.starteffect;

	if(options.reverteffect)
	  options_for_draggable.reverteffect = options.reverteffect;
	else
	  if(options.ghosting) options_for_draggable.reverteffect = function(element) {
		element.style.top  = 0;
		element.style.left = 0;
	  };

	if(options.endeffect)
	  options_for_draggable.endeffect = options.endeffect;

	if(options.zindex)
	  options_for_draggable.zindex = options.zindex;

	// build options for the droppables  
	var options_for_droppable = {
	  overlap:     options.overlap,
	  containment: options.containment,
	  tree:        options.tree,
	  hoverclass:  options.hoverclass,
	  onHover:     Sortable.onHover
	}
	
	var options_for_tree = {
	  onHover:      Sortable.onEmptyHover,
	  overlap:      options.overlap,
	  containment:  options.containment,
	  hoverclass:   options.hoverclass
	}

	// fix for gecko engine
	Element.cleanWhitespace(element); 

	options.draggables = [];
	options.droppables = [];

	// drop on empty handling
	if(options.dropOnEmpty || options.tree) {
	  Droppables.add(element, options_for_tree);
	  options.droppables.push(element);
	}

	(this.findElements(element, options) || []).each( function(e) {
	  // handles are per-draggable
	  var handle = options.handle ? 
		$(e).down('.'+options.handle,0) : e;    
	  options.draggables.push(
		new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
	  Droppables.add(e, options_for_droppable);
	  if(options.tree) e.treeNode = element;
	  options.droppables.push(e);      
	});
	
	if(options.tree) {
	  (Sortable.findTreeElements(element, options) || []).each( function(e) {
		Droppables.add(e, options_for_tree);
		e.treeNode = element;
		options.droppables.push(e);
	  });
	}

	// keep reference
	this.sortables[element.id] = options;

	// for onupdate
	Draggables.addObserver(new SortableObserver(element, options.onUpdate));

  },

  // return all suitable-for-sortable elements in a guaranteed order
  findElements: function(element, options) {
	return Element.findChildren(
	  element, options.only, options.tree ? true : false, options.tag);
  },
  
  findTreeElements: function(element, options) {
	return Element.findChildren(
	  element, options.only, options.tree ? true : false, options.treeTag);
  },

  onHover: function(element, dropon, overlap) {
	if(Element.isParent(dropon, element)) return;

	if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
	  return;
	} else if(overlap>0.5) {
	  Sortable.mark(dropon, 'before');
	  if(dropon.previousSibling != element) {
		var oldParentNode = element.parentNode;
		element.style.visibility = "hidden"; // fix gecko rendering
		dropon.parentNode.insertBefore(element, dropon);
		if(dropon.parentNode!=oldParentNode) 
		  Sortable.options(oldParentNode).onChange(element);
		Sortable.options(dropon.parentNode).onChange(element);
	  }
	} else {
	  Sortable.mark(dropon, 'after');
	  var nextElement = dropon.nextSibling || null;
	  if(nextElement != element) {
		var oldParentNode = element.parentNode;
		element.style.visibility = "hidden"; // fix gecko rendering
		dropon.parentNode.insertBefore(element, nextElement);
		if(dropon.parentNode!=oldParentNode) 
		  Sortable.options(oldParentNode).onChange(element);
		Sortable.options(dropon.parentNode).onChange(element);
	  }
	}
  },
  
  onEmptyHover: function(element, dropon, overlap) {
	var oldParentNode = element.parentNode;
	var droponOptions = Sortable.options(dropon);
		
	if(!Element.isParent(dropon, element)) {
	  var index;
	  
	  var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
	  var child = null;
			
	  if(children) {
		var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
		
		for (index = 0; index < children.length; index += 1) {
		  if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
			offset -= Element.offsetSize (children[index], droponOptions.overlap);
		  } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
			child = index + 1 < children.length ? children[index + 1] : null;
			break;
		  } else {
			child = children[index];
			break;
		  }
		}
	  }
	  
	  dropon.insertBefore(element, child);
	  
	  Sortable.options(oldParentNode).onChange(element);
	  droponOptions.onChange(element);
	}
  },

  unmark: function() {
	if(Sortable._marker) Sortable._marker.hide();
  },

  mark: function(dropon, position) {
	// mark on ghosting only
	var sortable = Sortable.options(dropon.parentNode);
	if(sortable && !sortable.ghosting) return; 

	if(!Sortable._marker) {
	  Sortable._marker = 
		($('dropmarker') || Element.extend(document.createElement('DIV'))).
		  hide().addClassName('dropmarker').setStyle({position:'absolute'});
	  document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
	}    
	var offsets = Position.cumulativeOffset(dropon);
	Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
	
	if(position=='after')
	  if(sortable.overlap == 'horizontal') 
		Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
	  else
		Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
	
	Sortable._marker.show();
  },
  
  _tree: function(element, options, parent) {
	var children = Sortable.findElements(element, options) || [];
  
	for (var i = 0; i < children.length; ++i) {
	  var match = children[i].id.match(options.format);

	  if (!match) continue;
	  
	  var child = {
		id: encodeURIComponent(match ? match[1] : null),
		element: element,
		parent: parent,
		children: [],
		position: parent.children.length,
		container: $(children[i]).down(options.treeTag)
	  }
	  
	  /* Get the element containing the children and recurse over it */
	  if (child.container)
		this._tree(child.container, options, child)
	  
	  parent.children.push (child);
	}

	return parent; 
  },

  tree: function(element) {
	element = $(element);
	var sortableOptions = this.options(element);
	var options = Object.extend({
	  tag: sortableOptions.tag,
	  treeTag: sortableOptions.treeTag,
	  only: sortableOptions.only,
	  name: element.id,
	  format: sortableOptions.format
	}, arguments[1] || {});
	
	var root = {
	  id: null,
	  parent: null,
	  children: [],
	  container: element,
	  position: 0
	}
	
	return Sortable._tree(element, options, root);
  },

  /* Construct a [i] index for a particular node */
  _constructIndex: function(node) {
	var index = '';
	do {
	  if (node.id) index = '[' + node.position + ']' + index;
	} while ((node = node.parent) != null);
	return index;
  },

  sequence: function(element) {
	element = $(element);
	var options = Object.extend(this.options(element), arguments[1] || {});
	
	return $(this.findElements(element, options) || []).map( function(item) {
	  return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
	});
  },

  setSequence: function(element, new_sequence) {
	element = $(element);
	var options = Object.extend(this.options(element), arguments[2] || {});
	
	var nodeMap = {};
	this.findElements(element, options).each( function(n) {
		if (n.id.match(options.format))
			nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
		n.parentNode.removeChild(n);
	});
   
	new_sequence.each(function(ident) {
	  var n = nodeMap[ident];
	  if (n) {
		n[1].appendChild(n[0]);
		delete nodeMap[ident];
	  }
	});
  },
  
  serialize: function(element) {
	element = $(element);
	var options = Object.extend(Sortable.options(element), arguments[1] || {});
	var name = encodeURIComponent(
	  (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
	
	if (options.tree) {
	  return Sortable.tree(element, arguments[1]).children.map( function (item) {
		return [name + Sortable._constructIndex(item) + "[id]=" + 
				encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
	  }).flatten().join('&');
	} else {
	  return Sortable.sequence(element, arguments[1]).map( function(item) {
		return name + "[]=" + encodeURIComponent(item);
	  }).join('&');
	}
  }
}

// Returns true if child is contained within element
Element.isParent = function(child, element) {
  if (!child.parentNode || child == element) return false;
  if (child.parentNode == element) return true;
  return Element.isParent(child.parentNode, element);
}

Element.findChildren = function(element, only, recursive, tagName) {    
  if(!element.hasChildNodes()) return null;
  tagName = tagName.toUpperCase();
  if(only) only = [only].flatten();
  var elements = [];
  $A(element.childNodes).each( function(e) {
	if(e.tagName && e.tagName.toUpperCase()==tagName &&
	  (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
		elements.push(e);
	if(recursive) {
	  var grandchildren = Element.findChildren(e, only, recursive, tagName);
	  if(grandchildren) elements.push(grandchildren);
	}
  });

  return (elements.length>0 ? elements.flatten() : []);
}

Element.offsetSize = function (element, type) {
  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
}

var dragdrop_loaded = true;/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;//based on prototype's ajax class
//to be used with prototype.lite, moofx.mad4milk.net.

ajax = Class.create();
ajax.prototype = {
	initialize: function(url, options){
		options = options || {};
		this.transport = Ajax.getTransport();
		this.postBody = options.postBody || '';
		this.method = this.postBody != '' ? 'post' : 'get';
		this.onComplete = options.onComplete || null;
		this.onError = options.onError || null;
		this.update = $(options.update) || null;
		this.request (url);
	},

	request: function(url){
		url += url.indexOf ('?') == -1 ? "?" : "&";
		url += "anticache="+Math.random ();
		this.transport.open(this.method, url, true);
		this.transport.onreadystatechange = this.onStateChange.bind(this);
		if (this.method == 'post') {
			this.transport.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			if (this.transport.overrideMimeType) this.transport.setRequestHeader('Connection', 'close');
		}
		this.transport.send(this.postBody);
	},

	onStateChange: function(){
		if (this.transport.readyState == 4) {
			if (this.transport.status == 200) {
				if (this.onComplete) 
					setTimeout(function(){this.onComplete(this.transport);}.bind(this), 10);
				if (this.update)
					setTimeout(function(){this.update.innerHTML = this.transport.responseText;}.bind(this), 10);
			} else {
				if (this.onError)
					setTimeout(function(){this.onError(this.transport);}.bind(this), 10);
			}
			this.transport.onreadystatechange = function(){};
		}
	}
};

function ajaxError (request) {
if (debug_autoAjax != null)
	if (request.readyState != 4 || request.status != null)
		alert ("error "+request.readyState+" "+request.status);
}

var ajax_loaded = true;// JavaScript Document
var debug_autoAjax = null;

function removeElement (elem)
{
	elem.parentNode.removeChild(elem);
}

autoAjax = Class.create();
autoAjax.prototype = {
	initialize: function (url, options) {
		options = options || {};
		this.url = url;
		this.onComplete = options.onComplete || null;
		this.onError = options.onError || null;
		this.elem = $(options.update);
		options.update = null;
		options.onComplete = this.contentOk.bind(this);
		options.onError = this.contentError.bind(this);

		if (this.elem) {
			document.write ('<div id="wc_'+this.elem.id+'" style="position:absolute;background-color:white;-moz-opacity: 0.6;opacity: 0.6;filter:alpha(opacity=60);">');
			document.write ('</div>');
			document.write ('<div id="wc2_'+this.elem.id+'" style="position:absolute;">');
			document.write ('	<table id="wct_'+this.elem.id+'" cellpadding=0 cellspacing=0><tr valign=middle><td align=center>');
			document.write ('		<img src="https://www.etenderscotland.com/client/images/simplebidder/Anim_logo.gif">');
			document.write ('	</td></tr></table>');
			document.write ('</div>');
			
			var objOverlay = $('wc_'+this.elem.id);
			var x = findPosX (this.elem);
			var y = findPosY (this.elem);
			objOverlay.style.left   = x +"px";
			objOverlay.style.top    = y +"px";
			objOverlay.style.width  = this.elem.style.width;
			objOverlay.style.height = this.elem.style.height;
			
			var objOTable = $('wc2_'+this.elem.id);
			objOTable.style.left   = x +"px";
			objOTable.style.top    = y +"px";
			objOTable.style.width  = objOverlay.style.width;
			objOTable.style.height = objOverlay.style.height;
			
			var objTable = $('wct_'+this.elem.id);
			objTable.style.width  = objOverlay.style.width;
			objTable.style.height = objOverlay.style.height;
		}
		
		new ajax (url, options);
	},
	
	contentOk : function (request) {
		this.removeWC ();
		
		if (debug_autoAjax == true)
			$('ajax_debug').innerHTML += this.url+'<br>';
			
		var result;
		
		try {
			result = eval('(' + request.responseText + ')');
		}
		catch (e) {
			if (debug_autoAjax == true)
				$('ajax_debug').innerHTML += request.responseText+'<br>';
			
			return;
		}

		for (var elem in result) {
			var dest = elem;
						
			if (dest == "elem")
				dest = this.elem.id;
				
			for (var key in result[elem]) {
				if (debug_autoAjax == true) {
					$('ajax_debug').innerHTML += (key+" "+result[elem][key].replace (/\</g, "&lt;"))+'<br>';
				}
			
				if (key == "script") {
					eval (result[elem][key]);
					continue;
				}
				if (key == "alert") {
					alert (result[elem][key]);
					continue;
				}
				if (key == "prompt") {
					prompt ("autoAjax", result[elem][key]);
					continue;
				}

				dest = $(dest);
				if (dest == null)
					continue;

				switch (key) {
				case "prepend":
					dest.innerHTML = result[elem][key] + dest.innerHTML;
					break;
				case "innerHTML":
					dest.innerHTML = result[elem][key];
					break;
				case "append":
					dest.innerHTML += result[elem][key];
					break;
				case "value":
					dest.value = result[elem][key];
					break;
				}
			}				
		}
		
		if (this.onComplete)
			this.onComplete (request);
	},

	contentError : function (request) {
		this.removeWC ();
		if (this.onError)
			this.onError (request);
		else
			ajaxError (request);
	},
	
	removeWC : function () {
		if (this.elem) {
			removeElement ($('wc_'+this.elem.id));
			removeElement ($('wc2_'+this.elem.id));
		}
	}
};

var autoAjax_loaded = true;/*
 ModalBox - The pop-up window thingie with AJAX, based on prototype and script.aculo.us.

 Copyright Andrey Okonetchnikov (andrej.okonetschnikow@gmail.com), 2006
 All rights reserved.
 
 VERSION 1.5.3
 Last Modified: 04/21/2007
 
 Changelog:

ver 1.5.3 (04/21/2007)
 Added: 	Unit and functional tests added
 Added: 	"Close window" text can be customized through the optional parameter 'closeString' [issue #41]
 Added: 	Custom effects duration in parameters [issue #21]
 Added: 	Ajax request method can be changed trough options (method) [issue #54]
 Fixed: 	Executing JS from MB content window fixed
 Fixed: 	MSIE horizontal scrolling after closing MB
 Fixed: 	Resize method now resize correctly [issue #42]
 Fixed: 	Loading string container doesn't appear on update (appears only loadingString text)
 Fixed: 	Bug with unfired afterLoad callback and not executed helpers methods due to bind(window) for evalScript section in loadContent method
 Fixed: 	Bug with beforeLoad callback return value (content loaded even with return value == false)

ver 1.5.2 (02/26/2007)
 Fixed: 	Scrolling by "space" key disabled then MB is visible
 Fixed: 	Scrolling by navigational keys (Up / Down / PageUp / PageDown / Home / End) keys disabled then MB is visible
 Changed: 	Keyboard handlers implementation re-factored (Closes issues #9, #11)
 Changed: 	Markup generated by modalbox optimized
 Changed: 	Scrolling on top removed for all modern browsers except IE6 and lower (Closes issues #1, #9, #11)

ver 1.5.1 (02/15/2007)
 Added: 	Callback 'beforeLoad' fired right before loading MB content. If the callback function returns false, loading will skipped.
 Changed: 	Implementation of callbacks calls changed. Callbacks now removes after execution. 
			Callbacks now have a return value (true | false). Default is true. Fixes: Issue 2 (http://code.google.com/p/modalbox/issues/detail?id=2&can=1&q=)

ver 1.5: (02/02/2007)
 Added: 	URL parameters are now passing to AJAX.Request. Use postOptions to pass parameters
 Added: 	Loading message can be customized through options. Use loadingString option
 Added:		Script.aculo.us 1.64 and prototype 1.5rc1 support
 Added:		Callbacks added on first showing, updating, loading content, closing modalbox
 Added:		Callback can be passed through hide method
 Added: 	resize method resize modalbox without loading any content into it
 Changed:	Evaluating contained scripts (striping HTML comments)
 Changed: 	Appearing of overlay is now animated
 Changed: 	Attaching events on window and overlay
 Changed: 	Method hide now get the callbacks as a parameter
 Changed: 	Inititalization redone with Builder class
 Changed: 	Minor fixes and refactoring done
 Changed: 	Focus loop engine redone
 Changed: 	self variables replaced to bind(this) functions
 Fixed: 	Scrolling to initial scroll position after closing MB
 Fixed: 	Fixed bug in IE with body's overflow auto
 
ver 1.4: (06/20/2006)
 Added: 	Core definitions rewriten. Modalbox can now be accessed thorugh Modalbox object with public methods show and hide
 Added: 	License added
 Changed:	kbdHandler method is now public, so it can be stopped from other functions
 Fixed: 	Stopping of event observing in hide method
 Fixed: 	Hiding selects for IE issue (was applied on element ID)
 Removed:	Redundant 'globalMB' global variable removed
 Removed:	Scroll window events observerving
 Removed:	Redundant effect ScalyTo
 Issue: 	IE display bug then hidding scrollbars. Document body should have zero margins
 
 ver 1.3: (06/18/2006)
 Added: 	ModalBox will now get focus after opening
 Added: 	Keystrokes handler added (Tab key is looped on ModalBox and closing ModalBox by pressing Esc)
 Added: 	Window scrolling disabled (known issue: content jupms on top then opening ModalBox)
 Fixed: 	All dependent event handlers now unloads then closing ModalBox
 Fixed: 	SELECT element hiding function executes now only in MSIE
 Fixed: 	'Close' button has now href attribute to receive focus
 Fixed: 	Click on 'Close' button doesn't adds an href value to URL string
 
 ver 1.2: 
 Added: Global variable 'globalMB' added to the file. Use this variable to acces one instance of ModalBox and call methods on it
 
 ver 1.1: 
 Added: Added SELECT elements hiding for IE (should be rewriten later)
 
 ver 1.0: 
 Added: Core class description
 
 
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 Andrey Okonetchnikov nor the names of its 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.

* http://www.opensource.org/licenses/bsd-license.php
 
See scriptaculous.js for full scriptaculous licence

*/

if (!window.Modalbox)
	var Modalbox = new Object();

Modalbox.Methods = {
	focusableElements: new Array,
	
	setOptions: function(options) {
		this.options = {
			overlayClose: true, // Close modal box by clicking on overlay
			width: 365, // Default width in px
			height: 392, // Default height in px
			overlayDuration: .50, // Default overlay fade in/out duration in seconds
			slideDownDuration: .75, // Default Modalbox appear slide down effect in seconds
			slideUpDuration: .35, // Default Modalbox hiding slide up effect in seconds
			resizeDuration: .50, // Default resize duration seconds
			loadingString: TEXT_LOADING, // Default loading string message
			closeString: TEXT_CLOSE, // Default title attribute for close window link
			params: {},
			method: 'get' // Default Ajax request method
		};
		Object.extend(this.options, options || {});
	},
	
	_init: function() {
		//Create the overlay
		this.MBoverlay = Builder.node("div", { id: "MB_overlay" });
		Element.setOpacity(this.MBoverlay, 0);
		
		//Create the window
		this.MBwindow = Builder.node("div", {id: "MB_window", style: "display: none"}, [
			this.MBframe = Builder.node("div", {id: "MB_frame"}, [
/*				this.MBheader = Builder.node("div", {id: "MB_header"}, [
					this.MBcaption = Builder.node("div", {id: "MB_caption"}),
					this.MBclose = Builder.node("a", {id: "MB_close", title: this.options.closeString, href: "#"}, [
						Builder.build("<span>&times;</span>"),
					]),
				]),
*/				this.MBcontent = Builder.node("div", {id: "MB_content"}, [
					this.MBloading = Builder.node("div", {id: "MB_loading"}, this.options.loadingString),
				]),
			]),
		]);
		// Inserting into DOM
		document.body.insertBefore(this.MBwindow, document.body.childNodes[0]);
		document.body.insertBefore(this.MBoverlay, document.body.childNodes[0]);
		//Adding event observers
		this.hide = this.hide.bindAsEventListener(this);
		this.close = this._hide.bindAsEventListener(this);
		
		// Initial scrolling position of the window. To be used for remove scrolling effect during ModalBox appearing
		this.initScrollX = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft;
		this.initScrollY = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
		
//		Event.observe(this.MBclose, "click", this.close); // Close link overver
		if(this.options.overlayClose) Event.observe(this.MBoverlay, "click", this.hide); // Overlay close obersver

		this.isInitialized = true; // Mark as initialized
	},
	
	show: function(title, url, options) {
		this.title = title;
		this.url = url;
		this.setOptions(options);
		if(!this.isInitialized) this._init(); // Check for is already initialized
//		Element.update(this.MBcaption, title); // Updating title of the MB
		
		if(this.MBwindow.style.display == "none") { // First modal box appearing
			this._appear();
			this.event("onShow"); // Passing onShow callback
		}
		else { // If MB already on the screen, update it
			this._update();
			this.event("onUpdate"); // Passing onShow callback
		} 
	},
	
	hide: function(options) { // External hide method to use from external HTML and JS
		if(options) Object.extend(this.options, options); // Passing callbacks
		Effect.SlideUp(this.MBwindow, { duration: this.options.slideUpDuration, afterFinish: this._deinit.bind(this) } );
	},
	
	_hide: function(event) { // Internal hide method to use inside MB class
		if(event) Event.stop(event);
		this.hide();
	},
	
	_appear: function() { // First appearing of MB
		this._toggleSelects();
		this._setOverlay();
		this._setWidth();
		this._setPosition();
		new Effect.Fade(this.MBoverlay, {
				from: 0, 
				to: 0.75, 
				duration: this.options.overlayDuration, 
				afterFinish: function() {
					new Effect.SlideDown(this.MBwindow, {
						duration: this.options.slideDownDuration, 
						afterFinish: function(){ 
							this._setPosition(); 
							this.loadContent();
						}.bind(this)
					});
				}.bind(this)
		});
		
		this._setWidthAndPosition = this._setWidthAndPosition.bindAsEventListener(this);
		Event.observe(window, "resize", this._setWidthAndPosition);
		
		this.kbdHandler = this.kbdHandler.bindAsEventListener(this);
		Event.observe(document, "keypress", this.kbdHandler);
	},
	
	resize: function(byWidth, byHeight, options) { // Change size of MB without loading content
		if(options) Object.extend(this.options, options); // Passing callbacks
		this.currentDims = [this.MBwindow.offsetWidth, this.MBwindow.offsetHeight];
		new Effect.ScaleBy(this.MBwindow, 
			(byWidth), //New width calculation
			(byHeight), //New height calculation
			{ duration: this.options.resizeDuration, 
			  afterFinish: function() { this.event("afterResize") }.bind(this) // Passing callback
			});
	},
	
	_update: function() { // Updating MB in case of wizards
		this.currentDims = [this.MBwindow.offsetWidth, this.MBwindow.offsetHeight];
		if((this.options.width + 10 != this.currentDims[0]) || (this.options.height + 5 != this.currentDims[1]))
			new Effect.ScaleBy(this.MBwindow, 
				(this.options.width + 10 - this.currentDims[0]), //New width calculation
				(this.options.height + 5 - this.currentDims[1]), //New height calculation
			{
				duration: this.options.resizeDuration, 
				afterFinish: this._loadAfterResize.bind(this), 
				beforeStart: function(effect) { 
					Element.update(this.MBcontent, "");
					this.MBcontent.appendChild(this.MBloading);
					Element.update(this.MBloading, this.options.loadingString);
				}.bind(this) 
			});
		else {
			Element.update(this.MBcontent, "");
			this.MBcontent.appendChild(this.MBloading);
			Element.update(this.MBloading, this.options.loadingString);
			this._loadAfterResize();
		}
	},
	
	loadContent: function () { // Load content into MB through AJAX
		if (typeof(this.url) != "string" && this.url.innerHTML)
		{
			var response = this.url.innerHTML;
			this.url.innerHTML = "";
			this.MBcontent.innerHTML = response;
			this.focusableElements = this._findFocusableElements();
			this._moveFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button)
			this.event("afterLoad"); // Passing callback
//			response.extractScripts().map(function(script) { 
//				return eval(script.replace("<!--", "").replace("// -->", ""));
//			}).bind(window);

			return;
		}
	
		if(this.event("beforeLoad") != false) // If callback passed false, skip loading of the content
			new Ajax.Request( this.url, { method: this.options.method.toLowerCase(), parameters: this.options.params, 
				onComplete: function(transport) {
					var response = new String(transport.responseText);
					this.MBcontent.innerHTML = response;
					this.focusableElements = this._findFocusableElements();
					this._moveFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button)
					this.event("afterLoad"); // Passing callback
					response.extractScripts().map(function(script) { 
						return eval(script.replace("<!--", "").replace("// -->", ""));
					}).bind(window);
				}.bind(this)
			});
	},
	
	_loadAfterResize: function() {
		this._setWidth();
		this._setPosition();
		this.loadContent();
	},
	
	_moveFocus: function() { // Setting focus to be looped inside current MB
		if(this.focusableElements.length > 0)
			this.focusableElements.first().focus(); // Focus on first focusable element except close button
//		else
//			$("MB_close").focus(); // If no focusable elements exist focus on close button
	},
	
	_findFocusableElements: function(){ // Collect form elements or links from MB content
		return $A($("MB_content").descendants()).findAll(function(node){
			return (["INPUT", "TEXTAREA", "SELECT", "A", "BUTTON"].include(node.tagName));
		});
	},
	
	kbdHandler: function(e) {
		var node = Event.element(e);
		switch(e.keyCode) {
			case Event.KEY_TAB:
				if(Event.element(e) == this.focusableElements.last()) {
					Event.stop(e);
					this._moveFocus();  // Find last element in MB to handle event on it. If no elements found, uses close ModalBox button
				}
			break;			
			case Event.KEY_ESC:
				this._hide(e);
			break;
			case 32:
				this._preventScroll(e);
			break;
			case 0: // For Gecko browsers compatibility
				if(e.which == 32) this._preventScroll(e);
			break;
			case Event.KEY_UP:
			case Event.KEY_DOWN:
			case Event.KEY_PAGEDOWN:
			case Event.KEY_PAGEUP:
			case Event.KEY_HOME:
			case Event.KEY_END:
				if(!["TEXTAREA", "SELECT"].include(node.tagName) || 
				(node.tagName == "INPUT" && (node.type == "submit" || node.type == "button")) ) Event.stop(e);
			break;
		}
	},
	
	_preventScroll: function(event) { // Disabling scrolling by "space" key
		if(!["INPUT", "TEXTAREA", "SELECT", "BUTTON"].include(Event.element(event).tagName)) Event.stop(event);
	},
	
	_deinit: function()
	{	
		this._toggleSelects(); // Toggle back 'select' element in IE
//		Event.stopObserving(this.MBclose, "click", this.close );
		if(this.options.overlayClose)
			Event.stopObserving(this.MBoverlay, "click", this.hide );
		Event.stopObserving(window, "resize", this._setWidthAndPosition );
		Event.stopObserving(document, "keypress", this.kbdHandler );
		Effect.toggle(this.MBoverlay, 'appear', {duration: this.options.overlayDuration, afterFinish: this._removeElements.bind(this) });
		
		if (typeof (this.url) != "string") {
			this.url.innerHTML = this.MBcontent.innerHTML;
			this.MBcontent.innerHTML = "";
		}
	},
	
	_removeElements: function () {
		if (navigator.appVersion.match(/\bMSIE\b/)) {
			this._prepareIE("", ""); // If set to auto MSIE will show horizontal scrolling
			window.scrollTo(this.initScrollX, this.initScrollY);
		}
		Element.remove(this.MBoverlay);
		Element.remove(this.MBwindow);
		this.isInitialized = false;
		this.event("afterHide"); // Passing afterHide callback
	},
	
	_setOverlay: function () {
		if (navigator.appVersion.match(/\bMSIE\b/)) {
			this._prepareIE("100%", "hidden");
			if (!navigator.appVersion.match(/\b7.0\b/)) window.scrollTo(0,0); // Disable scrolling on top for IE7
		}
	},
	
	_setWidth: function () { //Set size
		this.MBwindow.style.width = this.options.width + "px";
		this.MBwindow.style.height = this.options.height + "px";
	},
	
	_setPosition: function () {
		this.MBwindow.style.left = Math.round((Element.getWidth(document.body) - Element.getWidth(this.MBwindow)) / 2 ) + "px";
	},
	
	_setWidthAndPosition: function () {
		this._setWidth();
		this._setPosition();
	},
	
	_getScrollTop: function () { //From: http://www.quirksmode.org/js/doctypes.html
		var theTop;
		if (document.documentElement && document.documentElement.scrollTop)
			theTop = document.documentElement.scrollTop;
		else if (document.body)
			theTop = document.body.scrollTop;
		return theTop;
	},
	// For IE browsers -- IE requires height to 100% and overflow hidden (taken from lightbox)
	_prepareIE: function(height, overflow){
		bod = document.getElementsByTagName('body')[0];
		bod.style.height = height;
		bod.style.overflow = overflow;
  
		htm = document.getElementsByTagName('html')[0];
		htm.style.height = height;
		htm.style.overflow = overflow; 
	},
	// For IE browsers -- hiding all SELECT elements
	_toggleSelects: function() {
		if (navigator.appVersion.match(/\bMSIE\b/))
			$$("select").each( function(select) { 
				select.style.visibility = (select.style.visibility == "") ? "hidden" : "";
			});
	},
	event: function(eventName) {
		if(this.options[eventName]) {
			var returnValue = this.options[eventName](); // Executing callback
			this.options[eventName] = null; // Removing callback after execution
			if(returnValue != undefined) 
				return returnValue;
			else 
				return true;
		}
		return true;
	}
}

Object.extend(Modalbox, Modalbox.Methods);

Effect.ScaleBy = Class.create();
Object.extend(Object.extend(Effect.ScaleBy.prototype, Effect.Base.prototype), {
  initialize: function(element, byWidth, byHeight, options) {
	this.element = $(element)
	var options = Object.extend({
	  scaleFromTop: true,
	  scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
	  scaleByWidth: byWidth,
	  scaleByHeight: byHeight
	}, arguments[3] || {});
	this.start(options);
  },
  setup: function() {
	this.elementPositioning = this.element.getStyle('position');
	  
	this.originalTop  = this.element.offsetTop;
	this.originalLeft = this.element.offsetLeft;
	
	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];
	  
	this.deltaY = this.options.scaleByHeight;
	this.deltaX = this.options.scaleByWidth;
  },
  update: function(position) {
	var currentHeight = this.dims[0] + (this.deltaY * position);
	var currentWidth = this.dims[1] + (this.deltaX * position);
	
	this.setDimensions(currentHeight, currentWidth);
  },

  setDimensions: function(height, width) {
	var d = {};
	d.width = width + 'px';
	d.height = height + 'px';
	
	var topd  = Math.round((height - this.dims[0])/2);
	var leftd = Math.round((width  - this.dims[1])/2);
	if(this.elementPositioning == 'absolute' || this.elementPositioning == 'fixed') {
		if(!this.options.scaleFromTop) d.top = this.originalTop-topd + 'px';
		d.left = this.originalLeft-leftd + 'px';
	} else {
		if(!this.options.scaleFromTop) d.top = -topd + 'px';
		d.left = -leftd + 'px';
	}
	this.element.setStyle(d);
  }
});// Copyright (c) 2006 Sébastien Gruhier (http://xilinus.com, http://itseb.com)
// 
// 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.
//
// VERSION 0.26

var Carousel = Class.create();
Carousel.prototype = {
	// Constructor
	initialize: function(carouselElemID, options) {
	
		this.element = $(carouselElemID);
		this.clip = $('carouselClip');
		this.list = $('carouselList');

		this.options = Object.extend({
			numVisible:           5,
			scrollInc:            5,
			animParameters:      {},
			buttonStateHandler:  null,
			animHandler:         null,
			ajaxHandler:         null,
			initDoneHandler:     null,
			queue:               "carousel",
			size:                0,
			prevElementID:       "prev-arrow",
			nextElementID:       "next-arrow",
			ajaxParameters:      null,
			elementSize:         120,
			url:                 null
		}, options || {});
		
		this.initDone = false;
		this.animRunning = "none";
		this.requestIsRunning = false;
		
		// add afterFinish options to animParameters (store old function)
		this.animAfterFinish = this.options.animParameters.afterFinish;
		Object.extend(this.options.animParameters, {afterFinish:  this._animDone.bind(this), queue: { position:'end', scope: this.options.queue }});
		
		// Event bindings
		this.prevScroll = this._prevScroll.bindAsEventListener(this);
		this.nextScroll = this._nextScroll.bindAsEventListener(this);
		this.onComplete = this._onComplete.bindAsEventListener(this);
		this.onFailure  = this._onFailure.bindAsEventListener(this);
		
		Event.observe(this.options.prevElementID, "click", this.prevScroll);
		Event.observe(this.options.nextElementID, "click", this.nextScroll);
		
		// Init data
		this._init();
	},
  
  // Destructor
	destroy: function() {
		Event.stopObserving(this.options.prevElementID, "click", this.prevScroll);
		Event.stopObserving(this.options.nextElementID, "click", this.nextScroll);
	},
	
	scrollTo: function(newStart) {
		var old_inc = this.options.scrollInc;
		this.ignoreNoMoreImages = true;
		if(newStart > this.currentIndex) {
			this.options.scrollInc = newStart - this.currentIndex;
			this._nextScroll(this);
		} else {
			this.options.scrollInc = this.currentIndex - newStart;
			this._prevScroll(this);
		}
		this.options.scrollInc = old_inc;
	},
	
	showAt : function(nstart) {
		this.currentIndex = nstart;
		this.clip.scrollLeft = nstart*this.options.elementSize;

		var enable = this.currentIndex != 0;
		this._updateButtonStateHandler(this.options.prevElementID, enable);
		enable = (this.currentIndex*1 + this.options.numVisible*1) < this.options.size;
		this._updateButtonStateHandler(this.options.nextElementID, enable);
	},
	
  // "Private" functions
	_init: function(size) {
		this.currentIndex = 0;
		this.clip.scrollLeft = '0px';
		
		if (size)
			this.options.size = size;
		// Ajax content
		if (this.options.url)
			this._request(this.currentIndex, this.options.numVisible);
		// Static content
		else {
			this._updateButtonStateHandler(this.options.prevElementID, false);
			this._updateButtonStateHandler(this.options.nextElementID, this.options.size > this.options.numVisible);
		}
		
	},
  
	_prevScroll: function(event) {
		if (this.animRunning != "none" || this.currentIndex == 0)
			return;
		
		var inc = this.options.scrollInc;
		
		if (this.currentIndex - inc < 0)
			inc = this.currentIndex;
		
		this._scroll(inc)		  
		return false;
	},
  
	_nextScroll: function(event) {    
		if (this.animRunning != "none")
			return false;
		
		// Check if there are enough elements in cache
		if (this.currentIndex + this.options.numVisible + this.options.scrollInc <= this.options.size) 
			this._scroll(-this.options.scrollInc);
		else {
			// Compute how many are in the cache
			this.nbInCache = this.options.size - (this.currentIndex + this.options.numVisible);
			if (this.options.url && this.noMoreImages == false) 
				this._request(this.currentIndex + this.options.numVisible + this.nbInCache, this.options.scrollInc - this.nbInCache);
			else  {
				if (this.nbInCache > 0)
				this._scroll(-this.nbInCache);
			}
		}
		return false;
	},
  
	_request: function(start, nb) {
		if (this.options.url && ! this.requestIsRunning) {
			this.requestIsRunning = true;
		
			if (this.options.ajaxHandler)
				this.options.ajaxHandler(this, "before");
		
			var params = "start=" + start + "&nb=" + nb;
			if (this.options.ajaxParameters != null)
				params += "&" + this.options.ajaxParameters
		
			new Ajax.Request(this.options.url, {parameters: params, onComplete: this.onComplete, onFailure: this.onFailure});
		}
	},
  
	_onComplete: function(originalRequest){
		this.requestIsRunning = false;
		this.carouselList.innerHTML += originalRequest.responseText;
		// Compute how many new elements we have
		var size = this.options.size;
		this.options.size = this.carouselList.getElementsByTagName("li").length;
		var inc = this.options.size - size;
		
		// First run, compute li size
		if (this.initDone == false) {
			//  		this._getLiElementSize()
			this.currentIndex = 0;
			this.initDone = true;
			if (this.options.initDoneHandler) 
				this.options.initDoneHandler(this);
			
			// Update button states
			this._updateButtonStateHandler(this.options.prevElementID, false);
			this._updateButtonStateHandler(this.options.nextElementID, this.options.size == this.options.numVisible);
			this.noMoreImages = this.options.size < this.options.numVisible
		}
		// Add images
		else {
			if (!this.ignoreNoMoreImages)
				this.noMoreImages = inc != this.options.scrollInc;
			else
				this.ignoreNoMoreImages = false;
			// Add images
			if (inc > 0) {
				this._scroll(-inc, this.noMoreImages)
			}
			// No more images, disable next button
			else {
				if (this.nbInCache >0)
				this._scroll(-this.nbInCache, true);
			
				this._updateButtonStateHandler(this.options.nextElementID, false);
			}
		}
		if (this.options.ajaxHandler)
			this.options.ajaxHandler(this, "after");
	},
  
	_onFailure: function(originalRequest){    
		this.requestIsRunning = false;
	},

	_animDone: function(event){   
		if (this.options.animHandler)
			this.options.animHandler(this.element, "after", this.animRunning);
		
		this.animRunning = "none";
		// Call animAfterFinish if exists
		if (this.animAfterFinish)
			this.animAfterFinish(event);
	},
  
	_updateButtonStateHandler: function(button, state) {
		if (this.options.buttonStateHandler) 
			this.options.buttonStateHandler(button, state)
	},
  
	_scroll: function(delta, forceDisableNext) {      
		this.animRunning = delta > 0 ? "prev" : "next";
		
		if (this.options.animHandler)
			this.options.animHandler(this.element, "before", this.animRunning);
		
		new Effect.ScrollByX(this.clip, -delta * this.options.elementSize, this.options.animParameters);
		
		this.currentIndex -= delta;
		this._updateButtonStateHandler(this.options.prevElementID, this.currentIndex != 0);
		
		if (this.options.url && this.noMoreImages == false)
			enable = true;
		else
			enable = (this.currentIndex + this.options.numVisible < this.options.size);
		this._updateButtonStateHandler(this.options.nextElementID, (forceDisableNext ? false : enable));

		document.cookie = "qsearch_start="+escape(this.currentIndex);
	}
}

Effect.ScrollByX = Class.create();
Object.extend(Object.extend(Effect.ScrollByX.prototype, Effect.Base.prototype), {
	initialize: function(element, scrollByX) {
		this.element = $(element);
		if(!this.element) throw(Effect._elementDoesNotExistError);
		
		var options = Object.extend({
			x:    scrollByX
		}, arguments[2] || {});
		this.start(options);
	},
	setup: function() {
		this.originalLeft = this.element.scrollLeft;
	},
	update: function(position) {
		this.element.scrollLeft = Math.round(this.options.x * position + this.originalLeft);
	}
});
// JavaScript Document
var tabs = []
function tabOpen (id) {
	for (var i = 0; i < tabs.length; i++) {
		if (tabs[i] == id) {
			$('tabC_'+tabs[i]).hide ();
			$('tabO_'+tabs[i]).show ();
		}
		else {
			$('tabC_'+tabs[i]).show ();
			$('tabO_'+tabs[i]).hide ();
		}
	}
}