
function jpRequest(form_id, dynamic_id, form_post)
{
 if($(form_id) && $(dynamic_id))
 { 
 $(form_id).addEvent('submit', function(g) {
  g.stop();  
  var output = $(dynamic_id);  
  var submitform = new Request.HTML({
  url: $(form_id).get('action'),
  evalScripts : true,
  evalResponse :true,
  async : false,
  onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript) {    
   output.removeClass('loading');   
   //output.erase('html');
   // chrome will das nicht   
   output.set('html', responseHTML); 
   eval(responseJavaScript);  
  },
  onFailure: function() {
   //output.set('html', 'fehler');
  },
  onRequest: function() {
   //output.erase('html');
   output.addClass('loading');   
  }  
  });

  if(form_post){
  // sendet kompletten formularinhalt
  submitform.post($(form_id)); 
  } else { 
  // nur link
  submitform.send();
  }  
 });
 }
}

function jpFormRequest(form_id, dynamic_id)
{ 
 var output = $(dynamic_id);
 
 var submitform = new Form.Request($(form_id),dynamic_id, { 
 evalScripts : true,
 evalResponse :true,
 async : true, 
 onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript) {
  output.removeClass('loading');     
  eval(responseJavaScript);    
 },
 onFailure: function() {
  output.set('html', 'fehler');
 },
 onSend: function() {
  output.addClass('loading');
  
 }  
 }); 
}

function jpLink(element_id, dynamic_id, url_str)
{
 
 if($(element_id))
 {
 $(element_id).addEvent('click', function(c) {
  c.stop();
  
  var output = $(dynamic_id);
  
  var submitform = new Request.HTML({
  url: url_str,
  onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript) {
   output.removeClass('loading');    
   output.set('html', responseHTML);  
   eval(responseJavaScript);  
   
  },
  onFailure: function() {
   output.set('html', 'fehler');
  },
  onRequest: function() {
   output.addClass('loading');
   
  }  
  });
  // nur link
  submitform.send();
 });
 }
}

var MultipleOpenAccordion = new Class({
 Implements: [Options, Events, Chain],
 options: {
 togglers: [],
 elements: [],
 openAll: false,
 firstElementsOpen: [0],
 fixedHeight: false,
 fixedWidth: false,
 height: true,
 opacity: true,
 width: false
// onActive: $empty,
// onBackground: $empty
 },
 togglers: [],
 elements: [],
 initialize: function(options){
 var args = Array.link(arguments, {options: Object.type, elements: Array.type});
 this.setOptions(args.options);
 elements = $$(this.options.elements);
 $$(this.options.togglers).each(function(toggler, idx){
  this.addSection(toggler, elements[idx], idx);
 }, this);
 if (this.togglers.length) {
  if (this.options.openAll) this.showAll();
  else this.toggleSections(this.options.firstElementsOpen, false, true);
 }
 this.openSections = this.showSections.bind(this);
 this.closeSections = this.hideSections.bind(this);
 },
 addSection: function(toggler, element){
 toggler = document.id(toggler);
 element = document.id(element);
 var test = this.togglers.contains(toggler);
 var len = this.togglers.length;
 this.togglers.include(toggler);
 this.elements.include(element);
 var idx = this.togglers.indexOf(toggler);
 toggler.addEvent('click', this.toggleSection.bind(this, idx));
 var mode;
 if (this.options.height && this.options.width) mode = "both";
 else mode = (this.options.height)?"vertical":"horizontal";
 element.store('reveal', new Fx.Reveal(element, {
  transitionOpacity: this.options.opacity,
  mode: mode,
  heightOverride: this.options.fixedHeight,
  widthOverride: this.options.fixedWidth
 }));
 return this;
 },
 onComplete: function(idx, callChain){
 this.fireEvent(this.elements[idx].isDisplayed()?'onActive':'onBackground', [this.togglers[idx], this.elements[idx]]);
 this.callChain();
 return this;
 },
 showSection: function(idx, useFx){
 this.toggleSection(idx, useFx, true);
 },
 hideSection: function(idx, useFx){
 this.toggleSection(idx, useFx, false);
 },
 toggleSection: function(idx, useFx, show, callChain){
 var method = show?'reveal':$defined(show)?'dissolve':'toggle';
 callChain = $pick(callChain, true);
 var el = this.elements[idx];
 if ($pick(useFx, true)) {
  el.retrieve('reveal')[method]().chain(
  this.onComplete.bind(this, [idx, callChain])
  );
 } else {
  if (method == "toggle") el.togglek();
  else el[method == "reveal"?'show':'hide']();
  this.onComplete(idx, callChain);
 }
 return this;
 },
 toggleAll: function(useFx, show){
 var method = show?'reveal':$chk(show)?'disolve':'toggle';
 var last = this.elements.getLast();
 this.elements.each(function(el, idx){
  this.toggleSection(idx, useFx, show, el == last);
 }, this);
 return this;
 },
 toggleSections: function(sections, useFx, show) {
 last = sections.getLast();
 this.elements.each(function(el,idx){
  this.toggleSection(idx, useFx, sections.contains(idx)?show:!show, idx == last);
 }, this);
 return this;
 },
 showSections: function(sections, useFx){
 sections.each(function(i){
  this.showSection(i, useFx);
 }, this);
 },
 hideSections: function(sections, useFx){
 sections.each(function(i){
  this.hideSection(i, useFx);
 }, this);
 },
 showAll: function(useFx){
 return this.toggleAll(useFx, true);
 },
 hideAll: function(useFx){
 return this.toggleAll(useFx, false);
 }
});


function jpTipps(){ 
 $$('.help').each(function(element,index){
 var cnt = element.getChildren('.helpdsc').get('html');
 cnt = String(cnt);
 element.store('tip:title', 'Hilfe');
 element.store('tip:text',cnt);      
 }); 
 var helpTips = new Tips('.help',{
 className: 'helptipp',
 showDelay: 250,
 hideDelay: 250 
 }); 
 helpTips.addEvents({
 'show': function(tip) {tip.fade('in');},
 'hide': function(tip) {tip.fade('out');}
 });   
}

function jpPurchaseTipps(){ 
 $$('label').each(function(element,index){
 element.getChildren('span').hide();
 var cnt = element.getChildren('span').get('html');
 cnt = String(cnt);
 element.store('tip:title', 'Kontostand');
 element.store('tip:text',cnt);      
 }); 
 var helpTips = new Tips('label',{
 className: 'helptipp purchase',
 showDelay: 250,
 hideDelay: 250 
 }); 
 helpTips.addEvents({
 'show': function(tip) {tip.fade('in');},
 'hide': function(tip) {tip.fade('out');}
 });   
}


function str_replace(search, replace, subject) {
 return subject.split(search).join(replace);
}



(function(global, $){
 
 var isIE6 = Browser.ie6; // better compression and faster

 var BgIframe = new Class({
 Implements: Options,
 options: {
  top : 'auto',
  left : 'auto',
  width : 'auto',
  height : 'auto',
  opacity : true,
  src : 'javascript:false;'
 },
 initialize: function(element, options){
  if (!isIE6) return;
  this.setOptions(options);
  this.element = $(element);
  var firstChild = this.element.getFirst();
  if (!(firstChild && firstChild.hasClass('bgiframe'))){
  this.element.grab(document.createElement(this.render()), 'top');
  }
 },
 toPx: function(n){ 
  return isFinite(n) ? n + 'px' : n;
 },
 render: function(){
  var options = this.options;
  return '<iframe class="bgiframe" frameborder="0" tabindex="-1" src="' + options.src + '" ' +
  'style="display:block;position:absolute;z-index:-1;' +
  (options.opacity !== false ? 'filter:alpha(opacity=\'0\');' : '') +
  'top:' + (options.top == 'auto' ? 'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')' : this.toPx(options.top)) + ';' +
  'left:' + (options.left == 'auto' ? 'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')' : this.toPx(options.left)) + ';' +
  'width:' + (options.width == 'auto' ? 'expression(this.parentNode.offsetWidth+\'px\')' : this.toPx(options.width)) + ';' +
  'height:' + (options.height == 'auto' ? 'expression(this.parentNode.offsetHeight+\'px\')' : this.toPx(options.height)) + ';' +
  '"/>';
 }
 });
 
 Element.implement('bgiframe', function(options){
 if (isIE6) new BgIframe(this, options);
 return this;
 });
 
})(this, document.id || $);



(function(global, $){

 var browser = Browser; // better compression and faster

 // Custom Events

 // thanks Jan Kassens
 Object.append(Element.NativeEvents, {
 'paste': 2, 'input': 2
 });
 Element.Events.paste = {
 base : (browser.opera || (browser.firefox && browser.version < 3)) ? 'input' : 'paste',
 condition: function(e){
  this.fireEvent('paste', e, 1);
  return false;
 }
 };
 
 // the key event that repeats
 Element.Events.keyrepeat = {
 base : (browser.firefox || browser.opera) ? 'keypress' : 'keydown',
 condition: Function.from(true)
 };
 
 // Autocomplete itself

 var Meio = global.Meio || {};
 var globalCache;
 
 var keysThatDontChangeValueOnKeyUp = {
 9:  1, // tab
 16: 1, // shift
 17: 1, // control
 18: 1, // alt
 224: 1, // command (meta onkeypress)
 91: 1, // command (meta onkeydown)
 37: 1, // left
 38: 1, // up
 39: 1, // right
 40: 1  // down
 }; 
 
 var encode = function(str){
 return str.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
 };
 
 Meio.Widget = new Class({
 
 initialize: function(){
  this.elements = {};
 },
 
 addElement: function(name, obj){
  this.elements[name] = obj;
 },
 
 addEventToElement: function(name, eventName, event){
  this.elements[name].addEvent(eventName, event.bind(this));
 },
 
 addEventsToElement: function(name, events){
  for (var eventName in events){
  this.addEventToElement(name, eventName, events[eventName]);
  }
 },
 
 attach: function(){
  for (var element in this.elements){
  this.elements[element].attach();
  }
 },
 
 detach: function(){
  for (var element in this.elements){
  this.elements[element].detach();
  }
 },
 
 destroy: function(){
  for (var element in this.elements){
  this.elements[element] && this.elements[element].destroy();
  }
 }
 });
 
 Meio.Autocomplete = new Class({
 
 Extends: Meio.Widget,
 
 Implements: [Options, Events],
 
 options: {
  
  delay: 200,
  minChars: 0,
  cacheLength: 20,
  selectOnTab: true,
  maxVisibleItems: 10,
  cacheType: 'shared', // 'shared' or 'own'
  
  filter: {
  
  },
  
  
  
  fieldOptions: {}, // see Element options
  listOptions: {}, // see List options
  requestOptions: {}, // see DataRequest options
  urlOptions: {} // see URL options
  
 },
 
 initialize: function(input, data, options, listInstance){
  this.parent();
  this.setOptions(options);
  this.active = 0;
  
  this.filters = Meio.Autocomplete.Filter.get(this.options.filter);
  
  this.addElement('list', listInstance || new Meio.Element.List(this.options.listOptions));
  this.addListEvents();
  
  this.addElement('field', new Meio.Element.Field(input, this.options.fieldOptions));
  this.addFieldEvents();
  
  this.addSelectEvents();
  
  this.attach();
  this.initCache();
  this.initData(data);
 },
 
 addFieldEvents: function(){
  this.addEventsToElement('field', {
  'beforeKeyrepeat': function(e){
   this.active = 1;
   var e_key = e.key, list = this.elements.list;
   if (e_key == 'up' || e_key == 'down' || (e_key == 'enter' && list.showing)) e.preventDefault();
  },
  'delayedKeyrepeat': function(e){
   var e_key = e.key, field = this.elements.field;
   field.keyPressControl[e_key] = true;
   switch (e_key){
   case 'up': case 'down':
   this.focusItem(e_key);
   break;
   case 'enter':
   this.setInputValue();
   break;
   case 'tab':
   if (this.options.selectOnTab) this.setInputValue();
   field.keyPressControl[e_key] = false; // tab blurs the input so the keyup event wont happen at the same input you made a keydown
   break;
   case 'esc':
   this.elements.list.hide();
   break;
   default:
   this.setupList();
   }
   this.oldInputedText = field.node.get('value');
  },
  'keyup': function(e){
   var field = this.elements.field;
   if (!keysThatDontChangeValueOnKeyUp[e.code]){
   if (!field.keyPressControl[e.key]) this.setupList();
   field.keyPressControl[e.key] = false;
   }
  },
  'focus': function(){
   this.active = 1;
   var list = this.elements.list;
   list.focusedItem = null;
   list.positionNextTo(this.elements.field.node);
  },
  'click': function(){
   if (++this.active > 2 && !this.elements.list.showing){
   this.forceSetupList();
   }
  },
  'blur': function(e){
   this.active = 0;
   var list = this.elements.list;
   if (list.shouldNotBlur){
   this.elements.field.node.setCaretPosition('end');
   list.shouldNotBlur = false;
   if (list.focusedItem) list.hide();
   } else {
   list.hide();
   }
  },
  'paste': function(){
   return this.setupList();
  }
  });
 },
 
 addListEvents: function(){
  this.addEventsToElement('list', {
  'mousedown': function(e){
   if (this.active && !e.dontHide) this.setInputValue();
  }
  });
 },
 
 update: function(){
  var data = this.data, list = this.elements.list;
  var cacheKey = data.getKey(), cached = this.cache.get(cacheKey), html;
  if (cached){
  html = cached.html;
  this.itemsData = cached.data;
  } else {
  data = data.get();
  var itemsHtml = [], itemsData = [], classes = list.options.classes, text = this.inputedText;
  var filter = this.filters.filter, formatMatch = this.filters.formatMatch, formatItem = this.filters.formatItem;
  for (var row, i = 0, n = 0; row = data[i++];) if (filter.call(this, text, row)){
   itemsHtml.push(
   '<li title="', encode(formatMatch.call(this, text, row)),
   '" data-index="', n,
   '" class="', (n%2 ? classes.even : classes.odd), '">',
   formatItem.call(this, text, row, n),
   '</li>'
   );
   itemsData.push(row);
   n++;
  }
  html = itemsHtml.join('');
  this.cache.set(cacheKey, {html: html, data: itemsData});
  this.itemsData = itemsData;
  }
  list.focusedItem = null;
  this.fireEvent('deselect', [this.elements]);
  list.list.set('html', html);
  if (this.options.maxVisibleItems) list.applyMaxHeight(this.options.maxVisibleItems);
 },
 
 setupList: function(){
  this.inputedText = this.elements.field.node.get('value');
  if (this.inputedText !== this.oldInputedText){
  this.forceSetupList(this.inputedText);
  } else {
  this.elements.list.hide();
  }
  return true;
 },
 
 forceSetupList: function(inputedText){
  inputedText = inputedText || this.elements.field.node.get('value');
  if (inputedText.length >= this.options.minChars){
  clearInterval(this.prepareTimer);
  this.prepareTimer = this.data.prepare.delay(this.options.delay, this.data, this.inputedText);
  }
 },
 
 dataReady: function(){
  this.update();
  if (this.onUpdate){
  this.onUpdate();
  this.onUpdate = null;
  }
  var list = this.elements.list;
  if (list.list.get('html')){
  if (this.active) list.show();
  } else {
  this.fireEvent('noItemToList', [this.elements]);
  list.hide();
  }
 },
 
 setInputValue: function(){
  var list = this.elements.list;
  if (list.focusedItem){
  var text = list.focusedItem.get('title');
  this.elements.field.node.set('value', text);
  var index = list.focusedItem.get('data-index');
  this.fireEvent('select', [this.elements, this.itemsData[index], text, index]);
  }
  list.hide();
 },
 
 focusItem: function(direction){
  var list = this.elements.list;
  if (list.showing){
  list.focusItem(direction);
  } else {
  this.forceSetupList();
  this.onUpdate = function(){ list.focusItem(direction); };
  }
 },
 
 addSelectEvents: function(){
  this.addEvents({
  select: function(elements){
   elements.field.addClass('selected');
  },
  deselect: function(elements){
   elements.field.removeClass('selected');
  }
  });
 },
 
 initData: function(data){
  this.data = (typeOf(data) == 'string') ?
  new Meio.Autocomplete.Data.Request(data, this.cache, this.elements.field, this.options.requestOptions, this.options.urlOptions) :
  new Meio.Autocomplete.Data(data, this.cache);
  this.data.addEvent('ready', this.dataReady.bind(this));
 },
 
 initCache: function(){
  var cacheLength = this.options.cacheLength;
  if (this.options.cacheType == 'shared'){
  this.cache = globalCache;
  this.cache.setMaxLength(cacheLength);
  } else { // 'own'
  this.cache = new Meio.Autocomplete.Cache(cacheLength);
  }
 },
 
 refreshCache: function(cacheLength){
  this.cache.refresh();
  this.cache.setMaxLength(cacheLength || this.options.cacheLength);
 },
 
 refreshAll: function(cacheLength, urlOptions){
  // TODO, do you really need to refresh the url? see a better way of doing this
  this.refreshCache(cacheLength);
  this.data.refreshKey(urlOptions);
 }

 });
 
 // This is the same autocomplete class but it acts like a normal select element.
 // When you select an option from the autocomplete it will set the value of a given element (valueField)
 // with the return of the valueFilter.
 // if the syncAtInit option is set to true, it will synchonize the value of the autocomplete with the corresponding data
 // from the valueField's value.
 // to understand better see the user specs.
 
 Meio.Autocomplete.Select = new Class({
 
 Extends: Meio.Autocomplete,
 
 options: {
  syncName: 'id', // if falsy it wont sync at start
  valueField: null,
  valueFilter: function(data){
  return data.id;
  }
 },
 
 // overwritten
 initialize: function(input, data, options, listInstance){
  this.parent(input, data, options, listInstance);
  this.valueField = $(this.options.valueField);
  
  if (!this.valueField) return;
  
  this.syncWithValueField(data);
 },
 
 syncWithValueField: function(data){
  var value = this.getValueFromValueField();
  
  if (value && this.options.syncName){
  this.addParameter(data);
  this.addDataReadyEvent(value);
  this.data.prepare(this.elements.field.node.get('value'));
  } else {
  this.addValueFieldEvents();
  }
 },
 
 addValueFieldEvents: function(){
  this.addEvents({
  'select': function(elements, data){
   this.valueField.set('value', this.options.valueFilter.call(this, data));
  },
  'deselect': function(elements){
   this.valueField.set('value', '');
  }
  });
 },
 
 addParameter: function(data){
  this.parameter = {
  name: this.options.syncName,
  value: function(){
   return this.valueField.value;
  }.bind(this)
  };
  if (this.data.url) this.data.url.addParameter(this.parameter);
 },
 
 addDataReadyEvent: function(value){
  var self = this;
  var runOnce = function(){
  self.addValueFieldEvents();
  var values = this.get();
  for (var i = values.length; i--;){
   if (self.options.valueFilter.call(self, values[i]) == value){
   var text = self.filters.formatMatch.call(self, '', values[i], 0);
   self.elements.field.node.set('value', text);
   self.fireEvent('select', [self.elements, values[i], text, i]);
   break;
   }
  }
  if (this.url) this.url.removeParameter(self.parameter);
  this.removeEvent('ready', runOnce);
  };
  this.data.addEvent('ready', runOnce);
 },
 
 getValueFromValueField: function(){
  return this.valueField.get('value');
 }
 
 });
 
 // Transforms a select on an autocomplete field
 
 Meio.Autocomplete.Select.One = new Class({
 
 Extends: Meio.Autocomplete.Select,
 
 options: {
  filter: {
  path: 'text' // path to the text value on each object thats contained on the data array
  }
 },
 
 //overwritten
 initialize: function(select, options, listInstance){
  this.select = $(select);
  this.replaceSelect();
  this.parent(this.field, this.createDataArray(), Object.merge(options || {}, {
  valueField: this.select,
  valueFilter: function(data){ return data.value; }
  }), listInstance);
 },
 
 replaceSelect: function(){
  var selectedOption = this.select.getSelected()[0];
  this.field = new Element('input', {type: 'text'});
  var optionValue = selectedOption.get('value');
  if (optionValue || optionValue === 0) this.field.set('value', selectedOption.get('html'));
  this.select.setStyle('display', 'none');
  this.field.inject(this.select, 'after');
 },
 
 createDataArray: function(){
  var selectOptions = this.select.options, data = [];
  for(var i = 0, selectOption, optionValue; selectOption = selectOptions[i++];){
  optionValue = selectOption.value;
  if (optionValue || optionValue === 0) data.push({value: optionValue, text: selectOption.innerHTML});
  }
  return data;
 },
 
 addValueFieldEvents: function(){
  this.addEvents({
  'select': function(elements, data, text, index){
   var option = this.valueField.getElement('option[value="' + this.options.valueFilter.call(this, data) + '"]');
   if (option) option.selected = true;
  },
  'deselect': function(elements){
   var option = this.valueField.getSelected()[0];
   if (option) option.selected = false;
  }
  });
 },
 
 getValueFromValueField: function(){
  return this.valueField.getSelected()[0].get('value');
 }
 
 });
 
 Meio.Element = new Class({
 
 Implements: [Events],
 
 initialize: function(node){
  this.setNode(node);
  this.createBoundEvents();
  this.attach();
 },
 
 setNode: function(node){
  this.node = node ? $(node) || $$(node)[0] : this.render();
 },
 
 createBoundEvents: function(){
  this.bound = {};
  this.boundEvents.each(function(evt){
  this.bound[evt] = function(e){
   this.fireEvent('before' + evt.capitalize(), e);
   this[evt] && this[evt](e);
   this.fireEvent(evt, e);
   return true;
  }.bind(this);
  }, this);
 },
 
 attach: function(){
  for (var e in this.bound){
  this.node.addEvent(e, this.bound[e]);
  }
 },
 
 detach: function(){
  for (var e in this.bound){
  this.node.removeEvent(e, this.bound[e]);
  }
 },
 
 addClass: function(type){
  this.node.addClass(this.options.classes[type]);
 },
 
 removeClass: function(type){
  this.node.removeClass(this.options.classes[type]);
 },
 
 toElement: function(){
  this.node;
 },
 
 render: function(){}
 
 });

 Meio.Element.Field = new Class({
 
 Extends: Meio.Element,
 
 Implements: [Options],
 
 options: {
  classes: {
  loading: 'ma-loading',
  selected: 'ma-selected'
  }
 },
 
 initialize: function(field, options){
  this.keyPressControl = {};
  this.boundEvents = ['paste', 'focus', 'blur', 'click', 'keyup', 'keyrepeat'];
  if (browser.ie6) this.boundEvents.push('keypress'); // yeah super ugly, but what can be awesome with ie?
  this.setOptions(options);
  this.parent(field);
  
  $(global).addEvent('unload', function(){
  if (this.node) this.node.set('autocomplete', 'on'); // if autocomplete is off when you reload the page the input value gets erased
  }.bind(this));
 },
 
 setNode: function(element){
  this.parent(element);
  this.node.set('autocomplete', 'off');
 },
 
 // this let me get the value of the input on keydown and keypress
 keyrepeat: function(e){
  clearInterval(this.keyrepeatTimer);
  this.keyrepeatTimer = this._keyrepeat.delay(1, this, e);
 },
 
 _keyrepeat: function(e){
  this.fireEvent('delayedKeyrepeat', e);
 },
 
 destroy: function(){
  this.detach();
  this.node.removeAttribute('autocomplete');
 },
 
 // ie6 only, uglyness
 // this fix the form being submited on the press of the enter key
 keypress: function(e){
  if (e.key == 'enter') this.bound.keyrepeat(e);
 }
 
 });

 Meio.Element.List = new Class({
 
 Extends: Meio.Element,
 
 Implements: [Options],
 
 options: {
  width: 'field', // you can pass any other value settable by set('width') to the list container
  classes: {
  container: 'ma-container',
  hover: 'ma-hover',
  odd: 'ma-odd',
  even: 'ma-even'
  }
 },
 
 initialize: function(options){
  this.boundEvents = ['mousedown', 'mouseover'];
  this.setOptions(options);
  this.parent();
  this.focusedItem = null;
 },
 
 applyMaxHeight: function(maxVisibleItems){
  var listChildren = this.list.childNodes;
  var node = listChildren[maxVisibleItems - 1] || (listChildren.length ? listChildren[listChildren.length - 1] : null);
  if (!node) return;
  node = $(node);
  // uggly hack to fix the height of the autocomplete list
  for (var i = 2; i--;) this.node.setStyle('height', node.getCoordinates(this.list).bottom);
 },
 
 mouseover: function(e){
  var item = this.getItemFromEvent(e), hoverClass = this.options.classes.hover;
  if (!item) return true;
  if (this.focusedItem) this.focusedItem.removeClass(hoverClass);
  item.addClass(hoverClass);
  this.focusedItem = item;
  this.fireEvent('focusItem', [this.focusedItem]);
 },
 
 mousedown: function(e){
  e.preventDefault();
  this.shouldNotBlur = true;
  if (!(this.focusedItem = this.getItemFromEvent(e))){
  e.dontHide = true;
  return true;
  } 
  this.focusedItem.removeClass(this.options.classes.hover);
 },
 
 focusItem: function(direction){
  var hoverClass = this.options.classes.hover, newFocusedItem;
  if (this.focusedItem){
  if ((newFocusedItem = this.focusedItem[direction == 'up' ? 'getPrevious' : 'getNext']())){
   this.focusedItem.removeClass(hoverClass);
   newFocusedItem.addClass(hoverClass);
   this.focusedItem = newFocusedItem;
   this.scrollFocusedItem(direction);
  }
  } else {
  if ((newFocusedItem = this.list.getFirst())){
   newFocusedItem.addClass(hoverClass);
   this.focusedItem = newFocusedItem;
  }
  }
 },
 
 scrollFocusedItem: function(direction){
  var focusedItemCoordinates = this.focusedItem.getCoordinates(this.list),
  scrollTop = this.node.scrollTop;
  if (direction == 'down'){
  var delta = focusedItemCoordinates.bottom - this.node.getStyle('height').toInt();
  if ((delta - scrollTop) > 0){
   this.node.scrollTop = delta;
  }
  } else {
  var top = focusedItemCoordinates.top;
  if (scrollTop && scrollTop > top){
   this.node.scrollTop = top;
  }
  }
 },
 
 getItemFromEvent: function(e){
  var target = e.target;
  while (target && target.tagName.toLowerCase() != 'li'){
  if (target === this.node) return null;
  target = target.parentNode;
  }
  return $(target);
 },
 
 render: function(){
  var node = new Element('div', {'class': this.options.classes.container});
  if (node.bgiframe) node.bgiframe({top: 0, left: 0});
  this.list = new Element('ul').inject(node);
  $(document.body).grab(node);
  return node;
 },
 
 positionNextTo: function(fieldNode){
  var width = this.options.width, listNode = this.node;
  var elPosition = fieldNode.getCoordinates();
  listNode.setStyle('width', width == 'field' ? fieldNode.getWidth().toInt() - listNode.getStyle('border-left-width').toInt() - listNode.getStyle('border-right-width').toInt() : width);
  listNode.setPosition({x: elPosition.left, y: elPosition.bottom});
 },
 
 show: function(){
  this.node.scrollTop = 0;
  this.node.setStyle('visibility', 'visible');
  this.showing = true;
 },
 
 hide: function(){
  this.showing = false;
  this.node.setStyle('visibility', 'hidden');
 }
 
 });
 
 Meio.Autocomplete.Filter = {
 
 filters: {},
 
 get: function(options){
  var type = options.type, keys = (options.path || '').split('.');
  
  var filters = (type && this.filters[type]) ? this.filters[type](this, keys) : options;
  return Object.merge(this.defaults(keys), filters);
 },
 
 define: function(name, options){
  this.filters[name] = options;
 },
 
 defaults: function(keys){
  var self = this;
  return {
  filter: function(text, data){
   return text ? self._getValueFromKeys(data, keys).test(new RegExp(text.escapeRegExp(), 'i')) : true;
  },
  formatMatch: function(text, data){
   return self._getValueFromKeys(data, keys);
  },
  formatItem: function(text, data, i){
   return text ? self._getValueFromKeys(data, keys).replace(new RegExp('(' + text.escapeRegExp() + ')', 'gi'), '<strong>$1</strong>') : self._getValueFromKeys(data, keys);
  }
  };
 },
 
 _getValueFromKeys: function(obj, keys){
  var key, value = obj;
  for (var i = 0; key = keys[i++];) value = value[key];
  return value;
 }
 
 };
 
 Meio.Autocomplete.Filter.define('contains', function(self, keys){return {};});
 Meio.Autocomplete.Filter.define('startswith', function(self, keys){
 return {
  filter: function(text, data){
  return text ? self._getValueFromKeys(data, keys).test(new RegExp('^' + text.escapeRegExp(), 'i')) : true;
  }
 };
 });
 
 Meio.Autocomplete.Data = new Class({
 
 Implements: [Options, Events],
 
 initialize: function(data, cache){
  this._cache = cache;
  this.data = data;
  this.dataString = JSON.encode(this.data);
 },
 
 get: function(){
  return this.data;
 },
 
 getKey: function(){
  return this.cachedKey;
 },
 
 prepare: function(text){
  this.cachedKey = this.dataString + (text || '');
  this.fireEvent('ready');
 },
 
 cache: function(key, data){
  this._cache.set(key, data);
 },
 
 refreshKey: function(){}
 
 });
 
 Meio.Autocomplete.Data.Request = new Class({
 
 Extends: Meio.Autocomplete.Data,
 
 options: {
  noCache: true,
  formatResponse: function(jsonResponse){
  return jsonResponse;
  }
 },
 
 initialize: function(url, cache, element, options, urlOptions){
  this.setOptions(options);
  this.rawUrl = url;
  this._cache = cache;
  this.element = element;
  this.urlOptions = urlOptions;
  this.refreshKey();
  this.createRequest();
 },
 
 prepare: function(text){
  this.cachedKey = this.url.evaluate(text);
  if (this._cache.has(this.cachedKey)){
  this.fireEvent('ready');
  } else {
  this.request.send({url: this.cachedKey});
  }
 },
 
 createRequest: function(){
  var self = this;
  this.request = new Request.JSON(this.options);
  this.request.addEvents({
  request: function(){
   self.element.addClass('loading');
  },
  complete: function(){
   self.element.removeClass('loading');
  },
  success: function(jsonResponse){
   self.data = self.options.formatResponse(jsonResponse);
   self.fireEvent('ready');
  }
  });
 },
 
 refreshKey: function(urlOptions){
  urlOptions = Object.merge(this.urlOptions, {url: this.rawUrl}, urlOptions || {});
  this.url = new Meio.Autocomplete.Data.Request.URL(urlOptions.url, urlOptions);
 }
 
 });
 
 Meio.Autocomplete.Data.Request.URL = new Class({
 
 Implements: [Options],
 
 options: {
  queryVarName: 'q',
  extraParams: null,
  max: 20
 },
 
 initialize: function(url, options){
  this.setOptions(options);
  this.rawUrl = url;
  this.url = url;
  this.url += this.url.contains('?') ? '&' : '?';
  this.dynamicExtraParams = [];
  var params = Array.from(this.options.extraParams);
  for (var i = params.length; i--;){
  this.addParameter(params[i]);
  }
  if (this.options.max) this.addParameter('limit=' + this.options.max);
 },
 
 evaluate: function(text){
  text = text || '';
  var params = this.dynamicExtraParams, url = [];
  url.push(this.options.queryVarName + '=' + encodeURIComponent(text));
  for (var i = params.length; i--;){
  url.push(encodeURIComponent(params[i].name) + '=' + encodeURIComponent(Function.from(params[i].value)()));
  }
  return this.url + url.join('&');
 },
 
 addParameter: function(param){
  if (param.nodeType == 1 || typeOf(param.value) == 'function'){
  this.dynamicExtraParams.push(param);
  } else {
  this.url += ((typeOf(param) == 'string') ? param : encodeURIComponent(param.name) + '=' + encodeURIComponent(param.value)) + '&';
  }
 },
 
 // TODO remove non dynamic parameters
 removeParameter: function(param){
  this.dynamicExtraParams.erase(param);
 }
 
 });
 
 Meio.Autocomplete.Cache = new Class({
 
 initialize: function(maxLength){
  this.refresh();
  this.setMaxLength(maxLength);
 },
 
 set: function(key, value){
  if (!this.cache[key]){
  if (this.getLength() >= this.maxLength){
   var keyToRemove = this.pos.shift();
   this.cache[keyToRemove] = null;
   delete this.cache[keyToRemove];
  }
  this.cache[key] = value;
  this.pos.push(key);
  }
  return this;
 },
 
 get: function(key){
  return this.cache[key || ''] || null;
 },
 
 has: function(key){
  return !!this.get(key);
 },
 
 getLength: function(){
  return this.pos.length;
 },
 
 refresh: function(){
  this.cache = {};
  this.pos = [];
 },
 
 setMaxLength: function(maxLength){
  this.maxLength = Math.max(maxLength, 1);
 }
 
 });
 
 globalCache = new Meio.Autocomplete.Cache();
 
 global.Meio = Meio;
 
})(this, document.id || $); 



(function(){

if (!this.Form) this.Form = {};

var supportsPlaceholder = ('placeholder' in document.createElement('input'));
if (!('supportsPlaceholder' in this) && this.supportsPlaceholder !== false && supportsPlaceholder) {
 this.Form.Placeholder = new Class({});
 return;
}

this.Form.Placeholder = new Class({
 Implements: Options,
 options: {
 color: '#A9A9A9',
 clearOnSubmit: true
 },
 initialize: function(element, options) {
 this.setOptions(options);
 this.element = $(element);
 
 this.placeholder = this.element.get('placeholder');
 this.original_color = this.element.getStyle('color');
 this.is_password = this.element.get('type') == 'password' ? true : false;
 
 this.activatePlaceholder();

 this.element.addEvents({
  'focus': function() {
  this.deactivatePlaceholder();
  }.bind(this),
  'blur': function() {
  this.activatePlaceholder();
  }.bind(this)
 });
 
 if (this.element.getParent('form') && this.options.clearOnSubmit) {
  this.element.getParent('form').addEvent('submit', function(e){
  if (this.element.get('value') == this.placeholder) {
   this.element.set('value', '');
  }
  }.bind(this));
 }
 },
 activatePlaceholder: function() {
 if (this.element.get('value') == '' || this.element.get('value') == this.placeholder) {
  if (this.is_password) {
  this.element.set('type', 'text');
  }
  this.element.setStyle('color', this.options.color);
  this.element.set('value', this.placeholder);
 }
 },
 deactivatePlaceholder: function() {
 if (this.element.get('value') == this.placeholder) {
  if (this.is_password) {
  this.element.set('type', 'password');
  }
  this.element.set('value', '');
  this.element.setStyle('color', this.original_color);
 }
 }
});

})();// Calendar: a Javascript class for Mootools that adds accessible and unobtrusive date pickers to your form elements <http://electricprism.com/aeron/calendar>
// Calendar RC4, Copyright (c) 2007 Aeron Glemann <http://electricprism.com/aeron>, MIT Style License.
// Mootools 1.2 compatibility by Davorin Aeego

var Calendar = new Class({ 

 Implements: Options,

 options: {
 blocked: [], // blocked dates 
 classes: [], // ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite']
 days: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], // days of the week starting at sunday
 direction: 0, // -1 past, 0 past + future, 1 future
 draggable: true,
 months: ['Januar','Februar','MÃ¤rz','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'],
 navigation: 1, // 0 = no nav; 1 = single nav for month; 2 = dual nav for month and year
 offset: 0, // first day of the week: 0 = sunday, 1 = monday, etc..
 onHideStart: Class.empty,
 onHideComplete: Class.empty,
 onShowStart: Class.empty,
 onShowComplete: Class.empty,
 pad: 1, // padding between multiple calendars
 tweak: {x: 0, y: 0} // tweak calendar positioning
 },

 // initialize: calendar constructor
 // @param obj (obj) a js object containing the form elements and format strings { id: 'format', id: 'format' etc }
 // @param props (obj) optional properties

 initialize: function(obj, options) {
 // basic error checking
 if (!obj) { return false; }

 this.setOptions(options);

 // create our classes array
 var keys = ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite'];

 var values = keys.map(function(key, i) {
  if (this.options.classes[i]) {
  if (this.options.classes[i].length) { key = this.options.classes[i]; }
  }
  return key;
 }, this);

 this.classes = values.associate(keys);

 // create cal element with css styles required for proper cal functioning
 this.calendar = new Element('div', { 
  'styles': { left: '-1000px', opacity: 0, position: 'absolute', top: '-1000px', zIndex: 1000 }
 }).addClass(this.classes.calendar).injectInside(document.body);

 // iex 6 needs a transparent iframe underneath the calendar in order to not allow select elements to render through
 if (window.ie6) {
  this.iframe = new Element('iframe', { 
  'styles': { left: '-1000px', position: 'absolute', top: '-1000px', zIndex: 999 }
  }).injectInside(document.body);
  this.iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
 }

 // initialize fade method
 this.fx = new Fx.Tween(this.calendar, {
  onStart: function() { 
  if (this.calendar.getStyle('opacity') == 0) { // show
   if (window.ie6) { this.iframe.setStyle('display', 'block'); }
   this.calendar.setStyle('display', 'block');
   this.fireEvent('onShowStart', this.element);
  }
  else { // hide
   this.fireEvent('onHideStart', this.element);
  }
  }.bind(this),
  onComplete: function() { 
  if (this.calendar.getStyle('opacity') == 0) { // hidden
   this.calendar.setStyle('display', 'none');
   if (window.ie6) { this.iframe.setStyle('display', 'none'); }
   this.fireEvent('onHideComplete', this.element);
  }
  else { // shown
   this.fireEvent('onShowComplete', this.element);
  }
  }.bind(this)
 });

 // initialize drag method
 if (window.Drag && this.options.draggable) {
  this.drag = new Drag.Move(this.calendar, { 
  onDrag: function() {
   if (window.ie6) { this.iframe.setStyles({ left: this.calendar.style.left, top: this.calendar.style.top }); } 
  }.bind(this) 
  }); 
 }
 
 // create calendars array
 this.calendars = [];

 var id = 0;
 var d = new Date(); // today

 d.setDate(d.getDate() + this.options.direction.toInt()); // correct today for directional offset

 for (var i in obj) {
  var cal = { 
  button: new Element('button', { 'type': 'button' }),
  el: $(i),
  els: [],
  id: id++,
  month: d.getMonth(),
  visible: false,
  year: d.getFullYear()
  };

  // fix for bad element (naughty, naughty element!)
  if (!this.element(i, obj[i], cal)) { continue; }
  
  cal.el.addClass(this.classes.calendar);

  // create cal button
  cal.button.addClass(this.classes.calendar).addEvent('click', function(cal) { this.toggle(cal); }.pass(cal, this)).injectAfter(cal.el);

  // read in default value
  cal.val = this.read(cal);

  $extend(cal, this.bounds(cal)); // abs bounds of calendar

  $extend(cal, this.values(cal)); // valid days, months, years

  this.rebuild(cal);

  this.calendars.push(cal); // add to cals array 
 } 
 },


 // blocked: returns an array of blocked days for the month / year
 // @param cal (obj)
 // @returns blocked days (array)

 blocked: function(cal) {
 var blocked = [];
 var offset = new Date(cal.year, cal.month, 1).getDay(); // day of the week (offset)
 var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
 
 this.options.blocked.each(function(date){
  var values = date.split(' ');
  
  // preparation
  for (var i = 0; i <= 3; i++){ 
  if (!values[i]){ values[i] = (i == 3) ? '' : '*'; } // make sure blocked date contains values for at least d, m and y
  values[i] = values[i].contains(',') ? values[i].split(',') : new Array(values[i]); // split multiple values
  var count = values[i].length - 1;
  for (var j = count; j >= 0; j--){
   if (values[i][j].contains('-')){ // a range
   var val = values[i][j].split('-');
   for (var k = val[0]; k <= val[1]; k++){
    if (!values[i].contains(k)){ values[i].push(k + ''); }
   }
   values[i].splice(j, 1);
   }
  }
  }

  // execution
  if (values[2].contains(cal.year + '') || values[2].contains('*')){
  if (values[1].contains(cal.month + 1 + '') || values[1].contains('*')){
   values[0].each(function(val){ // if blocked value indicates this month / year
   if (val > 0){ blocked.push(val.toInt()); } // add date to blocked array
   });

   if (values[3]){ // optional value for day of week
   for (var i = 0; i < last; i++){
    var day = (i + offset) % 7;
 
    if (values[3].contains(day + '')){ 
     blocked.push(i + 1); // add every date that corresponds to the blocked day of the week to the blocked array
    }
   }
   }
  }
  }
 }, this);

 return blocked;
 },


 // bounds: returns the start / end bounds of the calendar
 // @param cal (obj)
 // @returns obj 

 bounds: function(cal) {
 // 1. first we assume the calendar has no bounds (or a thousand years in either direction)
 
 // by default the calendar will accept a millennium in either direction
 var start = new Date(1000, 0, 1); // jan 1, 1000
 var end = new Date(2999, 11, 31); // dec 31, 2999

 // 2. but if the cal is one directional we adjust accordingly
 var date = new Date().getDate() + this.options.direction.toInt();

 if (this.options.direction > 0) {
  start = new Date();
  start.setDate(date + this.options.pad * cal.id);
 }
 
 if (this.options.direction < 0) {
  end = new Date();
  end.setDate(date - this.options.pad * (this.calendars.length - cal.id - 1));
 }

 // 3. then we can further filter the limits by using the pre-existing values in the selects
 cal.els.each(function(el) { 
  if (el.get('tag') == 'select') { 
  if (el.format.test('(y|Y)')) { // search for a year select
   var years = [];

   el.getChildren().each(function(option) { // get options
   var values = this.unformat(option.value, el.format);
 
   if (!years.contains(values[0])) { years.push(values[0]); } // add to years array
   }, this);
 
   years.sort(this.sort);
  
   if (years[0] > start.getFullYear()) { 
   d = new Date(years[0], start.getMonth() + 1, 0); // last day of new month
   
   if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); }
 
   start.setYear(years[0]); 
   }
   
   if (years.getLast() < end.getFullYear()) { 
   d = new Date(years.getLast(), end.getMonth() + 1, 0); // last day of new month
   
   if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); }
 
   end.setYear(years.getLast());
   } 
  }
 
  if (el.format.test('(F|m|M|n)')) { // search for a month select
   var months_start = [];
   var months_end = [];

   el.getChildren().each(function(option) { // get options
   var values = this.unformat(option.value, el.format);
 
   if ($type(values[0]) != 'number' || values[0] == years[0]) { // if it's a year / month combo for curr year, or simply a month select
    if (!months_start.contains(values[1])) { months_start.push(values[1]); } // add to months array
   }
 
   if ($type(values[0]) != 'number' || values[0] == years.getLast()) { // if it's a year / month combo for curr year, or simply a month select
    if (!months_end.contains(values[1])) { months_end.push(values[1]); } // add to months array
   }
   }, this);
 
   months_start.sort(this.sort);
   months_end.sort(this.sort);
   
   if (months_start[0] > start.getMonth()) { 
   d = new Date(start.getFullYear(), months_start[0] + 1, 0); // last day of new month
   
   if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); }
 
   start.setMonth(months_start[0]); 
   }
   
   if (months_end.getLast() < end.getMonth()) { 
   d = new Date(start.getFullYear(), months_end.getLast() + 1, 0); // last day of new month
   
   if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); }
 
   end.setMonth(months_end.getLast());
   } 
  }
  }
 }, this);
 
 return { 'start': start, 'end': end };
 },


 // caption: returns the caption element with header and navigation
 // @param cal (obj)
 // @returns caption (element)

 caption: function(cal) {
 // start by assuming navigation is allowed
 var navigation = {
  prev: { 'month': true, 'year': true },
  next: { 'month': true, 'year': true }
 };
 
 // if we're in an out of bounds year
 if (cal.year == cal.start.getFullYear()) { 
  navigation.prev.year = false; 
  if (cal.month == cal.start.getMonth() && this.options.navigation == 1) { 
  navigation.prev.month = false;
  } 
 } 
 if (cal.year == cal.end.getFullYear()) { 
  navigation.next.year = false; 
  if (cal.month == cal.end.getMonth() && this.options.navigation == 1) { 
  navigation.next.month = false;
  }
 }

 // special case of improved navigation but months array with only 1 month we can disable all month navigation
 if ($type(cal.months) == 'array') {
  if (cal.months.length == 1 && this.options.navigation == 2) {
  navigation.prev.month = navigation.next.month = false;
  }
 }

 var caption = new Element('caption');

 var prev = new Element('a').addClass(this.classes.prev).appendText('\x3c'); // < 
 var next = new Element('a').addClass(this.classes.next).appendText('\x3e'); // >

 if (this.options.navigation == 2) {
  var month = new Element('span').addClass(this.classes.month).injectInside(caption);
  
  if (navigation.prev.month) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).injectInside(month); }
  
  month.adopt(new Element('span').appendText(this.options.months[cal.month]));

  if (navigation.next.month) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).injectInside(month); }

  var year = new Element('span').addClass(this.classes.year).injectInside(caption);

  if (navigation.prev.year) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', -1); }.pass(cal, this)).injectInside(year); }
  
  year.adopt(new Element('span').appendText(cal.year));

  if (navigation.next.year) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', 1); }.pass(cal, this)).injectInside(year); }
 }
 else { // 1 or 0
  if (navigation.prev.month && this.options.navigation) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).injectInside(caption); }

  caption.adopt(new Element('span').addClass(this.classes.month).appendText(this.options.months[cal.month]));
  
  caption.adopt(new Element('span').addClass(this.classes.year).appendText(cal.year));
  
  if (navigation.next.month && this.options.navigation) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).injectInside(caption); }

 }

 return caption;
 },


 // changed: run when a select value is changed
 // @param cal (obj)

 changed: function(cal) {
 cal.val = this.read(cal); // update calendar val from inputs 

 $extend(cal, this.values(cal)); // update bounds - based on curr month

 this.rebuild(cal); // rebuild days select

 if (!cal.val) { return; } // in case the same date was clicked the cal has no set date we should exit 

 if (cal.val.getDate() < cal.days[0]) { cal.val.setDate(cal.days[0]); }
 if (cal.val.getDate() > cal.days.getLast()) { cal.val.setDate(cal.days.getLast()); }
 
 cal.els.each(function(el) { // then we can set the value to the field
  el.value = this.format(cal.val, el.format);  
 }, this);
 
 this.check(cal); // checks other cals

 this.calendars.each(function(kal) { // update cal graphic if visible
  if (kal.visible) { this.display(kal); }
 }, this);
 },


 // check: checks other calendars to make sure no overlapping values
 // @param cal (obj)

 check: function(cal) {
 this.calendars.each(function(kal, i) {
  if (kal.val) { // if calendar has value set
  var change = false;
  
  if (i < cal.id) { // preceding calendar
   var bound = new Date(Date.parse(cal.val));
   
   bound.setDate(bound.getDate() - (this.options.pad * (cal.id - i)));

   if (bound < kal.val) { change = true; }
  }
  if (i > cal.id) { // following calendar
   var bound = new Date(Date.parse(cal.val));
   
   bound.setDate(bound.getDate() + (this.options.pad * (i - cal.id)));
   
   if (bound > kal.val) { change = true; }
  }

  if (change) {
   if (kal.start > bound) { bound = kal.start; }
   if (kal.end < bound) { bound = kal.end; }

   kal.month = bound.getMonth();
   kal.year = bound.getFullYear(); 

   $extend(kal, this.values(kal));  

   // TODO - IN THE CASE OF SELECT MOVE TO NEAREST VALID VALUE
   // IN THE CASE OF INPUT DISABLE

   // if new date is not valid better unset cal value
   // otherwise it would mean incrementally checking to find the nearest valid date which could be months / years away
   kal.val = kal.days.contains(bound.getDate()) ? bound : null;

   this.write(kal);

   if (kal.visible) { this.display(kal); } // update cal graphic if visible
  }
  }
  else {
  kal.month = cal.month;
  kal.year = cal.year;
  }
 }, this);
 },
 

 // clicked: run when a valid day is clicked in the calendar
 // @param cal (obj)

 clicked: function(td, day, cal) {
 cal.val = (this.value(cal) == day) ? null : new Date(cal.year, cal.month, day); // set new value - if same then disable

 this.write(cal); 

 // ok - in the special case that it's all selects and there's always a date no matter what (at least as far as the form is concerned)
 // we can't let the calendar undo a date selection - it's just not possible!!
 if (!cal.val) { cal.val = this.read(cal); }

 if (cal.val) {
  this.check(cal); // checks other cals   
  this.toggle(cal); // hide cal
 } 
 else { // remove active class and replace with valid
  td.addClass(this.classes.valid);
  td.removeClass(this.classes.active);
 }
 },
 

 // display: create calendar element
 // @param cal (obj)

 display: function(cal) {
 // 1. header and navigation
 this.calendar.empty(); // init div

 this.calendar.className = this.classes.calendar + ' ' + this.options.months[cal.month].toLowerCase();

 var div = new Element('div').injectInside(this.calendar); // a wrapper div to help correct browser css problems with the caption element

 var table = new Element('table').injectInside(div).adopt(this.caption(cal));
  
 // 2. day names 
 var thead = new Element('thead').injectInside(table);

 var tr = new Element('tr').injectInside(thead);
 
 for (var i = 0; i <= 6; i++) {
  var th = this.options.days[(i + this.options.offset) % 7];
  
  tr.adopt(new Element('th', { 'title': th }).appendText(th.substr(0, 1)));
 }

 // 3. day numbers
 var tbody = new Element('tbody').injectInside(table);
 var tr = new Element('tr').injectInside(tbody);

 var d = new Date(cal.year, cal.month, 1);
 var offset = ((d.getDay() - this.options.offset) + 7) % 7; // day of the week (offset)
 var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
 var prev = new Date(cal.year, cal.month, 0).getDate(); // last day of previous month
 var active = this.value(cal); // active date (if set and within curr month)
 var valid = cal.days; // valid days for curr month
 var inactive = []; // active dates set by other calendars
 var hilited = [];
 this.calendars.each(function(kal, i) {
  if (kal != cal && kal.val) {
  if (cal.year == kal.val.getFullYear() && cal.month == kal.val.getMonth()) { inactive.push(kal.val.getDate()); }

  if (cal.val) {
   for (var day = 1; day <= last; day++) {
   d.setDate(day);
   
   if ((i < cal.id && d > kal.val && d < cal.val) || (i > cal.id && d > cal.val && d < kal.val)) { 
    if (!hilited.contains(day)) { hilited.push(day); }
   }
   }
  }
  }
 }, this);
 var d = new Date();
 var today = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); // today obv 
 
 for (var i = 1; i < 43; i++) { // 1 to 42 (6 x 7 or 6 weeks)
  if ((i - 1) % 7 == 0) { tr = new Element('tr').injectInside(tbody); } // each week is it's own table row

  var td = new Element('td').injectInside(tr);
   
  var day = i - offset;
  var date = new Date(cal.year, cal.month, day);
  
  var cls = '';
  
  if (day === active) { cls = this.classes.active; } // active
  else if (inactive.contains(day)) { cls = this.classes.inactive; } // inactive
  else if (valid.contains(day)) { cls = this.classes.valid; } // valid
  else if (day >= 1 && day <= last) { cls = this.classes.invalid; } // invalid

  if (date.getTime() == today) { cls = cls + ' ' + this.classes.today; } // adds class for today

  if (hilited.contains(day)) { cls = cls + ' ' + this.classes.hilite; } // adds class if hilited

  td.addClass(cls);

  if (valid.contains(day)) { // if it's a valid - clickable - day we add interaction
  td.setProperty('title', this.format(date, 'D M jS Y'));
  
  td.addEvents({
   'click': function(td, day, cal) { 
   this.clicked(td, day, cal); 
   }.pass([td, day, cal], this),
   'mouseover': function(td, cls) { 
   td.addClass(cls); 
   }.pass([td, this.classes.hover]),
   'mouseout': function(td, cls) { 
   td.removeClass(cls); 
   }.pass([td, this.classes.hover])
  });
  }

  // pad calendar with last days of prev month and first days of next month
  if (day < 1) { day = prev + day; }
  else if (day > last) { day = day - last; }

  td.appendText(day);
 }
 },


 // element: helper function
 // @param el (string) element id
 // @param f (string) format string
 // @param cal (obj)

 element: function(el, f, cal) {
 if ($type(f) == 'object') { // in the case of multiple inputs per calendar
  for (var i in f) { 
  if (!this.element(i, f[i], cal)) { return false; } 
  }
  
  return true;
 }

 el = $(el);

 if (!el) { return false; }
 
 el.format = f;
 
 if (el.get('tag') == 'select') { // select elements allow the user to manually set the date via select option
  el.addEvent('change', function(cal) { this.changed(cal); }.pass(cal, this));
 }
 else { // input (type text) elements restrict the user to only setting the date via the calendar
  //el.readOnly = true;
  el.addEvent('focus', function(cal) { this.toggle(cal); }.pass(cal, this));
 }

 cal.els.push(el);

 return true;
 },


 // format: formats a date object according to passed in instructions
 // @param date (obj)
 // @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
 // @returns string

 format: function(date, format) {
 var str = '';
 
 if (date) {
  var j = date.getDate(); // 1 - 31
   var w = date.getDay(); // 0 - 6
  var l = this.options.days[w]; // Sunday - Saturday
  var n = date.getMonth() + 1; // 1 - 12
  var f = this.options.months[n - 1]; // January - December
  var y = date.getFullYear() + ''; // 19xx - 20xx
  
  for (var i = 0, len = format.length; i < len; i++) {
  var cha = format.charAt(i); // format char
  
  switch(cha) {
   // year cases
   case 'y': // xx - xx
   y = y.substr(2);
   case 'Y': // 19xx - 20xx
   str += y;
   break;
 
   // month cases
   case 'm': // 01 - 12
   if (n < 10) { n = '0' + n; }
   case 'n': // 1 - 12
   str += n;
   break;
 
   case 'M': // Jan - Dec
   f = f.substr(0, 3);
   case 'F': // January - December
   str += f;
   break;
 
   // day cases
   case 'd': // 01 - 31
   if (j < 10) { j = '0' + j; }
   case 'j': // 1 - 31
   str += j;
   break;
 
   case 'D': // Sun - Sat
   l = l.substr(0, 3);
   case 'l': // Sunday - Saturday
   str += l;
   break;
 
   case 'N': // 1 - 7
   w += 1;
   case 'w': // 0 - 6
   str += w;
   break;

   case 'S': // st, nd, rd or th (works well with j)
   if (j % 10 == 1 && j != '11') { str += 'st'; }
   else if (j % 10 == 2 && j != '12') { str += 'nd'; }
   else if (j % 10 == 3 && j != '13') { str += 'rd'; }
   else { str += 'th'; }
   break;
 
   default:
   str += cha;
  }
  }
 }

  return str; // return format with values replaced
 },


 // navigate: calendar navigation
 // @param cal (obj)
 // @param type (str) m or y for month or year
 // @param n (int) + or - for next or prev

 navigate: function(cal, type, n) {
 switch (type) {
  case 'm': // month
   if ($type(cal.months) == 'array') {
   var i = cal.months.indexOf(cal.month) + n; // index of current month
   
   if (i < 0 || i == cal.months.length) { // out of range
    if (this.options.navigation == 1) { // if type 1 nav we'll need to increment the year
    this.navigate(cal, 'y', n); 
    }
 
    i = (i < 0) ? cal.months.length - 1 : 0;
   }

   cal.month = cal.months[i];
   }
   else { 
   var i = cal.month + n;
 
   if (i < 0 || i == 12) {
    if (this.options.navigation == 1) {
    this.navigate(cal, 'y', n); 
    }
 
    i = (i < 0) ? 11 : 0;
   }
   
   cal.month = i;
   } 
   break;

  case 'y': // year
   if ($type(cal.years) == 'array') {
   var i = cal.years.indexOf(cal.year) + n;

   cal.year = cal.years[i]; 
   }
   else { 
   cal.year += n;
   }   
   break; 
 }

 $extend(cal, this.values(cal));

 if ($type(cal.months) == 'array') { // if the calendar has a months select
  var i = cal.months.indexOf(cal.month); // and make sure the curr months exists for the new year

  if (i < 0) { cal.month = cal.months[0]; } // otherwise we'll reset the month
 }


 this.display(cal);
 },


 // read: compiles cal value based on array of inputs passed in
 // @param cal (obj)
 // @returns date (obj) or (null)

 read: function(cal) {
 var arr = [null, null, null];

 cal.els.each(function(el) {
  // returns an array which may contain empty values
  var values = this.unformat(el.value, el.format);
  
  values.each(function(val, i) { 
  if ($type(val) == 'number') { arr[i] = val; }
  }); 
 }, this);

 // we can update the cals month and year values
 if ($type(arr[0]) == 'number') { cal.year = arr[0]; }
 if ($type(arr[1]) == 'number') { cal.month = arr[1]; }

 var val = null;

 if (arr.every(function(i) { return $type(i) == 'number'; })) { // if valid date
  var last = new Date(arr[0], arr[1] + 1, 0).getDate(); // last day of month

  if (arr[2] > last) { arr[2] = last; } // make sure we stay within the month (ex in case default day of select is 31 and month is feb)
  
  val = new Date(arr[0], arr[1], arr[2]);
 }

 return (cal.val == val) ? null : val; // if new date matches old return null (same date clicked twice = disable)
 },

 
 // rebuild: rebuilds days + months selects
 // @param cal (obj)

 rebuild: function(cal) {
 cal.els.each(function(el) {  
  

  if (el.get('tag') == 'select' && el.format.test('^(d|j)$')) { // special case for days-only select
  var d = this.value(cal);

  if (!d) { d = el.value.toInt(); } // if the calendar doesn't have a set value, try to use value from select

  el.empty(); // initialize select

  cal.days.each(function(day) {
   // create an option element
   var option = new Element('option', {
   'selected': (d == day),
   'value': ((el.format == 'd' && day < 10) ? '0' + day : day)
   }).appendText(day).injectInside(el);
  }, this);
  }
 }, this); 
 },


 // sort: helper function for numerical sorting

 sort: function(a, b) {
 return a - b;
 },


 // toggle: show / hide calendar 
 // @param cal (obj)

 toggle: function(cal) {
 document.removeEvent('mousedown', this.fn); // always remove the current mousedown script first
  
 if (cal.visible) { // simply hide curr cal   
  cal.visible = false;
  cal.button.removeClass(this.classes.active); // active
  
  this.fx.start('opacity', 1, 0);
 }
 else { // otherwise show (may have to hide others)
  // hide cal on out-of-bounds click
  this.fn = function(e, cal) { 
  var e = new Event(e);
  
  var el = e.target;

  var stop = false;
  
  while (el != document.body && el.nodeType == 1) {
   if (el == this.calendar) { stop = true; }
   this.calendars.each(function(kal) {
   if (kal.button == el || kal.els.contains(el)) { stop = true; }
   });

   if (stop) { 
   e.stop();
   return false;
   }
   else { el = el.parentNode; }
  }
  
  this.toggle(cal);
  }.create({ 'arguments': cal, 'bind': this, 'event': true });  

  document.addEvent('mousedown', this.fn);

  this.calendars.each(function(kal) {
  if (kal == cal) {
   kal.visible = true;
   kal.button.addClass(this.classes.active); // css c-icon-active
  }
  else {
   kal.visible = false;
   kal.button.removeClass(this.classes.active); // css c-icon-active
  }
  }, this);
  
  var size = window.getScrollSize();
  
  var coord = cal.button.getCoordinates();

  var x = coord.right + this.options.tweak.x;
  var y = coord.top + this.options.tweak.y;

  // make sure the calendar doesn't open off screen
  if (!this.calendar.coord) { this.calendar.coord = this.calendar.getCoordinates(); }

  if (x + this.calendar.coord.width > size.x) { x -= (x + this.calendar.coord.width - size.x); }
  if (y + this.calendar.coord.height > size.y) { y -= (y + this.calendar.coord.height - size.y); }
  
  this.calendar.setStyles({ left: x + 'px', top: y + 'px' });

  if (window.ie6) { 
  this.iframe.setStyles({ height: this.calendar.coord.height + 'px', left: x + 'px', top: y + 'px', width: this.calendar.coord.width + 'px' }); 
  }

  this.display(cal);
  
  this.fx.start('opacity', 0, 1);
 }
 },


 // unformat: takes a value from an input and parses the d, m and y elements
 // @param val (string)
 // @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
 // @returns array
 
 unformat: function(val, f) {
 f = f.escapeRegExp();
 
 var re = {
  d: '([0-9]{2})',
  j: '([0-9]{1,2})',
  D: '(' + this.options.days.map(function(day) { return day.substr(0, 3); }).join('|') + ')',   
  l: '(' + this.options.days.join('|') + ')',
  S: '(st|nd|rd|th)',
  F: '(' + this.options.months.join('|') + ')',
  m: '([0-9]{2})',
  M: '(' + this.options.months.map(function(month) { return month.substr(0, 3); }).join('|') + ')',   
  n: '([0-9]{1,2})',
  Y: '([0-9]{4})',
  y: '([0-9]{2})'
 }

 var arr = []; // array of indexes

 var g = '';

 // convert our format string to regexp
 for (var i = 0; i < f.length; i++) {
  var c = f.charAt(i);
  
  if (re[c]) {
  arr.push(c);

  g += re[c];
  }
  else {
  g += c;
  }
 }

 // match against date
 var matches = val.match('^' + g + '$');
 
 var dates = new Array(3);

 if (matches) {
  matches = matches.slice(1); // remove first match which is the date

  arr.each(function(c, i) {
  i = matches[i];
  
  switch(c) {
   // year cases
   case 'y':
   i = '19' + i; // 2 digit year assumes 19th century (same as JS)
   case 'Y':
   dates[0] = i.toInt();
   break;

   // month cases
   case 'F':
   i = i.substr(0, 3);
   case 'M':
   i = this.options.months.map(function(month) { return month.substr(0, 3); }).indexOf(i) + 1;
   case 'm':
   case 'n':
   dates[1] = i.toInt() - 1;
   break;

   // day cases
   case 'd':
   case 'j':
   dates[2] = i.toInt();
   break;
  }
  }, this);
 }

 return dates;
 },


 // value: returns day value of calendar if set
 // @param cal (obj)
 // @returns day (int) or null

 value: function(cal) {
 var day = null;

 if (cal.val) {
  if (cal.year == cal.val.getFullYear() && cal.month == cal.val.getMonth()) { day = cal.val.getDate(); }
 }

 return day;
 },
 

 // values: returns the years, months (for curr year) and days (for curr month and year) for the calendar
 // @param cal (obj)
 // @returns obj 

 values: function(cal) {
 var years, months, days;

 cal.els.each(function(el) { 
  if (el.get('tag') == 'select') { 
  if (el.format.test('(y|Y)')) { // search for a year select
   years = [];

   el.getChildren().each(function(option) { // get options
   var values = this.unformat(option.value, el.format);
 
   if (!years.contains(values[0])) { years.push(values[0]); } // add to years array
   }, this);
 
   years.sort(this.sort);
  }
 
  if (el.format.test('(F|m|M|n)')) { // search for a month select
   months = []; // 0 - 11 should be

   el.getChildren().each(function(option) { // get options
   var values = this.unformat(option.value, el.format);
 
   if ($type(values[0]) != 'number' || values[0] == cal.year) { // if it's a year / month combo for curr year, or simply a month select
    if (!months.contains(values[1])) { months.push(values[1]); } // add to months array
   }
   }, this);
 
   months.sort(this.sort);
  }
  
  if (el.format.test('(d|j)') && !el.format.test('^(d|j)$')) { // search for a day select, but NOT a days only select
   days = []; // 1 - 31
   
   el.getChildren().each(function(option) { // get options
   var values = this.unformat(option.value, el.format);

   // in the special case of days we dont want the value if its a days only select
   // otherwise that will screw up the options rebuilding
   // we will take the values if they are exact dates though
   if (values[0] == cal.year && values[1] == cal.month) {
    if (!days.contains(values[2])) { days.push(values[2]); } // add to days array
   }
   }, this);
  }
  }
 }, this);
 
 // we start with what would be the first and last days were there no restrictions
 var first = 1;
 var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of the month
 
 // if we're in an out of bounds year
 if (cal.year == cal.start.getFullYear()) {
  // in the special case of improved navigation but no months array, we'll need to construct one
  if (months == null && this.options.navigation == 2) {
  months = [];
  
  for (var i = 0; i < 12; i ++) { 
   if (i >= cal.start.getMonth()) { months.push(i); } 
  }
  }
  
  // if we're in an out of bounds month
  if (cal.month == cal.start.getMonth()) { 
  first = cal.start.getDate(); // first day equals day of bound
  }
 } 
 if (cal.year == cal.end.getFullYear()) {
  // in the special case of improved navigation but no months array, we'll need to construct one
  if (months == null && this.options.navigation == 2) {
  months = [];
  
  for (var i = 0; i < 12; i ++) { 
   if (i <= cal.end.getMonth()) { months.push(i); } 
  }
  }

  if (cal.month == cal.end.getMonth()) { 
  last = cal.end.getDate(); // last day equals day of bound
  }
 }

 // let's get our invalid days
 var blocked = this.blocked(cal);

 // finally we can prepare all the valid days in a neat little array
 if ($type(days) == 'array') { // somewhere there was a days select
  days = days.filter(function(day) {
  if (day >= first && day <= last && !blocked.contains(day)) { return day; }
  });
 }
 else { // no days select we'll need to construct a valid days array
  days = [];
  
  for (var i = first; i <= last; i++) { 
  if (!blocked.contains(i)) { days.push(i); }
  }
 } 

 days.sort(this.sort); // sorting our days will give us first and last of month

 return { 'days': days, 'months': months, 'years': years };
 },


 // write: sets calendars value to form elements
 // @param cal (obj)

 write: function(cal) {
 this.rebuild(cal); // in the case of options, we'll need to make sure we have the correct number of days available
 
 cal.els.each(function(el) { // then we can set the value to the field
  el.value = this.format(cal.val, el.format);  
 }, this);
 }
});

Calendar.implement(new Events, new Options);

var DatePicker = new Class({

 Implements: Options,

 // working date, which we will keep modifying to render the calendars
 d: '',

 // just so that we need not request it over and over
 today: '',

 // current user-choice in date object format
 choice: {},

 // size of body, used to animate the sliding
 bodysize: {},

 // to check availability of next/previous buttons
 limit: {},

 // element references:
 attachTo: null,  // selector for target inputs
 picker: null,   // main datepicker container
 slider: null,   // slider that contains both oldContents and newContents, used to animate between 2 different views
 oldContents: null, // used in animating from-view to new-view
 newContents: null, // used in animating from-view to new-view
 input: null,    // original input element (used for input/output)
 visual: null,   // visible input (used for rendering)

 options: {
 pickerClass: 'datepicker',
 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
 months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
 dayShort: 2,
 monthShort: 3,
 startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin
 timePicker: false,
 timePickerOnly: false,
 yearPicker: true,
 yearsPerPage: 20,
 format: 'd-m-Y',
 allowEmpty: false,
 inputOutputFormat: 'U', // default to unix timestamp
 animationDuration: 400,
 useFadeInOut: !Browser.Engine.trident, // dont animate fade-in/fade-out for IE
 startView: 'month', // allowed values: {time, month, year, decades}
 positionOffset: { x: 0, y: 0 },
 minDate: null, // { date: '[date-string]', format: '[date-string-interpretation-format]' }
 maxDate: null, // same as minDate
 debug: false,
 toggleElements: null,

 // and some event hooks:
 onShow: $empty,  // triggered when the datepicker pops up
 onClose: $empty, // triggered after the datepicker is closed (destroyed)
 onSelect: $empty // triggered when a date is selected
 },

 initialize: function(attachTo, options) {
 this.attachTo = attachTo;
 this.setOptions(options).attach();
 if (this.options.timePickerOnly) {
  this.options.timePicker = true;
  this.options.startView = 'time';
 }
 this.formatMinMaxDates();
 document.addEvent('mousedown', this.close.bind(this));
 },

 
 formatMinMaxDates: function() {

 if (this.options.minDate && $type(this.options.minDate) != 'date') {
  this.options.minDate = this.unformat(this.options.minDate.date, this.options.minDate.format);
 }
 if (this.options.maxDate && $type(this.options.maxDate) != 'date') {
  this.options.maxDate = this.unformat(this.options.maxDate.date, this.options.maxDate.format);
  this.options.maxDate.setHours(23);
  this.options.maxDate.setMinutes(59);
  this.options.maxDate.setSeconds(59);
 }
 },

 attach: function() {
 // toggle the datepicker through a separate element?
 if ($chk(this.options.toggleElements)) {
  var togglers = $$(this.options.toggleElements);
  document.addEvents({
  'keydown': function(e) {
   if (e.key == "tab") {
   this.close(null, true);
   }
  }.bind(this)
  });
 };

 // attach functionality to the inputs
 $$(this.attachTo).each(function(item, index) {

  // never double attach
  if (item.retrieve('datepicker')) return;

  // determine starting value(s)
  if ($chk(item.get('value'))) {
  var init_clone_val = this.format(new Date(this.unformat(item.get('value'), this.options.inputOutputFormat)), this.options.format);
  } else if (!this.options.allowEmpty) {
  var init_clone_val = this.format(new Date(), this.options.format);
  } else {
  var init_clone_val = '';
  }

  // create clone
  var display = item.getStyle('display');
  var clone = item
  .setStyle('display', this.options.debug ? display : 'none')
  .store('datepicker', true) // to prevent double attachment...
  .clone()
  .store('datepicker', true) // ...even for the clone (!)
  .removeProperty('name')  // secure clean (form)submission
  .setStyle('display', display)
  .set('value', init_clone_val)
  .inject(item, 'after');

  // events
  if ($chk(this.options.toggleElements)) {
  togglers[index]
   .setStyle('cursor', 'pointer')
   .addEvents({
   'click': function(e) {
    this.onFocus(item, clone);
   }.bind(this)
   });
  clone.addEvents({
   'blur': function() {
   item.set('value', clone.get('value'));
   }
  });
  } else {
  clone.addEvents({
   'keydown': function(e) {
   if (this.options.allowEmpty && (e.key == "delete" || e.key == "backspace")) {
    item.set('value', '');
    e.target.set('value', '');
    this.close(null, true);
   } else if (e.key == "tab") {
    this.close(null, true);
   } else {
    e.stop();
   }
   }.bind(this),
   'focus': function(e) {
   this.onFocus(item, clone);
   }.bind(this)
  });
  }
 }.bind(this));
 },

 onFocus: function(original_input, visual_input) {
 var init_visual_date, d = visual_input.getCoordinates();

 if ($chk(original_input.get('value'))) {
  init_visual_date = this.unformat(original_input.get('value'), this.options.inputOutputFormat).valueOf();
 } else {
  init_visual_date = new Date();
  if ($chk(this.options.maxDate) && init_visual_date.valueOf() > this.options.maxDate.valueOf()) {
  init_visual_date = new Date(this.options.maxDate.valueOf());
  }
  if ($chk(this.options.minDate) && init_visual_date.valueOf() < this.options.minDate.valueOf()) {
  init_visual_date = new Date(this.options.minDate.valueOf());
  }
 }

 this.show({ left: d.left + this.options.positionOffset.x, top: d.top + d.height + this.options.positionOffset.y }, init_visual_date);
 this.input = original_input;
 this.visual = visual_input;
 this.options.onShow();
 },

 dateToObject: function(d) {
 return {
  year: d.getFullYear(),
  month: d.getMonth(),
  day: d.getDate(),
  hours: d.getHours(),
  minutes: d.getMinutes(),
  seconds: d.getSeconds()
 };
 },

 dateFromObject: function(values) {
 var d = new Date();
 d.setDate(1);
 ['year', 'month', 'day', 'hours', 'minutes', 'seconds'].each(function(type) {
  var v = values[type];
  if (!$chk(v)) return;
  switch (type) {
  case 'day': d.setDate(v); break;
  case 'month': d.setMonth(v); break;
  case 'year': d.setFullYear(v); break;
  case 'hours': d.setHours(v); break;
  case 'minutes': d.setMinutes(v); break;
  case 'seconds': d.setSeconds(v); break;
  }
 });
 return d;
 },

 show: function(position, timestamp) {
 this.formatMinMaxDates();
 if ($chk(timestamp)) {
  this.d = new Date(timestamp);
 } else {
  this.d = new Date();
 }
 this.today = new Date();
 this.choice = this.dateToObject(this.d);
 this.mode = (this.options.startView == 'time' && !this.options.timePicker) ? 'month' : this.options.startView;
 this.render();
 this.picker.setStyles(position);
 },

 render: function(fx) {
 if (!$chk(this.picker)) {
  this.constructPicker();
 } else {
  // swap contents so we can fill the newContents again and animate
  var o = this.oldContents;
  this.oldContents = this.newContents;
  this.newContents = o;
  this.newContents.empty();
 }

 // remember current working date
 var startDate = new Date(this.d.getTime());

 // intially assume both left and right are allowed
 this.limit = { right: false, left: false };

 // render! booty!
 if (this.mode == 'decades') {
  this.renderDecades();
 } else if (this.mode == 'year') {
  this.renderYear();
 } else if (this.mode == 'time') {
  this.renderTime();
  this.limit = { right: true, left: true }; // no left/right in timeview
 } else {
  this.renderMonth();
 }

 this.picker.getElement('.previous').setStyle('visibility', this.limit.left ? 'hidden' : 'visible');
 this.picker.getElement('.next').setStyle('visibility', this.limit.right ? 'hidden' : 'visible');
 this.picker.getElement('.titleText').setStyle('cursor', this.allowZoomOut() ? 'pointer' : 'default');

 // restore working date
 this.d = startDate;

 // if ever the opacity is set to '0' it was only to have us fade it in here
 // refer to the constructPicker() function, which instantiates the picker at opacity 0 when fading is desired
 if (this.picker.getStyle('opacity') == 0) {
  this.picker.tween('opacity', 0, 1);
 }

 // animate
 if ($chk(fx)) this.fx(fx);
 },

 fx: function(fx) {
 if (fx == 'right') {
  this.oldContents.setStyles({ left: 0, opacity: 1 });
  this.newContents.setStyles({ left: this.bodysize.x, opacity: 1 });
  this.slider.setStyle('left', 0).tween('left', 0, -this.bodysize.x);
 } else if (fx == 'left') {
  this.oldContents.setStyles({ left: this.bodysize.x, opacity: 1 });
  this.newContents.setStyles({ left: 0, opacity: 1 });
  this.slider.setStyle('left', -this.bodysize.x).tween('left', -this.bodysize.x, 0);
 } else if (fx == 'fade') {
  this.slider.setStyle('left', 0);
  this.oldContents.setStyle('left', 0).set('tween', { duration: this.options.animationDuration / 2 }).tween('opacity', 1, 0);
  this.newContents.setStyles({ opacity: 0, left: 0}).set('tween', { duration: this.options.animationDuration }).tween('opacity', 0, 1);
 }
 },

 constructPicker: function() {
 this.picker = new Element('div', { 'class': this.options.pickerClass }).inject(document.body);
 if (this.options.useFadeInOut) {
  this.picker.setStyle('opacity', 0).set('tween', { duration: this.options.animationDuration });
 }

 var h = new Element('div', { 'class': 'header' }).inject(this.picker);
 var titlecontainer = new Element('div', { 'class': 'title' }).inject(h);
 new Element('div', { 'class': 'previous' }).addEvent('click', this.previous.bind(this)).set('text', 'Ã‚Â«').inject(h);
 new Element('div', { 'class': 'next' }).addEvent('click', this.next.bind(this)).set('text', 'Ã‚Â»').inject(h);
 new Element('div', { 'class': 'closeButton' }).addEvent('click', this.close.bindWithEvent(this, true)).set('text', 'x').inject(h);
 new Element('span', { 'class': 'titleText' }).addEvent('click', this.zoomOut.bind(this)).inject(titlecontainer);

 var b = new Element('div', { 'class': 'body' }).inject(this.picker);
 this.bodysize = b.getSize();
 this.slider = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: 2 * this.bodysize.x, height: this.bodysize.y }})
   .set('tween', { duration: this.options.animationDuration, transition: Fx.Transitions.Quad.easeInOut }).inject(b);
 this.oldContents = new Element('div', { styles: { position: 'absolute', top: 0, left: this.bodysize.x, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
 this.newContents = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
 },

 renderTime: function() {
 var container = new Element('div', { 'class': 'time' }).inject(this.newContents);

 if (this.options.timePickerOnly) {
  this.picker.getElement('.titleText').set('text', 'Select a time');
 } else {
  this.picker.getElement('.titleText').set('text', this.format(this.d, 'j M, Y'));
 }

 new Element('input', { type: 'text', 'class': 'hour' })
  .set('value', this.leadZero(this.d.getHours()))
  .addEvents({
  mousewheel: function(e) {
   var i = e.target, v = i.get('value').toInt();
   i.focus();
   if (e.wheel > 0) {
   v = (v < 23) ? v + 1 : 0;
   } else {
   v = (v > 0) ? v - 1 : 23;
   }
   i.set('value', this.leadZero(v));
   e.stop();
  }.bind(this)
  })
  .set('maxlength', 2)
  .inject(container);

 new Element('input', { type: 'text', 'class': 'minutes' })
  .set('value', this.leadZero(this.d.getMinutes()))
  .addEvents({
  mousewheel: function(e) {
   var i = e.target, v = i.get('value').toInt();
   i.focus();
   if (e.wheel > 0) {
   v = (v < 59) ? v + 1 : 0;
   } else {
   v = (v > 0) ? v - 1 : 59;
   }
   i.set('value', this.leadZero(v));
   e.stop();
  }.bind(this)
  })
  .set('maxlength', 2)
  .inject(container);

 new Element('div', { 'class': 'separator' }).set('text', ':').inject(container);

 new Element('input', { type: 'submit', value: 'OK', 'class': 'ok' })
  .addEvents({
  click: function(e) {
   e.stop();
   this.select($merge(this.dateToObject(this.d), { hours: this.picker.getElement('.hour').get('value').toInt(), minutes: this.picker.getElement('.minutes').get('value').toInt() }));
  }.bind(this)
  })
  .set('maxlength', 2)
  .inject(container);
 },

 renderMonth: function() {
 var month = this.d.getMonth();

 this.picker.getElement('.titleText').set('text', this.options.months[month] + ' ' + this.d.getFullYear());

 this.d.setDate(1);
 while (this.d.getDay() != this.options.startDay) {
  this.d.setDate(this.d.getDate() - 1);
 }

 var container = new Element('div', { 'class': 'days' }).inject(this.newContents);
 var titles = new Element('div', { 'class': 'titles' }).inject(container);
 var d, i, classes, e, weekcontainer;

 for (d = this.options.startDay; d < (this.options.startDay + 7); d++) {
  new Element('div', { 'class': 'title day day' + (d % 7) }).set('text', this.options.days[(d % 7)].substring(0,this.options.dayShort)).inject(titles);
 }

 var available = false;
 var t = this.today.toDateString();
 var currentChoice = this.dateFromObject(this.choice).toDateString();

 for (i = 0; i < 42; i++) {
  classes = [];
  classes.push('day');
  classes.push('day'+this.d.getDay());
  if (this.d.toDateString() == t) classes.push('today');
  if (this.d.toDateString() == currentChoice) classes.push('selected');
  if (this.d.getMonth() != month) classes.push('otherMonth');

  if (i % 7 == 0) {
  weekcontainer = new Element('div', { 'class': 'week week'+(Math.floor(i/7)) }).inject(container);
  }

  e = new Element('div', { 'class': classes.join(' ') }).set('text', this.d.getDate()).inject(weekcontainer);
  if (this.limited('date')) {
  e.addClass('unavailable');
  if (available) {
   this.limit.right = true;
  } else if (this.d.getMonth() == month) {
   this.limit.left = true;
  }
  } else {
  available = true;
  e.addEvent('click', function(e, d) {
   if (this.options.timePicker) {
   this.d.setDate(d.day);
   this.d.setMonth(d.month);
   this.mode = 'time';
   this.render('fade');
   } else {
   this.select(d);
   }
  }.bindWithEvent(this, { day: this.d.getDate(), month: this.d.getMonth(), year: this.d.getFullYear() }));
  }
  this.d.setDate(this.d.getDate() + 1);
 }
 if (!available) this.limit.right = true;
 },

 renderYear: function() {
 var month = this.today.getMonth();
 var thisyear = this.d.getFullYear() == this.today.getFullYear();
 var selectedyear = this.d.getFullYear() == this.choice.year;

 this.picker.getElement('.titleText').set('text', this.d.getFullYear());
 this.d.setMonth(0);

 var i, e;
 var available = false;
 var container = new Element('div', { 'class': 'months' }).inject(this.newContents);

 for (i = 0; i <= 11; i++) {
  e = new Element('div', { 'class': 'month month'+(i+1)+(i == month && thisyear ? ' today' : '')+(i == this.choice.month && selectedyear ? ' selected' : '') })
  .set('text', this.options.monthShort ? this.options.months[i].substring(0, this.options.monthShort) : this.options.months[i]).inject(container);

  if (this.limited('month')) {
  e.addClass('unavailable');
  if (available) {
   this.limit.right = true;
  } else {
   this.limit.left = true;
  }
  } else {
  available = true;
  e.addEvent('click', function(e, d) {
   this.d.setDate(1);
   this.d.setMonth(d);
   this.mode = 'month';
   this.render('fade');
  }.bindWithEvent(this, i));
  }
  this.d.setMonth(i);
 }
 if (!available) this.limit.right = true;
 },

 renderDecades: function() {
 // start neatly at interval (eg. 1980 instead of 1987)
 while (this.d.getFullYear() % this.options.yearsPerPage > 0) {
  this.d.setFullYear(this.d.getFullYear() - 1);
 }

 this.picker.getElement('.titleText').set('text', this.d.getFullYear() + '-' + (this.d.getFullYear() + this.options.yearsPerPage - 1));

 var i, y, e;
 var available = false;
 var container = new Element('div', { 'class': 'years' }).inject(this.newContents);

 if ($chk(this.options.minDate) && this.d.getFullYear() <= this.options.minDate.getFullYear()) {
  this.limit.left = true;
 }

 for (i = 0; i < this.options.yearsPerPage; i++) {
  y = this.d.getFullYear();
  e = new Element('div', { 'class': 'year year' + i + (y == this.today.getFullYear() ? ' today' : '') + (y == this.choice.year ? ' selected' : '') }).set('text', y).inject(container);

  if (this.limited('year')) {
  e.addClass('unavailable');
  if (available) {
   this.limit.right = true;
  } else {
   this.limit.left = true;
  }
  } else {
  available = true;
  e.addEvent('click', function(e, d) {
   this.d.setFullYear(d);
   this.mode = 'year';
   this.render('fade');
  }.bindWithEvent(this, y));
  }
  this.d.setFullYear(this.d.getFullYear() + 1);
 }
 if (!available) {
  this.limit.right = true;
 }
 if ($chk(this.options.maxDate) && this.d.getFullYear() >= this.options.maxDate.getFullYear()) {
  this.limit.right = true;
 }
 },

 limited: function(type) {
 var cs = $chk(this.options.minDate);
 var ce = $chk(this.options.maxDate);
 if (!cs && !ce) return false;

 switch (type) {
  case 'year':
  return (cs && this.d.getFullYear() < this.options.minDate.getFullYear()) || (ce && this.d.getFullYear() > this.options.maxDate.getFullYear());

  case 'month':
  // todo: there has got to be an easier way...?
  var ms = ('' + this.d.getFullYear() + this.leadZero(this.d.getMonth())).toInt();
  return cs && ms < ('' + this.options.minDate.getFullYear() + this.leadZero(this.options.minDate.getMonth())).toInt()
   || ce && ms > ('' + this.options.maxDate.getFullYear() + this.leadZero(this.options.maxDate.getMonth())).toInt()

  case 'date':
  return (cs && this.d < this.options.minDate) || (ce && this.d > this.options.maxDate);
 }
 },

 allowZoomOut: function() {
 if (this.mode == 'time' && this.options.timePickerOnly) return false;
 if (this.mode == 'decades') return false;
 if (this.mode == 'year' && !this.options.yearPicker) return false;
 return true;
 },

 zoomOut: function() {
 if (!this.allowZoomOut()) return;
 if (this.mode == 'year') {
  this.mode = 'decades';
 } else if (this.mode == 'time') {
  this.mode = 'month';
 } else {
  this.mode = 'year';
 }
 this.render('fade');
 },

 previous: function() {
 if (this.mode == 'decades') {
  this.d.setFullYear(this.d.getFullYear() - this.options.yearsPerPage);
 } else if (this.mode == 'year') {
  this.d.setFullYear(this.d.getFullYear() - 1);
 } else if (this.mode == 'month') {
  this.d.setDate(1);
  this.d.setMonth(this.d.getMonth() - 1);
 }
 this.render('left');
 },

 next: function() {
 if (this.mode == 'decades') {
  this.d.setFullYear(this.d.getFullYear() + this.options.yearsPerPage);
 } else if (this.mode == 'year') {
  this.d.setFullYear(this.d.getFullYear() + 1);
 } else if (this.mode == 'month') {
  this.d.setDate(1);
  this.d.setMonth(this.d.getMonth() + 1);
 }
 this.render('right');
 },

 close: function(e, force) {
 if (!$(this.picker)) return;
 var clickOutside = ($chk(e) && e.target != this.picker && !this.picker.hasChild(e.target) && e.target != this.visual);
 if (force || clickOutside) {
  if (this.options.useFadeInOut) {
  this.picker.set('tween', { duration: this.options.animationDuration / 2, onComplete: this.destroy.bind(this) }).tween('opacity', 1, 0);
  } else {
  this.destroy();
  }
 }
 },

 destroy: function() {
 this.picker.destroy();
 this.picker = null;
 this.options.onClose();
 },

 select: function(values) {
 this.choice = $merge(this.choice, values);
 var d = this.dateFromObject(this.choice);
 this.input.set('value', this.format(d, this.options.inputOutputFormat));
 this.visual.set('value', this.format(d, this.options.format));
 this.options.onSelect(d);
 this.close(null, true);
 },

 leadZero: function(v) {
 return v < 10 ? '0'+v : v;
 },

 format: function(t, format) {
 var f = '';
 var h = t.getHours();
 var m = t.getMonth();

 for (var i = 0; i < format.length; i++) {
  switch(format.charAt(i)) {
  case '\\': i++; f+= format.charAt(i); break;
  case 'y': f += (t.getFullYear() + '').substring(2); break;
  case 'Y': f += t.getFullYear(); break;
  case 'm': f += this.leadZero(m + 1); break;
  case 'n': f += (m + 1); break;
  case 'M': f += this.options.months[m].substring(0,this.options.monthShort); break;
  case 'F': f += this.options.months[m]; break;
  case 'd': f += this.leadZero(t.getDate()); break;
  case 'j': f += t.getDate(); break;
  case 'D': f += this.options.days[t.getDay()].substring(0,this.options.dayShort); break;
  case 'l': f += this.options.days[t.getDay()]; break;
  case 'G': f += h; break;
  case 'H': f += this.leadZero(h); break;
  case 'g': f += (h % 12 ? h % 12 : 12); break;
  case 'h': f += this.leadZero(h % 12 ? h % 12 : 12); break;
  case 'a': f += (h > 11 ? 'pm' : 'am'); break;
  case 'A': f += (h > 11 ? 'PM' : 'AM'); break;
  case 'i': f += this.leadZero(t.getMinutes()); break;
  case 's': f += this.leadZero(t.getSeconds()); break;
  case 'U': f += Math.floor(t.valueOf() / 1000); break;
  default: f += format.charAt(i);
  }
 }
 return f;
 },

 unformat: function(t, format) {
 var d = new Date();
 d.setMonth(0);
 d.setDate(1);
 var a = {};
 var c, m;
 t = t.toString();

 for (var i = 0; i < format.length; i++) {
  c = format.charAt(i);
  switch(c) {
  case '\\': r = null; i++; break;
  case 'y': r = '[0-9]{2}'; break;
  case 'Y': r = '[0-9]{4}'; break;
  case 'm': r = '0[1-9]|1[012]'; break;
  case 'n': r = '[1-9]|1[012]'; break;
  case 'M': r = '[A-Za-z]{'+this.options.monthShort+'}'; break;
  case 'F': r = '[A-Za-z]+'; break;
  case 'd': r = '0[1-9]|[12][0-9]|3[01]'; break;
  case 'j': r = '[12][0-9]|3[01]|[1-9]'; break;
  case 'D': r = '[A-Za-z]{'+this.options.dayShort+'}'; break;
  case 'l': r = '[A-Za-z]+'; break;
  case 'G':
  case 'H':
  case 'g':
  case 'h': r = '[0-9]{1,2}'; break;
  case 'a': r = '(am|pm)'; break;
  case 'A': r = '(AM|PM)'; break;
  case 'i':
  case 's': r = '[012345][0-9]'; break;
  case 'U': r = '-?[0-9]+$'; break;
  default: r = null;
  }

  if ($chk(r)) {
  m = t.match('^'+r);
  if ($chk(m)) {
   a[c] = m[0];
   t = t.substring(a[c].length);
  } else {
   if (this.options.debug) alert("Fatal Error in DatePicker\n\nUnexpected format at: '"+t+"' expected format character '"+c+"' (pattern '"+r+"')");
   return d;
  }
  } else {
  t = t.substring(1);
  }
 }

 for (c in a) {
  var v = a[c];
  switch(c) {
  case 'y': d.setFullYear(v < 30 ? 2000 + v.toInt() : 1900 + v.toInt()); break; // assume between 1930 - 2029
  case 'Y': d.setFullYear(v); break;
  case 'm':
  case 'n': d.setMonth(v - 1); break;
  // FALL THROUGH NOTICE! "M" has no break, because "v" now is the full month (eg. 'February'), which will work with the next format "F":
  case 'M': v = this.options.months.filter(function(item, index) { return item.substring(0,this.options.monthShort) == v }.bind(this))[0];
  case 'F': d.setMonth(this.options.months.indexOf(v)); break;
  case 'd':
  case 'j': d.setDate(v); break;
  case 'G':
  case 'H': d.setHours(v); break;
  case 'g':
  case 'h': if (a['a'] == 'pm' || a['A'] == 'PM') { d.setHours(v == 12 ? 0 : v.toInt() + 12); } else { d.setHours(v); } break;
  case 'i': d.setMinutes(v); break;
  case 's': d.setSeconds(v); break;
  case 'U': d = new Date(v.toInt() * 1000);
  }
 };

 return d;
 }
});var FormCheck=new Class({Implements:[Options,Events],options:{tipsClass:"fc-tbx",errorClass:"fc-error",fieldErrorClass:"fc-field-error",trimValue:false,validateDisabled:false,submitByAjax:false,ajaxResponseDiv:false,ajaxEvalScripts:false,onAjaxRequest:$empty,onAjaxSuccess:$empty,onAjaxFailure:$empty,display:{showErrors:0,titlesInsteadNames:0,errorsLocation:1,indicateErrors:1,indicateErrorsInit:0,keepFocusOnError:0,checkValueIfEmpty:1,addClassErrorToField:0,fixPngForIe:1,replaceTipsEffect:1,flashTips:0,closeTipsButton:1,tipsPosition:"right",tipsOffsetX:-45,tipsOffsetY:0,listErrorsAtTop:false,scrollToFirst:true,fadeDuration:300},alerts:{required:"This field is required.",alpha:"This field accepts alphabetic characters only.",alphanum:"This field accepts alphanumeric characters only.",nodigit:"No digits are accepted.",digit:"Please enter a valid integer.",digitltd:"The value must be between %0 and %1",number:"Please enter a valid number.",email:"Please enter a valid email.",phone:"Please enter a valid phone.",url:"Please enter a valid url.",confirm:"This field is different from %0",differs:"This value must be different of %0",length_str:"The length is incorrect, it must be between %0 and %1",length_fix:"The length is incorrect, it must be exactly %0 characters",lengthmax:"The length is incorrect, it must be at max %0",lengthmin:"The length is incorrect, it must be at least %0",checkbox:"Please check the box",radios:"Please select a radio",select:"Please choose a value"},regexp:{required:/[^.*]/,alpha:/^[a-z ._-]+$/i,alphanum:/^[a-z0-9 ._-]+$/i,digit:/^[-+]?[0-9]+$/,nodigit:/^[^0-9]+$/,number:/^[-+]?\d*\.?\d+$/,email:/^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,phone:/^[\d\s ().-]+$/,url:/^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i}},initialize:function(C,A){if(this.form=$(C)){this.form.isValid=true;this.regex=["length"];this.setOptions(A);if(typeof (formcheckLanguage)!="undefined"){this.options.alerts=$merge(this.options.alerts,formcheckLanguage)}this.validations=[];this.alreadyIndicated=false;this.firstError=false;var B=new Hash(this.options.regexp);B.each(function(E,D){this.regex.push(D)},this);this.form.getElements("*[class*=validate]").each(function(D){this.register(D)},this);this.form.addEvents({submit:this.onSubmit.bind(this)});if(this.options.display.fixPngForIe){this.fixIeStuffs()}document.addEvent("mousewheel",function(){this.isScrolling=false}.bind(this))}},register:function(el){el.validation=[];el.getProperty("class").split(" ").each(function(classX){if(classX.match(/^validate(\[.+\])$/)){var validators=eval(classX.match(/^validate(\[.+\])$/)[1]);for(var i=0;i<validators.length;i++){el.validation.push(validators[i]);if(validators[i].match(/^confirm\[/)){var field=eval(validators[i].match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i,"'$1'"));if(this.form[field].validation.contains("required")){el.validation.push("required")}}}this.addListener(el)}},this)},dispose:function(A){this.validations.erase(A)},addListener:function(B){this.validations.push(B);B.errors=[];if(this.options.display.indicateErrorsInit){this.validations.each(function(C){if(!this.manageError(C,"submit")){this.form.isValid=false}},this);return true}if(B.validation[0]=="submit"){B.addEvent("click",function(C){this.onSubmit(C)}.bind(this));return true}if(this.isChildType(B)==false){B.addEvent("blur",function(){(function(){if(!this.fxRunning&&(B.element||this.options.display.showErrors==1)&&(this.options.display.checkValueIfEmpty||B.value)){this.manageError(B,"blur")}}.bind(this)).delay(100)}.bind(this))}else{if(this.isChildType(B)==true){var A=this.form.getElements('input[name="'+B.getProperty("name")+'"]');A.each(function(C){C.addEvent("blur",function(){(function(){if((B.element||this.options.display.showErrors==1)&&(this.options.display.checkValueIfEmpty||B.value)){this.manageError(B,"click")}}.bind(this)).delay(100)}.bind(this))},this)}}},validate:function(el){el.errors=[];el.isOk=true;if(!this.options.validateDisabled&&el.get("disabled")){return true}if(this.options.trimValue&&el.value){el.value=el.value.trim()}el.validation.each(function(rule){if(this.isChildType(el)){if(this.validateGroup(el)==false){el.isOk=false}}else{var ruleArgs=[];if(rule.match(/^.+\[/)){var ruleMethod=rule.split("[")[0];ruleArgs=eval(rule.match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i,"'$1'"))}else{var ruleMethod=rule}if(this.regex.contains(ruleMethod)&&el.get("tag")!="select"){if(this.validateRegex(el,ruleMethod,ruleArgs)==false){el.isOk=false}}if(ruleMethod=="confirm"){if(this.validateConfirm(el,ruleArgs)==false){el.isOk=false}}if(ruleMethod=="differs"){if(this.validateDiffers(el,ruleArgs)==false){el.isOk=false}}if(el.get("tag")=="select"||(el.type=="checkbox"&&ruleMethod=="required")){if(this.simpleValidate(el)==false){el.isOk=false}}if(rule.match(/%[A-Z0-9\._-]+$/i)||(el.isOk&&rule.match(/~[A-Z0-9\._-]+$/i))){if(eval(rule.slice(1)+"(el)")==false){el.isOk=false}}}},this);if(el.isOk){return true}else{return false}},simpleValidate:function(A){if(A.get("tag")=="select"&&A.selectedIndex<=0){A.errors.push(this.options.alerts.select);return false}else{if(A.type=="checkbox"&&A.checked==false){A.errors.push(this.options.alerts.checkbox);return false}}return true},validateRegex:function(C,B,D){var E="";if(D[1]&&B=="length"){if(D[1]==-1){this.options.regexp.length=new RegExp("^[\\s\\S]{"+D[0]+",}$");E=this.options.alerts.lengthmin.replace("%0",D[0])}else{if(D[0]==D[1]){this.options.regexp.length=new RegExp("^[\\s\\S]{"+D[0]+"}$");E=this.options.alerts.length_fix.replace("%0",D[0])}else{this.options.regexp.length=new RegExp("^[\\s\\S]{"+D[0]+","+D[1]+"}$");E=this.options.alerts.length_str.replace("%0",D[0]).replace("%1",D[1])}}}else{if(D[0]&&B=="length"){this.options.regexp.length=new RegExp("^.{0,"+D[0]+"}$");E=this.options.alerts.lengthmax.replace("%0",D[0])}else{E=this.options.alerts[B]}}if(D[1]&&B=="digit"){var A=true;if(!this.options.regexp.digit.test(C.value)){C.errors.push(this.options.alerts[B]);A=false}if(D[1]==-1){if(C.value>=D[0]){var F=true}else{var F=false}E=this.options.alerts.digitmin.replace("%0",D[0])}else{if(C.value>=D[0]&&C.value<=D[1]){var F=true}else{var F=false}E=this.options.alerts.digitltd.replace("%0",D[0]).replace("%1",D[1])}if(A==false||F==false){C.errors.push(E);return false}}else{if(this.options.regexp[B].test(C.value)==false){C.errors.push(E);return false}}return true},validateConfirm:function(B,C){var A=C[0];if(B.value!=this.form[A].value){if(this.options.display.titlesInsteadNames){var D=this.options.alerts.confirm.replace("%0",this.form[A].getProperty("title"))}else{var D=this.options.alerts.confirm.replace("%0",A)}B.errors.push(D);return false}return true},validateDiffers:function(A,C){var B=C[0];if(A.value==this.form[B].value){if(this.options.display.titlesInsteadNames){var D=this.options.alerts.differs.replace("%0",this.form[B].getProperty("title"))}else{var D=this.options.alerts.differs.replace("%0",B)}A.errors.push(D);return false}return true},isChildType:function(A){return($defined(A.type)&&A.type=="radio")?true:false},validateGroup:function(D){D.errors=[];var A=this.form[D.getProperty("name")];D.group=A;var C=false;for(var B=0;B<A.length;B++){if(A[B].checked){C=true}}if(C==false){D.errors.push(this.options.alerts.radios);return false}else{return true}},listErrorsAtTop:function(A){if(!this.form.element){this.form.element=new Element("div",{id:"errorlist","class":this.options.errorClass}).injectTop(this.form)}if($type(A)=="collection"){new Element("p").set("html","<span>"+A[0].name+" : </span>"+A[0].errors[0]).injectInside(this.form.element)}else{if((A.validation.contains("required")&&A.errors.length>0)||(A.errors.length>0&&A.value&&A.validation.contains("required")==false)){A.errors.each(function(B){new Element("p").set("html","<span>"+A.name+" : </span>"+B).injectInside(this.form.element)},this)}}},manageError:function(A,C){var B=this.validate(A);if((!B&&A.validation.flatten()[0].contains("confirm["))||(!B&&A.validation.contains("required"))||(!A.validation.contains("required")&&A.value&&!B)){if(this.options.display.listErrorsAtTop==true&&C=="submit"){this.listErrorsAtTop(A,C)}if(this.options.display.indicateErrors==2||this.alreadyIndicated==false||A.name==this.alreadyIndicated.name){if(!this.firstError){this.firstError=A}this.alreadyIndicated=A;if(this.options.display.keepFocusOnError&&A.name==this.firstError.name){(function(){A.focus()}).delay(20)}this.addError(A);return false}}else{if((B||(!A.validation.contains("required")&&!A.value))&&A.element){this.removeError(A);return true}}return true},addError:function(C){if(!C.element&&this.options.display.indicateErrors!=0){if(this.options.display.errorsLocation==1){var E=(this.options.display.tipsPosition=="left")?C.getCoordinates().left:C.getCoordinates().right;var B={opacity:0,position:"absolute","float":"left",left:E+this.options.display.tipsOffsetX};C.element=new Element("div",{"class":this.options.tipsClass,styles:B}).injectInside(document.body);this.addPositionEvent(C)}else{if(this.options.display.errorsLocation==2){C.element=new Element("div",{"class":this.options.errorClass,styles:{opacity:0}}).injectBefore(C)}else{if(this.options.display.errorsLocation==3){C.element=new Element("div",{"class":this.options.errorClass,styles:{opacity:0}});if($type(C.group)=="object"||$type(C.group)=="collection"){C.element.injectAfter(C.group[C.group.length-1])}else{C.element.injectAfter(C)}}}}}if(C.element&&C.element!=true){C.element.empty();if(this.options.display.errorsLocation==1){var D=[];C.errors.each(function(F){D.push(new Element("p").set("html",F))});var A=this.makeTips(D).injectInside(C.element);if(this.options.display.closeTipsButton){A.getElements("a.close").addEvent("mouseup",function(){this.removeError(C)}.bind(this))}C.element.setStyle("top",C.getCoordinates().top-A.getCoordinates().height+this.options.display.tipsOffsetY)}else{C.errors.each(function(F){new Element("p").set("html",F).injectInside(C.element)})}if(!this.options.display.fadeDuration||Browser.Engine.trident&&Browser.Engine.version==5&&this.options.display.errorsLocation<2){C.element.setStyle("opacity",1)}else{C.fx=new Fx.Tween(C.element,{duration:this.options.display.fadeDuration,ignore:true,onStart:function(){this.fxRunning=true}.bind(this),onComplete:function(){this.fxRunning=false;if(C.element&&C.element.getStyle("opacity").toInt()==0){C.element.destroy();C.element=false}}.bind(this)});if(C.element.getStyle("opacity").toInt()!=1){C.fx.start("opacity",1)}}}if(this.options.display.addClassErrorToField&&this.isChildType(C)==false){C.addClass(this.options.fieldErrorClass);C.element=C.element||true}},addPositionEvent:function(A){if(this.options.display.replaceTipsEffect){A.event=function(){new Fx.Morph(A.element,{duration:this.options.display.fadeDuration}).start({left:[A.element.getStyle("left"),A.getCoordinates().right+this.options.display.tipsOffsetX],top:[A.element.getStyle("top"),A.getCoordinates().top-A.element.getCoordinates().height+this.options.display.tipsOffsetY]})}.bind(this)}else{A.event=function(){A.element.setStyles({left:A.getCoordinates().right+this.options.display.tipsOffsetX,top:A.getCoordinates().top-A.element.getCoordinates().height+this.options.display.tipsOffsetY})}.bind(this)}window.addEvent("resize",A.event)},removeError:function(A){this.alreadyIndicated=false;A.errors=[];A.isOK=true;window.removeEvent("resize",A.event);if(this.options.display.errorsLocation>=2&&A.element){new Fx.Tween(A.element,{duration:this.options.display.fadeDuration}).start("height",0)}if(!this.options.display.fadeDuration||Browser.Engine.trident&&Browser.Engine.version==5&&this.options.display.errorsLocation==1&&A.element){this.fxRunning=true;A.element.destroy();A.element=false;(function(){this.fxRunning=false}.bind(this)).delay(200)}else{if(A.element&&A.element!=true){A.fx.start("opacity",0)}}if(this.options.display.addClassErrorToField&&!this.isChildType(A)){A.removeClass(this.options.fieldErrorClass)}},focusOnError:function(B){if(this.options.display.scrollToFirst&&!this.alreadyFocused&&!this.isScrolling){if(!this.options.display.indicateErrors||!this.options.display.errorsLocation){var A=B.getCoordinates().top-30}else{if(this.alreadyIndicated.element){switch(this.options.display.errorsLocation){case 1:var A=B.element.getCoordinates().top;break;case 2:var A=B.element.getCoordinates().top-30;break;case 3:var A=B.getCoordinates().top-30;break}this.isScrolling=true}}if(window.getScroll.y!=A){new Fx.Scroll(window,{onComplete:function(){this.isScrolling=false;B.focus()}.bind(this)}).start(0,A)}else{this.isScrolling=false;B.focus()}this.alreadyFocused=true}},fixIeStuffs:function(){if(Browser.Engine.trident4){var F=new RegExp("url\\(([.a-zA-Z0-9_/:-]+.png)\\)");var H=new RegExp("(.+)formcheck.css");for(var C=0;C<document.styleSheets.length;C++){if(document.styleSheets[C].href.match(/formcheck\.css$/)){var E=document.styleSheets[C].href.replace(H,"$1");var D=document.styleSheets[C].rules.length;for(var B=0;B<D;B++){var I=document.styleSheets[C].rules[B].style;var G=E+I.backgroundImage.replace(F,"$1");if(G&&G.match(/\.png/i)){var A=(I.backgroundRepeat=="no-repeat")?"crop":"scale";I.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, src='"+G+"', sizingMethod='"+A+"')";I.backgroundImage="none"}}}}}},makeTips:function(C){var E=new Element("table");E.cellPadding="0";E.cellSpacing="0";E.border="0";var D=new Element("tbody").injectInside(E);var B=new Element("tr").injectInside(D);new Element("td",{"class":"tl"}).injectInside(B);new Element("td",{"class":"t"}).injectInside(B);new Element("td",{"class":"tr"}).injectInside(B);var H=new Element("tr").injectInside(D);new Element("td",{"class":"l"}).injectInside(H);var A=new Element("td",{"class":"c"}).injectInside(H);var G=new Element("div",{"class":"err"}).injectInside(A);C.each(function(I){I.injectInside(G)});if(this.options.display.closeTipsButton){new Element("a",{"class":"close"}).injectInside(A)}new Element("td",{"class":"r"}).injectInside(H);var F=new Element("tr").injectInside(D);new Element("td",{"class":"bl"}).injectInside(F);new Element("td",{"class":"b"}).injectInside(F);new Element("td",{"class":"br"}).injectInside(F);return E},reinitialize:function(){this.validations.each(function(A){if(A.element){A.errors=[];A.isOK=true;if(this.options.display.flashTips==1){A.element.destroy();A.element=false}}},this);if(this.form.element){this.form.element.empty()}this.alreadyFocused=false;this.firstError=false;this.elementToRemove=this.alreadyIndicated;this.alreadyIndicated=false;this.form.isValid=true},submitByAjax:function(){var A=this.form.getProperty("action");this.fireEvent("ajaxRequest");new Request({url:A,method:this.form.getProperty("method"),data:this.form.toQueryString(),evalScripts:this.options.ajaxEvalScripts,onFailure:function(B){this.fireEvent("ajaxFailure",B)}.bind(this),onSuccess:function(B){this.fireEvent("ajaxSuccess",B);if(this.options.ajaxResponseDiv){$(this.options.ajaxResponseDiv).set("html",B)}}.bind(this)}).send()},onSubmit:function(A){this.reinitialize();this.validations.each(function(C){var B=this.manageError(C,"submit");if(!B){this.form.isValid=false}},this);if(this.form.isValid){if(this.options.submitByAjax){new Event(A).stop();this.submitByAjax()}}else{new Event(A).stop();if(this.elementToRemove&&this.elementToRemove!=this.firstError&&this.options.display.indicateErrors==1){this.removeError(this.elementToRemove)}this.focusOnError(this.firstError)}}});formcheckLanguage = {
 required: "Bitte f&uuml;llen Sie das Feld aus!.",
 alpha: "In diesem Feld sind nur Buchstaben zul&auml;ssig.",
 alphanum: "In diesem Feld sind nur Zahlen zul&auml;ssig.",
 nodigit: "Eingabe von Nummern nicht m&ouml;glich.",
 digit: "Nur Eingabe von Zahlen m&ouml;glich.",
 digitmin: "Die kleinstm&ouml;gliche Zahl ist %0.",
 digitltd: "Der Wert muss zwischen %0 und %1 liegen",
 number: "Geben Sie bitte eine g&uuml;ltige Zahl ein.",
 email: "Geben Sie bitte eine g&uuml;ltige E-Mail ein.",
 phone: "Geben Sie bitte eine g&uuml;ltige Telefonnummer ein.",
 url: "Geben Sie bitte eine g&uuml;ltige Internetadresse ein.",
 
 confirm: "Das Feld ist verschieden von %0.",
 differs: "Der Wert muss unterschiedlich zu %0 sein.",
 length_str: "Das Feld ist verschieden von %0.",
 length_fix: "Falsche Länge, es müssen exakt %0 Buchstaben sein.",
 lengthmax: "Der Wert ist nicht korrekt, maximale Anzahl Charakter %0.",
 lengthmin: "Der Wert ist nicht korrekt, minimale Anzahl Charakter %0.",
 checkbox: "Bitte aktivieren.",
 radios: "Bitte einen Wert ausw&auml;hlen.",
 select: "Bitte einen Wert ausw&auml;hlen."
}
