var Prototype = {
  Version: '1.5.0_rc2_mini',
  BrowserFeatures: {
    XPath: !!document.evaluate
  },

  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
  emptyFunction: function() {},
  K: function(x) { return x }
}

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

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

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },
  
  _addQueryParam: function(key, value, separator) {
	var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
	if (this == '' || this[this.length - 1] == '?') {
	  return this + param;  
	}
	if (this.indexOf('?') == -1) {
		return this + '?' + param;
	}
	return this + (separator || '&') + param;
  },
  
  addQueryParam: function(key, values, separator) {	
	var params = this;
	if (values && values.constructor == Array) {
	  values.each(function (value) {
	    if(value != null) {
		  params = params._addQueryParam(key, value);
		}
	  });
	} else if (typeof values == 'undefined' || values == null) {
	  params = params._addQueryParam(key, '');
	} else {
	  params = params._addQueryParam(key, values);
	}
	return params;
  },
  
  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return {};
	
	var hash = {};
	(match[1].split(separator || '&')).each(function (param) {
	  var pair = param.split('=');
	  if (pair[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;
  }
});

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}
  
Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

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;
  },

  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;
  },

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

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

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  select:  Enumerable.findAll
});

var Hash = {
  _each: function(iterator) {
    for (var key in this) {
      var value = this[key];
      if (typeof value != 'function') {
        var pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
	  }
    }
  },
  
  toQueryString: function() {
	var params = '';
	this.each(function (pair) {
	  params = params.addQueryParam(pair.key, pair.value);
	});
    return params;
  }
}

function $H(object) {
  var hash = Object.extend({}, object || {});
  Object.extend(hash, Enumerable);
  Object.extend(hash, Hash);
  return hash;
}

Object.extend(Array.prototype, Enumerable);

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },
  
  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  }
});

var $A = Array.from = function(iterable) {
  if (!iterable) {
    return [];
  }
  if (iterable.toArray) {
    return iterable.toArray();
  }
  var results = [];
  for (var i = 0, length = iterable.length; i < length; i++) {
    results.push(iterable[i]);
  }
  return results;
}

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 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();
    this.options.parameters = $H(typeof this.options.parameters == 'string' ? this.options.parameters.toQueryParams() : this.options.parameters);
  } 
};

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) {
    var params = this.options.parameters;
    if (params.any()) {
	  params['_'] = '';
	}

    if (!['get', 'post'].include(this.options.method)) {
      params['_method'] = this.options.method;
      this.options.method = 'post';
    }
    
    this.url = url;
    
    if (this.options.method == 'get' && params.any()) {
      this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') + params.toQueryString();
	}
     
    try {
      Ajax.Responders.dispatch('onCreate', this, this.transport);
  
      this.transport.open(this.options.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.options.method == 'post' ? (this.options.postBody || params.toQueryString()) : 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.options.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 (extras && extras.constructor == Array) {
        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);
      }
    }

    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') {
      if ((this.getHeader('Content-type') || '').strip().match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) {
        this.evalResponse();
	  }
      
      // 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.Request end

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

Object.extend(String.prototype, {
  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);
    });
  }
});

Object.extend(Number.prototype, {
  succ: function() {
    return this + 1;
  },
  
  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  }
});

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

if (!window.Element) {
  Element = new Object();
}
  
var _nativeExtensions = false;

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';
  },

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

  show: function(element) {
    $(element).style.display = '';
    return element;
  },
  
  update: function(element, html) {
    html = typeof html == 'undefined' ? '' : html.toString();
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  }
};

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    return $(element).getAttributeNode(attribute).specified;
  }
}

// IE is missing .innerHTML support for TABLE-related elements
if(document.all){
  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;
  }
}

function $(element) {
  if (arguments.length > 1) {
	var elements = [];
    for (var i = 0, length = arguments.length; i < length; i++) {
      elements.push($(arguments[i]));
	}
    return elements;
  }
  if (typeof element == 'string') {
    element = document.getElementById(element);
  }
  return Element.extend(element);
}

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.Updater end

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

//-------------------------------------------------------------------------------------------------Ajax.PeriodicalUpdater end

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);
	copy(Form.Element.Methods, HTMLInputElement);
	copy(Form.Element.Methods, HTMLTextAreaElement);
	copy(Form.Element.Methods, HTMLSelectElement);
    _nativeExtensions = true;
  }
}

var Form = {
  serializeElements: function(elements) {
    var params = '';
	elements.each(function (element) {
      element = $(element);
      if (!element.disabled) {	  
        var pair = Form.Element.Serializers[element.tagName.toLowerCase()](element);
	    if (pair) {
	      params = params.addQueryParam(pair[0], pair[1]);
        }
	  }
	});
	return params;
  }
};

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

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

Object.extend(Form, Form.Methods);

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

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

Form.Element.Methods = {
  getValue: function(element) {
    element = $(element);
    var pair = Form.Element.Serializers[element.tagName.toLowerCase()](element);
    if (pair) {
      return pair[1];
	}
  }
};

Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;

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);
    }
    return false;
  },

  inputSelector: function(element) {
    if (element.checked) {
      return [element.name, element.value];
	}
  },

  textarea: function(element) {
    return [element.name, element.value];
  },

  select: function(element) {
    return Form.Element.Serializers[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var value = '', opt, index = element.selectedIndex;
    if (index >= 0) {
      opt = Element.extend(element.options[index]);
      // Uses the new potential extension if hasAttribute isn't native.
      value = opt.hasAttribute('value') ? opt.value : opt.text;
    }
    return [element.name, value];
  },

  selectMany: function(element) {
    var value = [];
    for (var i = 0, length = element.length; i < length; i++) {
      var opt = Element.extend(element.options[i]);
      if (opt.selected)
        // Uses the new potential extension if hasAttribute isn't native.
        value.push(opt.hasAttribute('value') ? opt.value : opt.text);
    }
    return [element.name, value];
  }
};

var $F = Form.Element.getValue;

Element.addMethods();
