/**
 * Main Seagull JavaScript library.
 *
 * @package seagull
 * @subpackage SGL
 */
var SGL = {
    isReady: false,
    ready: function(f) {
        // If the DOM is already ready
        if (SGL.isReady) {
            // Execute the function immediately
            if (typeof f == 'string') {
                eval(f);
            } else if (typeof f == 'function') {
                f.apply(document);
            }
        // Otherwise add the function to the wait list
        } else {
            SGL.onReadyDomEvents.push(f);
        }
    },
    onReadyDomEvents: [],
    onReadyDom: function() {
        // make sure that the DOM is not already loaded
        if (!SGL.isReady) {
            // Flag the DOM as ready
            SGL.isReady = true;

            if (SGL.onReadyDomEvents) {
                for (var i = 0, j = SGL.onReadyDomEvents.length; i < j; i++) {
                    if (typeof SGL.onReadyDomEvents[i] == 'string') {
                        eval(SGL.onReadyDomEvents[i]);
                    } else if (typeof SGL.onReadyDomEvents[i] == 'function') {
                        SGL.onReadyDomEvents[i].apply(document);
                    }
                }
                // Reset the list of functions
				SGL.onReadyDomEvents = null;
            }
        }
    }
};

/**
 *  Cross-browser onDomReady solution
 *  Dean Edwards/Matthias Miller/John Resig
 */
new function() {
    /* for Mozilla/Opera9 */
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", SGL.onReadyDom, false);
        return;
    }

    /* for Internet Explorer */
    /*@cc_on @*/
    /*@if (@_win32)
        document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
        var script = document.getElementById("__ie_onload");
        script.onreadystatechange = function() {
            if (this.readyState == "complete") {
                SGL.onReadyDom(); // call the onload handler
            }
        };
        return;
    /*@end @*/

    /* for Safari */
    if (/WebKit/i.test(navigator.userAgent)) { // sniff
        SGL.webkitTimer = setInterval(function() {
            if (/loaded|complete/.test(document.readyState)) {
                // Remove the timer
                clearInterval(SGL.webkitTimer);
                SGL.webkitTimer = null;
                // call the onload handler
                SGL.onReadyDom();
            }
        }, 10);
        return;
    }

    /* for other browsers */
    oldWindowOnload = window.onload || null;
    window.onload = function() {
        if (oldWindowOnload) {
            oldWindowOnload();
        }
        SGL.onReadyDom();
        return;
    }
}

// ----------
// --- BC ---
// ----------

/**
 * Used for async load of sourcefourge bloody button,
 */
function async_load()
{
    var node;
    try {
        // variable _asyncDom is set from JavaScript in the iframe
        // node = top._asyncDom.cloneNode(true); // kills Safari 1.2.4
        node = top._asyncDom;
        // try to remove the first script element, the one that
        // executed all document.writes().
        node.removeChild(node.getElementsByTagName('script')[0]);
    } catch (e) {}
    try {
        // insert DOM fragment at a DIV with id "async_demo" on current page
        document.getElementById('async_demo').appendChild(node);
    } catch (e) {
        try {
            // fallback for some non DOM compliant browsers
            document.getElementById('async_demo').innerHTML = node.innerHTML;
        } catch (e2) {};
    }
}

/**
 * Make Seagull SEF URL.
 *
 * @param object params
 *
 * @return string
 */
function makeUrl(params)
{
    var ret = SGL_JS_FRONT_CONTROLLER != ''
        ? SGL_JS_WEBROOT + '/' + SGL_JS_FRONT_CONTROLLER
        : SGL_JS_WEBROOT;
    var moduleName = params.module ? params.module : '';
    var managerName = params.manager ? params.manager : moduleName;

    switch (SGL_JS_URL_STRATEGY) {

    // make classic URL
    case 'SGL_UrlParser_ClassicStrategy':
        if (ret.charAt(ret.length - 1) != '?') {
            ret = ret + '?';
        }
        ret = ret + 'moduleName=' + escape(moduleName) + '&managerName=' + escape(managerName);
        for (x in params) {
            if (x == 'module' || x == 'manager') {
                continue;
            }
            // add param
            ret = '&' + ret + escape(x) + '=' + escape(params[x]);
        }
        break;

    // make default Seagull SEF URL
    default:
        ret = ret + '/' + escape(moduleName) + '/' + escape(managerName) + '/';
        for (x in params) {
            if (x == 'module' || x == 'manager') {
                continue;
            }
            ret = ret + escape(x) + '/' + escape(params[x]) + '/';
        }
        break;
    }
    return ret;
}

SGL.ready(function() {
    var msg = document.getElementById('broadcastMessage');
    if (msg) {
        msg.getElementsByTagName('a')[0].onclick = function() {
            msg.style.display = 'none';
        }
    }
});

String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g,'') };

/**
 * Main CSI JavaScript library.
 *
 * @package csi
 */
var CSI = {
    isReady: false,
    blockSwitch: function(hidden){
    	if(typeof hidden == 'undefined'){
    		if(document.getElementById('local').style.display == 'none'){
	    		//Mostrar
	    		CSI.mostrarBloque();
	    	} else {
	    		//Ocultar
	    		CSI.ocultarBloque();
	    	}	
    	} else {
    		if(hidden){
    			CSI.ocultarBloque();
    		} else {
    			CSI.mostrarBloque();
    		}
    	}
    },
    mostrarBloque: function(){
    	$('local').show();
    	$('main').setStyle({marginLeft: '200px'});
		$('blockSwitcher').setStyle({left: '195px',backgroundImage: 'url(' + SGL_JS_WEBROOT + '/themes/' + SGL_JS_THEME + '/images/bg/switcher_lf.gif)'});
    },
    ocultarBloque: function(){
    	$('local').hide();
    	$('main').setStyle({marginLeft: '0px'});
		$('blockSwitcher').setStyle({left: '1px',backgroundImage: 'url(' + SGL_JS_WEBROOT + '/themes/' + SGL_JS_THEME + '/images/bg/switcher_rg.gif)'});
    },
    handleCollapsibleElements: function(parentElement, triggerElement){
    	parentElement.select('.collapsible').each(
    		function(element){
    			if(element.hasClassName('collapsed')){
    				CSI.extendElement(element);
    			} else if (element.hasClassName('extended')){
    				CSI.collapseElement(element);
    			}
    		}
    	);
    	CSI.changeTriggerIcon(triggerElement);
    },
    changeTriggerIcon:function (element){
    	if(element.innerHTML == ' + info'){
    		element.innerHTML = '-';
    	} else {
    		element.innerHTML = ' + info';
    	}
    },
    extendElement: function(element){
    	element.removeClassName('collapsed');
		element.addClassName('extended');
    },
    collapseElement: function(element){
    	element.removeClassName('extended');
		element.addClassName('collapsed');
    },
    
    generateSimpleTip: function(elementToAttachToID, contentElementID, title, isElementText,vHook,vStem,showOn){
    	if(typeof $(elementToAttachToID).prototip != 'undefined' && $(elementToAttachToID).prototip != null && $(elementToAttachToID).prototip != 'undefined'){
    		$(elementToAttachToID).prototip.show();
    	} else {
    		var dummyElement;
    		if(!isElementText){
    			dummyElement = $(contentElementID);
    		} else {
    			dummyElement = contentElementID;
    		}
    					
    		if(!vHook){
    			vHook = { target: 'bottomMiddle', tip: 'topRight' };
    		}
    							
			if(!vStem){
				vStem = 'topRight';
			}
			
			if(!showOn){
				showOn = 'click';
				hideOn = { element: 'closeButton', event: 'click' };
			} else {
				hideOn = 'mouseout';
			}
			
    		new Tip(elementToAttachToID, 
				dummyElement, 
				{ 
					title : title,
					images: SGL_JS_WEBROOT + '/js/prototip/images/prototip/styles/default/',
					closeButton: true,
					showOn: showOn,
					hideOn: hideOn,
					stem: vStem,
				  	hook: vHook,
				  	offset: { x: 0, y: 5 },
				  	width: 'auto',
				  	hideOthers: true,
					hideAfter: 1
				}
			);
			$(elementToAttachToID).prototip.show();
    	}
    },
    
    translate: function (cadena){
		if(typeof CSI.Localisation != 'undefined') {
			if(CSI.Localisation[cadena]) {
				return CSI.Localisation[cadena];
			}
		}
		return '_' + cadena + '_';
	}
};


/**
 * Library to deal with AJAX requests.
 *
 * @package csi
 */
CSI.Ajax =
{
	genericErrorMessage: CSI.translate('There was a problem performing your request. Please, try again later'),
    onCompleteCallback: false,
    onLoadingCallback: false,
    onFailureCallback: false,
    sanitizeObject: function(data) {
    	var dummy = $H(data);
    	var res = new Hash();
    	dummy.each(function(item){
    		tmp = parseInt(item.key);
    		if(!isNaN(tmp)){
    			res.set(item.key,item.value);
    		}
    	});
    	return res;
    },
    buildNames: function(data){
    	data = CSI.Ajax.sanitizeObject(data);
        return $H($H($H(data).get($H(data).keys().first())).keys());   
    },
    executeCallbacks: function(){
        if(CSI.Ajax.onCompleteCallback != false && typeof CSI.Ajax.onCompleteCallback == 'function'){
            CSI.Ajax.onCompleteCallback.call(CSI.Ajax);
        }
    }
};

/**
 * Library to deal with C17 ajax requests.
 *
 * @package csi
 */
 
CSI.Ajax.Ayuda = {
    save: function (params){
        new Ajax.Request(
            makeUrl({module: 'default', action: 'saveAyuda'}),
            {
                parameters: params,
                method: 'post',
                asynchronous:true,
                evalScripts:false,
                onSuccess: function(req) {
                    var txt = req.responseText; 
                    $('helpContent').update(txt);
                    $('helpTxt').update(txt);
                    $('helpForm').hide();
                },
	            onFailure: function(){
	                $('helpForm').reset();
	                $('helpIndicator').hide();
	                alert(CSI.Ajax.genericErrorMessage);
	            },
	            onLoading: function(){
	                $('helpIndicator').show();
	            },
	            onComplete: function(){
	                $('helpIndicator').hide();
	            }
            }
        );
 	}
};

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

var Prototype = {
  Version: '1.6.0.2',

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

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

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

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

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


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

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

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

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

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

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

    klass.prototype.constructor = klass;

    return klass;
  }
};

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

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

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

    return this;
  }
};

var Abstract = { };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return returnValue;
  }
};

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

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

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

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

    this.registerCallback();
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return camelized;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var $break = { };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];
    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
        iterable.toArray) return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    get: function(key) {
      return this._object[key];
    },

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

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

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

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

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

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

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

    toQueryString: function() {
      return this.map(function(pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

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

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

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

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

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

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

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

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

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

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

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

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

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

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

Object.extend(Ajax.Responders, Enumerable);

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

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

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

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

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

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

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

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

    this.parameters = params;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  status:      0,
  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

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

  getHeader: Ajax.Request.prototype.getHeader,

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

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

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

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

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

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

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

    $super(url, options);
  },

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

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

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

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

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

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

    this.start();
  },

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

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

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

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

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

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

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

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

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

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

Element.cache = { };

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

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

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

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

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

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

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

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

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

    var content, insert, tagName, childNodes;

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

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

      content = Object.toHTML(content);

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

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

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

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

    return element;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    while (element = element.parentNode)
      if (element == originalAncestor) return true;
    return false;
  },

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

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

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

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

    return element;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return $(document.body);
  },

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

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

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

    } while (element = element.offsetParent);

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

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

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

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

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

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

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

Element.Methods.identify.counter = 1;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  Element._attributeTranslations.has = {};

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

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

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

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

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

    return element;
  };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

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

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

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

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

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

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

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

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

document.viewport = {
  getDimensions: function() {
    var dimensions = { };
    var B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
    });
    return dimensions;
  },

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

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

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

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

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

    var e = this.expression;

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

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

    return true;
  },

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

    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

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

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

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

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

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

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

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

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

  findElements: function(root) {
    root = root || document;
    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
    return this.matcher(root);
  },

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

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

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

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

    return match;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return matchingInputs;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  cache: { },

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

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

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

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

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

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

    element: function(event) {
      var node = Event.extend(event).target;
      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return Selector.findElement(elements, expression, 0);
    },

    pointer: function(event) {
      return {
        x: event.pageX || (event.clientX +
          (document.documentElement.scrollLeft || document.body.scrollLeft)),
        y: event.pageY || (event.clientY +
          (document.documentElement.scrollTop || document.body.scrollTop))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

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

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

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }

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

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

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

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    document.loaded = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  // Deprecation layer -- use newer Element methods now (1.5.2).

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

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

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

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

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

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

Element.addMethods();

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

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

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

Scriptaculous.load();

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

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();
    
    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;
      
    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];
    
    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);
    
    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1]) 
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        } 

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return element;
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e)
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) { 
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
  
    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
  
    tags.each( function(tag){ 
      scope[tag] = function() { 
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
      } 
    });
  }
}


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return styleRules;
};

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

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

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

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

Element.addMethods(Effect.Methods);


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

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

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

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

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

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

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

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

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

    Element.hide(this.update);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


/* Optional: Temporarily hide the "tabber" class so it does not "flash"
   on the page as plain HTML. After tabber runs, the class is changed
   to "tabberlive" and it will appear. */

document.write('<style type="text/css">.tabber{display:none;}<\/style>');

/*==================================================
  Set the tabber options (must do this before including tabber.js)
  ==================================================*/
var tabberOptions = {
  	'cookie': "catalogo_search", /* Name to use for the cookie */
  
	'onLoad': function(argsObj) {
		var t = argsObj.tabber;
	    var i;
	
	    /* Optional: Add the id of the tabber to the cookie name to allow
	       for multiple tabber interfaces on the site.  If you have
	       multiple tabber interfaces (even on different pages) I suggest
	       setting a unique id on each one, to avoid having the cookie set
	       the wrong tab.
	    */
	    if (t.id) {
	      t.cookie = t.id + t.cookie;
	    }
	
	    /* If a cookie was previously set, restore the active tab */
	    i = parseInt(CSI.Cookie.readCookie(t.cookie));
	    if (isNaN(i)) { return; }
	    t.tabShow(i);
	    
	    var inputname = '';
	    
	    try{
	    	tmp = $('pmidValue').value;
    		reg = true;
	    } catch(err){
	    	reg = false;
	    }
	    
	    if(reg){
	    	//Modo registrado
	    	try{
		    	tmp = $('query_organisation').value;
	    		gest = true;
		    } catch(err){
		    	gest = false;
		    }
		    
		    if(gest){
		    	switch(i){
			    	case 0:
			    		inputname = 'query_revista';
			    		break;
			    	case 1:
			    		inputname = 'pmidValue';
			    		break;
			    	case 2:
			    		inputname = 'query_organisation';
			    		break;
			    	case 3:
			    		inputname = 'ff_title';
			    		break;
			    }
		    } else {
		    	switch(i){
			    	case 0:
			    		inputname = 'query_revista';
			    		break;
			    	case 1:
			    		inputname = 'pmidValue';
			    		break;
			    	case 2:
			    		inputname = 'ff_title';
			    		break;
			    }
		    }
	    	
	    } else {
	    	switch(i){
		    	case 0:
		    		inputname = 'query_revista';
		    		break;
		    	case 1:
		    		inputname = 'query_organisation';
		    		break;
		    }
	    }
	    
	    if(typeof $(inputname) != 'undefined'){
	    	try{
	    		$(inputname).focus();
	    	} catch(err){
	    		return;
	    	}
	    	
	    }
  	},

  	'onClick':function(argsObj) {
	    var c = argsObj.tabber.cookie;
	    var i = argsObj.index;
	    CSI.Cookie.createCookie(c, i, 1);
  	}
};


/**
 * NB! Untested, incompatible with scriptaculous loader.
 *
 * CSI javascript libraries loader.
 *
 * $output->addJavascriptFile('js/CSI/Loader.js?Cookie,Util');
 *   - load js/CSI/Cookie.js
 *   - load js/CSI/Util.js
 * $output->addJavascriptFile('js/CSI/Loader.js?Cookie,Util.String');
 *   - load js/CSI/Cookie.js
 *   - load js/CSI/Util/String.js
 *
 * @package csi
 */
CSI.Loader =
{
    path: SGL_JS_WEBROOT + '/js/CSI/',

    require: function(fileName) {
        document.write('<script type="text/javascript" '
            + 'src="' + this.path + fileName + '"></script>');
    },

    load: function(library) {
        var fileName = library.replace(/\./g, '/') + '.js';
        this.require(fileName);
    },

    init: function() {
        var aScripts = document.getElementsByTagName('script');
        for (var i = 0; i < aScripts.length; i++) {
            if (aScripts[i].src) {
                var matches;
                if (matches = aScripts[i].src.match(/CSI\/Loader\.js\?(.+)$/)) {
                    var aLibs = matches[1].split(',');
                    for (var j = 0, len = aLibs.length; j < len; j++) {
                    	if(aLibs[j].indexOf('/') == -1){
                    		CSI.Loader.load(aLibs[j]);
                    	}
                    }
                }
            }
        }
    }
}

CSI.Loader.init();

/*==================================================
  $Id: tabber.js,v 1.9 2006/04/27 20:51:51 pat Exp $
  tabber.js by Patrick Fitzgerald pat@barelyfitz.com

  Documentation can be found at the following URL:
  http://www.barelyfitz.com/projects/tabber/

  License (http://www.opensource.org/licenses/mit-license.php)

  Copyright (c) 2006 Patrick Fitzgerald

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation files
  (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge,
  publish, distribute, sublicense, and/or sell copies of the Software,
  and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
  ==================================================*/

function tabberObj(argsObj)
{
    //Modificado para asignar la pestaña activa por url
    if ( window.location.hash && window.location.hash.match( /^#/ ) )
    {
        var el = document.getElementById(window.location.hash.substr(2));
        if (el) {
            el.className = "tabbertab tabbertabdefault"
        }
    } 
  var arg; /* name of an argument to override */

  /* Element for the main tabber div. If you supply this in argsObj,
     then the init() method will be called.
  */
  this.div = null;

  /* Class of the main tabber div */
  this.classMain = "tabber";

  /* Rename classMain to classMainLive after tabifying
     (so a different style can be applied)
  */
  this.classMainLive = "tabberlive";

  /* Class of each DIV that contains a tab */
  this.classTab = "tabbertab";

  /* Class to indicate which tab should be active on startup */
  this.classTabDefault = "tabbertabdefault";

  /* Class for the navigation UL */
  this.classNav = "tabbernav";

  /* When a tab is to be hidden, instead of setting display='none', we
     set the class of the div to classTabHide. In your screen
     stylesheet you should set classTabHide to display:none.  In your
     print stylesheet you should set display:block to ensure that all
     the information is printed.
  */
  this.classTabHide = "tabbertabhide";

  /* Class to set the navigation LI when the tab is active, so you can
     use a different style on the active tab.
  */
  this.classNavActive = "tabberactive";

  /* Elements that might contain the title for the tab, only used if a
     title is not specified in the TITLE attribute of DIV classTab.
  */
  this.titleElements = ['h2','h3','h4','h5','h6'];

  /* Should we strip out the HTML from the innerHTML of the title elements?
     This should usually be true.
  */
  this.titleElementsStripHTML = true;

  /* If the user specified the tab names using a TITLE attribute on
     the DIV, then the browser will display a tooltip whenever the
     mouse is over the DIV. To prevent this tooltip, we can remove the
     TITLE attribute after getting the tab name.
  */
  this.removeTitle = true;

  /* If you want to add an id to each link set this to true */
  this.addLinkId = false;

  /* If addIds==true, then you can set a format for the ids.
     <tabberid> will be replaced with the id of the main tabber div.
     <tabnumberzero> will be replaced with the tab number
       (tab numbers starting at zero)
     <tabnumberone> will be replaced with the tab number
       (tab numbers starting at one)
     <tabtitle> will be replaced by the tab title
       (with all non-alphanumeric characters removed)
   */
  this.linkIdFormat = '<tabberid>nav<tabnumberone>';

  /* You can override the defaults listed above by passing in an object:
     var mytab = new tabber({property:value,property:value});
  */
  for (arg in argsObj) { this[arg] = argsObj[arg]; }

  /* Create regular expressions for the class names; Note: if you
     change the class names after a new object is created you must
     also change these regular expressions.
  */
  this.REclassMain = new RegExp('\\b' + this.classMain + '\\b', 'gi');
  this.REclassMainLive = new RegExp('\\b' + this.classMainLive + '\\b', 'gi');
  this.REclassTab = new RegExp('\\b' + this.classTab + '\\b', 'gi');
  this.REclassTabDefault = new RegExp('\\b' + this.classTabDefault + '\\b', 'gi');
  this.REclassTabHide = new RegExp('\\b' + this.classTabHide + '\\b', 'gi');

  /* Array of objects holding info about each tab */
  this.tabs = new Array();

  /* If the main tabber div was specified, call init() now */
  if (this.div) {

    this.init(this.div);

    /* We don't need the main div anymore, and to prevent a memory leak
       in IE, we must remove the circular reference between the div
       and the tabber object. */
    this.div = null;
  }
}


/*--------------------------------------------------
  Methods for tabberObj
  --------------------------------------------------*/


tabberObj.prototype.init = function(e)
{
  /* Set up the tabber interface.

     e = element (the main containing div)

     Example:
     init(document.getElementById('mytabberdiv'))
   */

  var
  childNodes, /* child nodes of the tabber div */
  i, i2, /* loop indices */
  t, /* object to store info about a single tab */
  defaultTab=0, /* which tab to select by default */
  DOM_ul, /* tabbernav list */
  DOM_li, /* tabbernav list item */
  DOM_a, /* tabbernav link */
  aId, /* A unique id for DOM_a */
  headingElement; /* searching for text to use in the tab */

  /* Verify that the browser supports DOM scripting */
  if (!document.getElementsByTagName) { return false; }

  /* If the main DIV has an ID then save it. */
  if (e.id) {
    this.id = e.id;
  }

  /* Clear the tabs array (but it should normally be empty) */
  this.tabs.length = 0;

  /* Loop through an array of all the child nodes within our tabber element. */
  childNodes = e.childNodes;
  for(i=0; i < childNodes.length; i++) {

    /* Find the nodes where class="tabbertab" */
    if(childNodes[i].className &&
       childNodes[i].className.match(this.REclassTab)) {
      
      /* Create a new object to save info about this tab */
      t = new Object();
      
      /* Save a pointer to the div for this tab */
      t.div = childNodes[i];
      
      /* Add the new object to the array of tabs */
      this.tabs[this.tabs.length] = t;

      /* If the class name contains classTabDefault,
	 then select this tab by default.
      */
      if (childNodes[i].className.match(this.REclassTabDefault)) {
	defaultTab = this.tabs.length-1;
      }
    }
  }

  /* Create a new UL list to hold the tab headings */
  DOM_ul = document.createElement("ul");
  DOM_ul.className = this.classNav;
  
  /* Loop through each tab we found */
  for (i=0; i < this.tabs.length; i++) {

    t = this.tabs[i];

    /* Get the label to use for this tab:
       From the title attribute on the DIV,
       Or from one of the this.titleElements[] elements,
       Or use an automatically generated number.
     */
    t.headingText = t.div.title;

    /* Remove the title attribute to prevent a tooltip from appearing */
    if (this.removeTitle) { t.div.title = ''; }

    if (!t.headingText) {

      /* Title was not defined in the title of the DIV,
	 So try to get the title from an element within the DIV.
	 Go through the list of elements in this.titleElements
	 (typically heading elements ['h2','h3','h4'])
      */
      for (i2=0; i2<this.titleElements.length; i2++) {
	headingElement = t.div.getElementsByTagName(this.titleElements[i2])[0];
	if (headingElement) {
	  t.headingText = headingElement.innerHTML;
	  if (this.titleElementsStripHTML) {
	    t.headingText.replace(/<br>/gi," ");
	    t.headingText = t.headingText.replace(/<[^>]+>/g,"");
	  }
	  break;
	}
      }
    }

    if (!t.headingText) {
      /* Title was not found (or is blank) so automatically generate a
         number for the tab.
      */
      t.headingText = i + 1;
    }

    /* Create a list element for the tab */
    DOM_li = document.createElement("li");

    /* Save a reference to this list item so we can later change it to
       the "active" class */
    t.li = DOM_li;

    /* Create a link to activate the tab */
    DOM_a = document.createElement("a");
    DOM_a.appendChild(document.createTextNode(t.headingText));
    DOM_a.href = "javascript:void(null);";
    DOM_a.title = t.headingText;
    DOM_a.onclick = this.navClick;

    /* Add some properties to the link so we can identify which tab
       was clicked. Later the navClick method will need this.
    */
    DOM_a.tabber = this;
    DOM_a.tabberIndex = i;

    /* Do we need to add an id to DOM_a? */
    if (this.addLinkId && this.linkIdFormat) {

      /* Determine the id name */
      aId = this.linkIdFormat;
      aId = aId.replace(/<tabberid>/gi, this.id);
      aId = aId.replace(/<tabnumberzero>/gi, i);
      aId = aId.replace(/<tabnumberone>/gi, i+1);
      aId = aId.replace(/<tabtitle>/gi, t.headingText.replace(/[^a-zA-Z0-9\-]/gi, ''));

      DOM_a.id = aId;
    }

    /* Add the link to the list element */
    DOM_li.appendChild(DOM_a);

    /* Add the list element to the list */
    DOM_ul.appendChild(DOM_li);
  }

  /* Add the UL list to the beginning of the tabber div */
  e.insertBefore(DOM_ul, e.firstChild);

  /* Make the tabber div "live" so different CSS can be applied */
  e.className = e.className.replace(this.REclassMain, this.classMainLive);

  /* Activate the default tab, and do not call the onclick handler */
  this.tabShow(defaultTab);

  /* If the user specified an onLoad function, call it now. */
  if (typeof this.onLoad == 'function') {
    this.onLoad({tabber:this});
  }

  return this;
};


tabberObj.prototype.navClick = function(event)
{
  /* This method should only be called by the onClick event of an <A>
     element, in which case we will determine which tab was clicked by
     examining a property that we previously attached to the <A>
     element.

     Since this was triggered from an onClick event, the variable
     "this" refers to the <A> element that triggered the onClick
     event (and not to the tabberObj).

     When tabberObj was initialized, we added some extra properties
     to the <A> element, for the purpose of retrieving them now. Get
     the tabberObj object, plus the tab number that was clicked.
  */

  var
  rVal, /* Return value from the user onclick function */
  a, /* element that triggered the onclick event */
  self, /* the tabber object */
  tabberIndex, /* index of the tab that triggered the event */
  onClickArgs; /* args to send the onclick function */

  a = this;
  if (!a.tabber) { return false; }

  self = a.tabber;
  tabberIndex = a.tabberIndex;

  /* Remove focus from the link because it looks ugly.
     I don't know if this is a good idea...
  */
  a.blur();

  /* If the user specified an onClick function, call it now.
     If the function returns false then do not continue.
  */
  if (typeof self.onClick == 'function') {

    onClickArgs = {'tabber':self, 'index':tabberIndex, 'event':event};

    /* IE uses a different way to access the event object */
    if (!event) { onClickArgs.event = window.event; }

    rVal = self.onClick(onClickArgs);
    if (rVal === false) { return false; }
  }

  self.tabShow(tabberIndex);

  return false;
};


tabberObj.prototype.tabHideAll = function()
{
  var i; /* counter */

  /* Hide all tabs and make all navigation links inactive */
  for (i = 0; i < this.tabs.length; i++) {
    this.tabHide(i);
  }
};


tabberObj.prototype.tabHide = function(tabberIndex)
{
  var div;

  if (!this.tabs[tabberIndex]) { return false; }

  /* Hide a single tab and make its navigation link inactive */
  div = this.tabs[tabberIndex].div;

  /* Hide the tab contents by adding classTabHide to the div */
  if (!div.className.match(this.REclassTabHide)) {
    div.className += ' ' + this.classTabHide;
  }
  this.navClearActive(tabberIndex);

  return this;
};


tabberObj.prototype.tabShow = function(tabberIndex)
{
  /* Show the tabberIndex tab and hide all the other tabs */

  var div;

  if (!this.tabs[tabberIndex]) { return false; }

  /* Hide all the tabs first */
  this.tabHideAll();

  /* Get the div that holds this tab */
  div = this.tabs[tabberIndex].div;

  /* Remove classTabHide from the div */
  div.className = div.className.replace(this.REclassTabHide, '');

  /* Mark this tab navigation link as "active" */
  this.navSetActive(tabberIndex);

  /* If the user specified an onTabDisplay function, call it now. */
  if (typeof this.onTabDisplay == 'function') {
    this.onTabDisplay({'tabber':this, 'index':tabberIndex});
  }

  return this;
};

tabberObj.prototype.navSetActive = function(tabberIndex)
{
  /* Note: this method does *not* enforce the rule
     that only one nav item can be active at a time.
  */

  /* Set classNavActive for the navigation list item */
  this.tabs[tabberIndex].li.className = this.classNavActive;

  return this;
};


tabberObj.prototype.navClearActive = function(tabberIndex)
{
  /* Note: this method does *not* enforce the rule
     that one nav should always be active.
  */

  /* Remove classNavActive from the navigation list item */
  this.tabs[tabberIndex].li.className = '';

  return this;
};


/*==================================================*/


function tabberAutomatic(tabberArgs)
{
  /* This function finds all DIV elements in the document where
     class=tabber.classMain, then converts them to use the tabber
     interface.

     tabberArgs = an object to send to "new tabber()"
  */
  var
    tempObj, /* Temporary tabber object */
    divs, /* Array of all divs on the page */
    i; /* Loop index */

  if (!tabberArgs) { tabberArgs = {}; }

  /* Create a tabber object so we can get the value of classMain */
  tempObj = new tabberObj(tabberArgs);

  /* Find all DIV elements in the document that have class=tabber */

  /* First get an array of all DIV elements and loop through them */
  divs = document.getElementsByTagName("div");
  for (i=0; i < divs.length; i++) {
    
    /* Is this DIV the correct class? */
    if (divs[i].className &&
	divs[i].className.match(tempObj.REclassMain)) {
      
      /* Now tabify the DIV */
      tabberArgs.div = divs[i];
      divs[i].tabber = new tabberObj(tabberArgs);
    }
  }
  
  return this;
}


/*==================================================*/


function tabberAutomaticOnLoad(tabberArgs)
{
  /* This function adds tabberAutomatic to the window.onload event,
     so it will run after the document has finished loading.
  */
  var oldOnLoad;

  if (!tabberArgs) { tabberArgs = {}; }

  /* Taken from: http://simon.incutio.com/archive/2004/05/26/addLoadEvent */

  oldOnLoad = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = function() {
      tabberAutomatic(tabberArgs);
    };
  } else {
    window.onload = function() {
      oldOnLoad();
      tabberAutomatic(tabberArgs);
    };
  }
}


/*==================================================*/


/* Run tabberAutomaticOnload() unless the "manualStartup" option was specified */

if (typeof tabberOptions == 'undefined') {

    tabberAutomaticOnLoad();

} else {

  if (!tabberOptions['manualStartup']) {
    tabberAutomaticOnLoad(tabberOptions);
  }

}


CSI.Localisation = {
	'Welcome to' : 'Welcome to',	'Home' : 'Home',	'Categories' : 'Categories',	'Documents' : 'Documents',	'Articles' : 'Articles',	'Permissions' : 'Permissions',	'No expire' : 'No expire',	'Module Manager' : 'Module Manager',	'Module Manager :: Add' : 'Module Manager :: Add',	'Module Manager :: Edit' : 'Module Manager :: Edit',	'Refresh Module Listing' : 'Refresh Module Listing',	'WARNING: This will drop your database' : 'WARNING: This will drop your database and create your Seagull environment from discovered data files. It will only work correctly if your current database user has full privileges, ie to drop and create databases.',	'with sample data' : 'with sample data',	'Maintainer' : 'Maintainer',	'License' : 'License',	'State' : 'State',	'Reload' : 'Reload',	'reload' : 'reload',	'Module Config Manager' : 'Module Config Manager',	'Some errors occured. Please see following message(s)' : 'Some errors occured. Please see following message(s)',	'Config Manager' : 'Config Manager',	'config info successfully updated' : 'Config info successfully updated',	'config info successfully updated but failed syncing sequences' : 'Config info successfully updated but failed syncing sequences',	'Deny list' : 'Deny list',	'Allow list' : 'Allow list',	'Execution Time' : 'Execution Time',	'seconds' : 'seconds',	'ms' : 'ms',	'queries' : 'queries',	'allocated' : 'allocated',	'Powered by' : 'Powered by',	'Seagull PHP Framework' : 'Seagull PHP Framework',	'authorisation failed' : 'You do not have sufficient privileges to view this area.',	'authentication required' : 'authentication required',	'session timeout' : 'Your session has timed out, please login again',	'You have been successfully logged out' : 'You have been successfully logged out',	'password emailed out' : 'A new password has been emailed to the address you registered with',	'email not in system' : 'The credentials you entered could not be recognised, please try again',	'email submitted successfully' : 'Your email has been submitted successfully',	'There was a problem sending the email' : 'There was a problem sending the email',	'message ID not recognised' : 'message ID not recognised',	'Please fill in the indicated fields' : 'Please fill in the indicated fields',	'Your alert has been sent successfully' : 'Your alert has been sent successfully',	'Are you sure you want to delete this' : 'Are you sure you want to delete this',	'module deregister msg' : 'An attempt will be made to drop this module\\\'s tables and data, are you sure you want to proceed?',	'module reload msg' : 'An attempt will be made to reinstall this module, any data entered will be lost, are you sure you want to proceed?',	'module deletion msg' : 'An attempt will be made to delete this module\\\'s files from your filesystem, are you sure you want to proceed?',	'show uninstalled modules' : 'show uninstalled modules',	'Below is a list' : 'Below is a list of modules registered in the \'module\' table.  Some modules may be present in        your [install-dir]/seagull/modules directory, but will not show up in the list unless        you tick the box below.',	'and modify the password to set your preferred one' : 'and modify the password to set your preferred one',	'Module' : 'Module',	'Module list' : 'Module list',	'Active' : 'Active',	'install' : 'install',	'uninstall' : 'uninstall',	'deregister' : 'deregister',	'remove' : 'remove',	'module successfully updated' : 'Module info successfully updated',	'module successfully removed' : 'Module successfully removed',	'The name of the module must be the exact name of the folder containing files, beware of case sensitivity' : 'The name of the module must be the exact name of the folder containing files, beware of case sensitivity',	'Here you can write what you want' : 'Here you can write what you want',	'Simply provide an icon' : 'Simply provide an icon named "module_$moduleName.gif" in "www/themes/default_admin/images/16"',	'Remember me' : 'Remember me',	'Are you sure you want to report that this comment IS spam?' : 'Are you sure you want to report that this comment IS spam?',	'Are you sure you want to report that this comment IS NOT spam?' : 'Are you sure you want to report that this comment IS NOT spam?',	'Are you sure you want to approve the comment?' : 'Are you sure you want to approve the comment?',	'comments must be approved' : 'Note: Comments must be approved before being displayed.',	'E-mail' : 'E-mail',	'Lists' : 'Lists',	'Subscribe' : 'Subscribe',	'Unsubscribe' : 'Unsubscribe',	'Send' : 'Send',	'Editor type' : 'WYSIWYG editor type',	'user' : 'user',	'User' : 'User',	'Username' : 'Username',	'Action' : 'Action',	'Select' : 'Select',	'delete' : 'delete',	'delete selected' : 'delete selected',	'Edit' : 'Edit',	'View' : 'View',	'move up' : 'move up',	'move down' : 'move down',	'finished' : 'finished',	'back to top' : 'back to top',	'currently_logged_on_as' : 'user',	'guest' : 'guest',	'login' : 'login',	'logout' : 'logout',	'Logout' : 'Logout',	'session started at' : 'session started at',	'logged in at' : 'logged in at',	'displaying results' : 'displaying results',	'to' : 'to',	'from a total of' : 'from a total of',	'back' : 'back',	'next' : 'next',	'finish' : 'finish',	'yes' : 'yes',	'no' : 'no',	'Send it' : 'Send it',	'Submit' : 'Submit',	'Cancel' : 'Cancel',	'Reset' : 'Reset',	'reset' : 'reset',	'Save' : 'Save',	'add' : 'add',	'edit' : 'Edit',	'move' : 'move',	'Manage' : 'Manage',	'Title' : 'Title',	'Status' : 'Status',	'ID' : 'ID',	'Name' : 'Name',	'check to activate' : 'check to activate',	'Password' : 'Password',	'Login' : 'Login',	'Forgot Password' : 'Forgot Your Password',	'Not Registered' : 'Not Registered?',	'Email' : 'Email',	'Total Links' : 'Total Links',	'Bug Report' : 'Bug Report',	'First Name' : 'First Name',	'Last Name' : 'Last Name',	'Severity of bug' : 'Severity of bug',	'Comment' : 'Comment',	'Your environment' : 'Your environment',	'You must fill in your description' : 'You must fill in your description',	'You must fill in your comment' : 'You must fill in your comment',	'Your email is not correctly formatted' : 'Your email is not correctly formatted',	'You must enter your email' : 'You must enter your email',	'Enabled' : 'Enabled',	'disable' : 'disable',	'Disabled' : 'Disabled',	'You must select an element to delete' : 'You must select an element to delete',	'no results found' : 'no results found',	'You have been banned' : 'You have been banned from this site. Contact the administrator for more information',	'Invalid POST source' : 'The form appears to have been posted from an unauthorised source',	'You are here' : 'You are here',	'whats this?' : 'what\'s this?',	'denotes required field' : 'denotes required field',	'Return to browse' : 'Return to browse',	'ModuleManager Mgr' : 'ModuleManager Mgr',	'Add' : 'Add',	'Delete' : 'Delete',	'With selected module(s)' : 'With selected module(s)',	'Add a module' : 'Add a module',	'Module successfully registered.' : 'Module successfully  registered',	'Module(s) successfully removed.' : 'Module(s) successfully removed.',	'Configurable' : 'Configurable',	'Description' : 'Description',	'Admin URI' : 'Admin URI',	'Icon' : 'Icon',	'Please, specify a name' : 'Please, specify a name',	'Please, specify a title' : 'Please, specify a title',	'Please, specify a description' : 'Please, specify a description',	'Please, specify the url to link to' : 'Please, specify the url to link to',	'Please, specify the name of the icon-file' : 'Please, specify the name of the icon-file',	'you do not have perms' : 'You do not have the required perms to perform this action',	'you are not allowed to access this data' : 'You do not have the required perms to interact with this data',	'this element has been deleted' : 'This element has been deleted',	'Please use the following form to edit your config file' : 'Please use the following form to edit your config file',	'General Site Options' : 'General Site Options',	'Base URL' : 'Base URL',	'Session max lifetime (secs)' : 'Session max lifetime (secs)',	'Site name' : 'Site name',	'Show logo' : 'Show logo',	'Keywords' : 'Keywords',	'Gzip compression' : 'Gzip compression',	'Output buffering' : 'Output buffering',	'Enable IP banning' : 'Enable IP banning',	'Enable Tidy html cleaning' : 'Enable Tidy html cleaning',	'Session handler' : 'Session handler',	'Extended Session' : 'Extended Session',	'Enforce Single User' : 'Enforce Single User',	'You are allowed to connect from one computer at a time, other sessions were terminated!' : 'You are allowed to connect from one computer at a time, other sessions were terminated!',	'You have multiple sessions on this site!' : 'You have multiple sessions on this site!',	'Enables extended session API when using database sessions. This allows the site to enforce one session per user.' : 'Enables extended session API when using database sessions. This allows the site to enforce one session per user.',	'Enforces one session per user on this site (requires database session handling, and extended session to be on).' : 'Enforces one session per user on this site (requires database session handling, and extended session to be on).',	'Permissions retrieval method' : 'Permissions retrieval method',	'Guests' : 'Guests',	'Members' : 'Members',	'Total' : 'Total',	'Enable Blocks' : 'Enable blocks',	'Default article view type' : 'Default article view type',	'Front controller script name' : 'Front controller script name',	'Default module' : 'Default module',	'Default manager' : 'Default manager',	'Default master template' : 'Default master template',	'This is the master template that will be loaded' : 'This is the master template that will be loaded',	'Navigation Options' : 'Navigation Options',	'Enable Navigation' : 'Enable Navigation',	'Navigation driver' : 'Navigation driver',	'Navigation Html renderer' : 'Navigation Html renderer',	'Navigation menu stylesheet' : 'Navigation menu stylesheet',	'Debugging Options' : 'Debugging Options',	'Enable authorisation' : 'Enable authorisation',	'Enable custom error handler' : 'Enable custom error handler',	'Enable debug session' : 'Enable debug session',	'Enable debug block' : 'Enable debug block - [USE WITH CAUTION!]',	'Your database can be dropped if this block is enabled' : 'Your database can be dropped if this block is enabled, use for development purposes only',	'Production website' : 'Production website',	'Show backtrace' : 'Show backtrace',	'Enable Profiling' : 'Enable Profiling',	'Email admin threshold' : 'Email admin threshold',	'Mark words which were not translated' : 'Mark words which were not translated',	'Caching Options' : 'Caching Options',	'Enable global caching' : 'Enable global caching',	'Enable library caching' : 'Enable library caching',	'Cache lifetime (secs)' : 'Cache lifetime (secs)',	'Cleaning factor' : 'Cleaning factor',	'Cleaning factor tip' : '0 - automatic cache cleaning, 1 - systematic cache cleaning, x (integer) > 1 - automatic cleaning randomly 1 times on x cache write',	'Read control' : 'Read control',	'Read control tip' : 'If enabled, a control key is embeded in cache file and this key is compared with the one calculated after the reading',	'Write control' : 'Write control',	'Write control tip' : 'Enable write control will lightly slow the cache writing but not the cache reading. Write control can detect some corrupt cache files but maybe it\'s not a perfect control',	'Cache JavaScript' : 'Cache JavaScript',	'Cache Css' : 'Cache Css',	'Database Options' : 'Database Options',	'Type' : 'Type',	'server default' : 'server default',	'MySQL Default Storage Engine' : 'MySQL Default Storage Engine',	'Default MySQL storage engine for all tables in database' : 'Default MySQL storage engine for all tables in database',	'Separate table for each sequence' : 'Separate table for each sequence',	'Host' : 'Host',	'Port' : 'Port',	'Protocol' : 'Protocol',	'Socket' : 'Socket',	'DB username' : 'DB username',	'DB password' : 'DB password',	'DB name' : 'DB name',	'Table prefix' : 'Table prefix',	'This is used to prefix all tables in database, change with caution' : 'This is used to prefix all tables in database, change with caution',	'Only letters and digits are allowed, first symbol must be a letter, last symbol can be an underscore' : 'Only letters and digits are allowed, first symbol must be a letter, last symbol can be an underscore',	'Post-connection query' : 'Post-connection query',	'Database Table Mappings' : 'Database Table Mappings',	'Logging options' : 'Logging options',	'Enable logs' : 'Enable logs',	'Log name' : 'Log name',	'Priority' : 'Priority',	'Identifier' : 'Identifier',	'Target username' : 'Target username',	'Target password' : 'Target password',	'Email options' : 'Email options',	'Admin contact' : 'Admin contact',	'Support contact' : 'Support contact',	'Info contact' : 'Info contact',	'Popup window options' : 'Popup window options',	'Default popup window height' : 'Default popup window height',	'Default popup window width' : 'Default popup window width',	'Cookie options' : 'Cookie options',	'Path' : 'Path',	'Domain' : 'Domain',	'Secure' : 'Secure',	'Censorship' : 'Censorship',	'Mode' : 'Mode',	'Replace word with' : 'Replace word with',	'Disallowed word' : 'Disallowed word',	'P3P privacy policy' : 'P3P privacy policy',	'Policies' : 'Policies',	'Policy location' : 'Policy location',	'Compact policy' : 'Compact policy',	'Zero means until the browser is closed' : 'Zero means \'until the browser is closed\'',	'If path to image is specified, image will be shown; if left blank, Site name from above will appear as text' : 'If path to image is specified, image will be shown; if left blank, \'Site name\' from above will appear as text',	'Handy if you dont have access to Apache configuration' : 'Handy if you don\'t have access to Apache configuration',	'This way no content items are really deleting from DB, just marked as deleted' : 'This way no content items are really deleting from DB, just marked as deleted',	'Requires the tidy extension to be installed' : 'Requires the tidy extension to be installed',	'Use the database session handler if youre running a load-balanced environment' : 'Use the database session handler if you\'re running a load-balanced environment',	'You can turn the blocks off globally' : 'You can turn the blocks off globally',	'This options allows you to change the default type of article displayed. Default Article View Type: Html Articles (2)' : 'This options allows you to change the default type of article displayed. Default Article View Type: Html Articles (2)',	'The name of your Seagull index file' : 'The name of your Seagull index file',	'Currently supported editors are xinha, fck and htmlarea, and you must have the relevant libs in your www dir' : 'Currently supported editors are xinha, fck and htmlarea, and you must have the relevant libs in your www dir',	'This is the module that will be loaded if none are specified, ie, when you call index.php' : 'This is the module that will be loaded if none are specified, ie, when you call index.php',	'This is the manager class that will be loaded if none are specified' : 'This is the manager class that will be loaded if none are specified - use the shortname, ie use "faq" and not "FaqMgr"',	'Disable navigation altogether with this switch' : 'Disable navigation altogether with this switch',	'Use this option to choose from various menu types - currently only 1 provided' : 'Use this option to choose from various menu types - currently only 1 provided',	'Defines the appearance of the navigation menu. Preview and make additional changes in the navigation module manager' : 'Defines the appearance of the navigation menu. Preview and make additional changes in the navigation module manager',	'Debugging easier when this is disabled' : 'Debugging easier when this is disabled',	'Customise the way errors are handled' : 'Customise the way errors are handled',	'If your IP appears in the TRUSTED_IPS array, you will be able to view system errors on screen even in production mode (see below)' : 'If your IP appears in the TRUSTED_IPS array, you will be able to view system errors on screen even in production mode (see below)',	'Setting this to true will disable all screen-based error messages' : 'Setting this to true will disable all screen-based error messages',	'Requires to Xdebug extension to be installed' : 'Requires to Xdebug extension to be installed',	'Errors must be >= this level before they are emailed to the site admin' : 'Errors must be >= this level before they are emailed to the site admin',	'It is recommended to disable this while developing' : 'It is recommended to disable this while developing',	'Default is 24 hours' : 'Default is 24 hours',	'Make sure you load the relevant schema' : 'Make sure you load the relevant schema - "mysql_SGL" maintains all sequences in a single table (less clutter) whereas "mysql" uses one table per sequence giving twice as many tables (better for performance)',	'It is recommended to disable logging if you are running < PHP 4.3.x' : 'It is recommended to disable logging if you are running < PHP 4.3.x',	'If sql is used, use log_table as the log table name below' : 'If \'sql\' is used, use \'log_table\' as the log table name below',	'Use an absolute path or one relative to the Seagull root dir' : 'Use an absolute path or one relative to the Seagull root dir',	'Error messages get sent here' : 'Error messages get sent here',	'Contact us enquiries get sent here' : '\'Contact us\' enquiries get sent here',	'This will be your session identifier' : 'This will be your session identifier',	'Disallowed words' : 'Disallowed words',	'Enable Safe deleting' : 'Enable Safe deleting',	'Default params' : 'Default params',	'Use these params to specify, eg, a static article for your homepage' : 'Use these params to specify, eg, a static article for your homepage',	'file' : 'file',	'database' : 'database',	'never' : 'never',	'Show debug reporting link' : 'Show debug reporting link',	'Send feedback to project for bugs' : 'Send feedback to project for bugs',	'Words which system was unable to translate will be enclosed in "> <" marks' : 'Words which system was unable to translate will be enclosed in "> <" marks',	'Output URL handler' : 'Output URL handler',	'Input URL handlers' : 'Input URL handlers',	'Define the URL handlers that will be run on incoming requests' : 'Define the URL handlers that will be run on incoming requests',	'What format would you like your output URLs, Seagull Search Engine Friendly is the default' : 'What format would you like your output URLs, Seagull Search Engine Friendly is the default',	'The classic URL handler has not been implemented yet' : 'The classic URL handler has not been implemented yet',	'Template Engine' : 'Template engine',	'Seagull allows you to use the template engine of your choice' : 'Seagull allows you to use the template engine of your choice',	'The Smarty template hooks have not been implemented yet' : 'The Smarty template hooks have not been implemented yet',	'This query is used to set the default character set for the current connection (MySQL 4.1 or higher). For example: SET NAMES utf8' : 'This query is used to set the default character set for the current connection (MySQL 4.1 or higher). For example: SET NAMES utf8',	'Global Javascript Files' : 'Global javascript files',	'Global Javascript OnReadyDOM' : 'Global javascript OnReadyDOM',	'Global Javascript Onload' : 'Global javascript Onload',	'Global Javascript OnUnload' : 'Global javascript OnUnload',	'globalJavascriptFiles' : 'If you want a Javascript file included on every page of your site, put it here (separate with "," if several files)',	'globalJavascriptOnReadyDom' : 'The Javascript expression you put here will be called as soon as the DOM is ready, this happens before the onload event',	'globalJavascriptOnload' : 'If you want a Javascript onload expression called on every page of your site, put it here',	'globalJavascriptOnUnload' : 'If you want a Javascript onunload expression called on every page of your site, put it here',	'Custom Config File' : 'Custom config file',	'Custom output class' : 'Custom output class',	'Show errors' : 'Show errors',	'Select No if you do not want errors displayed in the logs' : 'Select No if you do not want errors displayed in the logs',	'Temp directory override' : 'Temp directory override',	'true' : 'true',	'false' : 'false',	'MTA options' : 'MTA options',	'Backend' : 'Backend',	'PEAR::Mail backend' : 'PEAR::Mail backend',	'Sendmail path' : 'Sendmail path',	'Mandatory if you use Sendmail as Backend' : 'Mandatory if you use \'Sendmail\' as Backend',	'Sendmail arguments' : 'Sendmail arguments',	'Optional if you use Sendmail as Backend' : 'Optional if you use \'Sendmail\' as Backend',	'SMTP host' : 'SMTP host',	'Optional if you use SMTP as Backend. Default: localhost' : 'Optional if you use \'SMTP\' as Backend. Default: localhost',	'SMTP port' : 'SMTP port',	'Optional if you use SMTP as Backend. Default: 25' : 'Optional if you use \'SMTP\' as Backend. Default: 25',	'Use SMTP authentication' : 'Use SMTP authentication',	'PEAR::Mail supported SMTP authentication methods' : 'PEAR::Mail supported SMTP authentication methods',	'SMTP username' : 'SMTP username',	'SMTP password' : 'SMTP password',	'Mandatory if you use SMTP as Backend and SMTP authentication is enabled' : 'Mandatory if you use \'SMTP\' as Backend and \'SMTP authentication\' is enabled',	'If users have cookies disabled, this will allow them to use sessions with Seagull' : 'If users have cookies disabled, this will allow them to use sessions with Seagull',	'Allow Session in URL' : 'Allow Session in URL',	'Check for Latest Version' : 'Check for Latest Version',	'Check Now' : 'Check Now',	'Your current version is up to date' : 'Your current version is up to date',	'remote interface problem' : 'There was a problem querying the remote interface',	'Maintenance' : 'Maintenance',	'Maintenance Manager' : 'Maintenance Manager',	'Back to Maintenance' : 'Back to Maintenance',	'Site under maintenance' : 'Site under maintenance',	'This site is currently under maintenance' : 'This site is currently under maintenance. We are planning some admin operations. Try to connect later.',	'Congratulations, the target translation appears to be up to date' : 'Congratulations, the target translation appears to be up to date',	'translation successfully updated' : 'translation successfully updated',	'This translation is being editted by somebody else. You can view translation data, but are not be able to save it.' : 'This translation is being editted by somebody else. You can view translation data, but are not be able to save it.',	'There was a problem updating the translation' : 'There was a problem updating the translation',	'Data Objects rebuilt successfully' : 'Data Objects rebuilt successfully',	'Cache files successfully deleted' : 'Cache files successfully deleted',	'Manage Translations' : 'Manage Translations',	'Check all modules for' : 'Check all modules for',	'check all modules' : 'check all modules',	'update' : 'update',	'Module Name' : 'Module Name',	'ok' : 'ok',	'no file' : 'no file',	'new strings' : 'new strings',	'old strings' : 'old strings',	'File not writeable' : 'File not writeable',	'Sequences rebuilt successfully' : 'Sequences rebuilt successfully',	'Rebuild DB Sequences' : 'Rebuild DB Sequences',	'Rebuild Sequences Now' : 'Rebuild Sequences Now',	'validate' : 'validate',	'Process' : 'Process',	'Manage Caches' : 'Manage Caches',	'Templates' : 'Templates',	'functions' : 'Functions',	'javascript' : 'Javascript',	'navigation' : 'navigation',	'blocks' : 'blocks',	'categories' : 'categories',	'permissions' : 'permissions',	'library' : 'library',	'Clear Selected Caches Now' : 'Clear Selected Caches Now',	'Rebuild Data Objects' : 'Rebuild Data Objects',	'Rebuild Dataobjects Now' : 'Rebuild Dataobjects Now',	'Delete cached configs' : 'Delete cached configuration files',	'Cached configs successfully deleted' : 'Cached configuration files successfully deleted',	'You are editing: Module' : 'You\'re editing: Module',	'You are updating: Module' : 'You are updating: Module',	'Master Value' : 'Master Value',	'Translated Value' : 'Translated Value',	'Save Translation' : 'Save Translation',	'Create a module' : 'Create a module',	'Manager Name' : 'Manager Name',	'Create Templates' : 'Create Templates',	'Create ini file' : 'Create ini file',	'Create language files' : 'Create language files',	'Create Module Now' : 'Create Module Now',	'Module files successfully created' : 'Module files successfully created',	'The source translation has' : 'The source translation has',	'elements' : 'elements',	'The target translation has' : 'The target translation has',	'Please add' : 'Please add',	'values for the following keys which appear to be missing from the' : 'values for the following keys which appear to be missing from the',	'module' : 'module',	'please specify an option' : 'please specify an option',	'please check at least one box' : 'please check at least one box',	'please enter module name' : 'please enter module name',	'please enter manager name' : 'please enter manager name',	'Manager already exists - please choose another manager name' : 'Manager already exists - please choose another manager name',	'Extended locale support' : 'Extended locale support',	'locale support info' : 'Enabling this feature gives you access to the extensive I18Nv2 API but at the expense of performance',	'Locale category' : 'Locale category',	'Paths' : 'Paths',	'Install Root' : 'Install root',	'Web Root' : 'Web root',	'With selected record(s)' : 'With selected record(s)',	'config options' : 'Config Options',	'action' : 'Action',	'preferences' : 'preferences',	'Section ID' : 'Section ID',	'Manager' : 'Manager',	'None' : 'None',	'Please supply full nav info' : 'Please supply full nav info',	'Add module' : 'Add module',	'New section' : 'New section',	'manage' : 'Manage',	'BodyHtml' : 'Body',	'Translation options' : 'Translation options',	'Container' : 'Container',	'Fallback Language' : 'Fallback Language',	'Add Missing Translations' : 'Add Missing Translations',	'Language auto discover' : 'Language auto discover',	'Translation from default module (BC)' : 'Use translations from default module bc style',	'General' : 'General',	'Navigation' : 'Navigation',	'Debug' : 'Debug',	'Caching' : 'Caching',	'DB' : 'DB',	'Logs' : 'Logs',	'MTA' : 'MTA',	'Popup' : 'Popup',	'Translation' : 'Translation',	'Cookie' : 'Cookie',	'P3P' : 'P3P',	'Admin GUI Feature' : 'Admin GUI Feature',	'allow backend to display in separate GUI' : 'allow backend to display in separate GUI',	'Configuration' : 'Configuration',	'Sort by' : 'Sort by',	'Publishing' : 'Publishing',	'Admin GUI theme' : 'Admin GUI theme',	'before' : 'before',	'after' : 'after',	'is' : 'is',	'between' : 'between',	'active' : 'active',	'inactive' : 'inactive',	'page' : 'page',	'Session' : 'Session',	'top' : 'top',	'check all' : 'check all',	'uncheck all' : 'uncheck all',	'Add following methods' : 'Add following methods',	'Editing options' : 'Editing options',	'Publish' : 'Publish',	'user profile' : 'user profile',	'PEAR Manager' : 'PEAR Manager',	'Choose channel' : 'Choose channel',	'List installed packages' : 'List installed packages',	'List remote packages' : 'List remote packages',	'Search package' : 'Search package',	'Pear Manager Notice' : 'Calling the full list of PEAR packages the first time can take a while as some 300+ packages get transferred via REST/XML-RPC - so please be patient, allow 30 seconds for a 512 kbps line.',	'Package Name' : 'Package Name',	'Local' : 'Local',	'Latest' : 'Latest',	'Install' : 'Install',	'Uninstall' : 'Uninstall',	'Translation Maintenance' : 'Translation Maintenance',	'Coming Soon - The ability to switch between translation storage containers.' : 'Coming Soon - The ability to switch between translation storage containers.',	'Language to use when the current language does not have a translation.' : 'Language to use when the current language does not have a translation.',	'Add missing translations to the database.' : 'EXPERIMENTAL - Add missing translations to the database.',	'the target lang file' : 'The Target Language file',	'is not writeable.' : 'is not writeable.',	'does not exist.' : 'does not exist.',	'Please change file permissions before editing.' : 'Please change file permissions before editing.',	'Please create it.' : 'Please create it.',	'Default theme' : 'Default theme',	'Additional Include Paths' : 'Additional include paths',	'Custom filter chain' : 'Custom filter chain',	'Create CRUD actions' : 'Create CRUD Actions',	'Broadcast message' : 'Broadcast message',	'Maintenance mode' : 'Maintenance mode',	'Admin key' : 'Admin key',	'tipAdminKey' : 'Enter a private key that will be used to authenticate a priviledged session. To start the privileged session pass the private key value to $_GET[\'adminKey\']',	'Rebuild Seagull' : 'Rebuild Seagull',	'Module Manager :: Discovered' : 'Module Manager :: Discovered',	'Register this module?' : 'Register this module?',	'DataObject debug level' : 'DataObject debug level',	'Please choose a simple, single word' : 'Please choose a simple, single word for your module name, it will be used in URIs.',	'Please give the webserver write permissions to the modules directory' : 'Please give the webserver write permissions to the modules directory',	'The manager, which can be' : 'The manager, which can be one of several per module, is the controller object, so if you want it to deliver pizzas call it PizzaMgr.',	'Module Directory Override' : 'Module directory override',	'Upload Directory Override' : 'Upload directory override',	'Submit login' : 'Submit login',	'send bug report' : 'send bug report',	'select all' : 'select all',	'templates' : 'templates',	'translations' : 'translations',	'Enter Captcha' : 'Please enter the number shown below in the relevant field',	'You must enter the number in this field' : 'You must enter the number in this field',	'prefixes not supported' : 'Currently the module generator only works when there are no prefixes set on the db tables',	'Enable persistent logins' : 'Enable persistent logins',	'Logout target' : 'Logout target',	'Roles that can access the admin GUI' : 'Roles that can access the admin GUI',	'logout target comment' : 'when a user is logged out, they will be redirect here, ie module^manager^action',	'days' : 'days',	'(public obs.) ' : '(public obs.) ',	'(private obs.) ' : '(private obs.) ',	'Your search - %value - did not match any serial' : 'Your search - %value - did not match any serial',	'Link' : 'Link',	'altPrev' : 'Previous',	'altNext' : 'Next',	'altPage' : 'Page',	'prevImg' : '&laquo; previous',	'nextImg' : 'next &raquo;',	'altFirst' : 'altFirst',	'altLast' : 'altLast',	'aMonths' : 'Array',	'Your subscription to' : 'Your subscription to',	'expires in' : 'expires in',	'User id not found' : 'User id not found',	'Can not build session' : 'Can not build session',	'Full name' : 'Full name',	'Enter' : 'Enter',	'Select your user and click Enter' : 'Select your user and click Enter',	'List of non downloaded petitions' : 'List of non downloaded petitions',	'Enter your email and click Send to access petition forms' : 'Enter your email and click Send to access petition forms',	'Enter your email and click Send to receive an email with your non downloaded articles' : 'Enter your email and click Send to receive an email with your non downloaded articles',	'Required parameters are missing' : 'Required parameters are missing',	'Wrong enviroment' : 'Wrong enviroment',	'There is no user with that email address on the database' : 'There is no user with that email address on the database',	'Only ILL17 users allowed' : 'Only ILL17 users allowed',	'The email does not belong to any organisation from the enviroment' : 'The email does not belong to any organisation from the enviroment',	'No direct access allowed' : 'No direct access allowed',	'No email has been sent. You have no articles pending' : 'No email has been sent. You have no articles pending',	'An email has been sent to your inbox folder' : 'An email has been sent to your inbox folder',	'The serials seems to be not identified. We can perform a holding search based on ISSN' : 'The serials seems to be not identified. We can perform a holding search based on ISSN',	'Ill' : 'ILL',	'New' : 'New',	'Served read' : 'Served Read',	'Invalid configuration file' : 'Invalid configuration file',	'Invalid role' : 'Invalid role',	'Your search did not match any serial with holdings' : 'Your search did not match any serial with holdings',	'The serial is on C17 catalog' : 'The serial is on C17 catalog',	'The serial is not on C17 catalog' : 'The serial is not on C17 catalog',	'There are no holdings for this serial mathing your filter criteria' : 'There are no holdings for this serial mathing your filter criteria',	'There are no holdings for this serial' : 'There are no holdings for this serial',	'Your search did not match any organisation' : 'Your search did not match any organisation',	'Authentication failed. Wrong username or password' : 'Authentication failed. Wrong username or password',	'You must authenticate before making the petition' : 'You must authenticate before making the petition',	'Your petition has been created successfully' : 'Your petition has been created successfully',	'C17' : 'C17',	'The following words has been ignored' : 'The following words has been ignored',	'Search' : 'Search',	'Paper ISSN' : 'Paper ISSN',	'Electronic ISSN' : 'Electronic ISSN',	'Publication Place' : 'Publication Place',	'Pubmed' : 'Pubmed',	'Locate Article' : 'Locate Article',	'Querying pubmed' : 'Querying pubmed',	'This may take a while' : 'This may take a while',	'Search results' : 'Search results',	'DOI' : 'DOI',	'Article title' : 'Article title',	'Authors' : 'Authors',	'Date' : 'Date',	'Volume' : 'Volume',	'Issue' : 'Issue',	'Start page' : 'Start page',	'PMID' : 'PMID',	'End page' : 'End page',	'Serial title' : 'Serial title',	'Single Citation Matcher' : 'Single Citation Matcher',	'Year' : 'Year',	'Organisations' : 'Organisations',	'Name, code or city' : 'Name, code or city',	'Title, ISSN' : 'Title, ISSN',	'Title, ISSN or PubMed ID' : 'Title, ISSN or PubMed ID',	'Code' : 'Code',	'City' : 'City',	'My collections' : 'My collections',	'Own collection' : 'Own collection',	'Filters' : 'Filters',	'Applying filters' : 'Applying filters',	'National' : 'National',	'Region' : 'Region',	'Electronic only' : 'Electronic only',	'Filter' : 'Filter',	'Organisation' : 'Organisation',	'Holdings' : 'Holdings',	'Alert' : 'Alert',	'Alert!' : 'Alert!!!',	'Petition' : 'Petition',	'No service' : 'No service',	'Make petition' : 'Make petition',	'You can send the petition to the library of your choice as well' : 'You can send the petition to the library of your choice as well',	'Petition form' : 'Petition form',	'Select the organisation for sending the petition' : 'Select the organisation for sending the petition',	'Retrieving Linkout information' : 'Retrieving Linkout information',	'Retrieving DOI information' : 'Retrieving DOI information',	'Note' : 'Note',	'Type in the user first or last name' : 'Type in the user first or last name',	'Currently assigned to' : 'Currently assigned to',	'Querying C17' : 'Querying C17',	'User reference' : 'User reference',	'Identification' : 'Identification',	'Library included in C17 catalog' : 'Library included in C17 catalog',	'Library not included in C17 catalog' : 'Library not included in C17 catalog',	'Center name' : 'Center name',	'Person to contact with' : 'Person to contact with',	'Telephone number' : 'Telephone number',	'Article data' : 'Article data',	'Starting page' : 'Starting page',	'Ending page' : 'Ending page',	'Empty value inserted' : 'Empty value inserted',	'See organisation info' : 'See organisation info',	'Help' : 'Help',	'Opened holdings' : 'Opened holdings',	'Closed holdings' : 'Closed holdings',	'Starred' : 'Starred',	'You can send the petition to the library you want. Only showing librarys with borrowing services' : 'You can send the petition to the library you want. Only showing librarys with borrowing services',	'The following words has been ignored:' : 'The following words has been ignored:',	' TÃ­tulo, ISSN or Id Medline' : ' TÃ­tulo, ISSN o Id Medline',	'Issues' : 'Issues',	'Retrieving kardex information' : 'Retrieving kardex information',	'Free form' : 'Free form',	'Department' : 'Department',	'Cannot retrieve the ISSN for the PMID' : 'Cannot retrieve the ISSN for the PMID',	'I accept the followings terms and conditions' : 'I accept the followings terms and conditions',	'New search' : 'New search',	'You are not allowed to make PMID searches' : 'You are not allowed to make PMID searches',	'Invalid query terms. Try to change them and repeat your search' : 'Invalid query terms. Try to change them and repeat your search',	'Your search can not be processed. Try to change them query terms and repeat your search' : 'Your search can not be processed. Try to change them query terms and repeat your search',	'Year must be four characters long' : 'Year must be four characters long',	'Must be a number' : 'Must be a number',	'The number must be a year representation' : 'The number must be a year representation',	'Balance' : 'Balance',	'Alternative download' : 'Alternative download',	'The file is available through other petition. Click to download.' : 'The file is available through other petition. Click to download.',	'Holdings lists' : 'Holdings List',	'Fasciculos faltantes' : 'No received fascs',	'K' : 'K',	'There is no notes.' : 'There is no notes.',	'No matches' : 'No matches',	'Organisation info' : 'Organisation info',	'Phone Number' : 'Phone Number',	'Website' : 'Website',	'See more info' : 'See more info',	'Logo' : 'Logo',	'Photo' : 'Photo',	'Address' : 'Address',	'Post code' : 'Post code',	'Email Organisation' : 'Email Organisation',	'Email Notification' : 'Email Notification',	'Serial data' : 'Serial data',	'Abreviated Title' : 'Abreviated Title',	'Variant Titles' : 'Variant Titles',	'Cancelled ISSNs' : 'Cancelled ISSNs',	'Publication place' : 'Publication place',	'Publication years' : 'Publication years',	'Electronic publication only' : 'Electronic publication only',	'Actual frequency' : 'Actual frequency',	'Historical Notes' : 'Historical notes',	'Since' : 'Desde',	'Kardex data will be erased.' : 'Kardex data will be erased.',	'Holdings will be modified.' : 'Holdings will be modified.',	'Holdings line will be modified.' : 'Holdings line will be modified.',	' does not belong to your holdings: modify holdings and send.' : ' does not belong to your holdings: modify holdings and send.',	'Holdings added successfully' : 'Holdings added successfully',	'Seems you want make an edit, not an insert' : 'Seems you want make an edit, not an insert',	'Holdings updated successfully' : 'Holdings updated successfully',	'Seems you want make an insert, not an edit' : 'Seems you want make an insert, not an edit',	'Holdings deleted successfully' : 'Holdings deleted successfully',	'Missing serial ID' : 'Missing serial ID',	'Kardex - Ficha general' : 'Kardex - Ficha general',	'Open' : 'Open',	'Closed' : 'Closed',	'Add Notes' : 'Add Notes',	'View Notes' : 'View Notes',	'Holdings :: Listing Own Collection' : 'Holdings :: Listing Own Collection',	'Choose the listing type' : 'Choose the listing type',	'listing HTML' : 'listing HTML',	'listing CSV' : 'listing CSV',	'List' : 'List',	'serials founded' : 'serials founded',	'Complete collection' : 'Complete collection',	'Error in update. Received issue in year(s) %value' : 'Error in update. Received issue in year(s) %value',	'Holdings :: Search' : 'Holdings :: Search',	'Holdings :: Add' : 'Holdings :: Add',	'Holdings :: Edit' : 'Holdings :: Edit',	'Select the file to upload' : 'Select the file to upload',	'Collection importer' : 'Collection importer',	'Collection name' : 'Collection name',	'The package file is on our systems. Everything seems ok' : 'The package file is on our systems. Everything seems ok',	'By pressing Send button, the importation process will start' : 'By pressing Send button, the importation process will start',	'Importing results' : 'Importing results',	'Download full report with problem locations' : 'Download full report with problem locations',	'Select own collection' : 'Select own collection',	'Add new collection' : 'Add new collection',	'Add collection' : 'Add collection',	'Subscribible collections' : 'Subscribible collections',	'Can not read the file' : 'Can not read the file',	'A ISSN and a holding string must be in the document' : 'A ISSN and a holding string must be in the document',	'WARNING:' : 'WARNING:',	'The field' : 'The field',	' will be ignored' : ' will be ignored',	'There was a problem inserting the collection' : 'There was a problem inserting the collection',	'No holdings will be deleted' : 'No holdings will be deleted',	'There was a problem updating the collection' : 'There was a problem updating the collection',	'Both ISSNs missing' : 'Both ISSNs missing',	'There is no serial in our DB with this ISSNs' : 'There is no serial in our DB with this ISSNs',	'Missing holding string' : 'Missing holding string',	'Embargo must be an integer value' : 'Embargo must be an integer value',	'Errors' : 'Errors',	'You have no collections to update' : 'You have no collections to update',	'Success: The collection has been correctly updated' : 'Success: The collection has been correctly updated',	'Success: The collection has been correctly imported' : 'Success: The collection has been correctly imported',	'Select the action' : 'Select the action',	'Update' : 'Update',	'Replace' : 'Replace',	'Select the collection' : 'Select the collection',	'Working with collection' : 'Working with collection',	'Please select the package file from your computer' : 'Please select the package file from your computer',	'A ISSN or Title and a holding string must be in the document' : 'A ISSN or Title and a holding string must be in the document',	'Wrong CSV headings. Check help for more information' : 'Wrong CSV headings. Check help for more information',	'Download addings report' : 'Download addings report',	'I want to finish the import process, ignoring errors' : 'I want to finish the import process, ignoring errors',	'Import checking results' : 'Import checking results',	'Updating collection' : 'Updating collection',	'Number of insertions' : 'Number of insertions',	'Updating reason' : 'Updating reason',	'Collection successfully updated' : 'Collection successfully updated',	'successfully updated' : 'successfully updated',	'Full report' : 'Full report',	'Download full report' : 'Download full report',	'Please notice that this action will REPLACE all the holdings from one type (paper or electronic) from the package with the file ones' : 'Please notice that this action will REPLACE all the holdings from one type (paper or electronic) from the package with the file ones',	'Wrong CSV headings. In a replacement operation, the column holding type MUST be present (heading "tipo_de_fondo" with column value P or E)' : 'Wrong CSV headings. In a replacement operation, the column holding type MUST be present (heading "tipo_de_fondo" with column value P or E)',	'Paper' : 'Paper',	'Electronic' : 'Electronic',	'Download deletion report' : 'Download deletion report',	'Replacing collection' : 'Replacing collection',	'successfully replaced' : 'successfully replaced',	'Collection successfully replaced' : 'Collection successfully replaced',	'You have no subscribible collections to update' : 'You have no subscribible collections to update',	'Holding string' : 'Holding string',	'was deleted' : 'was deleted',	'Holding string changed from' : 'Holding string changed from',	'was added' : 'was added',	'URL changed from' : 'URL changed from',	'URL' : 'URL',	'Download updates report' : 'Download updates report',	'Holdings :: Printouts :: Own paper holdings' : 'Holdings :: Printouts :: Own paper holdings',	'Holdings :: Printouts :: Own electronic holdings' : 'Holdings :: Printouts :: Own electronic holdings',	'Holdings :: Printouts :: Own paper holdings with open holding line' : 'Holdings :: Printouts :: Own paper holdings with open holding line',	'Holdings :: Printouts :: Own paper holdings with open holding line and serials with ending year' : 'Holdings :: Printouts :: Own paper holdings with open holding line and serials with ending year',	'Holdings :: Printouts :: Holdings Today' : 'Holdings :: Printouts :: Holdings Today',	'Total Holdings' : 'Total Holdings',	'Open Holdings' : 'Open Holdings',	'Own Holdings' : 'Own Holdings',	'Electronic holdings' : 'Electronic holdings',	'Embargo' : 'Embargo',	'Journal Link Provider' : 'Journal Link Provider',	'Not in list' : 'Not in list',	'Paper holdings' : 'Paper holdings',	'Go back' : 'Go back',	'Kardex' : 'Kardex',	'Provider' : 'Provider',	'The following words has been ignored: ' : 'The following words has been ignored: ',	'Only in my holdings: ' : 'Only in my holdings: ',	'Disable this to search any serial' : 'Disable this to search any serial',	'Only open paper holdings: ' : 'Only open paper holdings: ',	'Enable this to search only in your serials with open paper holdings' : 'Enable this to search only in your serials with open paper holdings',	'Add new holdigs' : 'Add new holdigs',	'Template' : 'Template',	'Are you sure you want to delete this holding line?' : 'Are you sure you want to delete this holding line?',	'[ILL17] Unrecognized SOD email' : '[ILL17] Unrecognized SOD email',	'Email ID not defined' : 'Email ID not defined',	'There was a problem getting the Email from the DB. Please, try again later.' : 'There was a problem getting the Email from the DB. Please, try again later.',	'You have no permission to view this email' : 'You have no permission to view this email',	'Requester organisation' : 'Requester organisation',	'Article title is empty' : 'Article title is empty',	'Year is not a numeric value' : 'Year is not a numeric value',	'You must fill the serial title or one issn at least' : 'You must fill the serial title or one issn at least',	'You must select a library' : 'You must select a library',	'This organisation will show up as the requester one. Only showing librarys with borrowing services' : 'This organisation will show up as the requester one. Only showing librarys with borrowing services',	'Generate petition' : 'Generate petition',	'Petition successfully created. It will show up in Pending folder' : 'Petition successfully created. It will show up in Pending folder',	'Petition successfully created. It will show up in Pending folder. The email has been successfully deleted' : 'Petition successfully created. It will show up in Pending folder. The email has been successfully deleted',	'Delete email' : 'Delete email',	'If the checkbox is marked, the email will be deleted after creating the petition' : 'If the checkbox is marked, the email will be deleted after creating the petition',	'The email has been successfully deleted' : 'The email has been successfully deleted',	'There is no emails to select' : 'There is no emails to select',	'You must select one email at least' : 'You must select one email at least',	'Are you sure you want to delete this emails?' : 'Are you sure you want to delete this emails?',	'Can not find the requester organisation' : 'Can not find the requester organisation',	'Can not find the responder organisation' : 'Can not find the responder organisation',	'Can not get the requester organisation from the email address' : 'Can not get the requester organisation from the email address',	'Can not insert the Mail' : 'Can not insert the Mail',	'Can not process the email' : 'Can not process the email',	'No file was added' : 'No file was added',	'Petition ID not defined' : 'Petition ID not defined',	'There was a problem extracting the petition from the DB. Please try again later.' : 'There was a problem extracting the petition from the DB. Please try again later.',	'You have no permission to view the petition.' : 'You have no permission to view the petition.',	'The petition has been successfully served' : 'The petition has been successfully served',	'You have no permission to download the file!' : 'You have no permission to download the file!',	'You have no permissions to delete this petition!' : 'You have no permissions to delete this petition!',	'File modified successfully' : 'File modified successfully',	'File added successfully' : 'File added successfully',	'You have no permission to serve that petition' : 'You have no permission to serve that petition',	'Requester' : 'Requester',	'Responder' : 'Responder',	'Internal notes' : 'Internal notes',	'External notes' : 'External notes',	'Automatic system note' : 'Automatic system note',	'The petition was marked as' : 'The petition was marked as',	'rejected' : 'rejected',	'served' : 'served',	'Resolution code' : 'Resolution code',	'Unknown' : 'Unknown',	'by the user' : 'by the user',	'of organisation' : 'of organisation',	'with date' : 'with date',	'Show news only' : 'Show news only',	'Community' : 'Community',	'Petition deleted successfully' : 'Petition deleted successfully',	'Emails' : 'Emails',	'More operations' : 'More operations',	'The following terms' : 'The following terms',	'were accepted' : 'were accepted',	'Clean' : 'Clean',	'There was no active filters' : 'There was no active filters',	'Reclamation done' : 'Reclamation done',	'Reclamation text' : 'Reclamation text',	'Reclamation done successfully' : 'Reclamation done successfully',	'Note added successfully' : 'Note added successfully',	'Internal message' : 'Internal message',	'External message' : 'External message',	'Article reclamation from' : 'Article reclamation from',	'If you get the file from another external source, you can add it and directly serve the petition. This option must be only used if is not possible to handle the petition on the typical way.' : 'If you get the file from another external source, you can add it and directly serve the petition. This option must be only used if is not possible to handle the petition on the typical way.',	'Add External File' : 'Add External File',	'%value% is not a valid email address' : '%value% is not a valid email address',	'You must select an user' : 'You must select an user',	'Send by email' : 'Send by email',	'Emails sepparated by comma' : 'Emails sepparated by comma',	'Petition sent to emails successfully' : 'Petition sent to emails successfully',	'Petition marked as read successfully' : 'Petition marked as read successfully',	'You can add a file to the petition' : 'You can add a file to the petition',	'If you have the file, you can add to the petition' : 'If you have the file, you can add to the petition',	'This petition was sent by email to the followings addresses' : 'This petition was sent by email to the followings addresses',	'QuickNav' : 'QuickNav',	'QuickInfo' : 'QuickInfo',	'Add one more file' : 'Add one more file',	'You have not selected any file' : 'You have not selected any file',	'Information' : 'Information',	'This petition was sent by email to CINDOC center. Every messages that CINDOC sends to your library will be received in your confirmation email, if configures on your preferences' : 'This petition was sent by email to CINDOC center. Every messages that CINDOC sends to your library will be received in your confirmation email, if configures on your preferences',	'Petition to CINDOC are only allowed for registered users' : 'Petition to CINDOC are only allowed for registered users',	'To ask for an article to a CINDOC center, you must set you username and CINDOC password on your preferences. If you don\'t have it, please contact CINDOC to get one.' : 'To ask for an article to a CINDOC center, you must set you username and CINDOC password on your preferences. If you don\'t have it, please contact CINDOC to get one.',	'Petition served successfully' : 'Petition served successfully',	'ILL17 :: petition list' : 'ILL17 :: petition list',	'ILL17 :: Resend petition' : 'ILL17 :: Resend petition',	'ILL17 :: Reserve petition' : 'ILL17 :: Reserve petition',	'ILL17 :: Serve petition' : 'ILL17 :: Serve petition',	'ILL17 :: Add File' : 'ILL17 :: Add File',	'ILL17 :: Reject petition' : 'ILL17 :: Reject petition',	'ILL17 :: Mark petition' : 'ILL17 :: Mark petition',	'ILL17 :: Modify File' : 'ILL17 :: Modify File',	'ILL17 :: View petition' : 'ILL17 :: View petition',	'Retrieving Linkout information...' : 'Retrieving Linkout information...',	'Are you sure you want to delete this petition' : 'Are you sure you want to delete this petition',	'Petition ID' : 'Petition ID',	'Telf' : 'Telf',	'Serial' : 'Serial',	'Started on' : 'Started on',	'Resolved on' : 'Resolved on',	'Pags' : 'Pags',	'Dou you want to print the attached messages' : 'Dou you want to print the attached messages',	'If you want to print the messages, press Accept. Otherwise, press Cancel' : 'If you want to print the messages, press Accept. Otherwise, press Cancel',	'You have not access to this section' : 'You have not access to this section',	'Petition resend to user successfully' : 'Petition resend to user successfully',	'It looks like someone has already serve the petition while you had it open' : 'It looks like someone has already serve the petition while you had it open',	'It looks like someone has already resolve the petition while you had it open' : 'It looks like someone has already resolve the petition while you had it open',	'Your ILL email' : 'Your ILL email',	'Received' : 'Received',	'All' : 'All',	'Fascicle' : 'Fascicle',	'ILL printouts' : 'ILL printouts',	'Resolved by own library' : 'Resolved by own library',	'Linkout/DOI resolved' : 'Linkout/DOI resolved',	'Total user petitions' : 'Total user petitions',	'Sent to other lybraries' : 'Sent to other lybraries',	'Date picker' : 'Date picker',	'Number of petitions' : 'Number of petitions',	'Petitions sent to other librarys' : 'Petitions sent to other librarys',	'Petitions made from other librarys to ours' : 'Petitions made from other librarys to ours',	'Statistic type' : 'Statistic type',	'ILL printouts :: Petition list' : 'ILL printouts :: Petition list',	'ILL printouts :: User statistics' : 'ILL printouts :: User statistics',	'Search user' : 'Search user',	'ILL printouts :: Serial statistics' : 'ILL printouts :: Serial statistics',	'ILL printouts :: Internal petitions statistics' : 'ILL printouts :: Internal petitions statistics',	'ILL printouts :: External petitions statistics' : 'ILL printouts :: External petitions statistics',	'By library' : 'By library',	'By rejection mode' : 'By rejection mode',	'By service mode' : 'By service mode',	'Library' : 'Library',	'Other states' : 'Other states',	'Sent by' : 'Sent by',	'Rejected  by' : 'Rejected by',	'Petition number' : 'Petition number',	'Average time (in days)' : 'Average time (in days)',	'Totals' : 'Totals',	'Rejected by' : 'Rejected by',	'ILL17 :: Printouts' : 'ILL17 :: Printouts',	'ILL printouts :: Summary Table' : 'ILL printouts :: Summary Table',	'Ill17 Petitions' : 'Ill17 Petitions',	'Total petition count' : 'Total petition count',	'Subscription stored successfully. You will receive an email with the statistics tomorrow' : 'Subscription stored successfully. You will receive an email with the statistics tomorrow',	'Request All users statistics' : 'Request All users statistics',	'Export to CSV' : 'Export to CSV',	'Export Summary table' : 'Export Summary table',	'You can not request two user reports' : 'You can not request two user reports',	'Your statistic request is stored and will be processed tomorrow' : 'Your statistic request is stored and will be processed tomorrow',	'Cancel request' : 'Cancel request',	'UserRef' : 'User Reference',	'Biblio Reference' : 'Reference',	'Request deleted successfully.' : 'Request deleted successfully.',	'You have no ILL17 users in your system' : 'You have no ILL17 users in your system',	'Totals from' : 'Total from',	'Graphics' : 'Graphics',	'Anio' : 'AÃ±o',	'From date' : 'From date',	'To date' : 'To date',	'There is no petition with that ID' : 'There is no petition with that ID',	'Locate by ID' : 'Locate by ID',	'Locate by multiple search criteria' : 'Locate by multiple search criteria',	'Ill :: Petition search' : 'Ill :: Petition search',	'Others' : 'Others',	'Fax' : 'Fax',	'Mail' : 'Mail',	'On hand' : 'On hand',	'Serial is not on our catalog' : 'Serial is not on our catalog',	'Pending number' : 'Pending number',	'No holdings for the indicated year' : 'No holdings for the indicated year',	'Unreceived number' : 'Unreceived number',	'No access to electronic publication' : 'No access to electronic publication',	'On embargo yet' : 'On embargo yet',	'On binding' : 'On binding',	'Wrong citation' : 'Wrong citation',	'On requester demand' : 'On requester demand',	'Your search did not match any article on your repository' : 'Your search did not match any article on your repository',	'Error: You have not selected a file. Operation cancelled' : 'Error: You have not selected a file. Operation cancelled',	'Success: The article has been added to your repository' : 'Success: The article has been added to your repository',	'Success: The file has been updated.' : 'Success: The file has been updated.',	'There was a problem searching in Pubmed database. Please, try again later' : 'There was a problem searching in Pubmed database. Please, try again later',	'Type in a PMID number and click Search button to start adding an article to your repository' : 'Type in a PMID number and click Search button to start adding an article to your repository',	'This action will locate articles in your repository matching indicated PMID. To add a new PMID to your repository, click Add new article button' : 'This action will locate articles in your repository matching indicated PMID. To add a new PMID to your repository, click Add new article button',	'Add new article' : 'Add new article',	'Ill - Repository :: Search' : 'Ill - Repository :: Search',	'Ill - Repository :: Edit article' : 'Ill - Repository :: Edit article',	'Ill - Repository :: Add new article' : 'Ill - Repository :: Add new article',	'No results' : 'No results',	'There was a problem searching in Pubmed database' : 'There was a problem searching in Pubmed database',	'The article is already on your repository. Operation cancelled' : 'The article is already on your repository. Operation cancelled',	'There was a problem searching in pubmed. Please, try again later.' : 'There was a problem searching in pubmed. Please, try again later.',	'The search didn\'t return any matches' : 'The search didn\'t return any matches',	'There was an error getting the linkout. PLease try again later.' : 'There was an error getting the linkout. PLease try again later.',	'Linkout' : 'Linkout',	'There is no Linkout links for this PMID' : 'There is no Linkout links for this PMID',	'There is no DOI link for this DOI number' : 'There is no DOI link for this DOI number',	'Pending' : 'Pending',	'In process' : 'In process',	'Served' : 'Served',	'Rejected' : 'Rejected',	'Error: You must fill price and number of pages.' : 'Error: You must fill price and number of pages.',	'Notification billed successfully' : 'Notification billed successfully',	'You have no permissions to bill this notification' : 'You have no permissions to bill this notification',	'Can\'t create the temporary file' : 'Can\'t create the temporary file',	'Petition blocked successfully' : 'Petition blocked successfully',	'Error: Can not block the petition' : 'Error: Can not block the petition',	'Error: petition is not blockable' : 'Error: petition is not blockable',	'Petition unblocked successfully' : 'Petition unblocked successfully',	'Error: Can not unblock the petition' : 'Error: Can not unblock the petition',	'Error: petition is not unblockable' : 'Error: petition is not unblockable',	'Your request has been saved. Thanks.' : 'Your request has been saved. Thanks.',	'There was a problem saving your request. Please, try again later' : 'There was a problem saving your request. Please, try again later',	'Serial Title' : 'Serial Title',	'Actions' : 'Actions',	'Internal' : 'Internal',	'Externa' : 'Externa',	'Sent' : 'Sent',	'External' : 'External',	'Only showing general actions' : 'Only showing general actions',	'For more specific ones, like sending messages or block the petition,' : 'For more specific ones, like sending messages or block the petition,',	'you should open the petition' : 'you should open the petition',	'Quick menu' : 'Quick menu',	'Class' : 'Class',	'Creation Date' : 'Creation Date',	'Requester user' : 'Requester user',	'Article' : 'Article',	'Send to' : 'Send to',	'Messages' : 'Messages',	'Go' : 'Go',	'DOI Link' : 'DOI Link',	'Resend to User' : 'Resend to User',	'Type in first or last name' : 'Type in first or last name',	'The email will show up here...' : 'The email will show up here...',	'Loading' : 'Loading',	'Unblock petition' : 'Unblock petition',	'Block petition' : 'Block petition',	'The petition has been blocked by your librarian. Contact him for further details' : 'The petition has been blocked by your librarian. Contact him for further details',	'Author' : 'Author',	'Read' : 'Read',	'Message' : 'Message',	'No' : 'No',	'Yes' : 'Yes',	'Billing' : 'Billing',	'Price' : 'Price',	'total' : 'total',	'Number of pages' : 'Number of pages',	'Historical' : 'Historical',	'Incoming from user' : 'Incoming from user',	'Sent to organisation' : 'Sent to organisation',	'sent to organisation' : 'sent to organisation',	'Incoming from organisation' : 'Incoming from organisation',	'Print' : 'Print',	'Make reclamation' : 'Make reclamation',	'Add message' : 'Add message',	'Serve' : 'Serve',	'Reserve' : 'Reserve',	'Reject' : 'Reject',	'Mark as served' : 'Mark as served',	'Mark as rejected' : 'Mark as rejected',	'Mark as read' : 'Mark as read',	'to historic' : 'to historic',	'Resend to user' : 'Resend to user',	'Bill' : 'Bill',	'Block' : 'Block',	'Download file' : 'Download file',	'Add file' : 'Add file',	'Modify file' : 'Modify file',	'Edit article' : 'Edit article',	'Resend to library' : 'Resend to library',	'The petition has been successfully rejected' : 'The petition has been successfully rejected',	'Your petition has been successfully sent' : 'Your petition has been successfully sent',	'Petition marked successfully' : 'Petition marked successfully',	'ILL17' : 'ILL17',	'Direct Access' : 'Direct Access',	'Is this the article you were looking for?' : 'Is this the article you were looking for?',	'Saving' : 'Saving',	'Select the file to add' : 'Select the file to add',	'Back to list' : 'Back to list',	'Comunity' : 'Comunity',	'Delete All' : 'Delete All',	'Querying C17...' : 'Querying C17...',	'Email view' : 'Email view',	'Email info' : 'Email info',	'From' : 'From',	'Subject' : 'Subject',	'Email data' : 'Email data',	'File information' : 'File information',	'File name' : 'File name',	'Size' : 'Size',	'Download' : 'Download',	'File exists?' : 'File exists?',	'The file exists' : 'The file exists',	'The file does not exists' : 'The file does not exists',	'Querying Ill17 for pending petitions...Please wait.' : 'Querying Ill17 for pending petitions...Please wait.',	'Querying Ill17 for petitions in process...Please wait.' : 'Querying Ill17 for petitions in process...Please wait.',	'Querying Ill17 for served petitions...Please wait.' : 'Querying Ill17 for served petitions...Please wait.',	'Querying Ill17 for rejected petitions...Please wait.' : 'Querying Ill17 for rejected petitions...Please wait.',	'Querying Ill17 for historical petitions...Please wait.' : 'Querying Ill17 for historical petitions...Please wait.',	'Mails' : 'Mails',	'Querying Ill17 for email petitions...Please wait.' : 'Querying Ill17 for email petitions...Please wait.',	'Mark petition as' : 'Mark petition as',	'Please, select one' : 'Please, select one',	'Modify File' : 'Modify File',	'Select the file to replace the original' : 'Select the file to replace the original',	'User data' : 'User data',	'Organisation data' : 'Organisation data',	'Operations' : 'Operations',	'Rejection form' : 'Rejection form',	'Locate File' : 'Locate File',	'Querying pubmed...' : 'Querying pubmed...',	'Search Results' : 'Search Results',	'Article Title' : 'Article Title',	'StartPage' : 'StartPage',	'End Page' : 'End Page',	'Select the file to ADD' : 'Select the file to ADD',	'The file does not exists. Can not download.' : 'The file does not exists. Can not download.',	'Edit file' : 'Edit file',	'Locate by PMID' : 'Locate by PMID',	'Serial title or ISSN' : 'Serial title or ISSN',	'Serial title or ISSN only search in serials into the C17 catalog' : 'Serial title or ISSN only search in serials into the C17 catalog',	'Article ID' : 'Article ID',	'Resend form' : 'Resend form',	'Service form' : 'Service form',	'Querying Ill17 for pendings petitions' : 'Querying Ill17 for pendings petitions',	'Querying Ill17 for served petitions' : 'Querying Ill17 for served petitions',	'Querying Ill17 for rejected petitions' : 'Querying Ill17 for rejected petitions',	'Short version' : 'Short version',	'Folder' : 'Folder',	'Petition type' : 'Petition type',	'Created in' : 'Created in',	'and solved in' : 'and solved in',	'Repository' : 'Repository',	'Repository access' : 'Repository access',	'The file is available trough your ILL repository' : 'The file is available trough your ILL repository',	'Retrieving DOI information...' : 'Retrieving DOI information...',	'Requester data' : 'Requester data',	'Owned' : 'Owned',	'Searching in your holdings...' : 'Searching in your holdings...',	'No holdings' : 'No holdings',	'Querying C17 for holdings...' : 'Querying C17 for holdings...',	'Organisation list' : 'Organisation list',	'You can send the petition to the library you want' : 'You can send the petition to the library you want',	'Serve File' : 'Serve File',	'Succesfull: Template created and assigned!!!' : 'Succesfull: Template created and assigned!!!',	'Succesfull: Template updated!!!' : 'Succesfull: Template updated!!!',	'Succesfull: Template unassigned!!!' : 'Succesfull: Template unassigned!!!',	'Template updated successfully' : 'Template updated successfully',	'back ficha general' : 'back ficha general',	'new search' : 'new search',	'Kardex - Ficha Anual' : 'Kardex - Ficha Anual',	'Kardex - Edition Template' : 'Kardex - Edition Template',	'Suppliers' : 'Suppliers',	'Kardex create fasc template' : 'Kardex create fasc template',	'Kardex create text template' : 'Kardex create text template',	'Vol field must be completed' : 'Vol field must be completed',	'Fasc field must be completed' : 'Fasc field must be completed',	'Test field must be completed' : 'Test field must be completed',	'field must be completed' : 'field must be completed',	'first_fasc field or renumbering field must be completed' : 'first_fasc field or renumbering field must be completed',	'Templates approvation' : 'Templates approvation',	'Days left' : 'Days left',	'Invalid ISSN' : 'Invalid ISSN',	'Inexistent year in fondos' : 'Inexistent year in fondos',	'asigned template' : 'asigned template',	'current template' : 'current template',	'You must fill every publication date' : 'You must fill every publication date',	'fecha_publicacion field must be completed' : 'fecha_publicacion field must be completed',	'Be careful with this template' : 'Be careful with this template',	'Collection' : 'Collection',	'Holding type' : 'Holding type',	'Holding' : 'Holding',	'Not to claim' : 'Not to claim',	'Kardex printouts' : 'Kardex :: Printouts',	'Kardex :: Printouts :: Open paper holdings without record for current year' : 'Kardex :: Printouts :: Open paper holdings without record for current year',	'Kardex :: Printouts :: Fascicles marked as not to claim anymore' : 'Kardex :: Printouts :: Fascicles marked as not to claim anymore',	'Kardex :: Printouts :: Fascicles not received yet' : 'Kardex :: Printouts :: Fascicles not received yet',	' doesn\'t belong to your holdings: modify holdings and send.' : ' doesn\'t belong to your holdings: modify holdings and send.',	'Template approved successfully' : 'Template approved successfully',	'Template deleted successfully' : 'Template deleted successfully',	'Template saved successfully' : 'Template saved successfully',	'Claim' : 'Claim',	'Claims' : 'Claims',	'Claims generated successfully' : 'Claims generated successfully',	'Claim sent successfully' : 'Claim sent successfully',	'Please print and send this claim to the supplier. We have already marked it as sent' : 'Please print and send this claim to the supplier. We have already marked it as sent',	'Claim deleted successfully' : 'Claim deleted successfully',	'Days to add to the current date for setting the estimated reception date for the claimed issues' : 'Days to add to the current date for setting the estimated reception date for the claimed issues',	'Claim data' : 'Claim data',	'Pleas, click the print button' : 'Please, click the print button',	'No issues to claim' : 'No issues to claim',	'not exist issues to claim' : 'not exist issues to claim',	'Unknown provider' : 'Unknown provider',	'Can no edit a template for assigning provider without year AND serial' : 'Can no edit a template for assigning provider without year AND serial',	'Supplier saved successfully' : 'Supplier saved successfully',	'Supplier deleted successfully' : 'Supplier deleted successfully',	'Your search did not match any serial with paper holdings from your current organisation' : 'Your search did not match any serial with paper holdings from your current organisation',	'Success: Providers updated successfully' : 'Success: Providers updated successfully',	'You have no kardex records for the selected date range' : 'You have no kardex records for the selected date range',	'You are trying to assign providers to records not present in anual data' : 'You are trying to assign providers to records not present in anual data',	'kardex - Add Suministradores' : 'kardex - Add Suministradores',	'Kardex - Assign Supplier' : 'Kardex - Assign Supplier',	'Kardex create template' : 'Kardex create template',	'Assign this supply type to all records selected' : 'Assign this supply type to all records selected',	'Assign provider between two dates' : 'Assign provider between two dates',	'Starting date' : 'Starting date',	'Ending date' : 'Ending date',	'Can not be empty' : 'Can not be empty',	'You must select a provider' : 'You must select a provider',	'Only showing years with any record created' : 'Only showing years with any record created',	'Supply type' : 'Supply type',	'Kardex - Assign Supplier By Default' : 'Kardex - Assign Supplier By Default',	'Assign provider by default' : 'Assign provider by default',	'Interval' : 'Interval',	'Assigned provider by default' : 'Assigned provider by default',	'Template for' : 'Template for',	'Choose the template type' : 'Choose the template type',	'Fascicle template' : 'Fascicle template',	'Text template' : 'Text template',	'Create selected template' : 'Create selected template',	'start numeration' : 'start numeration',	'first vol' : 'first vol',	'first fasc' : 'first fasc',	'regular recurrence' : 'regular recurrence',	'language' : 'language',	'number vols' : 'number vols',	'renumbering' : 'renumbering',	'Vol' : 'Vol',	'Fs' : 'Fs',	'Cover date' : 'Cover date',	'Publication date' : 'Publication date',	'Expected date' : 'Expected date',	'Receipt date' : 'Receipt date',	'Start' : 'Start',	'End' : 'End',	'Num Recl' : 'Num Recl',	'Do not claim' : 'Do not claim',	'Notes' : 'Notes',	'Receipt all' : 'Receipt all',	'Are you sure to receipt all' : 'Are you sure to receipt all',	'Are you sure to unassign template' : 'Are you sure to unassign template',	'Do not claim anyone' : 'Do not claim anyone',	'Not to assign' : 'Not to assign',	'Previous year' : 'Previous year',	'Next year' : 'Next year',	'Edit template' : 'Edit template',	'Text' : 'Text',	'Publishing Date' : 'Publishing Date',	'Add issue' : 'Add issue',	'Delete issue' : 'Delete issue',	'Add_issue' : 'Add issue',	'Del_issue' : 'Delete issue',	'Approve' : 'Approve',	'Organisations using this template' : 'Organisations using this template',	'Approved' : 'Approved',	'Generic template' : 'Generic template',	'Day' : 'Day',	'Month' : 'Month',	'Estimated reception date' : 'Estimated reception date',	'Claim selected issues' : 'Claim selected issues',	'Issues to claim' : 'Issues to claim',	'Issues without supplier' : 'Issues without supplier',	'Not implemented' : 'Not implemented',	'Not sent' : 'Not sent',	'Supplier' : 'Supplier',	'Generation date' : 'Generation date',	'Isues count' : 'Isues count',	'Send date' : 'Send date',	'Generate new claims' : 'Generate new claims',	'You must enter a numeric value' : 'You must enter a numeric value',	'How many days from today want you to add to estimated recpetion day for claimed issues?' : 'How many days from today want you to add to estimated reception day for claimed issues?',	'Reception date' : 'Reception date',	'back claims' : 'back claims',	'Helpers' : 'Helpers',	'Assign this provider to all records selected' : 'Assign this provider to all records selected',	'Select All' : 'Select All',	'Assign' : 'Assign',	'CIF' : 'CIF',	'Postal code' : 'Postal code',	'Telephone' : 'Telephone',	'Contact person' : 'Contact person',	'Claim type' : 'Claim type',	'Supplier data' : 'Supplier data',	'Code or Name' : 'Code or Name',	'Assign to serials' : 'Assign to serials',	'Search in my paper holdings' : 'Search in my paper holdings',	'You have no record created for this year. If you want to create a new one, click' : 'You have no record created for this year. If you want to create a new one, click',	'here' : 'here',	'Edit provider for' : 'Edit provider for',	'Edit provider for several years' : 'Edit provider for several years',	'**** EXAMPLE TEXT TEMPLATE ****' : '**** EXAMPLE TEXT TEMPLATE ****',	'Need fascs 5 y 6 ' : 'Need fascs 5 y 6 ',	'Kardex card for' : 'Kardex card for',	'delete template' : 'delete template',	'Extra data' : 'Extra data',	'Rebiun code' : 'Rebiun code',	'Departament' : 'Departament',	'Advice' : 'Advice',	'Alert shown until' : 'Alert shown until',	'Acceso' : 'Acceso',	'Precio' : 'Precio',	'Email address' : 'Email address',	'Cindoc' : 'Cindoc',	'Cindoc data' : 'Cindoc data',	'Ill17 email address' : 'Ill17 email address',	'SOD' : 'SOD',	'SOD email address' : 'SOD email address',	'Organisation updated successfully' : 'Organisation updated successfully',	'Success: Librarian in charge updated.' : 'Success: Librarian in charge updated.',	'Success: Librarian in charge registered.' : 'Success: Librarian in charge registered.',	'Success: Librarian in charge deleted.' : 'Success: Librarian in charge deleted.',	'Preferences updated successfully' : 'Preferences updated successfully',	'Services' : 'Servicios',	'There was a problem changing the notification service' : 'There was a problem changing the notification service',	'Notification service changed successfully' : 'Notification service changed successfully',	'Master organisation' : 'Master organisation',	'Your organisation does not belong to any organisation' : 'Your organisation does not belong to any organisation',	'C17+' : 'C17+',	'K17' : 'K17',	'P17+' : 'P17+',	'P17' : 'P17',	'Details' : 'Details',	'Location' : 'Location',	'Address 1' : 'Address 1',	'ZIP/Postal Code' : 'ZIP/Postal Code',	'Country' : 'Country',	'Contact' : 'Contact',	'Organisation Responsables' : 'Organisation Responsables',	'Service' : 'Service',	'Make peticion' : 'Make peticion',	'Notification email' : 'Notification email',	'Logo image' : 'Logo image',	'Organisation logo' : 'Organisation logo',	'Photo image' : 'Photo image',	'Organisation foto' : 'Organisation foto',	'CINDOC' : 'CINDOC',	'CINDOC account' : 'CINDOC account',	'CINDOC password' : 'CINDOC password',	'C17 PLUS' : 'C17 PLUS',	'Borrowing services' : 'Borrowing services',	'Library code/name' : 'Library code/name',	'Library order' : 'Library order',	'Up' : 'Up',	'Down' : 'Down',	'Remove' : 'Remove',	'Show serial and supplements issues merged' : 'Show serial and supplements issues merged',	'Days to claim issues after publication' : 'Days to claim issues after publication',	'Receive new article notification' : 'Receive new article notification',	'Text to appear on your claims' : 'Text to appear on your claims',	'Type responsable' : 'Type responsable',	'Is User' : 'Is User',	'Are you sure want to delete this person in charge?' : 'Are you sure want to delete this person in charge?',	'Librarian in charge' : 'Librarian in charge',	'Select type' : 'Select type',	'Is the user registered in the system?' : 'Is the user registered in the system?',	'User registered' : 'User registered',	'Current user is: ' : 'Current user is: ',	'User not registered' : 'User not registered',	'First surname' : 'First surname',	'Add responsable' : 'Add responsable',	'Currently assigned service' : 'Currently assigned service',	'No service assigned' : 'No service assigned',	'To change your current sevice, please select from the box bellow and click' : 'To change your current sevice, please select from the box bellow and click',	'Next' : 'Next',	'Services availables' : 'Services availables',	'Can not save the holding. URL and Provider ID can not be filled at the same time.' : 'Can not save the holding. URL and Provider ID can not be filled at the same time.',	'Can not save the holding. Please, fill the missing data.' : 'Can not save the holding. Please, fill the missing data.',	'The collection has no holdings. You can add serials performing a search and clicking Add button on the desired serial.' : 'The collection has no holdings. You can add serials performing a search and clicking Add button on the desired serial.',	'Error: You are master organisation of no consortium' : 'Error: You are master organisation of no consortium',	'Success: Collection updated successfully' : 'Success: Collection updated successfully',	'Your search did not match any serial' : 'Your search did not match any serial',	'The holding has been added succesfully' : 'The holding has been added succesfully',	'Holding updated succesfully' : 'Holding updated succesfully',	'Missing package name' : 'Missing package name',	'Packages' : 'Packages',	'package - editHolding' : 'package - editHolding',	'Editing collection:' : 'Editing collection:',	'Package - addHoldings' : 'Package - add Holdings',	'This package name already exists' : 'This package name already exists',	'P17 :: Package view' : 'P17 :: Package view',	'P17 :: Package edit' : 'P17 :: Package edit',	'P17 :: Package management' : 'P17 :: Package management',	'Edit visibility' : 'Edit visibility',	'Holding not found!' : 'Holding not found!',	'Error: No serial ID received' : 'Error: No serial ID received',	'There is no library with holdings for that serial' : 'There is no library with holdings for that serial',	'There is no holdings for this serial and organisation' : 'There is no holdings for this serial and organisation',	'You have no permission to modify this collection.' : 'You have no permission to modify this collection.',	'The holding is not present on the collection.' : 'The holding is not present on the collection.',	'Multiple holdings for one serial.' : 'Multiple holdings for one serial.',	'There was a problem deleting the holding. Operation aborted.' : 'There was a problem deleting the holding. Operation aborted.',	'Holding deleted successfully' : 'Holding deleted successfully',	'months' : 'months',	'Electronic Holdings' : 'Electronic Holdings',	'Paper Holdings' : 'Paper Holdings',	'Your subscription request has queued successfully. The suscription will be complete on 24h' : 'Your subscription request has queued successfully. The suscription will be complete on 24h',	'Package suscription' : 'Package suscription',	'Total holdings' : 'Total holdings',	'Request' : 'Request',	'P17 :: Package suscription' : 'P17 :: Package suscription',	'Total organisations' : 'Total orgs.',	'Editing visibility of collection' : 'Editing visibility of collection',	'for consortium' : 'for consortium',	'Visible' : 'Visible',	'Subscribed' : 'Subscribed',	'Mark All as visible' : 'Mark All as visible',	'Mark All as subscribed' : 'Mark All as subscribed',	'Search serial' : 'Search serial',	'Title or ISSN' : 'Title or ISSN',	'Only in the current package: ' : 'Only in the current package: ',	'Serials in collection' : 'Serials in collection',	'Inherited' : 'Inherited',	'Adding holding to collection: ' : 'Adding holding to collection: ',	'Editing collection: ' : 'Editing collection: ',	'Resource Holdings' : 'Resource Holdings',	'Holding data: ' : 'Holding data: ',	'Number of insertions will be made' : 'Number of insertions will be made',	'Number of errors detected' : 'Number of errors detected',	'If you want to check the errors detected, you can download the following CSV file for further details' : 'If you want to check the errors detected, you can download the following CSV file for further details',	'Select the collection from the list and the package file from your computer' : 'Select the collection from the list and the package file from your computer',	'Select the file' : 'Select the file',	'Please notice that this action will REPLACE all the holdings from the package with the file ones' : 'Please notice that this action will REPLACE all the holdings from the package with the file ones',	'By pressing Send button, the replace process will start' : 'By pressing Send button, the replace process will start',	'CAUTION:' : 'CAUTION:',	'Notice that this process will replace all the holdings from the collection with the file ones' : 'Notice that this process will replace all the holdings from the collection with the file ones',	'Number of holdings deleted' : 'Number of holdings deleted',	'Please notice that this action will only add new holdings to the collection and update the holdings line already present on the package' : 'Please notice that this action will only add new holdings to the collection and update the holdings line already present on the package',	'By pressing Send button, the updating process will start' : 'By pressing Send button, the updating process will start',	'Number of holdings detected but not changed' : 'Number of holdings detected but not changed',	'Number of updates will be made' : 'Number of updates will be made',	'Add new package' : 'Add new package',	'Packages list' : 'Packages list',	'Edit Visibility' : 'Edit Visibility',	'You must fill this field' : 'you must fill this field',	'Choose the consortium you wat to edit the visibility of' : 'Choose the consortium you wat to edit the visibility of',	'collection' : 'collection',	'Select the consortium' : 'Select the consortium',	'Consortium' : 'Consortium',	'Collections visible to your organisation' : 'Collections visible to your organisation',	'You have no collections to subscribe' : 'You have no collections to subscribe',	'Serial approved successfully' : 'Serial approved successfully',	'There is no serials to aprove' : 'There is no serials to aprove',	'Url' : 'Url',	'Publicacion place saved successfully' : 'Publicacion place saved successfully',	'There is no serial with historical notes pending' : 'There is no serial with historical notes pending',	'Historical notes saved successfully' : 'Historical notes saved successfully',	'Historical notes deleted successfully' : 'Historical notes deleted successfully',	'Relationship type' : 'Relationship type',	'Serial added successfully' : 'Serial added successfully',	'Serial updated successfully' : 'Serial updated successfully',	'Serials' : 'Serials',	'Value is empty, but a non-empty value is required' : 'Value is empty, but a non-empty value is required',	'Add new variant title' : 'Add new variant title',	'Add new variant cancelled ISSN' : 'Add new variant cancelled ISSN',	'Serials approvation' : 'Serials approvation',	'Serials :: Search' : 'Serials :: Search',	'Serials :: View' : 'Serials :: View',	'Serials :: Add' : 'Serials :: Add',	'Serials :: Edit' : 'Serials :: Edit',	'Serial added successfully. Now you can edit the holdings for it' : 'Serial added successfully. Now you can edit the holdings for it',	'Invalid anio_fin' : 'Invalid anio_fin',	'Invalid anio_electronico' : 'Invalid anio_electronico',	'Titles' : 'Titles',	'ISSNs' : 'ISSNs',	'Publication info' : 'Publication info',	'No publicaction place was defined' : 'No publicaction place was defined',	'First year' : 'First year',	'Last year' : 'Last year',	'Year of electronic publication only' : 'Year of electronic publication only',	'Catalogs' : 'Catalogs',	'Catalog' : 'Catalog',	'Original' : 'Original',	'Approved copy' : 'Approved copy',	'Historical notes' : 'Historical notes',	'Add new serial' : 'Add new serial',	'Country selection' : 'Country selection',	'Add new publication place' : 'Add new publication place',	'Insert' : 'Insert',	'Checking for duplicate' : 'Checking for duplicate',	'please wait' : 'please wait',	'There is no publication places for this country' : 'There is no publication places for this country',	'This publication place already exists. Operation cancelled' : 'Ese lugar de publicaciÃ³n ya existe. OperaciÃ³n abortada',	'Edit tree' : 'Edit tree',	'Insert new branch' : 'Insert new branch',	'Delete selected branch' : 'Delete selected branch',	'Global subject trees' : 'Global subject trees',	'Consortium subject trees' : 'Consortium subject trees',	'Your organisation subject tree' : 'Your organisation subject tree',	'Your organisation tree' : 'Your organisation tree',	'Do you want to create a new subject tree?' : 'Do you want to create a new subject tree?',	'C17 subjects' : 'C17 subjects',	'Group subjects' : 'Group subjects',	'Organisations subjects' : 'Organisations subjects',	'Add note' : 'Add note',	'Text note' : 'Text note',	'Public' : 'Public',	'user successfully added' : 'user successfully added',	'The user has been successfully added' : 'The user has been successfully added',	'Selected email is available' : 'Selected email is available',	'User details successfully updated' : 'User details successfully updated',	'This username already exist in the DB, please choose another' : 'This username already exist in the DB, please choose another',	'This email already exist in the DB, please choose another' : 'This email already exist in the DB, please choose another',	'You must enter a password' : 'You must enter a password',	'Password must be more than 5 characters' : 'Password must be more than 5 characters',	'Please confirm password' : 'Please confirm password',	'Passwords are not the same' : 'Passwords are not the same',	'You must enter a username' : 'You must enter a username',	'username min length' : 'username min length',	'You must select at least one role' : 'You must select at least one role',	'First Name, Last Name, Username' : 'First Name, Last Name, Username',	'First Name, Last Name, Username, Email' : 'First Name, Last Name, Username, Email',	'Inactive' : 'Inactive',	'Deactivate' : 'Deactivate',	'Activate' : 'Activate',	'Activate/Deactivate' : 'Activate/Deactivate',	'Add new user' : 'Add new user',	'Managers' : 'Managers',	'Users' : 'Users',	'Your email is invalid at this time. You should change it as soon as possible. To do that, click' : 'Your email is invalid at this time. You should change it as soon as possible. To do that, click',	'There is no resposible for your organisation' : 'There is no resposible for your organisation',	'You must fill in this field' : 'You must fill in this field',	'Email Service' : 'Email Service',	'Show left blocks by default?' : 'Show left blocks by default?',	'Default org' : 'Default org',	'Check Availability' : 'Check Availability',	'Organisation name' : 'Organisation name',	'Role' : 'Role',	'role' : 'role',	'Hiring' : 'ContrataciÃ³n',	'Publication Places' : 'Lugares de publicaciÃ³n',	'Generic Templates' : 'Plantillas genÃ©ricas',	'Your current organisation is now ' : 'Your current organisation is now ',	'change current organisation' : 'change current organisation',	'Change organisation' : 'Change organisation',	'Access' : 'Access',	'Can not notify messages between two only C17 librarys' : 'Can not notify messages between two only C17 librarys',	'Organisation ID is empty' : 'Organisation ID is empty',	'Can not build the message. Unknown type' : 'Can not build the message. Unknown type',	'Can not resend a non existing file' : 'Can not resend a non existing file',	'User and org can not be filled out at the same time' : 'User and org can not be filled out at the same time',	'You have not set the operation mode yet' : 'You have not set the operation mode yet',	'A new message incoming from' : 'A new message incoming from',	'New incoming petition from' : 'New incoming petition from',	'Can not notify a served petition un full C17 mode' : 'Can not notify a served petition un full C17 mode',	'Your article request has been resolved by' : 'Your article request has been resolved by',	'Your article request has been resolved' : 'Your article request has been resolved',	'Your article request has been rejected by' : 'Your article request has been rejected by',	'Can not set the operation mode' : 'Can not set the operation mode',	'The organisation has no notification service assigned' : 'The organisation has no notification service assigned',	'The responder organisation can not be notified. No confirmation mail configured yet.' : 'The responder organisation can not be notified. No confirmation mail configured yet.',	'New incoming petition to your ILL17' : 'New incoming petition to your ILL17',	'Confirmation email' : 'Confirmation email',	'Preferences' : 'Preferences',	'Preferences :: Organisation' : 'Preferences :: Organisation',	'Legal advice' : 'Legal advice',	'Alert for your OPAC' : 'Alert for OPAC',	'You must fill in date field' : 'You must fill in date field',	'Successfull update' : 'Successfull update',	'Are you sure?' : 'Are you sure?',	'organisation_id is empty' : 'organisation_id is empty',	'How to query C17' : 'How to query C17',	'How often is C17 data updated' : 'How often is C17 data updated',	'Which are the requirements to join C17 catalog' : 'Which are the requirements to join C17 catalog',	'How can I include a new library into C17 catalog' : 'How can I include a new library into C17 catalog',	'How much cost belongs to C17' : 'How much cost belongs to C17',	'Which are the free services for C17 members' : 'Which are the free services for C17 members',	'How can I update my C17 data' : 'How can I update my C17 data',	'Wich are C17 rules' : 'Wich are C17 rules',	'The CD version can be queried as well' : 'The CD version can be queried as well',	'The C17 catalog is updated online by the librarys. Universities and other centers with a high number of collections, update their catalog once a year at least, trough ISO standard complaint files' : 'The C17 catalog is updated online by the librarys. Universities and other centers with a high number of collections, update their catalog once a year at least, trough ISO standard complaint files',	'To be part of C17 catalog, you must guarantee you will make inter library loan of your collections included into C17 catalog' : 'To be part of C17 catalog, you must guarantee you will make inter library loan of your collections included into C17 catalog',	'The library must provide the proper technical, professional support, to make the loan service works into this terms' : 'The library must provide the proper technical, professional support, to make the loan service works into this terms',	'Rejection answer: Two working days' : 'Rejection answer: Two working days',	'Requested article delivery: Four working days' : 'Requested article delivery: Four working days',	'You must activate a petition reception system compatible through email or any other of our C17 systems' : 'You must activate a petition reception system compatible through email or any other of our C17 systems',	'The library must update their own data every time they change, and perfom collections updates once a year at least' : 'The library must update their own data every time they change, and perfom collections updates once a year at least',	'The librays that wants to enter into C17 catalog and acomplish the requirements, must request their inclusion through their Community representative, or to CSI, who will report to such representative' : 'The librays that wants to enter into C17 catalog and acomplish the requirements, must request their inclusion through their Community representative, or to CSI, who will report to such representative',	'Beyond the hard requirements, every new library must have at least 20 collections, or have Health Science serials wich no other librarys in it community had' : 'Beyond the hard requirements, every new library must have at least 20 collections, or have Health Science serials wich no other librarys in it community had',	'Belonging to C17 catalog is completely free of charge' : 'Belonging to C17 catalog is completely free of charge',	'The main service is the query to the catalog, making the document petitions so much easier. You will also get your own collections list to make you own catalog' : 'The main service is the query to the catalog, making the document petitions so much easier. You will also get your own collections list to make you own catalog',	'In C17 catalog, we have 3 different data types to update' : 'In C17 catalog, we have 3 different data types to update',	'Library data' : 'Library data',	'Collection data' : 'Collection data',	'For the first two of them, the fastest way is through updating page over the internet, accessing it with your own username and password' : 'For the first two of them, the fastest way is through updating page over the internet, accessing it with your own username and password',	'C17 catalog update' : 'C17 catalog update',	'To collections update, we have three different ways' : 'To collections update, we have three different ways',	'Through internet address we tell about before' : 'Through internet address we tell about before',	'Through a file generated by own library management software, wich must contain the proper data to do the update' : 'Through a file generated by own library management software, wich must contain the proper data to do the update',	'Title: The key title from ISSN International Center or the Adriadna bibliographic catalog from the Biblioteca Nacional de EspaÃ±a is used' : 'Title: The key title from ISSN International Center or the Adriadna bibliographic catalog from the Biblioteca Nacional de EspaÃ±a is used',	'Abreviated Title: The key title from ISSN International Center is used as well, obtained from the title abbreviation list, complaining ISO-4 normative' : 'Abreviated Title: The key title from ISSN International Center is used as well, obtained from the title abbreviation list, complaining ISO-4 normative',	'Countries are represented throug a three word code (Alpha 3) , accordding ISO-3166' : 'Countries are represented throug a three word code (Alpha 3) , accordding ISO-3166',	'Starting, and, in many cases, ending years, have been obtained from Biblioteca Nacional de EspaÃ±a or ISSN International Center' : 'Starting, and, in many cases, ending years, have been obtained from Biblioteca Nacional de EspaÃ±a or ISSN International Center',	'Holding normalization process' : 'Holding normalization process',	'For each library, we have two differents holdings lines, representing paper and electronic collections of a given title. Paper collection shows up in top position, and a blue font color. Electronic collections shows up in bottom position, on red color and cursive font, showing the month embargo period between brackets, if applicable' : 'For each library, we have two differents holdings lines, representing paper and electronic collections of a given title. Paper collection shows up in top position, and a blue font color. Electronic collections shows up in bottom position, on red color and cursive font, showing the month embargo period between brackets, if applicable',	'Every serial title collection of each library, is preceeded by a two differents parts key. The first part have one or two letters, matching the province code (as showed in old car enrollments). The second one is the library key, and may have up to seven letters; we try to meake it as mnemonicly as we could, of follows some rules. For example, hospital keys start by a H key, and Universities do it by U key' : 'Every serial title collection of each library, is preceeded by a two differents parts key. The first part have one or two letters, matching the province code (as showed in old car enrollments). The second one is the library key, and may have up to seven letters; we try to meake it as mnemonicly as we could, of follows some rules. For example, hospital keys start by a H key, and Universities do it by U key',	'Dash [-] has two functions, when enclosed between years, indicate collection continuity between those years. When positiones behind the last year, indicates the collection is being recieved continuedly as long as the fascicles are published' : 'Dash [-] has two functions, when enclosed between years, indicate collection continuity between those years. When positiones behind the last year, indicates the collection is being recieved continuedly as long as the fascicles are published',	'Comma [,] indicates collection break up between comma years separated' : 'Comma [,] indicates collection break up between comma years separated',	'Brackets [( )] encloses incomplete collection years. A collection may be incomplete a year, or more than one' : 'Brackets [( )] encloses incomplete collection years. A collection may be incomplete a year, or more than one',	'Dot [.] ending a collection indicates a closed serial subscription for the preceeding year, or a cancelled publishing' : 'Dot [.] ending a collection indicates a closed serial subscription for the preceeding year, or a cancelled publishing',	'C17 :: Bibliography' : 'C17 :: Bibliography',	'C17 :: Comite' : 'C17 :: Comite',	'C17 :: Faq' : 'C17 :: Faq',	'Frequently asked questions' : 'Frequently asked questions',	'Technical comite' : 'Technical comite',	'Bibliography' : 'Bibliography',	'Selected username is available' : 'Selected username is available',	'layout' : 'Layout',	'shelve' : 'Shelve',	'cover' : 'Cover',	'modify' : 'modify',	'Succesfull: template updated!!!' : 'Succesfull: template updated!',	'insert' : 'inser',	'Succesfull: Template deleted!!!' : 'Succesfull: Template deleted!!!',	'Your request cannot be processed. A bug report has been submitted automatically to the application maintainers' : 'Your request cannot be processed. A bug report has been submitted automatically to the application maintainers',	'Wrong holdings string' : 'Wrong holdings string',	'Opac' : 'Opac',	'The C17 catalog is free online accesible by ' : 'The C17 catalog is free online accesible by ',	'C17 is a union catalog that combines the collections of periodicals of 538 Spanish Health Sciences Libraries...' : 'C17 is a union catalog that combines the collections of periodicals of 538 Spanish Health Sciences Libraries. Its first edition was on CD-ROM in 1996. Currently, it\'s a centralized catalog and is queried via the Internet.',	'Subjects' : 'Subjects',	'Select the subject tree' : 'Select the subject tree',	'Trees' : 'Trees',	'OPAC' : 'OPAC',	'Select the subject tree for your OPAC' : 'Select the subject tree for your OPAC',	'links' : 'links',	'Links' : 'Links',	'view' : 'view',	'Subject review' : 'Subject',	'ISCIII :: Printouts' : 'ISCIII :: Printouts',	'comunidad' : 'Comunidad',	'titulo' : 'TÃ­tulo',	'issnp' : 'ISSNp',	'issne' : 'ISSNe',	'total_colecciones' : 'Total colecciones',	'Conversor Fondos Complu' : 'Conversor Fondos Complu',	'Conversor Fondos Complutense' : 'Conversor Fondos Complutense',	'tipo_de_fondo' : 'tipo_de_fondo',	'ISSNp' : 'issn_papel',	'ISSNe' : 'issn_electronico',	'titulo_de_la_revista' : 'titulo_de_la_revista',	'fondo' : 'fondo',	'embargo' : 'embargo',	'url' : 'url',	'suministrador' : 'suministrador',	'coleccion' : 'coleccion',	'revista_id' : 'revista_id',	'fondo_complu' : 'fondo_complu',	'Faltas' : 'faltas',	'bib' : 'bib'}