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

 Browser: (function(){
 var ua = navigator.userAgent;
 var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
 return {
 IE: !!window.attachEvent && !isOpera,
 Opera: isOpera,
 WebKit: ua.indexOf('AppleWebKit/') > -1,
 Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
 MobileSafari: /Apple.*Mobile.*Safari/.test(ua)
 }
 })(),

 BrowserFeatures: {
 XPath: !!document.evaluate,
 SelectorsAPI: !!document.querySelector,
 ElementExtensions: (function() {
 var constructor = window.Element || window.HTMLElement;
 return !!(constructor && constructor.prototype);
 })(),
 SpecificElementExtensions: (function() {
 if (typeof window.HTMLDivElement !== 'undefined')
 return true;

 var div = document.createElement('div');
 var form = document.createElement('form');
 var isSupported = false;

 if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
 isSupported = true;
 }

 div = form = null;

 return isSupported;
 })()
 },

 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;


var Abstract = { };


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

/* Based on Alex Arnell's inheritance implementation. */

var Class = (function() {
 function subclass() {};
 function create() {
 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) {
 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;
 }

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

 if (!Object.keys({ toString: true }).length) {
 if (source.toString != Object.prototype.toString)
 properties.push("toString");
 if (source.valueOf != Object.prototype.valueOf)
 properties.push("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 = (function(m) {
 return function() { return ancestor[m].apply(this, arguments); };
 })(property).wrap(method);

 value.valueOf = method.valueOf.bind(method);
 value.toString = method.toString.bind(method);
 }
 this.prototype[property] = value;
 }

 return this;
 }

 return {
 create: create,
 Methods: {
 addMethods: addMethods
 }
 };
})();
(function() {

 var _toString = Object.prototype.toString;

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

 function inspect(object) {
 try {
 if (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;
 }
 }

 function toJSON(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 (isElement(object)) return;

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

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

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

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

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

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

 function clone(object) {
 return extend({ }, object);
 }

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

 function isArray(object) {
 return _toString.call(object) == "[object Array]";
 }


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

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

 function isString(object) {
 return _toString.call(object) == "[object String]";
 }

 function isNumber(object) {
 return _toString.call(object) == "[object Number]";
 }

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

 extend(Object, {
 extend: extend,
 inspect: inspect,
 toJSON: toJSON,
 toQueryString: toQueryString,
 toHTML: toHTML,
 keys: keys,
 values: values,
 clone: clone,
 isElement: isElement,
 isArray: isArray,
 isHash: isHash,
 isFunction: isFunction,
 isString: isString,
 isNumber: isNumber,
 isUndefined: isUndefined
 });
})();
Object.extend(Function.prototype, (function() {
 var slice = Array.prototype.slice;

 function update(array, args) {
 var arrayLength = array.length, length = args.length;
 while (length--) array[arrayLength + length] = args[length];
 return array;
 }

 function merge(array, args) {
 array = slice.call(array, 0);
 return update(array, args);
 }

 function argumentNames() {
 var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
 .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
 .replace(/\s+/g, '').split(',');
 return names.length == 1 && !names[0] ? [] : names;
 }

 function bind(context) {
 if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
 var __method = this, args = slice.call(arguments, 1);
 return function() {
 var a = merge(args, arguments);
 return __method.apply(context, a);
 }
 }

 function bindAsEventListener(context) {
 var __method = this, args = slice.call(arguments, 1);
 return function(event) {
 var a = update([event || window.event], args);
 return __method.apply(context, a);
 }
 }

 function curry() {
 if (!arguments.length) return this;
 var __method = this, args = slice.call(arguments, 0);
 return function() {
 var a = merge(args, arguments);
 return __method.apply(this, a);
 }
 }

 function delay(timeout) {
 var __method = this, args = slice.call(arguments, 1);
 timeout = timeout * 1000
 return window.setTimeout(function() {
 return __method.apply(__method, args);
 }, timeout);
 }

 function defer() {
 var args = update([0.01], arguments);
 return this.delay.apply(this, args);
 }

 function wrap(wrapper) {
 var __method = this;
 return function() {
 var a = update([__method.bind(this)], arguments);
 return wrapper.apply(this, a);
 }
 }

 function methodize() {
 if (this._methodized) return this._methodized;
 var __method = this;
 return this._methodized = function() {
 var a = update([this], arguments);
 return __method.apply(null, a);
 };
 }

 return {
 argumentNames: argumentNames,
 bind: bind,
 bindAsEventListener: bindAsEventListener,
 curry: curry,
 delay: delay,
 defer: defer,
 wrap: wrap,
 methodize: methodize
 }
})());


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


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();
 this.currentlyExecuting = false;
 } catch(e) {
 this.currentlyExecuting = false;
 throw e;
 }
 }
 }
});
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, (function() {

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

 function gsub(pattern, replacement) {
 var result = '', source = this, match;
 replacement = prepareReplacement(replacement);

 if (Object.isString(pattern))
 pattern = RegExp.escape(pattern);

 if (!(pattern.length || pattern.source)) {
 replacement = replacement('');
 return replacement + source.split('').join(replacement) + 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;
 }

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

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

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

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

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

 function stripTags() {
 return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
 }

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

 function extractScripts() {
 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];
 });
 }

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

 function escapeHTML() {
 return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
 }

 function unescapeHTML() {
 return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
 }


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

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

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

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

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

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

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

 function dasherize() {
 return this.replace(/_/g, '-');
 }

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

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

 function unfilterJSON(filter) {
 return this.replace(filter || Prototype.JSONFilter, '$1');
 }

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

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

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

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

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

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

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

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

 return {
 gsub: gsub,
 sub: sub,
 scan: scan,
 truncate: truncate,
 strip: String.prototype.trim ? String.prototype.trim : strip,
 stripTags: stripTags,
 stripScripts: stripScripts,
 extractScripts: extractScripts,
 evalScripts: evalScripts,
 escapeHTML: escapeHTML,
 unescapeHTML: unescapeHTML,
 toQueryParams: toQueryParams,
 parseQuery: toQueryParams,
 toArray: toArray,
 succ: succ,
 times: times,
 camelize: camelize,
 capitalize: capitalize,
 underscore: underscore,
 dasherize: dasherize,
 inspect: inspect,
 toJSON: toJSON,
 unfilterJSON: unfilterJSON,
 isJSON: isJSON,
 evalJSON: evalJSON,
 include: include,
 startsWith: startsWith,
 endsWith: endsWith,
 empty: empty,
 blank: blank,
 interpolate: interpolate
 };
})());

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

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

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

 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].replace(/\\\\]/g, ']') : 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 = (function() {
 function each(iterator, context) {
 var index = 0;
 try {
 this._each(function(value) {
 iterator.call(context, value, index++);
 });
 } catch (e) {
 if (e != $break) throw e;
 }
 return this;
 }

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

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

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

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

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

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

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

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

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

 function include(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;
 }

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

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

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

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

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

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

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

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

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

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

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

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

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









 return {
 each: each,
 eachSlice: eachSlice,
 all: all,
 every: all,
 any: any,
 some: any,
 collect: collect,
 map: collect,
 detect: detect,
 findAll: findAll,
 select: findAll,
 filter: findAll,
 grep: grep,
 include: include,
 member: include,
 inGroupsOf: inGroupsOf,
 inject: inject,
 invoke: invoke,
 max: max,
 min: min,
 partition: partition,
 pluck: pluck,
 reject: reject,
 sortBy: sortBy,
 toArray: toArray,
 entries: toArray,
 zip: zip,
 size: size,
 inspect: inspect,
 find: detect
 };
})();
function $A(iterable) {
 if (!iterable) return [];
 if ('toArray' in Object(iterable)) return iterable.toArray();
 var length = iterable.length || 0, results = new Array(length);
 while (length--) results[length] = iterable[length];
 return results;
}

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

Array.from = $A;


(function() {
 var arrayProto = Array.prototype,
 slice = arrayProto.slice,
 _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available

 function each(iterator) {
 for (var i = 0, length = this.length; i < length; i++)
 iterator(this[i]);
 }
 if (!_each) _each = each;

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

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

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

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

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

 function without() {
 var values = slice.call(arguments, 0);
 return this.select(function(value) {
 return !values.include(value);
 });
 }

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

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

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


 function clone() {
 return slice.call(this, 0);
 }

 function size() {
 return this.length;
 }

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

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

 function indexOf(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;
 }

 function lastIndexOf(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;
 }

 function concat() {
 var array = slice.call(this, 0), item;
 for (var i = 0, length = arguments.length; i < length; i++) {
 item = arguments[i];
 if (Object.isArray(item) && !('callee' in item)) {
 for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
 array.push(item[j]);
 } else {
 array.push(item);
 }
 }
 return array;
 }

 Object.extend(arrayProto, Enumerable);

 if (!arrayProto._reverse)
 arrayProto._reverse = arrayProto.reverse;

 Object.extend(arrayProto, {
 _each: _each,
 clear: clear,
 first: first,
 last: last,
 compact: compact,
 flatten: flatten,
 without: without,
 reverse: reverse,
 uniq: uniq,
 intersect: intersect,
 clone: clone,
 toArray: clone,
 size: size,
 inspect: inspect,
 toJSON: toJSON
 });

 var CONCAT_ARGUMENTS_BUGGY = (function() {
 return [].concat(arguments)[0][0] !== 1;
 })(1,2)

 if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;

 if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
 if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
})();
function $H(object) {
 return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {
 function initialize(object) {
 this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
 }

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

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

 function get(key) {
 if (this._object[key] !== Object.prototype[key])
 return this._object[key];
 }

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

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

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

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

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

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

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

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

 function toQueryString() {
 return this.inject([], function(results, pair) {
 var key = encodeURIComponent(pair.key), values = pair.value;

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

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

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

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

 return {
 initialize: initialize,
 _each: _each,
 set: set,
 get: get,
 unset: unset,
 toObject: toObject,
 toTemplateReplacements: toObject,
 keys: keys,
 values: values,
 index: index,
 merge: merge,
 update: update,
 toQueryString: toQueryString,
 inspect: inspect,
 toJSON: toJSON,
 clone: clone
 };
})());

Hash.from = $H;
Object.extend(Number.prototype, (function() {
 function toColorPart() {
 return this.toPaddedString(2, 16);
 }

 function succ() {
 return this + 1;
 }

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

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

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

 function abs() {
 return Math.abs(this);
 }

 function round() {
 return Math.round(this);
 }

 function ceil() {
 return Math.ceil(this);
 }

 function floor() {
 return Math.floor(this);
 }

 return {
 toColorPart: toColorPart,
 succ: succ,
 times: times,
 toPaddedString: toPaddedString,
 toJSON: toJSON,
 abs: abs,
 round: round,
 ceil: ceil,
 floor: floor
 };
})());

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

var ObjectRange = Class.create(Enumerable, (function() {
 function initialize(start, end, exclusive) {
 this.start = start;
 this.end = end;
 this.exclusive = exclusive;
 }

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

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

 return {
 initialize: initialize,
 _each: _each,
 include: include
 };
})());



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)) {
 params['_method'] = this.method;
 this.method = 'post';
 }

 this.parameters = params;

 if (params = Object.toQueryString(params)) {
 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';
 }

 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') {
 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) {
 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(global) {

 var SETATTRIBUTE_IGNORES_NAME = (function(){
 var elForm = document.createElement("form");
 var elInput = document.createElement("input");
 var root = document.documentElement;
 elInput.setAttribute("name", "test");
 elForm.appendChild(elInput);
 root.appendChild(elForm);
 var isBuggy = elForm.elements
 ? (typeof elForm.elements.test == "undefined")
 : null;
 root.removeChild(elForm);
 elForm = elInput = null;
 return isBuggy;
 })();

 var element = global.Element;
 global.Element = function(tagName, attributes) {
 attributes = attributes || { };
 tagName = tagName.toLowerCase();
 var cache = Element.cache;
 if (SETATTRIBUTE_IGNORES_NAME && 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(global.Element, element || { });
 if (element) global.Element.prototype = element.prototype;
})(this);

Element.cache = { };
Element.idCounter = 1;

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

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

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

 update: (function(){

 var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
 var el = document.createElement("select"),
 isBuggy = true;
 el.innerHTML = "<option value=\"test\">test</option>";
 if (el.options && el.options[0]) {
 isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
 }
 el = null;
 return isBuggy;
 })();

 var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
 try {
 var el = document.createElement("table");
 if (el && el.tBodies) {
 el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
 var isBuggy = typeof el.tBodies[0] == "undefined";
 el = null;
 return isBuggy;
 }
 } catch (e) {
 return true;
 }
 })();

 var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
 var s = document.createElement("script"),
 isBuggy = false;
 try {
 s.appendChild(document.createTextNode(""));
 isBuggy = !s.firstChild ||
 s.firstChild && s.firstChild.nodeType !== 3;
 } catch (e) {
 isBuggy = true;
 }
 s = null;
 return isBuggy;
 })();

 function update(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 === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
 element.text = content;
 return element;
 }

 if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
 if (tagName in Element._insertionTranslations.tags) {
 while (element.firstChild) {
 element.removeChild(element.firstChild);
 }
 Element._getContentFromAnonymousElement(tagName, content.stripScripts())
 .each(function(node) {
 element.appendChild(node)
 });
 }
 else {
 element.innerHTML = content.stripScripts();
 }
 }
 else {
 element.innerHTML = content.stripScripts();
 }

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

 return update;
 })(),

 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(element, 'parentNode');
 },

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

 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(element, 'previousSibling');
 },

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

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

 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(element);
 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(element);
 return Object.isNumber(expression) ? Element.descendants(element)[expression] :
 Element.select(element, expression)[index || 0];
 },

 previous: function(element, expression, index) {
 element = $(element);
 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
 var previousSiblings = Element.previousSiblings(element);
 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(element);
 return Object.isNumber(expression) ? nextSiblings[expression] :
 Selector.findElement(nextSiblings, expression, index);
 },


 select: function(element) {
 var args = Array.prototype.slice.call(arguments, 1);
 return Selector.findChildElements(element, args);
 },

 adjacent: function(element) {
 var args = Array.prototype.slice.call(arguments, 1);
 return Selector.findChildElements(element.parentNode, args).without(element);
 },

 identify: function(element) {
 element = $(element);
 var id = Element.readAttribute(element, 'id');
 if (id) return id;
 do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
 Element.writeAttribute(element, '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(element).height;
 },

 getWidth: function(element) {
 return Element.getDimensions(element).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(element, 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(element, className) ?
 'removeClassName' : 'addClassName'](element, className);
 },

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

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

 if (ancestor.contains)
 return ancestor.contains(element) && ancestor !== element;

 while (element = element.parentNode)
 if (element == ancestor) return true;

 return false;
 },

 scrollTo: function(element) {
 element = $(element);
 var pos = Element.cumulativeOffset(element);
 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 || value == 'auto') {
 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(element, 'display');
 if (display != 'none' && display != null) // Safari bug
 return {width: element.offsetWidth, height: element.offsetHeight};

 var els = element.style;
 var originalVisibility = els.visibility;
 var originalPosition = els.position;
 var originalDisplay = els.display;
 els.visibility = 'hidden';
 if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
 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';
 if (Prototype.Browser.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.toUpperCase() == '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(element, 'position') == 'absolute') return element;

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

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

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

 relativize: function(element) {
 element = $(element);
 if (Element.getStyle(element, 'position') == 'relative') return element;

 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;

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

 } while (element = element.offsetParent);

 element = forElement;
 do {
 if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == '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] || { });

 source = $(source);
 var p = Element.viewportOffset(source);

 element = $(element);
 var delta = [0, 0];
 var parent = null;
 if (Element.getStyle(element, 'position') == 'absolute') {
 parent = Element.getOffsetParent(element);
 delta = Element.viewportOffset(parent);
 }

 if (parent == document.body) {
 delta[0] -= document.body.offsetLeft;
 delta[1] -= document.body.offsetTop;
 }

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

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':
 if (!Element.visible(element)) return null;

 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) {
 Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
 function(proceed, element) {
 element = $(element);
 try { element.offsetParent }
 catch(e) { return $(document.body) }
 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);
 try { element.offsetParent }
 catch(e) { return Element._returnOffset(0,0) }
 var position = element.getStyle('position');
 if (position !== 'static') return proceed(element);
 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.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
 function(proceed, element) {
 try { element.offsetParent }
 catch(e) { return Element._returnOffset(0,0) }
 return proceed(element);
 }
 );

 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 = (function(){

 var classProp = 'className';
 var forProp = 'for';

 var el = document.createElement('div');

 el.setAttribute(classProp, 'x');

 if (el.className !== 'x') {
 el.setAttribute('class', 'x');
 if (el.className === 'x') {
 classProp = 'class';
 }
 }
 el = null;

 el = document.createElement('label');
 el.setAttribute(forProp, 'x');
 if (el.htmlFor !== 'x') {
 el.setAttribute('htmlFor', 'x');
 if (el.htmlFor === 'x') {
 forProp = 'htmlFor';
 }
 }
 el = null;

 return {
 read: {
 names: {
 'class': classProp,
 'className': classProp,
 'for': forProp,
 'htmlFor': forProp
 },
 values: {
 _getAttr: function(element, attribute) {
 return element.getAttribute(attribute);
 },
 _getAttr2: function(element, attribute) {
 return element.getAttribute(attribute, 2);
 },
 _getAttrNode: function(element, attribute) {
 var node = element.getAttributeNode(attribute);
 return node ? node.value : "";
 },
 _getEv: (function(){

 var el = document.createElement('div');
 el.onclick = Prototype.emptyFunction;
 var value = el.getAttribute('onclick');
 var f;

 if (String(value).indexOf('{') > -1) {
 f = function(element, attribute) {
 attribute = element.getAttribute(attribute);
 if (!attribute) return null;
 attribute = attribute.toString();
 attribute = attribute.split('{')[1];
 attribute = attribute.split('}')[0];
 return attribute.strip();
 };
 }
 else if (value === '') {
 f = function(element, attribute) {
 attribute = element.getAttribute(attribute);
 if (!attribute) return null;
 return attribute.strip();
 };
 }
 el = null;
 return f;
 })(),
 _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 frameBorder').each(function(attr) {
 Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
 Element._attributeTranslations.has[attr.toLowerCase()] = attr;
 });

 (function(v) {
 Object.extend(v, {
 href: v._getAttr2,
 src: v._getAttr2,
 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);

 if (Prototype.BrowserFeatures.ElementExtensions) {
 (function() {
 function _descendants(element) {
 var nodes = element.getElementsByTagName('*'), results = [];
 for (var i = 0, node; node = nodes[i]; i++)
 if (node.tagName !== "!") // Filter out comment nodes.
 results.push(node);
 return results;
 }

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

}

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.toUpperCase() == 'IMG' && element.width) {
 element.width++; element.width--;
 } else try {
 var n = document.createTextNode(' ');
 element.appendChild(n);
 element.removeChild(n);
 } catch (e) { }

 return element;
 };

 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 ('outerHTML' in document.documentElement) {
 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() {
 var tags = Element._insertionTranslations.tags;
 Object.extend(tags, {
 THEAD: tags.TBODY,
 TFOOT: tags.TBODY,
 TH: tags.TD
 });
})();

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

(function(div) {

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

 div = null;

})(document.createElement('div'))

Element.extend = (function() {

 function checkDeficiency(tagName) {
 if (typeof window.Element != 'undefined') {
 var proto = window.Element.prototype;
 if (proto) {
 var id = '_' + (Math.random()+'').slice(2);
 var el = document.createElement(tagName);
 proto[id] = 'x';
 var isBuggy = (el[id] !== 'x');
 delete proto[id];
 el = null;
 return isBuggy;
 }
 }
 return false;
 }

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

 var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');

 if (Prototype.BrowserFeatures.SpecificElementExtensions) {
 if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
 return function(element) {
 if (element && typeof element._extendedByPrototype == 'undefined') {
 var t = element.tagName;
 if (t && (/^(?:object|applet|embed)$/i.test(t))) {
 extendElementWith(element, Element.Methods);
 extendElementWith(element, Element.Methods.Simulated);
 extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
 }
 }
 return element;
 }
 }
 return Prototype.K;
 }

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

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

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

 if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

 extendElementWith(element, methods);

 element._extendedByPrototype = Prototype.emptyFunction;
 return element;

 }, {
 refresh: function() {
 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];

 var element = document.createElement(tagName);
 var proto = element['__proto__'] || element.constructor.prototype;
 element = null;
 return proto;
 }

 var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
 Element.prototype;

 if (F.ElementExtensions) {
 copy(Element.Methods, elementPrototype);
 copy(Element.Methods.Simulated, elementPrototype, 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() {
 return { width: this.getWidth(), height: this.getHeight() };
 },

 getScrollOffsets: function() {
 return Element._returnOffset(
 window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
 window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
 }
};

(function(viewport) {
 var B = Prototype.Browser, doc = document, element, property = {};

 function getRootElement() {
 if (B.WebKit && !doc.evaluate)
 return document;

 if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
 return document.body;

 return document.documentElement;
 }

 function define(D) {
 if (!element) element = getRootElement();

 property[D] = 'client' + D;

 viewport['get' + D] = function() { return element[property[D]] };
 return viewport['get' + D]();
 }

 viewport.getWidth = define.curry('Width');

 viewport.getHeight = define.curry('Height');
})(document.viewport);


Element.Storage = {
 UID: 1
};

Element.addMethods({
 getStorage: function(element) {
 if (!(element = $(element))) return;

 var uid;
 if (element === window) {
 uid = 0;
 } else {
 if (typeof element._prototypeUID === "undefined")
 element._prototypeUID = [Element.Storage.UID++];
 uid = element._prototypeUID[0];
 }

 if (!Element.Storage[uid])
 Element.Storage[uid] = $H();

 return Element.Storage[uid];
 },

 store: function(element, key, value) {
 if (!(element = $(element))) return;

 if (arguments.length === 2) {
 Element.getStorage(element).update(key);
 } else {
 Element.getStorage(element).set(key, value);
 }

 return element;
 },

 retrieve: function(element, key, defaultValue) {
 if (!(element = $(element))) return;
 var hash = Element.getStorage(element), value = hash.get(key);

 if (Object.isUndefined(value)) {
 hash.set(key, defaultValue);
 value = defaultValue;
 }

 return value;
 },

 clone: function(element, deep) {
 if (!(element = $(element))) return;
 var clone = element.cloneNode(deep);
 clone._prototypeUID = void 0;
 if (deep) {
 var descendants = Element.select(clone, '*'),
 i = descendants.length;
 while (i--) {
 descendants[i]._prototypeUID = void 0;
 }
 }
 return Element.extend(clone);
 }
});
/* 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();

 if (this.shouldUseSelectorsAPI()) {
 this.mode = 'selectorsAPI';
 } else if (this.shouldUseXPath()) {
 this.mode = 'xpath';
 this.compileXPathMatcher();
 } else {
 this.mode = "normal";
 this.compileMatcher();
 }

 },

 shouldUseXPath: (function() {

 var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
 var isBuggy = false;
 if (document.evaluate && window.XPathResult) {
 var el = document.createElement('div');
 el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';

 var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
 "//*[local-name()='li' or local-name()='LI']";

 var result = document.evaluate(xpath, el, null,
 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

 isBuggy = (result.snapshotLength !== 2);
 el = null;
 }
 return isBuggy;
 })();

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

 var e = this.expression;

 if (Prototype.Browser.WebKit &&
 (e.include("-of-type") || e.include(":empty")))
 return false;

 if ((/(\[[\w-]*?:|:checked)/).test(e))
 return false;

 if (IS_DESCENDANT_SELECTOR_BUGGY) return false;

 return true;
 }

 })(),

 shouldUseSelectorsAPI: function() {
 if (!Prototype.BrowserFeatures.SelectorsAPI) return false;

 if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;

 if (!Selector._div) Selector._div = new Element('div');

 try {
 Selector._div.querySelector(this.expression);
 } catch(e) {
 return false;
 }

 return true;
 },

 compileMatcher: function() {
 var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
 c = Selector.criteria, le, p, m, len = ps.length, name;

 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 = 0; i<len; i++) {
 p = ps[i].re;
 name = ps[i].name;
 if (m = e.match(p)) {
 this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
 new Template(c[name]).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, len = ps.length, name;

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

 this.matcher = ['.//*'];
 while (e && le != e && (/\S/).test(e)) {
 le = e;
 for (var i = 0; i<len; i++) {
 name = ps[i].name;
 if (m = e.match(ps[i].re)) {
 this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
 new Template(x[name]).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;
 var e = this.expression, results;

 switch (this.mode) {
 case 'selectorsAPI':
 if (root !== document) {
 var oldId = root.id, id = $(root).identify();
 id = id.replace(/([\.:])/g, "\\$1");
 e = "#" + id + " " + e;
 }

 results = $A(root.querySelectorAll(e)).map(Element.extend);
 root.id = oldId;

 return results;
 case 'xpath':
 return document._getElementsByXPath(this.xpath, root);
 default:
 return this.matcher(root);
 }
 },

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

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

 while (e && le !== e && (/\S/).test(e)) {
 le = e;
 for (var i = 0; i<len; i++) {
 p = ps[i].re;
 name = ps[i].name;
 if (m = e.match(p)) {
 if (as[name]) {
 this.tokens.push([name, Object.clone(m)]);
 e = e.replace(m[0], '');
 } else {
 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() + ">";
 }
});

if (Prototype.BrowserFeatures.SelectorsAPI &&
 document.compatMode === 'BackCompat') {
 Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
 var div = document.createElement('div'),
 span = document.createElement('span');

 div.id = "prototype_test_id";
 span.className = 'Test';
 div.appendChild(span);
 var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
 div = span = null;
 return isIgnored;
 })();
}

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)]",
 'checked': "[@checked]",
 'disabled': "[(@disabled) and (@type!='hidden')]",
 'enabled': "[not(@disabled) and (@type!='hidden')]",
 'not': function(m) {
 var e = m[6], p = Selector.patterns,
 x = Selector.xpath, le, v, len = p.length, name;

 var exclusion = [];
 while (e && le != e && (/\S/).test(e)) {
 le = e;
 for (var i = 0; i<len; i++) {
 name = p[i].name
 if (m = e.match(p[i].re)) {
 v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).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: [
 { name: 'laterSibling', re: /^\s*~\s*/ },
 { name: 'child', re: /^\s*>\s*/ },
 { name: 'adjacent', re: /^\s*\+\s*/ },
 { name: 'descendant', re: /^\s/ },

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

 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: {
 concat: function(a, b) {
 for (var i = 0, node; node = b[i]; i++)
 a.push(node);
 return a;
 },

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

 unmark: (function(){

 var PROPERTIES_ATTRIBUTES_MAP = (function(){
 var el = document.createElement('div'),
 isBuggy = false,
 propName = '_countedByPrototype',
 value = 'x'
 el[propName] = value;
 isBuggy = (el.getAttribute(propName) === value);
 el = null;
 return isBuggy;
 })();

 return PROPERTIES_ATTRIBUTES_MAP ?
 function(nodes) {
 for (var i = 0, node; node = nodes[i]; i++)
 node.removeAttribute('_countedByPrototype');
 return nodes;
 } :
 function(nodes) {
 for (var i = 0, node; node = nodes[i]; i++)
 node._countedByPrototype = void 0;
 return nodes;
 }
 })(),

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

 unique: function(nodes) {
 if (nodes.length == 0) return nodes;
 var results = [], n;
 for (var i = 0, l = nodes.length; i < l; i++)
 if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
 n._countedByPrototype = Prototype.emptyFunction;
 results.push(Element.extend(n));
 }
 return Selector.handlers.unmark(results);
 },

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

 tagName: function(nodes, root, tagName, combinator) {
 var uTagName = tagName.toUpperCase();
 var results = [], h = Selector.handlers;
 if (nodes) {
 if (combinator) {
 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 (root == document) {
 if (!targetNode) return [];
 if (!nodes) return [targetNode];
 } else {
 if (!root.sourceIndex || root.sourceIndex < 1) {
 var nodes = root.getElementsByTagName('*');
 for (var j = 0, node; node = nodes[j]; j++) {
 if (node.id === id) return [node];
 }
 }
 }

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

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

 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++) {
 if (node.tagName == '!' || node.firstChild) 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 && (!node.type || node.type !== 'hidden'))
 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 == v || nv && nv.startsWith(v); },
 '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
 '*=': function(nv, v) { return nv == v || nv && 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, {
 concat: function(a, b) {
 for (var i = 0, node; node = b[i]; i++)
 if (node.tagName !== "!") a.push(node);
 return a;
 }
 });
}

function $$() {
 return Selector.findChildElements(document, $A(arguments));
}

var Form = {
 reset: function(form) {
 form = $(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 != 'file' && (element.type != 'submit' || (!submitted &&
 submit !== false && (!submit || key == submit) && (submitted = true)))) {
 if (key in result) {
 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) {
 var elements = $(form).getElementsByTagName('*'),
 element,
 arr = [ ],
 serializers = Form.Element.Serializers;
 for (var i = 0; element = elements[i]; i++) {
 arr.push(element);
 }
 return arr.inject([], function(elements, child) {
 if (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)$/i.test(element.tagName);
 });
 },

 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)$/i.test(element.type))))
 element.select();
 } catch (e) { }
 return element;
 },

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

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

 var 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: {}
 };

 var docEl = document.documentElement;
 var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
 && 'onmouseleave' in docEl;

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

 function isLeftClick(event) { return _isButton(event, 0) }

 function isMiddleClick(event) { return _isButton(event, 1) }

 function isRightClick(event) { return _isButton(event, 2) }

 function element(event) {
 event = Event.extend(event);

 var node = event.target, type = event.type,
 currentTarget = event.currentTarget;

 if (currentTarget && currentTarget.tagName) {
 if (type === 'load' || type === 'error' ||
 (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
 && currentTarget.type === 'radio'))
 node = currentTarget;
 }

 if (node.nodeType == Node.TEXT_NODE)
 node = node.parentNode;

 return Element.extend(node);
 }

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

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

 function pointerX(event) {
 var docElement = document.documentElement,
 body = document.body || { scrollLeft: 0 };

 return event.pageX || (event.clientX +
 (docElement.scrollLeft || body.scrollLeft) -
 (docElement.clientLeft || 0));
 }

 function pointerY(event) {
 var docElement = document.documentElement,
 body = document.body || { scrollTop: 0 };

 return event.pageY || (event.clientY +
 (docElement.scrollTop || body.scrollTop) -
 (docElement.clientTop || 0));
 }


 function stop(event) {
 Event.extend(event);
 event.preventDefault();
 event.stopPropagation();

 event.stopped = true;
 }

 Event.Methods = {
 isLeftClick: isLeftClick,
 isMiddleClick: isMiddleClick,
 isRightClick: isRightClick,

 element: element,
 findElement: findElement,

 pointer: pointer,
 pointerX: pointerX,
 pointerY: pointerY,

 stop: stop
 };


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

 if (Prototype.Browser.IE) {
 function _relatedTarget(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);
 }

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

 Event.extend = function(event, element) {
 if (!event) return false;
 if (event._extendedByPrototype) return event;

 event._extendedByPrototype = Prototype.emptyFunction;
 var pointer = Event.pointer(event);

 Object.extend(event, {
 target: event.srcElement || element,
 relatedTarget: _relatedTarget(event),
 pageX: pointer.x,
 pageY: pointer.y
 });

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

 function _createResponder(element, eventName, handler) {
 var registry = Element.retrieve(element, 'prototype_event_registry');

 if (Object.isUndefined(registry)) {
 CACHE.push(element);
 registry = Element.retrieve(element, 'prototype_event_registry', $H());
 }

 var respondersForEvent = registry.get(eventName);
 if (Object.isUndefined(respondersForEvent)) {
 respondersForEvent = [];
 registry.set(eventName, respondersForEvent);
 }

 if (respondersForEvent.pluck('handler').include(handler)) return false;

 var responder;
 if (eventName.include(":")) {
 responder = function(event) {
 if (Object.isUndefined(event.eventName))
 return false;

 if (event.eventName !== eventName)
 return false;

 Event.extend(event, element);
 handler.call(element, event);
 };
 } else {
 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
 (eventName === "mouseenter" || eventName === "mouseleave")) {
 if (eventName === "mouseenter" || eventName === "mouseleave") {
 responder = function(event) {
 Event.extend(event, element);

 var parent = event.relatedTarget;
 while (parent && parent !== element) {
 try { parent = parent.parentNode; }
 catch(e) { parent = element; }
 }

 if (parent === element) return;

 handler.call(element, event);
 };
 }
 } else {
 responder = function(event) {
 Event.extend(event, element);
 handler.call(element, event);
 };
 }
 }

 responder.handler = handler;
 respondersForEvent.push(responder);
 return responder;
 }

 function _destroyCache() {
 for (var i = 0, length = CACHE.length; i < length; i++) {
 Event.stopObserving(CACHE[i]);
 CACHE[i] = null;
 }
 }

 var CACHE = [];

 if (Prototype.Browser.IE)
 window.attachEvent('onunload', _destroyCache);

 if (Prototype.Browser.WebKit)
 window.addEventListener('unload', Prototype.emptyFunction, false);


 var _getDOMEventName = Prototype.K;

 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
 _getDOMEventName = function(eventName) {
 var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
 return eventName in translations ? translations[eventName] : eventName;
 };
 }

 function observe(element, eventName, handler) {
 element = $(element);

 var responder = _createResponder(element, eventName, handler);

 if (!responder) return element;

 if (eventName.include(':')) {
 if (element.addEventListener)
 element.addEventListener("dataavailable", responder, false);
 else {
 element.attachEvent("ondataavailable", responder);
 element.attachEvent("onfilterchange", responder);
 }
 } else {
 var actualEventName = _getDOMEventName(eventName);

 if (element.addEventListener)
 element.addEventListener(actualEventName, responder, false);
 else
 element.attachEvent("on" + actualEventName, responder);
 }

 return element;
 }

 function stopObserving(element, eventName, handler) {
 element = $(element);

 var registry = Element.retrieve(element, 'prototype_event_registry');

 if (Object.isUndefined(registry)) return element;

 if (eventName && !handler) {
 var responders = registry.get(eventName);

 if (Object.isUndefined(responders)) return element;

 responders.each( function(r) {
 Element.stopObserving(element, eventName, r.handler);
 });
 return element;
 } else if (!eventName) {
 registry.each( function(pair) {
 var eventName = pair.key, responders = pair.value;

 responders.each( function(r) {
 Element.stopObserving(element, eventName, r.handler);
 });
 });
 return element;
 }

 var responders = registry.get(eventName);

 if (!responders) return;

 var responder = responders.find( function(r) { return r.handler === handler; });
 if (!responder) return element;

 var actualEventName = _getDOMEventName(eventName);

 if (eventName.include(':')) {
 if (element.removeEventListener)
 element.removeEventListener("dataavailable", responder, false);
 else {
 element.detachEvent("ondataavailable", responder);
 element.detachEvent("onfilterchange", responder);
 }
 } else {
 if (element.removeEventListener)
 element.removeEventListener(actualEventName, responder, false);
 else
 element.detachEvent('on' + actualEventName, responder);
 }

 registry.set(eventName, responders.without(responder));

 return element;
 }

 function fire(element, eventName, memo, bubble) {
 element = $(element);

 if (Object.isUndefined(bubble))
 bubble = true;

 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 = bubble ? 'ondataavailable' : 'onfilterchange';
 }

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

 Object.extend(Event, {
 fire: fire,
 observe: observe,
 stopObserving: stopObserving
 });

 Element.addMethods({
 fire: fire,

 observe: observe,

 stopObserving: stopObserving
 });

 Object.extend(document, {
 fire: fire.methodize(),

 observe: observe.methodize(),

 stopObserving: stopObserving.methodize(),

 loaded: false
 });

 if (window.Event) Object.extend(window.Event, Event);
 else window.Event = Event;
})();

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

 var timer;

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

 function checkReadyState() {
 if (document.readyState === 'complete') {
 document.stopObserving('readystatechange', checkReadyState);
 fireContentLoadedEvent();
 }
 }

 function pollDoScroll() {
 try { document.documentElement.doScroll('left'); }
 catch(e) {
 timer = pollDoScroll.defer();
 return;
 }
 fireContentLoadedEvent();
 }

 if (document.addEventListener) {
 document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
 } else {
 document.observe('readystatechange', checkReadyState);
 if (window == top)
 timer = pollDoScroll.defer();
 }

 Event.observe(window, 'load', fireContentLoadedEvent);
})();

Element.addMethods();

/*------------------------------- 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');

var Position = {
 includeScrollOffsets: false,

 prepare: function() {
 this.deltaX = window.pageXOffset
 || document.documentElement.scrollLeft
 || document.body.scrollLeft
 || 0;
 this.deltaY = window.pageYOffset
 || document.documentElement.scrollTop
 || document.body.scrollTop
 || 0;
 },

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

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


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

/*--------------------------------------------------------------------------*/
/*
* Really easy field validation with Prototype
* http://tetlaw.id.au/view/javascript/really-easy-field-validation
* Andrew Tetlaw
* Version 1.5.4.1 (2007-01-05)
*
* Copyright (c) 2007 Andrew Tetlaw
* 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.
*
*/
var Validator = Class.create();

Validator.prototype = {
 initialize : function(className, error, test, options) {
 if(typeof test == 'function'){
 this.options = $H(options);
 this._test = test;
 } else {
 this.options = $H(test);
 this._test = function(){return true};
 }
 this.error = error || 'Validation failed.';
 this.className = className;
 },
 test : function(v, elm) {
 return (this._test(v,elm) && this.options.all(function(p){
 return Validator.methods[p.key] ? Validator.methods[p.key](v,elm,p.value) : true;
 }));
 }
}
Validator.methods = {
 pattern : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) || opt.test(v)},
 minLength : function(v,elm,opt) {return v.length >= opt},
 maxLength : function(v,elm,opt) {return v.length <= opt},
 min : function(v,elm,opt) {return v >= parseFloat(opt)},
 max : function(v,elm,opt) {return v <= parseFloat(opt)},
 notOneOf : function(v,elm,opt) {return $A(opt).all(function(value) {
 return v != value;
 })},
 oneOf : function(v,elm,opt) {return $A(opt).any(function(value) {
 return v == value;
 })},
 is : function(v,elm,opt) {return v == opt},
 isNot : function(v,elm,opt) {return v != opt},
 equalToField : function(v,elm,opt) {return v == $F(opt)},
 notEqualToField : function(v,elm,opt) {return v != $F(opt)},
 include : function(v,elm,opt) {return $A(opt).all(function(value) {
 return Validation.get(value).test(v,elm);
 })}
}

var Validation = Class.create();
Validation.defaultOptions = {
 onSubmit : true,
 stopOnFirst : false,
 immediate : false,
 focusOnError : true,
 useTitles : false,
 addClassNameToContainer: false,
 containerClassName: '.input-box',
 onFormValidate : function(result, form) {},
 onElementValidate : function(result, elm) {}
};

Validation.prototype = {
 initialize : function(form, options){
 this.form = $(form);
 if (!this.form) {
 return;
 }
 this.options = Object.extend({
 onSubmit : Validation.defaultOptions.onSubmit,
 stopOnFirst : Validation.defaultOptions.stopOnFirst,
 immediate : Validation.defaultOptions.immediate,
 focusOnError : Validation.defaultOptions.focusOnError,
 useTitles : Validation.defaultOptions.useTitles,
 onFormValidate : Validation.defaultOptions.onFormValidate,
 onElementValidate : Validation.defaultOptions.onElementValidate
 }, options || {});
 if(this.options.onSubmit) Event.observe(this.form,'submit',this.onSubmit.bind(this),false);
 if(this.options.immediate) {
 Form.getElements(this.form).each(function(input) { // Thanks Mike! 
 if (input.tagName.toLowerCase() == 'select') {
 Event.observe(input, 'blur', this.onChange.bindAsEventListener(this));
 }
 Event.observe(input, 'change', this.onChange.bindAsEventListener(this));
 }, this);
 }
 },
 onChange : function (ev) {
 Validation.isOnChange = true;
 Validation.validate(Event.element(ev),{
 useTitle : this.options.useTitles, 
 onElementValidate : this.options.onElementValidate
 });
 Validation.isOnChange = false; 
 },
 onSubmit : function(ev){
 if(!this.validate()) Event.stop(ev);
 },
 validate : function() {
 var result = false;
 var useTitles = this.options.useTitles;
 var callback = this.options.onElementValidate;
 try {
 
 //code added by Razib 05-05-09
 
 //var elms=document.getElementsByTagName('input');
 var elms=this.form.getInputs('text');
 for(var i=0;i<elms.length;i++){
 var inittext=elms[i].getAttribute('inittext');
 /**
 * Emptu field null population prob fixed
 */
 if(inittext == null || inittext==''){
 continue;
 }

 if(elms[i].value==inittext){
 elms[i].value='';
 }
 }
 
 if(this.options.stopOnFirst) {
 result = Form.getElements(this.form).all(function(elm) { return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); });
 } else {
 result = Form.getElements(this.form).collect(function(elm) { return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); }).all();
 }
 } catch (e) {

 }
 if(!result && this.options.focusOnError) {
 try{
 Form.getElements(this.form).findAll(function(elm){return $(elm).hasClassName('validation-failed')}).first().focus()
 }
 catch(e){

 }
 }
 this.options.onFormValidate(result, this.form);
 
 // Updated by Moinul 28 August, 2009 after upgrade magento version 1.3.2.3
 if(!result){
 for(var i=0;i<elms.length;i++){
 var inittext=elms[i].getAttribute('inittext');
 /**
 * Emptu field null population prob fixed
 */
 if(inittext == null || inittext==''){
 continue;
 }
 
 if(elms[i].value==''){
 elms[i].value=inittext;
 }
 }
 }
 
 return result;
 },
 reset : function() {
 Form.getElements(this.form).each(Validation.reset);
 }
}

Object.extend(Validation, {
 validate : function(elm, options){
 options = Object.extend({
 useTitle : false,
 onElementValidate : function(result, elm) {}
 }, options || {});
 elm = $(elm);

 var cn = $w(elm.className);
 return result = cn.all(function(value) {
 var test = Validation.test(value,elm,options.useTitle);
 options.onElementValidate(test, elm);
 return test;
 });
 },
 insertAdvice : function(elm, advice){
 var container = $(elm).up('.field-row');
 if(container){
 Element.insert(container, {after: advice});
 } else if (elm.up('td.value')) {
 elm.up('td.value').insert({bottom: advice});
 } else if (elm.advaiceContainer && $(elm.advaiceContainer)) {
 $(elm.advaiceContainer).update(advice);
 }
 else {
 switch (elm.type.toLowerCase()) {
 case 'checkbox':
 case 'radio':
 var p = elm.parentNode;
 if(p) {
 Element.insert(p, {'bottom': advice});
 } else {
 Element.insert(elm, {'after': advice});
 }
 break;
 default:
 Element.insert(elm, {'after': advice});
 }
 }
 },
 showAdvice : function(elm, advice, adviceName){
 if(!elm.advices){
 elm.advices = new Hash();
 }
 else{
 elm.advices.each(function(pair){
 this.hideAdvice(elm, pair.value);
 }.bind(this));
 }
 elm.advices.set(adviceName, advice);
 if(typeof Effect == 'undefined') {
 advice.style.display = 'block';
 } else {
 if(!advice._adviceAbsolutize) {
 new Effect.Appear(advice, {duration : 1 });
 } else {
 Position.absolutize(advice);
 advice.show();
 advice.setStyle({
 'top':advice._adviceTop,
 'left': advice._adviceLeft,
 'width': advice._adviceWidth,
 'z-index': 1000
 });
 advice.addClassName('advice-absolute');
 }
 }
 },
 hideAdvice : function(elm, advice){
 if(advice != null) advice.hide();
 },
 updateCallback : function(elm, status) {
 if (typeof elm.callbackFunction != 'undefined') {
 eval(elm.callbackFunction+'(\''+elm.id+'\',\''+status+'\')');
 }
 },
 ajaxError : function(elm, errorMsg) {
 var name = 'validate-ajax';
 var advice = Validation.getAdvice(name, elm);
 if (advice == null) {
 advice = this.createAdvice(name, elm, false, errorMsg);
 }
 this.showAdvice(elm, advice, 'validate-ajax');
 this.updateCallback(elm, 'failed');

 elm.addClassName('validation-failed');
 elm.addClassName('validate-ajax');
 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
 var container = elm.up(Validation.defaultOptions.containerClassName);
 if (container && this.allowContainerClassName(elm)) {
 container.removeClassName('validation-passed');
 container.addClassName('validation-error');
 }
 }
 },
 allowContainerClassName: function (elm) {
 if (elm.type == 'radio' || elm.type == 'checkbox') {
 return elm.hasClassName('change-container-classname');
 }
 
 return true;
 },
 test : function(name, elm, useTitle) {
 var v = Validation.get(name);
 var prop = '__advice'+name.camelize();
 try {
 if(Validation.isVisible(elm) && !v.test($F(elm), elm)) {
 //if(!elm[prop]) {
 var advice = Validation.getAdvice(name, elm);
 if (advice == null) {
 advice = this.createAdvice(name, elm, useTitle);
 }
 this.showAdvice(elm, advice, name);
 this.updateCallback(elm, 'failed');
 //}
 elm[prop] = 1;
 if (!elm.advaiceContainer) {
 elm.removeClassName('validation-passed');
 elm.addClassName('validation-failed');
 } 
 
 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
 var container = elm.up(Validation.defaultOptions.containerClassName);
 if (container && this.allowContainerClassName(elm)) {
 container.removeClassName('validation-passed');
 container.addClassName('validation-error');
 }
 }
 return false;
 } else {
 var advice = Validation.getAdvice(name, elm);
 this.hideAdvice(elm, advice);
 this.updateCallback(elm, 'passed');
 elm[prop] = '';
 elm.removeClassName('validation-failed');
 elm.addClassName('validation-passed');
 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
 var container = elm.up(Validation.defaultOptions.containerClassName);
 if (container && !container.down('.validation-failed') && this.allowContainerClassName(elm)) {
 if (!Validation.get('IsEmpty').test(elm.value) || !this.isVisible(elm)) { 
 container.addClassName('validation-passed');
 } else {
 container.removeClassName('validation-passed');
 }
 container.removeClassName('validation-error');
 }
 }
 return true;
 }
 } catch(e) {
 throw(e)
 }
 },
 isVisible : function(elm) {
 while(elm.tagName != 'BODY') {
 if(!$(elm).visible()) return false;
 elm = elm.parentNode;
 }
 return true;
 },
 getAdvice : function(name, elm) {
 return $('advice-' + name + '-' + Validation.getElmID(elm)) || $('advice-' + Validation.getElmID(elm));
 },
 createAdvice : function(name, elm, useTitle, customError) {
 var v = Validation.get(name);
 var errorMsg = useTitle ? ((elm && elm.title) ? elm.title : v.error) : v.error;
 if (customError) {
 errorMsg = customError;
 }
 try {
 if (Translator){
 errorMsg = Translator.translate(errorMsg);
 }
 }
 catch(e){}

 advice = '<div class="validation-advice" id="advice-' + name + '-' + Validation.getElmID(elm) +'" style="display:none">' + errorMsg + '</div>'


 Validation.insertAdvice(elm, advice);
 advice = Validation.getAdvice(name, elm);
 if($(elm).hasClassName('absolute-advice')) {
 var dimensions = $(elm).getDimensions();
 var originalPosition = Position.cumulativeOffset(elm);

 advice._adviceTop = (originalPosition[1] + dimensions.height) + 'px';
 advice._adviceLeft = (originalPosition[0]) + 'px';
 advice._adviceWidth = (dimensions.width) + 'px';
 advice._adviceAbsolutize = true;
 }
 return advice;
 },
 getElmID : function(elm) {
 return elm.id ? elm.id : elm.name;
 },
 reset : function(elm) {
 elm = $(elm);
 var cn = $w(elm.className);
 cn.each(function(value) {
 var prop = '__advice'+value.camelize();
 if(elm[prop]) {
 var advice = Validation.getAdvice(value, elm);
 if (advice) {
 advice.hide();
 }
 elm[prop] = '';
 }
 elm.removeClassName('validation-failed');
 elm.removeClassName('validation-passed');
 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
 var container = elm.up(Validation.defaultOptions.containerClassName);
 if (container) {
 container.removeClassName('validation-passed');
 container.removeClassName('validation-error');
 }
 }
 });
 },
 add : function(className, error, test, options) {
 var nv = {};
 nv[className] = new Validator(className, error, test, options);
 Object.extend(Validation.methods, nv);
 },
 addAllThese : function(validators) {
 var nv = {};
 $A(validators).each(function(value) {
 nv[value[0]] = new Validator(value[0], value[1], value[2], (value.length > 3 ? value[3] : {}));
 });
 Object.extend(Validation.methods, nv);
 },
 get : function(name) {
 return Validation.methods[name] ? Validation.methods[name] : Validation.methods['_LikeNoIDIEverSaw_'];
 },
 methods : {
 '_LikeNoIDIEverSaw_' : new Validator('_LikeNoIDIEverSaw_','',{})
 }
});

Validation.add('IsEmpty', '', function(v) {
 return (v == '' || (v == null) || (v.length == 0) || /^\s+$/.test(v)); // || /^\s+$/.test(v));
});

Validation.addAllThese([
 ['validate-select', 'Please select an option.', function(v) {
 return ((v != "none") && (v != null) && (v.length != 0));
 }],
 ['validate-select-acctype', 'Please select any one option.', function(v) {
 var radioGrp = document['forms']['registration-form-validate']['accountType'];
 for(i=0; i < radioGrp.length; i++){
 if (radioGrp[i].checked == true) {

 return true;
 }
 }
 return $('personal_acc_type').focus();
 

 }],
 ['required-entry', 'This is a required field.', function(v) {
 return !Validation.get('IsEmpty').test(v);
 }],
 
 
 ['required-name-address', 'Please enter Name or Company Name or at least one of the two.', function(v) {
 //return !Validation.get('IsEmpty').test(v);
 var firstname = $('firstname').value;
 var lastname = $('lastname').value;
 var company = $('company').value;
 
 if( firstname || lastname || company)
 return true;
 else false; 
 
 }],
 
 ['required-name', 'Please enter First and Last Name.', function(v) {
 
 //return !Validation.get('IsEmpty').test(v);
 var firstname = $('firstname').value;
 var lastname = $('lastname').value;
 var company = $('company').value;
 
 if (company)
 return true;
 else if( firstname && lastname )
 return true;
 else false;
 
 
 }],
 
 
 ['validate-number', 'Please enter a valid number in this field.', function(v) {
 return Validation.get('IsEmpty').test(v) || (!isNaN(parseNumber(v)) && !/^\s+$/.test(parseNumber(v)));
 }],
 
 
 ['validate-order-number', 'Please enter a valid order number or Document name or Order status and use only letters (a-z or A-Z) or numbers (0-9) or &,- in this field.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-&a-zA-Z0-9 ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 
 ['validate-digits', 'Please use numbers only in this field. please avoid spaces or other characters such as dots or commas.', function(v) {
 return Validation.get('IsEmpty').test(v) || !/[^\d]/.test(v);
 }],
 ['validate-alpha', 'Please use letters only (a-z or A-Z) in this field.', function (v) {
 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z]+$/.test(v)
 }],
 
 ['validate-name', 'Please use letters only (a-z or A-Z) and (-) in this field.', function (v) {
 return Validation.get('IsEmpty').test(v) || /^[-a-zA-Z ]+$/.test(v)
 }],
 
 ['validate-code', 'Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter.', function (v) {
 return Validation.get('IsEmpty').test(v) || /^[a-z]+[a-z0-9_]+$/.test(v)
 }],

 
 ['validate-username-character', 'Only letters, numbers and the characters @_-. allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-@.a-zA-Z0-9_]+$/.test(v) /*!/\W/.test(v)*/
 }],
 ['validate-bank-name', 'Only letters, numbers and the character & allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[&a-zA-Z0-9 ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 ['validate-bank-routing-number', 'Provide a valid bank routing number. Must be 9 digits.', function(v) {
 if(v.length != 9) return false;
 return Validation.get('IsEmpty').test(v) || !/[^\d]/.test(v);
 }],

 
 ['validate-address', 'Only letters, numbers and the characters \'&-,.# allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-&.,'a-zA-Z0-9_# ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 
 ['validate-mytext', 'Only letters, numbers and the characters \'&-,. allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-&.,'a-zA-Z0-9_ ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 
 ['validate-mytext2', 'Only letters, numbers and the characters \'&-,. allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-&.,'a-zA-Z0-9_/ ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 ['validate-alphanum', 'Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9 ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 ['validate-purchaseorder', 'Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9]+$/.test(v) /*!/\W/.test(v)*/
 }],
 ['validate-address-description', 'Please use only letters (a-z or A-Z) or numbers (0-9) and \', in this field.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9', ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 ['validate-street', 'Please use only letters (a-z or A-Z) or numbers (0-9) or spaces and # only in this field.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v)
 }],
 /*
 ['validate-phoneStrict', 'Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
 }],
 */
 
 ['validate-phoneStrict', 'Please enter a valid phone number.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-.)(0-9#_ ]+$/.test(v) /*!/\W/.test(v)*/
 }],
 
 ['validate-phoneLax', 'Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^((\d[-. ]?)?((\(\d{3}\))|\d{3}))?[-. ]?\d{3}[-. ]?\d{4}$/.test(v);
 }],
 /* ['validate-fax', 'Please enter a valid fax number. For example (123) 456-7890 or 123-456-7890.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
 }],
 */
 ['validate-fax', 'Please enter a valid fax number.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[-.)(0-9#_ ]+$/.test(v) /*!/\W/.test(v)*/
 }], 
 
 ['validate-date', 'Please enter a valid date.', function(v) {
 var test = new Date(v);
 return Validation.get('IsEmpty').test(v) || !isNaN(test);
 }],
 ['validate-email', 'Please enter a valid email address. For example johndoe@domain.com.', function (v) {
 //return Validation.get('IsEmpty').test(v) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(v)
 //return Validation.get('IsEmpty').test(v) || /^[\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9][\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9\.]{1,30}[\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9]@([a-z0-9_-]{1,30}\.){1,5}[a-z]{2,4}$/i.test(v)
 return Validation.get('IsEmpty').test(v) || /^[a-z0-9,!\#\$%&'\*\+/=\?\^_`\{\|}~-]+(\.[a-z0-9,!#\$%&'\*\+/=\?\^_`\{\|}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})/i.test(v)
 }],
 
 ['validate-username', 'Please enter 6 or more characters.', function(v) {
 var username=v.strip(); /*strip leading and trailing spaces*/
 return !(username.length>0 && username.length < 6);
 }],
 
 ['validate-password', 'Create the password for your account. Keep in mind your password must consist of a minimum of 8 characters, of which at least 1 must be upper case, 1 must be lower case, and 1 must be a number or digit.', function(v) {
 var pass=v.strip(); /*strip leading and trailing spaces*/
 
 
 numDigit=0;
 numLCase=0;
 numUCase=0;
 for(i=0;i<pass.length;i++){
 if(pass.charAt(i)>='A' && pass.charAt(i)<='Z'){
 numUCase+=1;
 }
 if(pass.charAt(i)>='a' && pass.charAt(i)<='z'){
 numLCase+=1;
 }
 if(pass.charAt(i)>='0' && pass.charAt(i)<='9'){
 numDigit+=1;
 }
 
 } 
 if(numUCase > 0 && numLCase > 0 && numDigit > 0 && !(pass.length>0 && pass.length < 8)){
 return true;
 }
 else{
 return false;
 }

 
 
 
 }],
 ['validate-cpassword', 'Please make sure your passwords match.', function(v) {
 /*
 var pass = $('password') ? $('password') : $$('.validate-password')[0];
 var conf = $('confirmation') ? $('confirmation') : $$('.validate-cpassword')[0];
 return (pass.value == conf.value);*/
 
 if ($('password')) {
 var pass = $('password');
 }
 else {
 var pass = $$('.validate-password').length ? $$('.validate-password')[0] : $$('.validate-admin-password')[0];
 }
 var conf = $('confirmation') ? $('confirmation') : $$('.validate-cpassword')[0];
 return (pass.value == conf.value);
 }],
 
 ['validate-security-answer-lenght', 'Please enter 3 or more characters.', function(v) {
 var sa=v.strip(); /*strip leading and trailing spaces*/
 return !(sa.length>0 && sa.length < 3);
 }],
 
 ['validate-security_answer', 'Please make sure your security answer match.', function(v) {
 var answer = $('hintAnswer');
 var conf = $('confirmHintAnswer');
 return (answer.value == conf.value);
 }],
 
 ['validate-email-duplicate', 'Email already exists.', function(v) {
 var emailExists = $('pEmailExist');
 return (emailExists.value == 'false');
 }],
 
 ['validate-username-duplicate', 'Username already exists.', function(v) {
 var usernameExists = $('userExist');
 return (usernameExists.value == 'false');
 }],
 
 ['validate-url', 'Please enter a valid URL. http:// is required', function (v) {
 return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v)
 }],
 ['validate-clean-url', 'Please enter a valid URL. For example http://www.example.com or www.example.com', function (v) {
 return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v)
 }],
 ['validate-identifier', 'Please enter a valid Identifier. For example example-page, example-page.html or anotherlevel/example-page', function (v) {
 return Validation.get('IsEmpty').test(v) || /^[A-Z0-9][A-Z0-9_\/-]+(\.[A-Z0-9_-]+)*$/i.test(v)
 }],
 ['validate-xml-identifier', 'Please enter a valid XML-identifier. For example something_1, block5, id-4', function (v) {
 return Validation.get('IsEmpty').test(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v)
 }],
 ['validate-ssn', 'Please enter a valid social security number. For example 123-45-6789.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v);
 }],
 ['validate-zip', 'Please enter a valid zip code.', function(v) {
 if($('country')){
 var country = $('country').value; 
 if(country=='US'){
 return Validation.get('IsEmpty').test(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v);
 }
 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9 -]+$/.test(v) /*!/\W/.test(v)*/
 }
 return Validation.get('IsEmpty').test(v) || /^[-0-9 ]+$/.test(v) 
 }],

 ['validate-zip-international', 'Please enter a valid zip code.', function(v) {
 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9 -]+$/.test(v)
 //return Validation.get('IsEmpty').test(v) || /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v);
 return true;
 }],
 ['validate-date-au', 'Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.', function(v) {
 if(Validation.get('IsEmpty').test(v)) return true;
 var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
 if(!regex.test(v)) return false;
 var d = new Date(v.replace(regex, '$2/$1/$3'));
 return ( parseInt(RegExp.$2, 10) == (1+d.getMonth()) ) &&
 (parseInt(RegExp.$1, 10) == d.getDate()) &&
 (parseInt(RegExp.$3, 10) == d.getFullYear() );
 }],
 ['validate-currency-dollar', 'Please enter a valid $ amount. For example $100.00.', function(v) {
 // [$]1[##][,###]+[.##]
 // [$]1###+[.##]
 // [$]0.##
 // [$].##
 return Validation.get('IsEmpty').test(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v)
 }],
 ['validate-one-required', 'Please select one of the above options.', function (v,elm) {
 var p = elm.parentNode;
 var options = p.getElementsByTagName('INPUT');
 return $A(options).any(function(elm) {
 return $F(elm);
 });
 }],
 ['validate-one-required-by-name', 'Please select one of the above options.', function (v,elm) {
 var inputs = $$('input');
 var error = 1;
 for( i in inputs ) {
 if( inputs[i].checked == true && inputs[i].name == elm.name ) {
 error = 0;
 }
 }

 if( error == 0 ) {
 return true;
 } else {
 return false;
 }
 }],
 ['validate-not-negative-number', 'Please enter a valid number in this field.', function(v) {
 v = parseNumber(v);
 return (!isNaN(v) && v>=0);
 }],
 ['validate-state', 'Please select State/Province.', function(v) {
 return (v!=0 || v == '');
 }],

 ['validate-new-password', 'Please enter 6 or more characters. Leading or trailing spaces will be ignored.', function(v) {
 if (!Validation.get('validate-password').test(v)) return false;
 if (Validation.get('IsEmpty').test(v) && v != '') return false;
 return true;
 }],
 ['validate-greater-than-zero', 'Please enter a number greater than 0 in this field.', function(v) {
 if(v.length)
 return parseFloat(v) > 0;
 else
 return true;
 }],
 ['validate-zero-or-greater', 'Please enter a number 0 or greater in this field.', function(v) {
 if(v.length)
 return parseFloat(v) >= 0;
 else
 return true;
 }],
 ['validate-cc-number', 'Please enter a valid credit card number.', function(v, elm) {
 // remove non-numerics
 var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_number')) + '_cc_type');
 if (ccTypeContainer && typeof Validation.creditCartTypes.get(ccTypeContainer.value) != 'undefined'
 && Validation.creditCartTypes.get(ccTypeContainer.value)[2] == false) {
 if (!Validation.get('IsEmpty').test(v) && Validation.get('validate-digits').test(v)) {
 return true;
 } else {
 return false;
 }
 }
 return validateCreditCard(v);
 }],
 ['validate-cc-type', 'Credit card number doesn\'t match credit card type', function(v, elm) {
 // remove credit card number delimiters such as "-" and space
 elm.value = removeDelimiters(elm.value);
 v = removeDelimiters(v);

 var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_number')) + '_cc_type');
 if (!ccTypeContainer) {
 return true;
 }
 var ccType = ccTypeContainer.value;

 if (typeof Validation.creditCartTypes.get(ccType) == 'undefined') {
 return false;
 }

 // Other card type or switch or solo card
 if (Validation.creditCartTypes.get(ccType)[0]==false) {
 return true;
 }

 // Matched credit card type
 var ccMatchedType = '';

 Validation.creditCartTypes.each(function (pair) {
 if (pair.value[0] && v.match(pair.value[0])) {
 ccMatchedType = pair.key;
 throw $break;
 }
 });

 if(ccMatchedType != ccType) {
 return false;
 }
 
 if (ccTypeContainer.hasClassName('validation-failed') && Validation.isOnChange) {
 Validation.validate(ccTypeContainer);
 }

 return true;
 }],
 ['validate-cc-type-select', 'Card type doesn\'t match credit card number', function(v, elm) {
 var ccNumberContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_type')) + '_cc_number');
 if (Validation.isOnChange && Validation.get('IsEmpty').test(ccNumberContainer.value)) {
 return true;
 }
 if (Validation.get('validate-cc-type').test(ccNumberContainer.value, ccNumberContainer)) {
 Validation.validate(ccNumberContainer);
 }
 return Validation.get('validate-cc-type').test(ccNumberContainer.value, ccNumberContainer);
 }],
 ['validate-cc-exp', 'Incorrect credit card expiration date', function(v, elm) {
 var ccExpMonth = v;
 var ccExpYear = $('ccsave_expiration_yr').value;
 var currentTime = new Date();
 var currentMonth = currentTime.getMonth() + 1;
 var currentYear = currentTime.getFullYear();
 if (ccExpMonth < currentMonth && ccExpYear == currentYear) {
 return false;
 }
 return true;
 }],
 ['validate-cc-cvn', 'Please enter a valid credit card verification number.', function(v, elm) {
 var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_cid')) + '_cc_type');
 if (!ccTypeContainer) {
 return true;
 }
 var ccType = ccTypeContainer.value;

 if (typeof Validation.creditCartTypes.get(ccType) == 'undefined') {
 return false;
 }

 var re = Validation.creditCartTypes.get(ccType)[1];

 if (v.match(re)) {
 return true;
 }

 return false;
 }],
 ['validate-ajax', '', function(v, elm) { return true; }],
 ['validate-data', 'Please use only letters (a-z or A-Z), numbers (0-9) or underscore(_) in this field, first character should be a letter.', function (v) {
 if(v != '' && v) {
 return /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);
 }
 return true;
 }],
 ['validate-css-length', 'Please input a valid CSS-length. For example 100px or 77pt or 20em or .5ex or 50%', function (v) {
 if (v != '' && v) {
 return /^[0-9\.]+(px|pt|em|ex|%)?$/.test(v) && (!(/\..*\./.test(v))) && !(/\.$/.test(v));
 }
 return true;
 }],
 ['validate-length', 'Maximum length exceeded.', function (v, elm) {
 var re = new RegExp(/^maximum-length-[0-9]+$/);
 var result = true;
 $w(elm.className).each(function(name, index) {
 if (name.match(re) && result) {
 var length = name.split('-')[2];
 result = (v.length <= length);
 }
 });
 return result;
 }]
]);


// Credit Card Validation Javascript
// copyright 12th May 2003, by Stephen Chapman, Felgall Pty Ltd

// You have permission to copy and use this javascript provided that
// the content of the script is not changed in any way.

function validateCreditCard(s) {
 // remove non-numerics
 var v = "0123456789";
 var w = "";
 for (i=0; i < s.length; i++) {
 x = s.charAt(i);
 if (v.indexOf(x,0) != -1)
 w += x;
 }
 // validate number
 j = w.length / 2;
 k = Math.floor(j);
 m = Math.ceil(j) - k;
 c = 0;
 for (i=0; i<k; i++) {
 a = w.charAt(i*2+m) * 2;
 c += a > 9 ? Math.floor(a/10 + a%10) : a;
 }
 for (i=0; i<k+m; i++) c += w.charAt(i*2+1-m) * 1;
 return (c%10 == 0);
}

function removeDelimiters (v) {
 v = v.replace(/\s/g, '');
 v = v.replace(/\-/g, '');
 return v;
}

function parseNumber(v)
{
 if (typeof v != 'string') {
 return parseFloat(v);
 }

 var isDot = v.indexOf('.');
 var isComa = v.indexOf(',');

 if (isDot != -1 && isComa != -1) {
 if (isComa > isDot) {
 v = v.replace('.', '').replace(',', '.');
 }
 else {
 v = v.replace(',', '');
 }
 }
 else if (isComa != -1) {
 v = v.replace(',', '.');
 }

 return parseFloat(v);
}

/**
 * Hash with credit card types wich can be simply extended in payment modules
 * 0 - regexp for card number
 * 1 - regexp for cvn
 * 2 - check or not credit card number trough Luhn algorithm by
 * function validateCreditCard wich you can find above in this file
 */
Validation.creditCartTypes = $H({
 'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
 'MC': [new RegExp('^5[1-5][0-9]{14}$'), new RegExp('^[0-9]{3}$'), true],
 'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],
 'DI': [new RegExp('^6011[0-9]{12}$'), new RegExp('^[0-9]{3}$'), true],
 'SS': [new RegExp('^((6759[0-9]{12})|(49[013][1356][0-9]{13})|(633[34][0-9]{12})|(633110[0-9]{10})|(564182[0-9]{10}))([0-9]{2,3})?$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
 'OT': [false, new RegExp('^([0-9]{3}|[0-9]{4})?$'), false]
});

// script.aculo.us builder.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

// Copyright (c) 2005-2009 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.3, Thu Oct 08 11:23:33 +0200 2009

// Copyright (c) 2005-2009 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) + .5;
 },
 reverse: function(pos) {
 return 1-pos;
 },
 flicker: function(pos) {
 var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
 return pos > 1 ? 1 : pos;
 },
 wobble: function(pos) {
 return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
 },
 pulse: function(pos, pulses) {
 return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
 },
 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, options) {
 element = $(element);
 effect = (effect || 'appear').toLowerCase();
 
 return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
 }, 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) {
 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;

 this.render = (function() {
 function dispatch(effect, eventName) {
 if (effect.options[eventName + 'Internal'])
 effect.options[eventName + 'Internal'](effect);
 if (effect.options[eventName])
 effect.options[eventName](effect);
 }

 return function(pos) {
 if (this.state === "idle") {
 this.state = "running";
 dispatch(this, 'beforeSetup');
 if (this.setup) this.setup();
 dispatch(this, 'afterSetup');
 }
 if (this.state === "running") {
 pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
 this.position = pos;
 dispatch(this, 'beforeUpdate');
 if (this.update) this.update(pos);
 dispatch(this, '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();

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

 return new Effect.Tween(null,
 scrollOffsets.top,
 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] || { },
 oldOpacity = element.getInlineOpacity(),
 transition = options.transition || Effect.Transitions.linear,
 reverser = function(pos){
 return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
 };

 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 dragdrop.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

// Copyright (c) 2005-2009 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/

if(Object.isUndefined(Effect))
 throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
 drops: [],

 remove: function(element) {
 this.drops = this.drops.reject(function(d) { return d.element==$(element) });
 },

 add: function(element) {
 element = $(element);
 var options = Object.extend({
 greedy: true,
 hoverclass: null,
 tree: false
 }, arguments[1] || { });

 // cache containers
 if(options.containment) {
 options._containers = [];
 var containment = options.containment;
 if(Object.isArray(containment)) {
 containment.each( function(c) { options._containers.push($(c)) });
 } else {
 options._containers.push($(containment));
 }
 }

 if(options.accept) options.accept = [options.accept].flatten();

 Element.makePositioned(element); // fix IE
 options.element = element;

 this.drops.push(options);
 },

 findDeepestChild: function(drops) {
 deepest = drops[0];

 for (i = 1; i < drops.length; ++i)
 if (Element.isParent(drops[i].element, deepest.element))
 deepest = drops[i];

 return deepest;
 },

 isContained: function(element, drop) {
 var containmentNode;
 if(drop.tree) {
 containmentNode = element.treeNode;
 } else {
 containmentNode = element.parentNode;
 }
 return drop._containers.detect(function(c) { return containmentNode == c });
 },

 isAffected: function(point, element, drop) {
 return (
 (drop.element!=element) &&
 ((!drop._containers) ||
 this.isContained(element, drop)) &&
 ((!drop.accept) ||
 (Element.classNames(element).detect(
 function(v) { return drop.accept.include(v) } ) )) &&
 Position.within(drop.element, point[0], point[1]) );
 },

 deactivate: function(drop) {
 if(drop.hoverclass)
 Element.removeClassName(drop.element, drop.hoverclass);
 this.last_active = null;
 },

 activate: function(drop) {
 if(drop.hoverclass)
 Element.addClassName(drop.element, drop.hoverclass);
 this.last_active = drop;
 },

 show: function(point, element) {
 if(!this.drops.length) return;
 var drop, affected = [];

 this.drops.each( function(drop) {
 if(Droppables.isAffected(point, element, drop))
 affected.push(drop);
 });

 if(affected.length>0)
 drop = Droppables.findDeepestChild(affected);

 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
 if (drop) {
 Position.within(drop.element, point[0], point[1]);
 if(drop.onHover)
 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));

 if (drop != this.last_active) Droppables.activate(drop);
 }
 },

 fire: function(event, element) {
 if(!this.last_active) return;
 Position.prepare();

 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
 if (this.last_active.onDrop) {
 this.last_active.onDrop(element, this.last_active.element, event);
 return true;
 }
 },

 reset: function() {
 if(this.last_active)
 this.deactivate(this.last_active);
 }
};

var Draggables = {
 drags: [],
 observers: [],

 register: function(draggable) {
 if(this.drags.length == 0) {
 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
 this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
 this.eventKeypress = this.keyPress.bindAsEventListener(this);

 Event.observe(document, "mouseup", this.eventMouseUp);
 Event.observe(document, "mousemove", this.eventMouseMove);
 Event.observe(document, "keypress", this.eventKeypress);
 }
 this.drags.push(draggable);
 },

 unregister: function(draggable) {
 this.drags = this.drags.reject(function(d) { return d==draggable });
 if(this.drags.length == 0) {
 Event.stopObserving(document, "mouseup", this.eventMouseUp);
 Event.stopObserving(document, "mousemove", this.eventMouseMove);
 Event.stopObserving(document, "keypress", this.eventKeypress);
 }
 },

 activate: function(draggable) {
 if(draggable.options.delay) {
 this._timeout = setTimeout(function() {
 Draggables._timeout = null;
 window.focus();
 Draggables.activeDraggable = draggable;
 }.bind(this), draggable.options.delay);
 } else {
 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
 this.activeDraggable = draggable;
 }
 },

 deactivate: function() {
 this.activeDraggable = null;
 },

 updateDrag: function(event) {
 if(!this.activeDraggable) return;
 var pointer = [Event.pointerX(event), Event.pointerY(event)];
 // Mozilla-based browsers fire successive mousemove events with
 // the same coordinates, prevent needless redrawing (moz bug?)
 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
 this._lastPointer = pointer;

 this.activeDraggable.updateDrag(event, pointer);
 },

 endDrag: function(event) {
 if(this._timeout) {
 clearTimeout(this._timeout);
 this._timeout = null;
 }
 if(!this.activeDraggable) return;
 this._lastPointer = null;
 this.activeDraggable.endDrag(event);
 this.activeDraggable = null;
 },

 keyPress: function(event) {
 if(this.activeDraggable)
 this.activeDraggable.keyPress(event);
 },

 addObserver: function(observer) {
 this.observers.push(observer);
 this._cacheObserverCallbacks();
 },

 removeObserver: function(element) { // element instead of observer fixes mem leaks
 this.observers = this.observers.reject( function(o) { return o.element==element });
 this._cacheObserverCallbacks();
 },

 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
 if(this[eventName+'Count'] > 0)
 this.observers.each( function(o) {
 if(o[eventName]) o[eventName](eventName, draggable, event);
 });
 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
 },

 _cacheObserverCallbacks: function() {
 ['onStart','onEnd','onDrag'].each( function(eventName) {
 Draggables[eventName+'Count'] = Draggables.observers.select(
 function(o) { return o[eventName]; }
 ).length;
 });
 }
};

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

var Draggable = Class.create({
 initialize: function(element) {
 var defaults = {
 handle: false,
 reverteffect: function(element, top_offset, left_offset) {
 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
 queue: {scope:'_draggable', position:'end'}
 });
 },
 endeffect: function(element) {
 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
 queue: {scope:'_draggable', position:'end'},
 afterFinish: function(){
 Draggable._dragging[element] = false
 }
 });
 },
 zindex: 1000,
 revert: false,
 quiet: false,
 scroll: false,
 scrollSensitivity: 20,
 scrollSpeed: 15,
 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
 delay: 0
 };

 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
 Object.extend(defaults, {
 starteffect: function(element) {
 element._opacity = Element.getOpacity(element);
 Draggable._dragging[element] = true;
 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
 }
 });

 var options = Object.extend(defaults, arguments[1] || { });

 this.element = $(element);

 if(options.handle && Object.isString(options.handle))
 this.handle = this.element.down('.'+options.handle, 0);

 if(!this.handle) this.handle = $(options.handle);
 if(!this.handle) this.handle = this.element;

 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
 options.scroll = $(options.scroll);
 this._isScrollChild = Element.childOf(this.element, options.scroll);
 }

 Element.makePositioned(this.element); // fix IE

 this.options = options;
 this.dragging = false;

 this.eventMouseDown = this.initDrag.bindAsEventListener(this);
 Event.observe(this.handle, "mousedown", this.eventMouseDown);

 Draggables.register(this);
 },

 destroy: function() {
 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
 Draggables.unregister(this);
 },

 currentDelta: function() {
 return([
 parseInt(Element.getStyle(this.element,'left') || '0'),
 parseInt(Element.getStyle(this.element,'top') || '0')]);
 },

 initDrag: function(event) {
 if(!Object.isUndefined(Draggable._dragging[this.element]) &&
 Draggable._dragging[this.element]) return;
 if(Event.isLeftClick(event)) {
 // abort on form elements, fixes a Firefox issue
 var src = Event.element(event);
 if((tag_name = src.tagName.toUpperCase()) && (
 tag_name=='INPUT' ||
 tag_name=='SELECT' ||
 tag_name=='OPTION' ||
 tag_name=='BUTTON' ||
 tag_name=='TEXTAREA')) return;

 var pointer = [Event.pointerX(event), Event.pointerY(event)];
 var pos = this.element.cumulativeOffset();
 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });

 Draggables.activate(this);
 Event.stop(event);
 }
 },

 startDrag: function(event) {
 this.dragging = true;
 if(!this.delta)
 this.delta = this.currentDelta();

 if(this.options.zindex) {
 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
 this.element.style.zIndex = this.options.zindex;
 }

 if(this.options.ghosting) {
 this._clone = this.element.cloneNode(true);
 this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
 if (!this._originallyAbsolute)
 Position.absolutize(this.element);
 this.element.parentNode.insertBefore(this._clone, this.element);
 }

 if(this.options.scroll) {
 if (this.options.scroll == window) {
 var where = this._getWindowScroll(this.options.scroll);
 this.originalScrollLeft = where.left;
 this.originalScrollTop = where.top;
 } else {
 this.originalScrollLeft = this.options.scroll.scrollLeft;
 this.originalScrollTop = this.options.scroll.scrollTop;
 }
 }

 Draggables.notify('onStart', this, event);

 if(this.options.starteffect) this.options.starteffect(this.element);
 },

 updateDrag: function(event, pointer) {
 if(!this.dragging) this.startDrag(event);

 if(!this.options.quiet){
 Position.prepare();
 Droppables.show(pointer, this.element);
 }

 Draggables.notify('onDrag', this, event);

 this.draw(pointer);
 if(this.options.change) this.options.change(this);

 if(this.options.scroll) {
 this.stopScrolling();

 var p;
 if (this.options.scroll == window) {
 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
 } else {
 p = Position.page(this.options.scroll);
 p[0] += this.options.scroll.scrollLeft + Position.deltaX;
 p[1] += this.options.scroll.scrollTop + Position.deltaY;
 p.push(p[0]+this.options.scroll.offsetWidth);
 p.push(p[1]+this.options.scroll.offsetHeight);
 }
 var speed = [0,0];
 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
 this.startScrolling(speed);
 }

 // fix AppleWebKit rendering
 if(Prototype.Browser.WebKit) window.scrollBy(0,0);

 Event.stop(event);
 },

 finishDrag: function(event, success) {
 this.dragging = false;

 if(this.options.quiet){
 Position.prepare();
 var pointer = [Event.pointerX(event), Event.pointerY(event)];
 Droppables.show(pointer, this.element);
 }

 if(this.options.ghosting) {
 if (!this._originallyAbsolute)
 Position.relativize(this.element);
 delete this._originallyAbsolute;
 Element.remove(this._clone);
 this._clone = null;
 }

 var dropped = false;
 if(success) {
 dropped = Droppables.fire(event, this.element);
 if (!dropped) dropped = false;
 }
 if(dropped && this.options.onDropped) this.options.onDropped(this.element);
 Draggables.notify('onEnd', this, event);

 var revert = this.options.revert;
 if(revert && Object.isFunction(revert)) revert = revert(this.element);

 var d = this.currentDelta();
 if(revert && this.options.reverteffect) {
 if (dropped == 0 || revert != 'failure')
 this.options.reverteffect(this.element,
 d[1]-this.delta[1], d[0]-this.delta[0]);
 } else {
 this.delta = d;
 }

 if(this.options.zindex)
 this.element.style.zIndex = this.originalZ;

 if(this.options.endeffect)
 this.options.endeffect(this.element);

 Draggables.deactivate(this);
 Droppables.reset();
 },

 keyPress: function(event) {
 if(event.keyCode!=Event.KEY_ESC) return;
 this.finishDrag(event, false);
 Event.stop(event);
 },

 endDrag: function(event) {
 if(!this.dragging) return;
 this.stopScrolling();
 this.finishDrag(event, true);
 Event.stop(event);
 },

 draw: function(point) {
 var pos = this.element.cumulativeOffset();
 if(this.options.ghosting) {
 var r = Position.realOffset(this.element);
 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
 }

 var d = this.currentDelta();
 pos[0] -= d[0]; pos[1] -= d[1];

 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
 }

 var p = [0,1].map(function(i){
 return (point[i]-pos[i]-this.offset[i])
 }.bind(this));

 if(this.options.snap) {
 if(Object.isFunction(this.options.snap)) {
 p = this.options.snap(p[0],p[1],this);
 } else {
 if(Object.isArray(this.options.snap)) {
 p = p.map( function(v, i) {
 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
 } else {
 p = p.map( function(v) {
 return (v/this.options.snap).round()*this.options.snap }.bind(this));
 }
 }}

 var style = this.element.style;
 if((!this.options.constraint) || (this.options.constraint=='horizontal'))
 style.left = p[0] + "px";
 if((!this.options.constraint) || (this.options.constraint=='vertical'))
 style.top = p[1] + "px";

 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
 },

 stopScrolling: function() {
 if(this.scrollInterval) {
 clearInterval(this.scrollInterval);
 this.scrollInterval = null;
 Draggables._lastScrollPointer = null;
 }
 },

 startScrolling: function(speed) {
 if(!(speed[0] || speed[1])) return;
 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
 this.lastScrolled = new Date();
 this.scrollInterval = setInterval(this.scroll.bind(this), 10);
 },

 scroll: function() {
 var current = new Date();
 var delta = current - this.lastScrolled;
 this.lastScrolled = current;
 if(this.options.scroll == window) {
 with (this._getWindowScroll(this.options.scroll)) {
 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
 var d = delta / 1000;
 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
 }
 }
 } else {
 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
 }

 Position.prepare();
 Droppables.show(Draggables._lastPointer, this.element);
 Draggables.notify('onDrag', this);
 if (this._isScrollChild) {
 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
 if (Draggables._lastScrollPointer[0] < 0)
 Draggables._lastScrollPointer[0] = 0;
 if (Draggables._lastScrollPointer[1] < 0)
 Draggables._lastScrollPointer[1] = 0;
 this.draw(Draggables._lastScrollPointer);
 }

 if(this.options.change) this.options.change(this);
 },

 _getWindowScroll: function(w) {
 var T, L, W, H;
 with (w.document) {
 if (w.document.documentElement && documentElement.scrollTop) {
 T = documentElement.scrollTop;
 L = documentElement.scrollLeft;
 } else if (w.document.body) {
 T = body.scrollTop;
 L = body.scrollLeft;
 }
 if (w.innerWidth) {
 W = w.innerWidth;
 H = w.innerHeight;
 } else if (w.document.documentElement && documentElement.clientWidth) {
 W = documentElement.clientWidth;
 H = documentElement.clientHeight;
 } else {
 W = body.offsetWidth;
 H = body.offsetHeight;
 }
 }
 return { top: T, left: L, width: W, height: H };
 }
});

Draggable._dragging = { };

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

var SortableObserver = Class.create({
 initialize: function(element, observer) {
 this.element = $(element);
 this.observer = observer;
 this.lastValue = Sortable.serialize(this.element);
 },

 onStart: function() {
 this.lastValue = Sortable.serialize(this.element);
 },

 onEnd: function() {
 Sortable.unmark();
 if(this.lastValue != Sortable.serialize(this.element))
 this.observer(this.element)
 }
});

var Sortable = {
 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,

 sortables: { },

 _findRootElement: function(element) {
 while (element.tagName.toUpperCase() != "BODY") {
 if(element.id && Sortable.sortables[element.id]) return element;
 element = element.parentNode;
 }
 },

 options: function(element) {
 element = Sortable._findRootElement($(element));
 if(!element) return;
 return Sortable.sortables[element.id];
 },

 destroy: function(element){
 element = $(element);
 var s = Sortable.sortables[element.id];

 if(s) {
 Draggables.removeObserver(s.element);
 s.droppables.each(function(d){ Droppables.remove(d) });
 s.draggables.invoke('destroy');

 delete Sortable.sortables[s.element.id];
 }
 },

 create: function(element) {
 element = $(element);
 var options = Object.extend({
 element: element,
 tag: 'li', // assumes li children, override with tag: 'tagname'
 dropOnEmpty: false,
 tree: false,
 treeTag: 'ul',
 overlap: 'vertical', // one of 'vertical', 'horizontal'
 constraint: 'vertical', // one of 'vertical', 'horizontal', false
 containment: element, // also takes array of elements (or id's); or false
 handle: false, // or a CSS class
 only: false,
 delay: 0,
 hoverclass: null,
 ghosting: false,
 quiet: false,
 scroll: false,
 scrollSensitivity: 20,
 scrollSpeed: 15,
 format: this.SERIALIZE_RULE,

 // these take arrays of elements or ids and can be
 // used for better initialization performance
 elements: false,
 handles: false,

 onChange: Prototype.emptyFunction,
 onUpdate: Prototype.emptyFunction
 }, arguments[1] || { });

 // clear any old sortable with same element
 this.destroy(element);

 // build options for the draggables
 var options_for_draggable = {
 revert: true,
 quiet: options.quiet,
 scroll: options.scroll,
 scrollSpeed: options.scrollSpeed,
 scrollSensitivity: options.scrollSensitivity,
 delay: options.delay,
 ghosting: options.ghosting,
 constraint: options.constraint,
 handle: options.handle };

 if(options.starteffect)
 options_for_draggable.starteffect = options.starteffect;

 if(options.reverteffect)
 options_for_draggable.reverteffect = options.reverteffect;
 else
 if(options.ghosting) options_for_draggable.reverteffect = function(element) {
 element.style.top = 0;
 element.style.left = 0;
 };

 if(options.endeffect)
 options_for_draggable.endeffect = options.endeffect;

 if(options.zindex)
 options_for_draggable.zindex = options.zindex;

 // build options for the droppables
 var options_for_droppable = {
 overlap: options.overlap,
 containment: options.containment,
 tree: options.tree,
 hoverclass: options.hoverclass,
 onHover: Sortable.onHover
 };

 var options_for_tree = {
 onHover: Sortable.onEmptyHover,
 overlap: options.overlap,
 containment: options.containment,
 hoverclass: options.hoverclass
 };

 // fix for gecko engine
 Element.cleanWhitespace(element);

 options.draggables = [];
 options.droppables = [];

 // drop on empty handling
 if(options.dropOnEmpty || options.tree) {
 Droppables.add(element, options_for_tree);
 options.droppables.push(element);
 }

 (options.elements || this.findElements(element, options) || []).each( function(e,i) {
 var handle = options.handles ? $(options.handles[i]) :
 (options.handle ? $(e).select('.' + options.handle)[0] : e);
 options.draggables.push(
 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
 Droppables.add(e, options_for_droppable);
 if(options.tree) e.treeNode = element;
 options.droppables.push(e);
 });

 if(options.tree) {
 (Sortable.findTreeElements(element, options) || []).each( function(e) {
 Droppables.add(e, options_for_tree);
 e.treeNode = element;
 options.droppables.push(e);
 });
 }

 // keep reference
 this.sortables[element.identify()] = options;

 // for onupdate
 Draggables.addObserver(new SortableObserver(element, options.onUpdate));

 },

 // return all suitable-for-sortable elements in a guaranteed order
 findElements: function(element, options) {
 return Element.findChildren(
 element, options.only, options.tree ? true : false, options.tag);
 },

 findTreeElements: function(element, options) {
 return Element.findChildren(
 element, options.only, options.tree ? true : false, options.treeTag);
 },

 onHover: function(element, dropon, overlap) {
 if(Element.isParent(dropon, element)) return;

 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
 return;
 } else if(overlap>0.5) {
 Sortable.mark(dropon, 'before');
 if(dropon.previousSibling != element) {
 var oldParentNode = element.parentNode;
 element.style.visibility = "hidden"; // fix gecko rendering
 dropon.parentNode.insertBefore(element, dropon);
 if(dropon.parentNode!=oldParentNode)
 Sortable.options(oldParentNode).onChange(element);
 Sortable.options(dropon.parentNode).onChange(element);
 }
 } else {
 Sortable.mark(dropon, 'after');
 var nextElement = dropon.nextSibling || null;
 if(nextElement != element) {
 var oldParentNode = element.parentNode;
 element.style.visibility = "hidden"; // fix gecko rendering
 dropon.parentNode.insertBefore(element, nextElement);
 if(dropon.parentNode!=oldParentNode)
 Sortable.options(oldParentNode).onChange(element);
 Sortable.options(dropon.parentNode).onChange(element);
 }
 }
 },

 onEmptyHover: function(element, dropon, overlap) {
 var oldParentNode = element.parentNode;
 var droponOptions = Sortable.options(dropon);

 if(!Element.isParent(dropon, element)) {
 var index;

 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
 var child = null;

 if(children) {
 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);

 for (index = 0; index < children.length; index += 1) {
 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
 offset -= Element.offsetSize (children[index], droponOptions.overlap);
 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
 child = index + 1 < children.length ? children[index + 1] : null;
 break;
 } else {
 child = children[index];
 break;
 }
 }
 }

 dropon.insertBefore(element, child);

 Sortable.options(oldParentNode).onChange(element);
 droponOptions.onChange(element);
 }
 },

 unmark: function() {
 if(Sortable._marker) Sortable._marker.hide();
 },

 mark: function(dropon, position) {
 // mark on ghosting only
 var sortable = Sortable.options(dropon.parentNode);
 if(sortable && !sortable.ghosting) return;

 if(!Sortable._marker) {
 Sortable._marker =
 ($('dropmarker') || Element.extend(document.createElement('DIV'))).
 hide().addClassName('dropmarker').setStyle({position:'absolute'});
 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
 }
 var offsets = dropon.cumulativeOffset();
 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});

 if(position=='after')
 if(sortable.overlap == 'horizontal')
 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
 else
 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});

 Sortable._marker.show();
 },

 _tree: function(element, options, parent) {
 var children = Sortable.findElements(element, options) || [];

 for (var i = 0; i < children.length; ++i) {
 var match = children[i].id.match(options.format);

 if (!match) continue;

 var child = {
 id: encodeURIComponent(match ? match[1] : null),
 element: element,
 parent: parent,
 children: [],
 position: parent.children.length,
 container: $(children[i]).down(options.treeTag)
 };

 /* Get the element containing the children and recurse over it */
 if (child.container)
 this._tree(child.container, options, child);

 parent.children.push (child);
 }

 return parent;
 },

 tree: function(element) {
 element = $(element);
 var sortableOptions = this.options(element);
 var options = Object.extend({
 tag: sortableOptions.tag,
 treeTag: sortableOptions.treeTag,
 only: sortableOptions.only,
 name: element.id,
 format: sortableOptions.format
 }, arguments[1] || { });

 var root = {
 id: null,
 parent: null,
 children: [],
 container: element,
 position: 0
 };

 return Sortable._tree(element, options, root);
 },

 /* Construct a [i] index for a particular node */
 _constructIndex: function(node) {
 var index = '';
 do {
 if (node.id) index = '[' + node.position + ']' + index;
 } while ((node = node.parent) != null);
 return index;
 },

 sequence: function(element) {
 element = $(element);
 var options = Object.extend(this.options(element), arguments[1] || { });

 return $(this.findElements(element, options) || []).map( function(item) {
 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
 });
 },

 setSequence: function(element, new_sequence) {
 element = $(element);
 var options = Object.extend(this.options(element), arguments[2] || { });

 var nodeMap = { };
 this.findElements(element, options).each( function(n) {
 if (n.id.match(options.format))
 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
 n.parentNode.removeChild(n);
 });

 new_sequence.each(function(ident) {
 var n = nodeMap[ident];
 if (n) {
 n[1].appendChild(n[0]);
 delete nodeMap[ident];
 }
 });
 },

 serialize: function(element) {
 element = $(element);
 var options = Object.extend(Sortable.options(element), arguments[1] || { });
 var name = encodeURIComponent(
 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);

 if (options.tree) {
 return Sortable.tree(element, arguments[1]).children.map( function (item) {
 return [name + Sortable._constructIndex(item) + "[id]=" +
 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
 }).flatten().join('&');
 } else {
 return Sortable.sequence(element, arguments[1]).map( function(item) {
 return name + "[]=" + encodeURIComponent(item);
 }).join('&');
 }
 }
};

// Returns true if child is contained within element
Element.isParent = function(child, element) {
 if (!child.parentNode || child == element) return false;
 if (child.parentNode == element) return true;
 return Element.isParent(child.parentNode, element);
};

Element.findChildren = function(element, only, recursive, tagName) {
 if(!element.hasChildNodes()) return null;
 tagName = tagName.toUpperCase();
 if(only) only = [only].flatten();
 var elements = [];
 $A(element.childNodes).each( function(e) {
 if(e.tagName && e.tagName.toUpperCase()==tagName &&
 (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
 elements.push(e);
 if(recursive) {
 var grandchildren = Element.findChildren(e, only, recursive, tagName);
 if(grandchildren) elements.push(grandchildren);
 }
 });

 return (elements.length>0 ? elements.flatten() : []);
};

Element.offsetSize = function (element, type) {
 return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
};
// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005-2009 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.unescapeHTML();
 },
 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));
 }
});
// script.aculo.us slider.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

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

if (!Control) var Control = { };

// options:
// axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
// onChange(value)
// onSlide(value)
Control.Slider = Class.create({
 initialize: function(handle, track, options) {
 var slider = this;

 if (Object.isArray(handle)) {
 this.handles = handle.collect( function(e) { return $(e) });
 } else {
 this.handles = [$(handle)];
 }

 this.track = $(track);
 this.options = options || { };

 this.axis = this.options.axis || 'horizontal';
 this.increment = this.options.increment || 1;
 this.step = parseInt(this.options.step || '1');
 this.range = this.options.range || $R(0,1);

 this.value = 0; // assure backwards compat
 this.values = this.handles.map( function() { return 0 });
 this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
 this.options.startSpan = $(this.options.startSpan || null);
 this.options.endSpan = $(this.options.endSpan || null);

 this.restricted = this.options.restricted || false;

 this.maximum = this.options.maximum || this.range.end;
 this.minimum = this.options.minimum || this.range.start;

 // Will be used to align the handle onto the track, if necessary
 this.alignX = parseInt(this.options.alignX || '0');
 this.alignY = parseInt(this.options.alignY || '0');

 this.trackLength = this.maximumOffset() - this.minimumOffset();

 this.handleLength = this.isVertical() ?
 (this.handles[0].offsetHeight != 0 ?
 this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
 (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
 this.handles[0].style.width.replace(/px$/,""));

 this.active = false;
 this.dragging = false;
 this.disabled = false;

 if (this.options.disabled) this.setDisabled();

 // Allowed values array
 this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
 if (this.allowedValues) {
 this.minimum = this.allowedValues.min();
 this.maximum = this.allowedValues.max();
 }

 this.eventMouseDown = this.startDrag.bindAsEventListener(this);
 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
 this.eventMouseMove = this.update.bindAsEventListener(this);

 // Initialize handles in reverse (make sure first handle is active)
 this.handles.each( function(h,i) {
 i = slider.handles.length-1-i;
 slider.setValue(parseFloat(
 (Object.isArray(slider.options.sliderValue) ?
 slider.options.sliderValue[i] : slider.options.sliderValue) ||
 slider.range.start), i);
 h.makePositioned().observe("mousedown", slider.eventMouseDown);
 });

 this.track.observe("mousedown", this.eventMouseDown);
 document.observe("mouseup", this.eventMouseUp);
 document.observe("mousemove", this.eventMouseMove);

 this.initialized = true;
 },
 dispose: function() {
 var slider = this;
 Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
 Event.stopObserving(document, "mouseup", this.eventMouseUp);
 Event.stopObserving(document, "mousemove", this.eventMouseMove);
 this.handles.each( function(h) {
 Event.stopObserving(h, "mousedown", slider.eventMouseDown);
 });
 },
 setDisabled: function(){
 this.disabled = true;
 },
 setEnabled: function(){
 this.disabled = false;
 },
 getNearestValue: function(value){
 if (this.allowedValues){
 if (value >= this.allowedValues.max()) return(this.allowedValues.max());
 if (value <= this.allowedValues.min()) return(this.allowedValues.min());

 var offset = Math.abs(this.allowedValues[0] - value);
 var newValue = this.allowedValues[0];
 this.allowedValues.each( function(v) {
 var currentOffset = Math.abs(v - value);
 if (currentOffset <= offset){
 newValue = v;
 offset = currentOffset;
 }
 });
 return newValue;
 }
 if (value > this.range.end) return this.range.end;
 if (value < this.range.start) return this.range.start;
 return value;
 },
 setValue: function(sliderValue, handleIdx){
 if (!this.active) {
 this.activeHandleIdx = handleIdx || 0;
 this.activeHandle = this.handles[this.activeHandleIdx];
 this.updateStyles();
 }
 handleIdx = handleIdx || this.activeHandleIdx || 0;
 if (this.initialized && this.restricted) {
 if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
 sliderValue = this.values[handleIdx-1];
 if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
 sliderValue = this.values[handleIdx+1];
 }
 sliderValue = this.getNearestValue(sliderValue);
 this.values[handleIdx] = sliderValue;
 this.value = this.values[0]; // assure backwards compat

 this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
 this.translateToPx(sliderValue);

 this.drawSpans();
 if (!this.dragging || !this.event) this.updateFinished();
 },
 setValueBy: function(delta, handleIdx) {
 this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
 handleIdx || this.activeHandleIdx || 0);
 },
 translateToPx: function(value) {
 return Math.round(
 ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
 (value - this.range.start)) + "px";
 },
 translateToValue: function(offset) {
 return ((offset/(this.trackLength-this.handleLength) *
 (this.range.end-this.range.start)) + this.range.start);
 },
 getRange: function(range) {
 var v = this.values.sortBy(Prototype.K);
 range = range || 0;
 return $R(v[range],v[range+1]);
 },
 minimumOffset: function(){
 return(this.isVertical() ? this.alignY : this.alignX);
 },
 maximumOffset: function(){
 return(this.isVertical() ?
 (this.track.offsetHeight != 0 ? this.track.offsetHeight :
 this.track.style.height.replace(/px$/,"")) - this.alignY :
 (this.track.offsetWidth != 0 ? this.track.offsetWidth :
 this.track.style.width.replace(/px$/,"")) - this.alignX);
 },
 isVertical: function(){
 return (this.axis == 'vertical');
 },
 drawSpans: function() {
 var slider = this;
 if (this.spans)
 $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
 if (this.options.startSpan)
 this.setSpan(this.options.startSpan,
 $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
 if (this.options.endSpan)
 this.setSpan(this.options.endSpan,
 $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
 },
 setSpan: function(span, range) {
 if (this.isVertical()) {
 span.style.top = this.translateToPx(range.start);
 span.style.height = this.translateToPx(range.end - range.start + this.range.start);
 } else {
 span.style.left = this.translateToPx(range.start);
 span.style.width = this.translateToPx(range.end - range.start + this.range.start);
 }
 },
 updateStyles: function() {
 this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
 Element.addClassName(this.activeHandle, 'selected');
 },
 startDrag: function(event) {
 if (Event.isLeftClick(event)) {
 if (!this.disabled){
 this.active = true;

 var handle = Event.element(event);
 var pointer = [Event.pointerX(event), Event.pointerY(event)];
 var track = handle;
 if (track==this.track) {
 var offsets = this.track.cumulativeOffset();
 this.event = event;
 this.setValue(this.translateToValue(
 (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
 ));
 var offsets = this.activeHandle.cumulativeOffset();
 this.offsetX = (pointer[0] - offsets[0]);
 this.offsetY = (pointer[1] - offsets[1]);
 } else {
 // find the handle (prevents issues with Safari)
 while((this.handles.indexOf(handle) == -1) && handle.parentNode)
 handle = handle.parentNode;

 if (this.handles.indexOf(handle)!=-1) {
 this.activeHandle = handle;
 this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
 this.updateStyles();

 var offsets = this.activeHandle.cumulativeOffset();
 this.offsetX = (pointer[0] - offsets[0]);
 this.offsetY = (pointer[1] - offsets[1]);
 }
 }
 }
 Event.stop(event);
 }
 },
 update: function(event) {
 if (this.active) {
 if (!this.dragging) this.dragging = true;
 this.draw(event);
 if (Prototype.Browser.WebKit) window.scrollBy(0,0);
 Event.stop(event);
 }
 },
 draw: function(event) {
 var pointer = [Event.pointerX(event), Event.pointerY(event)];
 var offsets = this.track.cumulativeOffset();
 pointer[0] -= this.offsetX + offsets[0];
 pointer[1] -= this.offsetY + offsets[1];
 this.event = event;
 this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
 if (this.initialized && this.options.onSlide)
 this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
 },
 endDrag: function(event) {
 if (this.active && this.dragging) {
 this.finishDrag(event, true);
 Event.stop(event);
 }
 this.active = false;
 this.dragging = false;
 },
 finishDrag: function(event, success) {
 this.active = false;
 this.dragging = false;
 this.updateFinished();
 },
 updateFinished: function() {
 if (this.initialized && this.options.onChange)
 this.options.onChange(this.values.length>1 ? this.values : this.value, this);
 this.event = null;
 }
});
/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE_AFL.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Magento to newer
 * versions in the future. If you wish to customize Magento for your
 * needs please refer to http://www.magentocommerce.com for more information.
 *
 * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 */
function popWin(url,win,para) {
 var win = window.open(url,win,para);
 win.focus();
}

function setLocation(url){
 window.location.href = url;
}

function setPLocation(url, setFocus){
 if( setFocus ) {
 window.opener.focus();
 }
 window.opener.location.href = url;
}

function setLanguageCode(code, fromCode){
 //TODO: javascript cookies have different domain and path than php cookies
 var href = window.location.href;
 var after = '', dash;
 if (dash = href.match(/\#(.*)$/)) {
 href = href.replace(/\#(.*)$/, '');
 after = dash[0];
 }

 if (href.match(/[?]/)) {
 var re = /([?&]store=)[a-z0-9_]*/;
 if (href.match(re)) {
 href = href.replace(re, '$1'+code);
 } else {
 href += '&store='+code;
 }

 var re = /([?&]from_store=)[a-z0-9_]*/;
 if (href.match(re)) {
 href = href.replace(re, '');
 }
 } else {
 href += '?store='+code;
 }
 if (typeof(fromCode) != 'undefined') {
 href += '&from_store='+fromCode;
 }
 href += after;

 setLocation(href);
}

/**
 * Add classes to specified elements.
 * Supported classes are: 'odd', 'even', 'first', 'last'
 *
 * @param elements - array of elements to be decorated
 * [@param decorateParams] - array of classes to be set. If omitted, all available will be used
 */
function decorateGeneric(elements, decorateParams)
{
 var allSupportedParams = ['odd', 'even', 'first', 'last'];
 var _decorateParams = {};
 var total = elements.length;

 if (total) {
 // determine params called
 if (typeof(decorateParams) == 'undefined') {
 decorateParams = allSupportedParams;
 }
 if (!decorateParams.length) {
 return;
 }
 for (var k in allSupportedParams) {
 _decorateParams[allSupportedParams[k]] = false;
 }
 for (var k in decorateParams) {
 _decorateParams[decorateParams[k]] = true;
 }

 // decorate elements
 // elements[0].addClassName('first'); // will cause bug in IE (#5587)
 if (_decorateParams.first) {
 Element.addClassName(elements[0], 'first');
 }
 if (_decorateParams.last) {
 Element.addClassName(elements[total-1], 'last');
 }
 for (var i = 0; i < total; i++) {
 if ((i + 1) % 2 == 0) {
 if (_decorateParams.even) {
 Element.addClassName(elements[i], 'even');
 }
 }
 else {
 if (_decorateParams.odd) {
 Element.addClassName(elements[i], 'odd');
 }
 }
 }
 }
}

/**
 * Decorate table rows and cells, tbody etc
 * @see decorateGeneric()
 */
function decorateTable(table, options) {
 var table = $(table);
 if (table) {
 // set default options
 var _options = {
 'tbody' : false,
 'tbody tr' : ['odd', 'even', 'first', 'last'],
 'thead tr' : ['first', 'last'],
 'tfoot tr' : ['first', 'last'],
 'tr td' : ['last']
 };
 // overload options
 if (typeof(options) != 'undefined') {
 for (var k in options) {
 _options[k] = options[k];
 }
 }
 // decorate
 if (_options['tbody']) {
 decorateGeneric(table.select('tbody'), _options['tbody']);
 }
 if (_options['tbody tr']) {
 decorateGeneric(table.select('tbody tr'), _options['tbody tr']);
 }
 if (_options['thead tr']) {
 decorateGeneric(table.select('thead tr'), _options['thead tr']);
 }
 if (_options['tfoot tr']) {
 decorateGeneric(table.select('tfoot tr'), _options['tfoot tr']);
 }
 if (_options['tr td']) {
 var allRows = table.select('tr');
 if (allRows.length) {
 for (var i = 0; i < allRows.length; i++) {
 decorateGeneric(allRows[i].getElementsByTagName('TD'), _options['tr td']);
 }
 }
 }
 }
}

/**
 * Set "odd", "even" and "last" CSS classes for list items
 * @see decorateGeneric()
 */
function decorateList(list, nonRecursive) {
 if ($(list)) {
 if (typeof(nonRecursive) == 'undefined') {
 var items = $(list).select('li')
 }
 else {
 var items = $(list).childElements();
 }
 decorateGeneric(items, ['odd', 'even', 'last']);
 }
}

/**
 * Set "odd", "even" and "last" CSS classes for list items
 * @see decorateGeneric()
 */
function decorateDataList(list) {
 list = $(list);
 if (list) {
 decorateGeneric(list.select('dt'), ['odd', 'even', 'last']);
 decorateGeneric(list.select('dd'), ['odd', 'even', 'last']);
 }
}

/**
 * Formats currency using patern
 * format - JSON (pattern, decimal, decimalsDelimeter, groupsDelimeter)
 * showPlus - true (always show '+'or '-'),
 * false (never show '-' even if number is negative)
 * null (show '-' if number is negative)
 */

function formatCurrency(price, format, showPlus){
 precision = isNaN(format.precision = Math.abs(format.precision)) ? 6 : format.precision;
 requiredPrecision = isNaN(format.requiredPrecision = Math.abs(format.requiredPrecision)) ? 2 : format.requiredPrecision;

 //precision = (precision > requiredPrecision) ? precision : requiredPrecision;
 //for now we don't need this difference so precision is requiredPrecision
 precision = requiredPrecision;

 integerRequired = isNaN(format.integerRequired = Math.abs(format.integerRequired)) ? 1 : format.integerRequired;

 decimalSymbol = format.decimalSymbol == undefined ? "," : format.decimalSymbol;
 groupSymbol = format.groupSymbol == undefined ? "." : format.groupSymbol;
 groupLength = format.groupLength == undefined ? 3 : format.groupLength;

 if (showPlus == undefined || showPlus == true) {
 s = price < 0 ? "-" : ( showPlus ? "+" : "");
 } else if (showPlus == false) {
 s = '';
 }

 i = parseInt(price = Math.abs(+price || 0).toFixed(precision)) + "";
 pad = (i.length < integerRequired) ? (integerRequired - i.length) : 0;
 while (pad) { i = '0' + i; pad--; }

 j = (j = i.length) > groupLength ? j % groupLength : 0;
 re = new RegExp("(\\d{" + groupLength + "})(?=\\d)", "g");

 /**
 * replace(/-/, 0) is only for fixing Safari bug which appears
 * when Math.abs(0).toFixed() executed on "0" number.
 * Result is "0.-0" :(
 */
 r = (j ? i.substr(0, j) + groupSymbol : "") + i.substr(j).replace(re, "$1" + groupSymbol) + (precision ? decimalSymbol + Math.abs(price - i).toFixed(precision).replace(/-/, 0).slice(2) : "")

 if (format.pattern.indexOf('{sign}') == -1) {
 pattern = s + format.pattern;
 } else {
 pattern = format.pattern.replace('{sign}', s);
 }

 return pattern.replace('%s', r).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
};

function expandDetails(el, childClass) {
 if (Element.hasClassName(el,'show-details')) {
 $$(childClass).each(function(item){item.hide()});
 Element.removeClassName(el,'show-details');
 }
 else {
 $$(childClass).each(function(item){item.show()});
 Element.addClassName(el,'show-details');
 }
}

// Version 1.0
var isIE = navigator.appVersion.match(/MSIE/) == "MSIE";

if (!window.Varien)
 var Varien = new Object();

Varien.showLoading = function(){
 Element.show('loading-process');
}
Varien.hideLoading = function(){
 Element.hide('loading-process');
}
Varien.GlobalHandlers = {
 onCreate: function() {
 Varien.showLoading();
 },

 onComplete: function() {
 if(Ajax.activeRequestCount == 0) {
 Varien.hideLoading();
 }
 }
};

Ajax.Responders.register(Varien.GlobalHandlers);

/**
 * Quick Search form client model
 */
Varien.searchForm = Class.create();
Varien.searchForm.prototype = {
 initialize : function(form, field, emptyText){
 this.form = $(form);
 this.field = $(field);
 this.emptyText = emptyText;

 Event.observe(this.form, 'submit', this.submit.bind(this));
 Event.observe(this.field, 'focus', this.focus.bind(this));
 Event.observe(this.field, 'blur', this.blur.bind(this));
 this.blur();
 },

 submit : function(event){
 if (this.field.value == this.emptyText || this.field.value == ''){
 Event.stop(event);
 return false;
 }
 return true;
 },

 focus : function(event){
 if(this.field.value==this.emptyText){
 this.field.value='';
 }

 },

 blur : function(event){
 if(this.field.value==''){
 this.field.value=this.emptyText;
 }
 },

 initAutocomplete : function(url, destinationElement){
 new Ajax.Autocompleter(
 this.field,
 destinationElement,
 url,
 {
 paramName: this.field.name,
 minChars: 2,
 updateElement: this._selectAutocompleteItem.bind(this),
 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});
 }

 }
 );
 },

 _selectAutocompleteItem : function(element){
 if(element.title){
 this.field.value = element.title;
 }
 this.form.submit();
 }
}

Varien.Tabs = Class.create();
Varien.Tabs.prototype = {
 initialize: function(selector) {
 var self=this;
 $$(selector+' a').each(this.initTab.bind(this));
 },

 initTab: function(el) {
 el.href = 'javascript:void(0)';
 if ($(el.parentNode).hasClassName('active')) {
 this.showContent(el);
 }
 el.observe('click', this.showContent.bind(this, el));
 },

 showContent: function(a) {
 var li = $(a.parentNode), ul = $(li.parentNode);
 ul.getElementsBySelector('li', 'ol').each(function(el){
 var contents = $(el.id+'_contents');
 if (el==li) {
 el.addClassName('active');
 contents.show();
 } else {
 el.removeClassName('active');
 contents.hide();
 }
 });
 }
}

Varien.DOB = Class.create();
Varien.DOB.prototype = {
 initialize: function(selector, required, format) {
 var el = $$(selector)[0];
 this.day = Element.select($(el), '.dob-day input')[0];
 this.month = Element.select($(el), '.dob-month input')[0];
 this.year = Element.select($(el), '.dob-year input')[0];
 this.dob = Element.select($(el), '.dob-full input')[0];
 this.advice = Element.select($(el), '.validation-advice')[0];
 this.required = required;
 this.format = format;

 this.day.validate = this.validate.bind(this);
 this.month.validate = this.validate.bind(this);
 this.year.validate = this.validate.bind(this);

 this.advice.hide();
 },

 validate: function() {
 var error = false;

 if (this.day.value=='' && this.month.value=='' && this.year.value=='') {
 if (this.required) {
 error = 'This date is a required value.';
 } else {
 this.dob.value = '';
 }
 } else if (this.day.value=='' || this.month.value=='' || this.year.value=='') {
 error = 'Please enter a valid full date.';
 } else {
 var date = new Date();
 if (this.day.value<1 || this.day.value>31) {
 error = 'Please enter a valid day (1-31).';
 } else if (this.month.value<1 || this.month.value>12) {
 error = 'Please enter a valid month (1-12).';
 } else if (this.year.value<1900 || this.year.value>date.getFullYear()) {
 error = 'Please enter a valid year (1900-'+date.getFullYear()+').';
 } else {
 this.dob.value = this.format.replace(/(%m|%b)/i, this.month.value).replace(/(%d|%e)/i, this.day.value).replace(/%y/i, this.year.value);
 var testDOB = this.month.value + '/' + this.day.value + '/'+ this.year.value;
 var test = new Date(testDOB);
 if (isNaN(test)) {
 error = 'Please enter a valid date.';
 }
 }
 }

 if (error !== false) {
 try {
 this.advice.innerHTML = Translator.translate(error);
 }
 catch (e) {
 this.advice.innerHTML = error;
 }
 this.advice.show();
 return false;
 }

 this.advice.hide();
 return true;
 }
}

Validation.addAllThese([
 ['validate-custom', ' ', function(v,elm) {
 return elm.validate();
 }]
]);

function truncateOptions() {
 $$('.truncated').each(function(element){
 Event.observe(element, 'mouseover', function(){
 if (element.down('div.truncated_full_value')) {
 element.down('div.truncated_full_value').addClassName('show')
 }
 });
 Event.observe(element, 'mouseout', function(){
 if (element.down('div.truncated_full_value')) {
 element.down('div.truncated_full_value').removeClassName('show')
 }
 });

 });
}
Event.observe(window, 'load', function(){
 truncateOptions();
});
/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE_AFL.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Magento to newer
 * versions in the future. If you wish to customize Magento for your
 * needs please refer to http://www.magentocommerce.com for more information.
 *
 * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 */
VarienForm = Class.create();
VarienForm.prototype = {
 initialize: function(formId, firstFieldFocus){
 this.form = $(formId);
 if (!this.form) {
 return;
 }
 this.cache = $A();
 this.currLoader = false;
 this.currDataIndex = false;
 this.validator = new Validation(this.form);
 this.elementFocus = this.elementOnFocus.bindAsEventListener(this);
 this.elementBlur = this.elementOnBlur.bindAsEventListener(this);
 this.childLoader = this.onChangeChildLoad.bindAsEventListener(this);
 this.highlightClass = 'highlight';
 this.extraChildParams = '';
 this.firstFieldFocus= firstFieldFocus || false;
 this.bindElements();
 if(this.firstFieldFocus){
 try{
 Form.Element.focus(Form.findFirstElement(this.form))
 }
 catch(e){}
 }
 },

 submit : function(url){
 if(this.validator && this.validator.validate()){
 this.form.submit();
 }
 return false;
 },

 bindElements:function (){
 var elements = Form.getElements(this.form);
 for (var row in elements) {
 if (elements[row].id) {
 Event.observe(elements[row],'focus',this.elementFocus);
 Event.observe(elements[row],'blur',this.elementBlur);
 }
 }
 },

 elementOnFocus: function(event){
 var element = Event.findElement(event, 'fieldset');
 if(element){
 Element.addClassName(element, this.highlightClass);
 }
 },

 elementOnBlur: function(event){
 var element = Event.findElement(event, 'fieldset');
 if(element){
 Element.removeClassName(element, this.highlightClass);
 }
 },

 setElementsRelation: function(parent, child, dataUrl, first){
 if (parent=$(parent)) {
 // TODO: array of relation and caching
 if (!this.cache[parent.id]){
 this.cache[parent.id] = $A();
 this.cache[parent.id]['child'] = child;
 this.cache[parent.id]['dataUrl'] = dataUrl;
 this.cache[parent.id]['data'] = $A();
 this.cache[parent.id]['first'] = first || false;
 }
 Event.observe(parent,'change',this.childLoader);
 }
 },

 onChangeChildLoad: function(event){
 element = Event.element(event);
 this.elementChildLoad(element);
 },

 elementChildLoad: function(element, callback){
 this.callback = callback || false;
 if (element.value) {
 this.currLoader = element.id;
 this.currDataIndex = element.value;
 if (this.cache[element.id]['data'][element.value]) {
 this.setDataToChild(this.cache[element.id]['data'][element.value]);
 }
 else{
 new Ajax.Request(this.cache[this.currLoader]['dataUrl'],{
 method: 'post',
 parameters: {"parent":element.value},
 onComplete: this.reloadChildren.bind(this)
 });
 }
 }
 },

 reloadChildren: function(transport){
 var data = eval('(' + transport.responseText + ')');
 this.cache[this.currLoader]['data'][this.currDataIndex] = data;
 this.setDataToChild(data);
 },

 setDataToChild: function(data){
 if (data.length) {
 var child = $(this.cache[this.currLoader]['child']);
 if (child){
 var html = '<select name="'+child.name+'" id="'+child.id+'" class="'+child.className+'" title="'+child.title+'" '+this.extraChildParams+'>';
 if(this.cache[this.currLoader]['first']){
 html+= '<option value="">'+this.cache[this.currLoader]['first']+'</option>';
 }
 for (var i in data){
 if(data[i].value) {
 html+= '<option value="'+data[i].value+'"';
 if(child.value && (child.value == data[i].value || child.value == data[i].label)){
 html+= ' selected';
 }
 html+='>'+data[i].label+'</option>';
 }
 }
 html+= '</select>';
 Element.insert(child, {before: html});
 Element.remove(child);
 }
 }
 else{
 var child = $(this.cache[this.currLoader]['child']);
 if (child){
 var html = '<input type="text" name="'+child.name+'" id="'+child.id+'" class="'+child.className+'" title="'+child.title+'" '+this.extraChildParams+'>';
 Element.insert(child, {before: html});
 Element.remove(child);
 }
 }

 this.bindElements();
 if (this.callback) {
 this.callback();
 }
 }
}

RegionUpdater = Class.create();
RegionUpdater.prototype = {
 initialize: function (countryEl, regionTextEl, regionSelectEl, regions, disableAction)
 {
 this.countryEl = $(countryEl);
 this.regionTextEl = $(regionTextEl);
 this.regionSelectEl = $(regionSelectEl);
 this.regions = regions;

 this.disableAction = (typeof disableAction=='undefined') ? 'hide' : disableAction;

 if (this.regionSelectEl.options.length<=1) {
 this.update();
 }

 Event.observe(this.countryEl, 'change', this.update.bind(this));
 },

 update: function()
 {
 if (this.regions[this.countryEl.value]) {
 var i, option, region, def;

 if (this.regionTextEl) {
 def = this.regionTextEl.value.toLowerCase();
 this.regionTextEl.value = '';
 }
 if (!def) {
 def = this.regionSelectEl.getAttribute('defaultValue');
 }

 this.regionSelectEl.options.length = 1;
 for (regionId in this.regions[this.countryEl.value]) {
 region = this.regions[this.countryEl.value][regionId];

 option = document.createElement('OPTION');
 option.value = regionId;
 option.text = region.name;

 if (this.regionSelectEl.options.add) {
 this.regionSelectEl.options.add(option);
 } else {
 this.regionSelectEl.appendChild(option);
 }

 if (regionId==def || region.name.toLowerCase()==def || region.code.toLowerCase()==def) {
 this.regionSelectEl.value = regionId;
 }
 }

 if (this.disableAction=='hide') {
 if (this.regionTextEl) {
 this.regionTextEl.style.display = 'none';
 }

 this.regionSelectEl.style.display = '';
 } else if (this.disableAction=='disable') {
 if (this.regionTextEl) {
 this.regionTextEl.disabled = true;
 }
 this.regionSelectEl.disabled = false;
 }
 this.setMarkDisplay(this.regionSelectEl, true);
 } else {
 if (this.disableAction=='hide') {
 if (this.regionTextEl) {
 this.regionTextEl.style.display = '';
 }
 this.regionSelectEl.style.display = 'none';
 Validation.reset(this.regionSelectEl);
 } else if (this.disableAction=='disable') {
 if (this.regionTextEl) {
 this.regionTextEl.disabled = false;
 }
 this.regionSelectEl.disabled = true;
 } else if (this.disableAction=='nullify') {
 this.regionSelectEl.options.length = 1;
 this.regionSelectEl.value = '';
 this.regionSelectEl.selectedIndex = 0;
 this.lastCountryId = '';
 }
 this.setMarkDisplay(this.regionSelectEl, false);
 }
 },

 setMarkDisplay: function(elem, display){
 elem = $(elem);
 var labelElement = elem.up(1).down('label > span.required') || 
 elem.up(2).down('label > span.required') ||
 elem.up(1).down('label.required > em') ||
 elem.up(2).down('label.required > em');
 if(labelElement) {
 display ? labelElement.show() : labelElement.hide();
 }
 }
}
/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE_AFL.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Magento to newer
 * versions in the future. If you wish to customize Magento for your
 * needs please refer to http://www.magentocommerce.com for more information.
 *
 * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 */
function toggleMenu(el, over)
{
 if (over) {
 Element.addClassName(el, 'over');
 }
 else {
 Element.removeClassName(el, 'over');
 }
}

/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE_AFL.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Magento to newer
 * versions in the future. If you wish to customize Magento for your
 * needs please refer to http://www.magentocommerce.com for more information.
 *
 * @category Mage
 * @package Js
 * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 */

var Translate = Class.create();
Translate.prototype = {
 initialize: function(data){
 this.data = $H(data);
 },

 translate : function(){
 var args = arguments;
 var text = arguments[0];

 if(this.data.get(text)){
 return this.data.get(text);
 }
 return text;
 },
 add : function() {
 if (arguments.length > 1) {
 this.data.set(arguments[0], arguments[1]);
 } else if (typeof arguments[0] =='object') {
 $H(arguments[0]).each(function (pair){
 this.data.set(pair.key, pair.value);
 }.bind(this));
 }
 }
}
/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE_AFL.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Magento to newer
 * versions in the future. If you wish to customize Magento for your
 * needs please refer to http://www.magentocommerce.com for more information.
 *
 * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 */
// old school cookie functions grabbed off the web

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

Mage.Cookies = {};
Mage.Cookies.set = function(name, value){
 var argv = arguments;
 var argc = arguments.length;
 var expires = (argc > 2) ? argv[2] : null;
 var path = (argc > 3) ? argv[3] : '/';
 var domain = (argc > 4) ? argv[4] : null;
 var secure = (argc > 5) ? argv[5] : false;
 document.cookie = name + "=" + escape (value) +
 ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
 ((path == null) ? "" : ("; path=" + path)) +
 ((domain == null) ? "" : ("; domain=" + domain)) +
 ((secure == true) ? "; secure" : "");
};

Mage.Cookies.get = function(name){
 var arg = name + "=";
 var alen = arg.length;
 var clen = document.cookie.length;
 var i = 0;
 var j = 0;
 while(i < clen){
 j = i + alen;
 if (document.cookie.substring(i, j) == arg)
 return Mage.Cookies.getCookieVal(j);
 i = document.cookie.indexOf(" ", i) + 1;
 if(i == 0)
 break;
 }
 return null;
};

Mage.Cookies.clear = function(name) {
 if(Mage.Cookies.get(name)){
 document.cookie = name + "=" +
 "; expires=Thu, 01-Jan-70 00:00:01 GMT";
 }
};

Mage.Cookies.getCookieVal = function(offset){
 var endstr = document.cookie.indexOf(";", offset);
 if(endstr == -1){
 endstr = document.cookie.length;
 }
 return unescape(document.cookie.substring(offset, endstr));
};
// retrive credit using ajax call

function fuseUserCredit(username){

var url =BASE_URL_PATH+'customer/account/fuseusercredit/uname/'+username+'/j/'+Math.random(); 

$('usercredit').innerHTML="&nbsp;&nbsp; <img src='"+IMG_URL+"/opc-ajax-loader.gif' title='Loding user credit' /> Loading user credit";
new Ajax.Request(url, { method: 'get', onSuccess: function(transport) 
{ 
 if (transport.responseText){
 $('usercredit').innerHTML=transport.responseText;
 }
 else{
 return false;
 }
 
 } }); 
}

// retrive user's recent order using ajax call

function fuseUserRecentOrder(username){
 var url =BASE_URL_PATH+'customer/account/fuseuserrecentorder/j/'+Math.random(); 
 $('recentorder').innerHTML="&nbsp;&nbsp; <img src='"+IMG_URL+"/opc-ajax-loader.gif' title='Loding recent order' /> Loading recent order";
 new Ajax.Request(url, { 
 method : 'post',
 parameters : { uname : username },
 onSuccess: function(transport) { 
 if (transport.responseText){
 $('recentorder').innerHTML = transport.responseText;
 }
 else {
 return false;
 }
 } 
 }); 
}


function validateSpecialChars(id) {
 //var string = document.form1.txt.value;
 var el=document.getElementById(id);
 var string = el.value;
 var initText=el.getAttribute('inittext');
 var iChars = "*|,\":<>[]{}`\';()@&$#%";
 
 for (var i = 0; i < string.length; i++) {
 if (iChars.indexOf(string.charAt(i)) != -1){
 //alert (initText+" contains illegal characters!");
 el.focus();
 return false;
 }
 
 }
 return true;
}

function validateField(){
var inputs=document.getElementsByClassName('sp-char');
//alert(inputs.length);
for(i=0;i<inputs.length;i++){
 var isValid=validateSpecialChars(inputs[i].id);
 if(isValid==false){
 return false;
 }
 }
return true;
}

function textOnClick(txt){
 var initText=txt.getAttribute('inittext');
 if(txt.value==initText){
 txt.value="";
 }
}
function textOnBlur(txt){
 var initText=txt.getAttribute('inittext');
 if(txt.value==""){
 txt.value=initText;
 }
}


function isUserNameExist(username){

if(username==null || username==""){
$('uname_status').innerHTML="";
return false;
}

var url =BASE_URL_PATH+'customer/account/login3/uname/'+username+'/j/'+Math.random(); 

// notice the use of a proxy to circumvent the Same Origin Policy. 
$('uname_status').innerHTML="<img src='/magento13/images/username_checking.gif' title='Checking username' />";
new Ajax.Request(url, { method: 'get', onSuccess: function(transport) 
{ 

 if (transport.responseText=="YES"){
 //alert(transport.responseText);
 //return;
 showValidatorBox('uname_status','Duplicate username');
 $('userExist').value='true';
 //$('uname_status').innerHTML="<img src='/magento4/images/cross.png' title='Duplicate username' />"; 
 return true;
 }
 else{
 //alert(transport.responseText);
 //return;
 $('userExist').value='false'; 
 //$('uname_status').innerHTML="<img src='/magento4/images/tick.png' title='Username not exists' />";
 //validateUserNameField(document.frmRegister);
 $('advice-validate-username-duplicate-username').hide();
 $('username').removeClassName('validation-failed');
 document.getElementById('uname_status').style.display='none';
 return false;
 }
 
 } }); 
}

function isPrimaryEmailExist(email){

 if(email==null || email==""){
 $('spn_email').innerHTML="";
 return false;
 }
 
 var accType = ($('bussiness_acc_type').checked)?'bussiness':'personal';
 

 var url =BASE_URL_PATH+'customer/account/login4/email/'+email+ '/acc_type/' + accType +'/j/'+Math.random(); 

 // notice the use of a proxy to circumvent the Same Origin Policy. 
 //$('spn_email').innerHTML="<img src='/magento13/images/username_checking.gif' title='Checking email' />";
 new Ajax.Request(url, { method: 'get', onSuccess: function(transport) 
 { 
 if (transport.responseText=="YES"){
 showValidatorBox('spn_email','Email address is already taken, <a href="javascript:forgotUsernamePopup();">click here</a> to recover the username for this account');
 $('pEmailExist').value='true';
 //$('uname_status').innerHTML="<img src='/magento4/images/cross.png' title='Duplicate username' />"; 
 return true;
 }
 else{
 $('pEmailExist').value='false'; 
 $('advice-validate-email-duplicate-email').hide();
 $('email').removeClassName('validation-failed');
 document.getElementById('spn_email').style.display='none';
 
 //$('uname_status').innerHTML="<img src='/magento4/images/tick.png' title='Username not exists' />";
 //validateUserNameField(document.frmRegister);
 return false;
 }
 
 } }); 
}


function validate_email(address) {
 var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
 //var address = document.forms[form_id].elements[email].value;
 if(reg.test(address) == false) {
 //alert('Invalid Email Address');
 return false;
 }
 return true;
}
function showValidatorBox(id,msg, cn){
 var spn=document.getElementById(id);
 if(!cn){
 spn.className='hint';
 }else{
 spn.className=cn;
 }
 var msgHTML=msg+'<span class="hint-pointer">&nbsp;</span>';
 spn.innerHTML=msgHTML;
 //spn.style.display='inline';
 
 /**
 * Added on 02-10-09
 */
 jQuery('span.hint').css('display','none');
 
 if( jQuery('pEmailExist').value == 'true'){
 if(jQuery('span#spn_email')){
 jQuery('span#spn_email').css('display','inline');
 }
 }else if(jQuery('userExist').value == 'true'){
 if(jQuery('span#uname_status')){
 jQuery('span#uname_status').css('display','inline');
 }
 }else{
 spn.style.display='inline';
 }
 
}

function showValidatorBox2(id,msg){
 var spn=document.getElementById('spnValidator');
 //spn.className='hint';
 var msgHTML=msg+'<span class="hint-pointer">&nbsp;</span>';
 spn.innerHTML=msgHTML;
 spn.style.left=getRealLeft(id);
 spn.style.top=getRealTop(id);
 spn.style.display='inline';
}

function getRealLeft(el){
xPos = document.getElementById(el).offsetLeft;
tempEl = document.getElementById(el).offsetParent;
//xPos += tempEl.offsetLeft;
while (tempEl != null) {
xPos += tempEl.offsetLeft;
tempEl = tempEl.offsetParent;
}
return xPos;
}

function getRealTop(el){
yPos = document.getElementById(el).offsetTop;
tempEl = document.getElementById(el).offsetParent;
//yPos += tempEl.offsetTop;
while (tempEl != null) {
yPos += tempEl.offsetTop;
tempEl = tempEl.offsetParent;
}
return yPos;
}

function passwordValidation(username){
numDigit=0;
numLCase=0;
numUCase=0;
for(i=0;i<username.length;i++){
if(username.charAt(i)>='A' && username.charAt(i)<='Z'){
numUCase+=1;
}
if(username.charAt(i)>='a' && username.charAt(i)<='z'){
numLCase+=1;
}
if(username.charAt(i)>='0' && username.charAt(i)<='9'){
numDigit+=1;
}

}

if(numUCase > 0 && numLCase > 0 && numDigit > 0){
return true;
}
else{
return false;
}

}

String.prototype.trim = function() {
 return this.replace(/^\s+|\s+$/g,"");
}

function validateUserNameField(frm){
/*if(!validateSpecialChars('username')){
 return "Please enter valid characters only";
}*/
if(frm.username.value==""){
 return "Enter the username for your account. Keep in mind username must be at least 6 characters in length.";
}
if(frm.username.value.length < 6){
 return 'Enter the username for your account. Keep in mind username must be at least 6 characters in length.';
}
isUserNameExist(frm.username.value);
document.getElementById('uname_status').style.display='none';
return "";
}

function userNameOnClick(frm){
 document.getElementById('uname_status').style.display='none';
 var msg=validateUserNameField(frm);
 if(msg!=""){
 showValidatorBox('uname_status',msg);
 }
}
function userNameOnBlur(frm){
 document.getElementById('uname_status').style.display='none';
 var msg=validateUserNameField(frm);
 if(msg!=""){
 frm.username.addClassName("invalid_input");
 }
 else{
 frm.username.removeClassName("invalid_input");
 }
}

function validatePasswordField(frm){
 
if(frm.password.value==""){
return 'Create the password for your account. Keep in mind your password must consist of a minimum of 8 characters, of which at least 1 must be upper case, 1 must be lower case, and 1 must be a number or digit.';
}
if(frm.password.value.length < 8){
return 'Create the password for your account. Keep in mind your password must consist of a minimum of 8 characters, of which at least 1 must be upper case, 1 must be lower case, and 1 must be a number or digit.';
}
if(!passwordValidation(frm.password.value)){
return 'Create the password for your account. Keep in mind your password must consist of a minimum of 8 characters, of which at least 1 must be upper case, 1 must be lower case, and 1 must be a number or digit.';
}

document.getElementById('spn_password').style.display='none';
return "";

}

function passwordOnClick(frm){
 document.getElementById('spn_password').style.display='none';
 var msg=validatePasswordField(frm);
 if(msg!=""){
 showValidatorBox('spn_password',msg);
 }
}
function passwordOnBlur(frm){
 document.getElementById('spn_password').style.display='none';
 var msg=validatePasswordField(frm);
 if(msg!=""){
 frm.password.addClassName("invalid_input");
 }
 else{
 frm.password.removeClassName("invalid_input");
 }
}



function validateConfirmPasswordField(frm){
if(frm.password.value!=frm.confirmation.value){
//showValidatorBox('spn_confirmation','Confirm password by rentering in this field.');
//return false;
return 'Confirm your password by re-entering it in this field.';
}
document.getElementById('spn_confirmPassword').style.display='none';
//return true;
return '';
}
function confirmPasswordOnClick(frm){
 document.getElementById('spn_confirmPassword').style.display='none';
 var msg=validateConfirmPasswordField(frm);
 if(msg!=""){
 showValidatorBox('spn_confirmPassword',msg);
 }
}
function confirmPasswordOnBlur(frm){
 document.getElementById('spn_confirmPassword').style.display='none';
 var msg=validateConfirmPasswordField(frm);
 if(msg!=""){
 frm.confirmation.addClassName("invalid_input");
 }
 else{
 frm.confirmation.removeClassName("invalid_input");
 }
}


function validateHintAnswerField(frm){
temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
showValidatorBox('spn_hintAnswer','Please enter hint answer');
//return "Please enter your hint's answer.";
return false;
}
document.getElementById('spn_hintAnswer').style.display='none';
//return true;
return '';
}
function hintAnswerOnClick(frm){
 document.getElementById('spn_hintAnswer').style.display='none';
 var msg=validateHintAnswerField(frm);
 if(msg!=""){
 showValidatorBox('spn_hintAnswer',msg);
 }
}
function hintAnswerOnBlur(frm){
 document.getElementById('spn_hintAnswer').style.display='none';
 var msg=validateHintAnswerField(frm);
 if(msg!=""){
 frm.hintAnswer.addClassName("invalid_input");
 }
 else{
 frm.hintAnswer.removeClassName("invalid_input");
 }
}





function validateConfirmHintAnswerField(frm){
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
//showValidatorBox('spn_confirmHintAnswer','Hint answer and confirm hint answer should match');
//return false;
return 'Hint answer and confirm hint answer should match';
}
document.getElementById('spn_confirmHintAnswer').style.display='none';
//return true;
return '';
}
function confirmHintAnswerOnClick(frm){
 document.getElementById('spn_confirmHintAnswer').style.display='none';
 var msg=validateConfirmHintAnswerField(frm);
 if(msg!=""){
 showValidatorBox('spn_confirmHintAnswer',msg);
 }
}
function confirmHintAnswerOnBlur(frm){
 document.getElementById('spn_confirmHintAnswer').style.display='none';
 var msg=validateConfirmHintAnswerField(frm);
 if(msg!=""){
 frm.confirmHintAnswer.addClassName("invalid_input");
 }
 else{
 frm.confirmHintAnswer.removeClassName("invalid_input");
 }
}




function validateHintQuestionField(frm){
if(frm.hintQuestion.value==""){
return 'Chose a security question, you will need this in the event that you need to reset the password for your account. You can chose from the list of predefined questions, or you can create a question of your own.';
}
document.getElementById('spn_hintQuestion').style.display='none';

return '';
}
function hintQuestionOnClick(frm){
 document.getElementById('spn_hintQuestion').style.display='none';
 var msg=validateHintQuestionField(frm);
 if(msg!=""){
 showValidatorBox('spn_hintQuestion',msg);
 }
}
function hintQuestionOnBlur(frm){
 document.getElementById('spn_hintQuestion').style.display='none';
 var msg=validateHintQuestionField(frm);
 if(msg!=""){
 frm.hintQuestion.addClassName("invalid_input");
 }
 else{
 frm.hintQuestion.removeClassName("invalid_input");
 }
}

function validateCustomHintQuestionField(frm){
if(frm.hintQuestion.value=="-2"){

if(frm.txtHintQuestion.value==""){
return 'Please enter your own question';
}

}
document.getElementById('spn_custom_hintQuestion').style.display='none';
return '';
}
function customHintQuestionOnClick(frm){
 document.getElementById('spn_custom_hintQuestion').style.display='none';
 var msg=validateCustomHintQuestionField(frm);
 if(msg!=""){
 showValidatorBox('spn_custom_hintQuestion',msg);
 }
}
function customHintQuestionOnBlur(frm){
 document.getElementById('spn_custom_hintQuestion').style.display='none';
 var msg=validateCustomHintQuestionField(frm);
 if(msg!=""){
 frm.txtHintQuestion.addClassName("invalid_input");
 }
 else{
 frm.txtHintQuestion.removeClassName("invalid_input");
 }
}



function validateMiddleInitialField(frm){
if(frm.mInitial.value.length > 1){
//alert("Middle initial should be 1 character long");
//$("lbl_mInitial").innerHTML="Middle initial should be 1 character long";
$("spn_mInitial").innerHTML="<img src='/magento4/images/cross.png' title='Please enter username' />";
//frm.mInitial.focus();
return false;
}
//$("lbl_mInitial").innerHTML="";
$("spn_mInitial").innerHTML="<img src='/magento4/images/tick.png' title='' />";
return true;
}
function validateFirstNameField(frm){
if(frm.fName.value==""){
//showValidatorBox('spn_fName','Please enter first name');
//return true;
return 'Enter your first name.';
}
document.getElementById('spn_fName').style.display='none';
//return true;
return '';
}
function firstNameOnClick(frm){
 document.getElementById('spn_fName').style.display='none';
 var msg=validateFirstNameField(frm);
 if(msg!=""){
 showValidatorBox('spn_fName',msg);
 }
}
function firstNameOnBlur(frm){
 document.getElementById('spn_fName').style.display='none';
 var msg=validateFirstNameField(frm);
 if(msg!=""){
 frm.fName.addClassName("invalid_input");
 }
 else{
 frm.fName.removeClassName("invalid_input");
 }
}



function validateLastNameField(frm){
if(frm.lName.value==""){
//showValidatorBox('spn_lName','Please enter last name');
//return false;
return 'Enter your last name.';
}
document.getElementById('spn_lName').style.display='none';
//return true;
return '';
}
function lastNameOnClick(frm){
 document.getElementById('spn_lName').style.display='none';
 var msg=validateLastNameField(frm);
 if(msg!=""){
 showValidatorBox('spn_lName',msg);
 }
}
function lastNameOnBlur(frm){
 document.getElementById('spn_lName').style.display='none';
 var msg=validateLastNameField(frm);
 if(msg!=""){
 frm.lName.addClassName("invalid_input");
 }
 else{
 frm.lName.removeClassName("invalid_input");
 }
}

function validateCountryField(frm){
validateStateField(frm);
validateZipField(frm);
if(frm.country.value=="-1"){
$("spn_country").innerHTML="<img src='/magento4/images/cross.png' title='Please enter username' />";

return false;
}

$("spn_country").innerHTML="<img src='/magento4/images/tick.png' title='' />";
return true;
}
function onCountryFieldChange(frm){
 if(frm.country.value=="840"){
 frm.state.value='';
 frm.state.setAttribute('maxlength', 2);
 //frm.state.setAttribute('size', 2);
// frm.state.maxlength=2;
 }
 else{
 frm.state.value='';
 //frm.state.maxlength=10;
 frm.state.setAttribute('maxlength', 50);
 //frm.state.setAttribute('size', 20);
 //document.getElementById(id).setAttribute('maxlength', length);

 }
}

function validatePurposeAccountField(frm){
if(frm.purposeAccount.value=="-1"){
//alert("Please select country");
//$("lbl_purposeAccount").innerHTML="Please select purpose account";
$("spn_purposeAccount").innerHTML="<img src='/magento4/images/cross.png' title='Please enter username' />";
//frm.purposeAccount.focus();
return false;
}
$("spn_purposeAccount").innerHTML="<img src='/magento4/images/tick.png' title='' />";
//$("lbl_purposeAccount").innerHTML="";
return true;
}

function validateAddress1Field(frm){
if(frm.address1.value==""){
//showValidatorBox('spn_address1','Please enter address');
//return false;
return 'Enter your address.';
}
document.getElementById('spn_address1').style.display='none';
//return true;
return '';
}
function validateAddress2Field(frm){
if(frm.address2.value==""){
//showValidatorBox('spn_address1','Please enter address');
//return false;
return 'Enter additional address information.';
}
document.getElementById('spn_address2').style.display='none';
//return true;
return '';
}
function validateAddress3Field(frm){
if(frm.address3.value==""){
//showValidatorBox('spn_address1','Please enter address');
//return false;
return 'Enter additional address information.';
}
document.getElementById('spn_address3').style.display='none';
//return true;
return '';
}
function address1OnClick(frm){
 document.getElementById('spn_address1').style.display='none';
 var msg=validateAddress1Field(frm);
 if(msg!=""){
 showValidatorBox('spn_address1',msg);
 }
}

function address2OnClick(frm){
 document.getElementById('spn_address2').style.display='none';
 var msg=validateAddress2Field(frm);
 if(msg!=""){
 showValidatorBox('spn_address2',msg);
 }
}

function address3OnClick(frm){
 document.getElementById('spn_address3').style.display='none';
 var msg=validateAddress3Field(frm);
 if(msg!=""){
 showValidatorBox('spn_address3',msg);
 }
}

function address1OnBlur(frm){
 document.getElementById('spn_address1').style.display='none';
 var msg=validateAddress1Field(frm);
 if(msg!=""){
 frm.address1.addClassName("invalid_input");
 }
 else{
 frm.address1.removeClassName("invalid_input");
 }
}

function address2OnBlur(frm){
 document.getElementById('spn_address2').style.display='none';
 var msg=validateAddress2Field(frm);
 if(msg!=""){
 frm.address1.addClassName("invalid_input");
 }
 else{
 frm.address1.removeClassName("invalid_input");
 }
}

function address3OnBlur(frm){
 document.getElementById('spn_address3').style.display='none';
 var msg=validateAddress3Field(frm);
 if(msg!=""){
 frm.address1.addClassName("invalid_input");
 }
 else{
 frm.address1.removeClassName("invalid_input");
 }
}


function validateCityField(frm){
if(frm.city.value==""){
//showValidatorBox('spn_city','Please enter city');
//return false;
return 'Enter your city.';
}
document.getElementById('spn_city').style.display='none';
//return true;
return '';
}
function cityOnClick(frm){
 document.getElementById('spn_city').style.display='none';
 var msg=validateCityField(frm);
 if(msg!=""){
 showValidatorBox('spn_city',msg);
 }
}
function cityOnBlur(frm){
 document.getElementById('spn_city').style.display='none';
 var msg=validateCityField(frm);
 if(msg!=""){
 frm.city.addClassName("invalid_input");
 }
 else{
 frm.city.removeClassName("invalid_input");
 }
}



function validateStateField(frm){
if(frm.state.value.length < 2 && frm.country.value=="840"){
//showValidatorBox('spn_state','Please enter state/province');
//return false;
return 'Please enter state/province';
}
document.getElementById('spn_state').style.display='none';
//return true;
return '';
}
function stateOnClick(frm){
 document.getElementById('spn_state').style.display='none';
 var msg=validateStateField(frm);
 if(msg!=""){
 showValidatorBox('spn_state',msg);
 }
}
function stateOnBlur(frm){
 document.getElementById('spn_state').style.display='none';
 var msg=validateStateField(frm);
 if(msg!=""){
 frm.state.addClassName("invalid_input");
 }
 else{
 frm.state.removeClassName("invalid_input");
 }
}


function validateStateCmb(frm){
document.getElementById('spn_txt_state').style.display='none';
document.getElementById('spn_state').style.display='none';
if(frm.region_id.value == "" && frm.country.value=="US"){
//showValidatorBox('spn_state','Please select state/province');
//return false;
return 'Please select state/province';
}
document.getElementById('spn_state').style.display='none';
//return true;
return '';
}
function stateCmbOnClick(frm){
 document.getElementById('spn_state').style.display='none';
 var msg=validateStateCmb(frm);
 if(msg!=""){
 showValidatorBox('spn_state',msg);
 }
}
function stateCmbOnBlur(frm){
 document.getElementById('spn_state').style.display='none';
 var msg=validateStateCmb(frm);
 if(msg!=""){
 frm.region_id.addClassName("invalid_input");
 }
 else{
 frm.region_id.removeClassName("invalid_input");
 }
}




function validateStateTxt(frm){
document.getElementById('spn_txt_state').style.display='none';
document.getElementById('spn_state').style.display='none';
if(frm.region.value == "" && frm.country.value!="US"){
//showValidatorBox('spn_txt_state','Please enter state/province');
//return false;
return 'Enter your province.';
}
document.getElementById('spn_txt_state').style.display='none';
//return true;
return '';
}
function stateTxtOnClick(frm){
 document.getElementById('spn_txt_state').style.display='none';
 var msg=validateStateTxt(frm);
 if(msg!=""){
 showValidatorBox('spn_txt_state',msg);
 }
}
function zipOnClick(frm){
 document.getElementById('spn_zip').style.display='none';
 var msg=validateZipField(frm);
 if(msg!=""){
 showValidatorBox('spn_zip',msg);
 }
}
function stateTxtOnBlur(frm){
 document.getElementById('spn_txt_state').style.display='none';
 var msg=validateStateTxt(frm);
 if(msg!=""){
 frm.region.addClassName("invalid_input");
 }
 else{
 frm.region.removeClassName("invalid_input");
 }
}


function isValidUSZip(strZip){
 if(strZip.length != 5 && strZip.length!=10){
 return false;
 }
 else{
 if(strZip.length==10){
 if(strZip.charAt(5)!='-'){
 return false;
 }
 }
 }
 return true;
}
function validateZipField(frm){
if(frm.zip.value=="" && frm.country.value=="US"){
//showValidatorBox('spn_zip','Please enter zip code');
//return false;
return 'Enter your zip code.';
}
if(frm.zip.value=="" && frm.country.value!="US"){
//showValidatorBox('spn_zip','Please enter zip code');
//return false;
return 'Enter your postal code.';
}
if(!isValidUSZip(frm.zip.value) && frm.country.value=="US"){
//showValidatorBox('spn_zip','Please enter a valid zip code');
//return false;
return 'Please enter a valid zip code';
}
document.getElementById('spn_zip').style.display='none';
//return true;
return '';
}
function zipOnClick(frm){
 document.getElementById('spn_zip').style.display='none';
 var msg=validateZipField(frm);
 if(msg!=""){
 showValidatorBox('spn_zip',msg);
 }
}
function zipOnBlur(frm){
 document.getElementById('spn_zip').style.display='none';
 
 var msg=validateZipField(frm);
 if(msg!=""){
 frm.zip.addClassName("invalid_input");
 }
 else{
 frm.zip.removeClassName("invalid_input");
 }
}

function zipOnFocus(frm){
 
 if(frm.country.value=='US'){
 frm.zip.maxLength=10;
 if(frm.zip.value.length>10){
 frm.zip.value=frm.zip.value.substr(0,10);
 }
 }
 else if(frm.country.value=='GB'){
 frm.zip.maxLength=15;
 if(frm.zip.value.length>15){
 frm.zip.value=frm.zip.value.substr(0,15);
 }
 }
 else frm.zip.maxLength=20;

}


function validateEmailField(frm){
 if(frm.email.value==""){
 return 'Please enter your primary email address.'; 
 }
 
 if(!validate_email(frm.email.value)){
 return 'Please enter a valid email address';
 }
 
 isPrimaryEmailExist(frm.email.value);
 document.getElementById('spn_email').style.display='none';

 return "";
}

function emailOnClick(frm){
 document.getElementById('spn_email').style.display='none';
 var msg=validateEmailField(frm);
 if(msg!=""){
 showValidatorBox('spn_email',msg);
 }
}
function emailOnBlur(frm){
 document.getElementById('spn_email').style.display='none';
 var msg=validateEmailField(frm);
 if(msg!=""){
 frm.email.addClassName("invalid_input");
 }
 else{
 frm.email.removeClassName("invalid_input");
 }
}



function validateSecondaryEmailField(frm){
 if(frm.sEmail.value!="" && !validate_email(frm.sEmail.value)){ 
 $("spn_semail").innerHTML="<img src='/magento4/images/cross.png' title='Please enter username' />";
 return false;
 
 }
 $("spn_semail").innerHTML="<img src='/magento4/images/tick.png' title='' />";
 //$("lbl_semail").innerHTML="";
 return true;

}

function validatePhoneField(frm){
if(frm.phone.value==""){
//showValidatorBox('spn_phone','Please enter phone number');
//return false;
return 'Enter your phone number.';
}
document.getElementById('spn_phone').style.display='none';
//return true;
return '';
}

function validateInitialField(frm){
if(frm.mInitial.value==""){
//showValidatorBox('spn_phone','Please enter phone number');
//return false;
return 'Enter your middle initial.';
}
document.getElementById('spn_mInitial').style.display='none';
//return true;
return '';
}

function validateReferField(frm){
if(frm.referralCode.value==""){
//showValidatorBox('spn_phone','Please enter phone number');
//return false;
return 'Optional Field: Referral Code only applies if provided by a Click2Mail affiliate such as USPS';
}
document.getElementById('spn_referralCode').style.display='none';
//return true;
return '';
}

function validatePromoteField(frm){
if(frm.promotionCode.value==""){
//showValidatorBox('spn_phone','Please enter phone number');
//return false;
return 'Enter your promotional code if you received one from Click2Mail.';
}

document.getElementById('spn_promotionCode').style.display='none';
//return true;
return '';
}
function phoneOnClick(frm){
 document.getElementById('spn_phone').style.display='none';
 var msg=validatePhoneField(frm);
 if(msg!=""){
 showValidatorBox('spn_phone',msg);
 }
}
function referOnClick(frm){
 document.getElementById('spn_referralCode').style.display='none';
 var msg=validateReferField(frm);
 if(msg!=""){
 showValidatorBox('spn_referralCode',msg);
 }
}
function promoteOnClick(frm){
 document.getElementById('spn_promotionCode').style.display='none';
 var msg=validatePromoteField(frm);
 if(msg!=""){
 showValidatorBox('spn_promotionCode',msg);
 }
}
function InitialOnClick(frm){
 document.getElementById('spn_mInitial').style.display='none';
 var msg=validateInitialField(frm);
 if(msg!=""){
 showValidatorBox('spn_mInitial',msg);
 }
}
function phoneOnBlur(frm){
 document.getElementById('spn_phone').style.display='none';
 var msg=validatePhoneField(frm);
 if(msg!=""){
 frm.phone.addClassName("invalid_input");
 }
 else{
 frm.phone.removeClassName("invalid_input");
 }
}

function InitialOnBlur(frm){
 document.getElementById('spn_mInitial').style.display='none';
 var msg=validateInitialField(frm);
 if(msg!=""){
 frm.phone.addClassName("invalid_input");
 }
 else{
 frm.phone.removeClassName("invalid_input");
 }
}

function referOnBlur(frm){
 document.getElementById('spn_referralCode').style.display='none';
 var msg=validateReferField(frm);
 if(msg!=""){
 frm.phone.addClassName("invalid_input");
 }
 else{
 frm.phone.removeClassName("invalid_input");
 }
}

function promoteOnBlur(frm){
 document.getElementById('spn_promotionCode').style.display='none';
 var msg=validatePromoteField(frm);
 if(msg!=""){
 frm.phone.addClassName("invalid_input");
 }
 else{
 frm.phone.removeClassName("invalid_input");
 }
}



function validateConfirmEmailField(frm){
if(frm.email.value!=frm.confirmEmail.value){
//alert("Email and confirm email should match");
$("lbl_confirmEmail").innerHTML="Email and confirm email should match";
//frm.confirmEmail.focus();
return false;
}
$("lbl_confirmEmail").innerHTML="";
return true;
}
function validateBusinessAccountTypeField(frm){
//if(frm.userAccountType.value=='2' && frm.company.value==""){
if(frm.company.value==""){
//alert("Please give company/organization name");
//$("lbl_company").innerHTML="Please give company/organization name";
$("spn_userAccountType").innerHTML="<img src='/magento4/images/cross.png' title='Please enter username' />";
//frm.company.focus();
return false;
}
//$("lbl_company").innerHTML="";
$("spn_userAccountType").innerHTML="<img src='/magento4/images/tick.png' title='' />";
return true;
}

function validateCompanyField(frm){
//if(frm.userAccountType.value=='2' && frm.company.value==""){
if(frm.company.value==""){
//alert("Please give company/organization name");
//$("lbl_company").innerHTML="Please give company/organization name";
$("spn_company").innerHTML="<img src='/magento4/images/cross.png' title='Please enter username' />";
//frm.company.focus();
return false;
}
//$("lbl_company").innerHTML="";
$("spn_company").innerHTML="<img src='/magento4/images/tick.png' title='' />";
return true;
}




function validate_common(frm){
 
 if(frm.email.value==""){
alert("Please enter primary email address");
frm.email.focus();
return false;
}

 if(!validate_email(frm.email.value)){
alert("Please enter a valid email address");
frm.email.focus();
return false;
}

 if(frm.username.value==""){
alert("Please enter username");
frm.username.focus();
return false;
}
if(frm.username.value.length < 6){
alert("Username should be at least 6 characters long");
frm.username.focus();
return false;
}
/*if(isUserNameExist(frm.username.value)){
alert("Username already exist. Please give another.");
frm.username.focus();
return false;

}*/

if(frm.userExist.value=='true'){
alert("Username already exists. Please try another.");
frm.username.focus();
return false;
}
if(frm.pEmailExist.value=='true'){
alert("Email already exists. Please try another.");
frm.email.focus();
return false;
}
if(frm.password.value==""){
alert("Please enter password");
frm.password.focus();
return false;
}
if(frm.password.value.length < 8){
alert("Password should be at least 8 characters");
frm.password.focus();
return false;
}
if(!passwordValidation(frm.password.value)){
alert("Password should have at least 1 lower case, 1 upper case and 1 digit.");
frm.password.focus();
return false;
}

if(frm.password.value!=frm.confirmation.value){
alert("Password and confirm password should match");
frm.confirmation.focus();
return false;
}
if(frm.hintQuestion.value=="-2"){
 if(frm.txtHintQuestion.value==""){
 alert("Please enter hint question");
 frm.txtHintQuestion.focus();
 return false;
 }
 }
 else{
 if(frm.hintQuestion.value=="-1"){
 alert("Please select hint question");
 frm.hintQuestion.focus();
 return false;
 }
}
temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
alert("Please enter hint answer");
frm.hintAnswer.focus();
return false;
}
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
alert("Hint answer and confirm hint answer should match");
frm.confirmHintAnswer.focus();
return false;
}
return true;
}


function validate_reg_2(frm){
/* if(frm.hintQuestion.value=="-2"){
 if(frm.txtHintQuestion.value==""){
 alert("Please enter hint question");
 frm.txtHintQuestion.focus();
 return false;
 }
 }
 else{
 if(frm.hintQuestion.value=="-1"){
 alert("Please select hint question");
 frm.hintQuestion.focus();
 return false;
 }
}*/

/*temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
alert("Please enter hint answer");
frm.hintAnswer.focus();
return false;
}
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
alert("Hint answer and confirm hint answer should match");
frm.confirmHintAnswer.focus();
return false;
}
*/
if(frm.fName.value==""){
alert("Please enter first name");
frm.fName.focus();
return false;
}
if(frm.mInitial.value.length > 1){
alert("Middle initial should be 1 character long");
frm.mInitial.focus();
return false;
}
if(frm.lName.value==""){
alert("Please enter last name");
frm.lName.focus();
return false;
}
if(frm.country.value=="-1"){
alert("Please select country");
frm.country.focus();
return false;
}

if(frm.address1.value==""){
alert("Please enter address");
frm.address1.focus();
return false;
}
if(frm.city.value==""){
alert("Please enter city");
frm.city.focus();
return false;
}
/*if(frm.state.value.length < 2 && frm.country.value=="840"){
alert("Please enter state/province");
frm.state.focus();
return false;
}*/
if(frm.region_id.value == "" && frm.country.value=="US"){
alert("Please select state/province");
frm.region_id.focus();
return false;
}
if(frm.region.value == "" && frm.country.value!="US"){
alert("Please enter state/province");
frm.region.focus();
return false;
}

if(frm.zip.value=="" && frm.country.value=="US"){
alert("Please enter Zip/Postal code");
frm.zip.focus();
return false;
}
if(!isValidUSZip(frm.zip.value) && frm.country.value=="US"){
alert("Please enter a valid Zip/Postal code");
frm.zip.focus();
return false;
}
if(frm.phone.value==""){
alert("Please enter primary phone number");
frm.phone.focus();
return false;
}
/*if(frm.phone.value.length !=10){
 alert("Please enter a valid phone number");
 frm.phone.focus();
 return false;
}*/

if(frm.sEmail.value!="" && !validate_email(frm.sEmail.value)){
alert("Please enter a valid email address");
frm.sEmail.focus();
return false;
}

if(frm.hdnAccountType.value=='Business'){
 if(frm.company.value==""){
frm.company.focus();
return false;
 }
 
}
if(frm.hdnAccountType.value=='Business'){
 if(frm.purposeAccount.value=="-1"){
frm.purposeAccount.focus();
return false;
 }
 
}

return true;
}

function validate(frm){


if(frm.username.value==""){
alert("Please enter username");
frm.username.focus();
return false;
}
if(frm.username.value.length < 6){
alert("Username should be at least 6 characters long");
frm.username.focus();
return false;
}
/*if(isUserNameExist(frm.username.value)){
alert("Username already exist. Please give another.");
frm.username.focus();
return false;

}*/
if(frm.userExist.value=='true'){
alert("Username already exists. Please try another.");
frm.username.focus();
return false;
}
if(frm.password.value==""){
alert("Please enter password");
frm.password.focus();
return false;
}
if(frm.password.value.length < 8){
alert("Password should be at least 8 characters");
frm.password.focus();
return false;
}
if(!passwordValidation(frm.password.value)){
alert("Password should have at least 1 lower case, 1 upper case and 1 digit.");
frm.password.focus();
return false;
}

if(frm.password.value!=frm.confirmation.value){
alert("Password and confirm password should match");
frm.confirmation.focus();
return false;
}
if(frm.hintQuestion.value=="-1"){
alert("Please select hint question");
frm.hintQuestion.focus();
return false;
}
temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
alert("Please enter hint answer");
frm.hintAnswer.focus();
return false;
}
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
alert("Hint answer and confirm hint answer should match");
frm.confirmHintAnswer.focus();
return false;
}
if(frm.fName.value==""){
alert("Please enter first name");
frm.fName.focus();
return false;
}
if(frm.mInitial.value.length > 1){
alert("Middle initial should be 1 character long");
frm.mInitial.focus();
return false;
}
if(frm.lName.value==""){
alert("Please enter last name");
frm.lName.focus();
return false;
}

if(frm.username.value==""){
alert("Please enter username");
frm.username.focus();
return false;
}
if(frm.country.value=="-1"){
alert("Please select country");
frm.country.focus();
return false;
}

if(frm.address1.value==""){
alert("Please enter address");
frm.address1.focus();
return false;
}
if(frm.city.value==""){
alert("Please enter city");
frm.city.focus();
return false;
}
if(frm.state.value==""){
alert("Please enter state/province");
frm.state.focus();
return false;
}

if(frm.zip.value==""){
alert("Please enter Zip/Postal code");
frm.zip.focus();
return false;
}
if(frm.email.value==""){
alert("Please enter email");
frm.email.focus();
return false;
}
if(frm.email.value!=frm.confirmEmail.value){
alert("Email and confirm email should match");
frm.confirmEmail.focus();
return false;
}
if(frm.userAccountType.value=='2' && frm.company.value==""){
alert("Please give company/organization name");
frm.company.focus();
return false;
}

return true;
}

function validateOnFocus(frm){


if(frm.username.value==""){
frm.username.focus();
return false;
}
if(frm.username.value.length < 6){
frm.username.focus();
return false;
}
/*if(isUserNameExist(frm.username.value)){
alert("Username already exist. Please give another.");
frm.username.focus();
return false;

}*/
if(frm.userExist.value=='true'){
frm.username.focus();
return false;
}
if(frm.password.value==""){
frm.password.focus();
return false;
}
if(frm.password.value.length < 8){
frm.password.focus();
return false;
}
if(!passwordValidation(frm.password.value)){
frm.password.focus();
return false;
}

if(frm.password.value!=frm.confirmation.value){
frm.confirmation.focus();
return false;
}
if(frm.hintQuestion.value=="-1"){
frm.hintQuestion.focus();
return false;
}
temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
frm.hintAnswer.focus();
return false;
}
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
frm.confirmHintAnswer.focus();
return false;
}
if(frm.fName.value==""){
frm.fName.focus();
return false;
}
if(frm.mInitial.value.length > 1){
frm.mInitial.focus();
return false;
}
if(frm.lName.value==""){
frm.lName.focus();
return false;
}

if(frm.username.value==""){
frm.username.focus();
return false;
}
if(frm.country.value=="-1"){
frm.country.focus();
return false;
}

if(frm.address1.value==""){
frm.address1.focus();
return false;
}
if(frm.city.value==""){
frm.city.focus();
return false;
}
if(frm.zip.value==""){
frm.zip.focus();
return false;
}
if(frm.email.value==""){
frm.email.focus();
return false;
}
if(frm.email.value!=frm.confirmEmail.value){
frm.confirmEmail.focus();
return false;
}
if(frm.userAccountType.value=='2' && frm.company.value==""){
frm.company.focus();
return false;
}

return true;
}



function toggleAccountType(ddl){
if(ddl.value=='1'){
//$$('.toggle').style.display='none';
tableRows=$$('.toggle_show');
//alert(tableRows.length);
for(i=0;i<tableRows.length;i++){
//tableRows[i].style.display='none';
tableRows[i].className='toggle_hide';
}
//$('toggle').style.display='none';
}
else if(ddl.value=='2'){
//$$('.toggle').style.display='block';
tableRows=$$('.toggle_hide');
//alert(tableRows.length);
for(i=0;i<tableRows.length;i++){
//tableRows[i].style.display='block';
tableRows[i].className='toggle_show';
}
//$('toggle').style.display='block';

}

}



function toggleBilling(chk){
if(chk.checked==false){
//$$('.toggle').style.display='none';
tableRows=$$('.billing_toggle_show');
//alert(tableRows.length);
for(i=0;i<tableRows.length;i++){
//tableRows[i].style.display='none';
tableRows[i].className='billing_toggle_hide';
}
//$('toggle').style.display='none';
}
else if(chk.checked==true){
//$$('.toggle').style.display='block';
tableRows=$$('.billing_toggle_hide');
//alert(tableRows.length);
for(i=0;i<tableRows.length;i++){
//tableRows[i].style.display='block';
tableRows[i].className='billing_toggle_show';
}
//$('toggle').style.display='block';

}

}




function toggleShipping(chk){
if(chk.checked==false){
//$$('.toggle').style.display='none';
tableRows=$$('.shipping_toggle_show');
//alert(tableRows.length);
for(i=0;i<tableRows.length;i++){
//tableRows[i].style.display='none';
tableRows[i].className='shipping_toggle_hide';
}
//$('toggle').style.display='none';
}
else if(chk.checked==true){
//$$('.toggle').style.display='block';
tableRows=$$('.shipping_toggle_hide');
//alert(tableRows.length);
for(i=0;i<tableRows.length;i++){
//tableRows[i].style.display='block';
tableRows[i].className='shipping_toggle_show';
}
//$('toggle').style.display='block';

}

}


function validate_edit_profile(frm){
/* if(frm.hintQuestion.value=="-1"){
alert("Please select hint question");
frm.hintQuestion.focus();
return false;
}
temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
alert("Please enter hint answer");
frm.hintAnswer.focus();
return false;
}
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
alert("Hint answer and confirm hint answer should match");
frm.confirmHintAnswer.focus();
return false;
}*/
if(frm.fName.value==""){
alert("Please enter first name");
frm.fName.focus();
return false;
}
if(frm.mInitial.value.length > 1){
alert("Middle initial should be 1 character long");
frm.mInitial.focus();
return false;
}
if(frm.lName.value==""){
alert("Please enter last name");
frm.lName.focus();
return false;
}
if(frm.country.value=="-1"){
alert("Please select country");
frm.country.focus();
return false;
}

if(frm.address1.value==""){
alert("Please enter address");
frm.address1.focus();
return false;
}
if(frm.city.value==""){
alert("Please enter city");
frm.city.focus();
return false;
}
if(frm.state.value=="" && frm.country.value=="840"){
alert("Please enter state/province");
frm.state.focus();
return false;
}

if(frm.zip.value=="" && frm.country.value=="840"){
alert("Please enter Zip/Postal code");
frm.zip.focus();
return false;
}
if(frm.phone.value==""){
alert("Please enter primary phone number");
frm.phone.focus();
return false;
}
/*if(frm.sEmail.value!="" && !validate_email(frm.sEmail.value)){
alert("Please enter a valid email address");
frm.sEmail.focus();
return false;
}*/

if(frm.hdnAccountType.value=='Business'){
 if(frm.company.value==""){
 alert("Please enter organization name");
frm.company.focus();
return false;
 }
 
}
/*if(frm.hdnAccountType.value=='Business'){
 if(frm.purposeAccount.value=="-1"){
 alert("Please select a purpose of account");
frm.purposeAccount.focus();
return false;
 }
 
}*/

return true;
}

function toogleHintQuestion(){
 if($('chkHintQuestion').checked==true){
 $('hintQuestion').style.display='none';
 $('txtHintQuestion').style.display='';
 }
 else{
 $('hintQuestion').style.display='';
 $('txtHintQuestion').style.display='none';
 }
}

function onHintQuestionChange(frm){
 if(frm.hintQuestion.value!='-2'){
 tableRows=$$('.hintQuestion_toggle_show');
 $('con').style.display='None'; 
 for(i=0;i<tableRows.length;i++){
 tableRows[i].className='hintQuestion_toggle_hide';
 } 
 }
 else{
 tableRows=$$('.hintQuestion_toggle_hide');
 $('con').style.display='Block';
 for(i=0;i<tableRows.length;i++){
 tableRows[i].className='hintQuestion_toggle_show';
 } 
 }
}

function validate_reg_customer(frm){

if(frm.username.value==""){
alert("Please enter username");
frm.username.focus();
return false;
}
if(frm.username.value.length < 6){
alert("Username should be at least 6 characters long");
frm.username.focus();
return false;
}
/*if(isUserNameExist(frm.username.value)){
alert("Username already exist. Please give another.");
frm.username.focus();
return false;

}*/
if(frm.userExist.value=='true'){
alert("Username already exists. Please try another.");
frm.username.focus();
return false;
}
if(frm.password.value==""){
alert("Please enter password");
frm.password.focus();
return false;
}
if(frm.password.value.length < 8){
alert("Password should be at least 8 characters");
frm.password.focus();
return false;
}
if(!passwordValidation(frm.password.value)){
alert("Password should have at least 1 lower case, 1 upper case and 1 digit.");
frm.password.focus();
return false;
}

if(frm.password.value!=frm.confirmation.value){
alert("Password and confirm password should match");
frm.confirmation.focus();
return false;
}

if(frm.hintQuestion.value=="-2"){
 if(frm.txtHintQuestion.value==""){
 alert("Please enter hint question");
 frm.txtHintQuestion.focus();
 return false;
 }
 }
 else{
 if(frm.hintQuestion.value=="-1"){
 alert("Please select hint question");
 frm.hintQuestion.focus();
 return false;
 }
}

temp_str=frm.hintAnswer.value;
if(temp_str.trim()==""){
alert("Please enter hint answer");
frm.hintAnswer.focus();
return false;
}
if(frm.hintAnswer.value!=frm.confirmHintAnswer.value){
alert("Hint answer and confirm hint answer should match");
frm.confirmHintAnswer.focus();
return false;
}


return true;


}
var show = new Array();

show['account_details_user_credit']="This section shows the amount of credit your account has to spend. You can click on the 'View History' link to see when you've purchased credit and how you've spent it.";

show['account_details_user_details']="This section lists the name, email address, and phone number of the primary contact for your account. You can edit these by clicking on the 'Edit' link to the right.";

show['account_details_user_address']="This section shows the primary return address associated with your account. You can edit this address here by clicking the 'Edit Address' link below, or view and edit all your addresses by clicking 'Manage Addresses' to the right.";

show['account_details_user_preferences']="This section shows your account type, lists your current group, and displays general information about your account. It also lists your status in, or lets you apply for, special groups and payment options.";

show['account_view_contact_information']="Allows you to change your personal information, including primary and secondary emails, primary and secondary phone numbers, and your fax number. Fields with asterisks are required.";

show['account_view_change_password']="Allows you to change your password. You must enter your current password correctly. Remember that passwords are case sensitive and require at least 8 characters, 1 uppercase, 1 lowercase, and 1 number.";

show['account_view_change_security_question']="Allows you to change your security question. You must enter your current password correctly. Remember that passwords are case sensitive and that your hint answer must be at least 3 characters long.";

show['account_preference_type']="Allows you to set your account type as business or personal. Business accounts allow multiple users to sign up with the same email address. Fields with asterisks are required.";

show['account_preference_group_status']="Lists what user group you belong to shows your status in, or lets you apply for, special groups and payment options. You must be a business account to apply for non-profit status and you must be registered with a USPS email address to apply for G10 status.";

show['account_preference_notification']="Allows you to add or remove yourself from our mailing list. While signed up, you will receive Click2Mail newsletters as well as special offers!";

show['account_preference_popup_applyForNonprofit']="This page allows you to apply to receive non profit price rates. You must enter your non profit authorization ID from the post office as well as fill in all fields marked with an asterisk.";

show['account_preference_popup_applyForG10']="This page allows you to apply for G10 postal status. You must enter your finance code and district information to verify your eligibility.";

show['account_preference_popup_applyForACH']="This page allows you to set up automated clearing house payments to withdraw funds directly from your bank account when paying for orders.";

show['account_customer_myaddress']="This page shows the addresses associated with your account. You can add new, or edit existing addresses, as well as set your primary return address and primary billing address.";

show['account_customer_default_primary_address']="This is your primary return address that customers will see on your mailings. You can edit this address or add a new address to be set as your primary.";

show['account_customer_default_billing_address']="This is your default billing address, this will populate the billing address fields in most forms. You can edit this address or add a new address to be set as your default billing address.";

show['account_customer_additional_address']="Lists any additional addresses you may have. Each address has a personal label and specifies the type of address. You can edit or delete addresses from this list, as well as set them as your primary return or default billing address.";

show['account_customer_update_address']="This page allows you to edit your address and/or your address' label. You can also use this page to set certain types of addresses to be your primary return or primary billing address.";

show['account_customer_new_address']="Please select the type of address you would like to add and enter a label to identify the address in your address book.";

show['sales_order_history']="Lists your most recent order history. Select view all to search order history, view order details and tracking or to print a copy of your invoice.";

show['sales_credit_history']="This page shows the amount of credit your account has to spend. It also lists your credit history, showing when you have purchased credit and when and how you have spent it, starting with your most recent activity.";

show['email2mail_index_mailboxlist']="Text should be entered here";
show['email2mail_edit_mailbox']="Text should be entered here";
show['email2mail_select_mailbox']="Text should be entered here";
show['email2mail_option_mailbox']="Text should be entered here";

show['account_preference_api_access'] = "Text should be entered here";
show['sales_order_history_fuse'] = "Text should be entered here";


var helpPopup =
{
 openPopup : function ()
 {
 jQuery("body").append("<div id='HelpToolTipDiv'>"+
 "<div id='tooltip-inner-con' style='width:257px;'>"+
 "<p class='nopadding-margin' id='helpid'></p>"+
 "</div>"+
 "<div id='tooltip-bot-con'></div>"+
 "</div>");
 
 jQuery(".user_help").each(function() 
 {
 jQuery(this).hover(function(e) 
 {
 img_id = jQuery(this).attr('id');
 
 jQuery().mousemove(function(e) 
 {
 var tipY = e.pageY - 20;
 var tipX = e.pageX + 5;
 
 var area = parseInt(tipX) + 310;
 
 if(area>jQuery(window).width())
 {
 jQuery("#tooltip-inner-con").attr("class","tooltip-inner-con-right");
 jQuery("#tooltip-bot-con").attr("class","tooltip-bot-con-right");
 jQuery("#HelpToolTipDiv").css({'top': tipY, 'left': e.pageX-310});
 }
 
 else
 {
 jQuery("#tooltip-inner-con").attr("class","tooltip-inner-con");
 jQuery("#tooltip-bot-con").attr("class","tooltip-bot-con");
 jQuery("#HelpToolTipDiv").css({'top': tipY, 'left': tipX}); 
 }
 });
 
 jQuery("#helpid").html(show[img_id]);
 jQuery("#HelpToolTipDiv").css("display","block");
 
 }, 
 
 function() 
 {
 jQuery("#HelpToolTipDiv").css("display","none");
 jQuery("#helpid").html();
 });
 });
 
 }
}



jQuery(document).ready(function() 
{
 helpPopup.openPopup();
});
/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
 * -----------------------------------------------------------
 *
 * The DHTML Calendar, version 1.0 "It is happening again"
 *
 * Details and latest version at:
 * www.dynarch.com/projects/calendar
 *
 * This script is developed by Dynarch.com. Visit us at www.dynarch.com.
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 */

// $Id: calendar.js,v 1.51 2005/03/07 16:44:31 mishoo Exp $

/** The Calendar object constructor. */
Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
 // member variables
 this.activeDiv = null;
 this.currentDateEl = null;
 this.getDateStatus = null;
 this.getDateToolTip = null;
 this.getDateText = null;
 this.timeout = null;
 this.onSelected = onSelected || null;
 this.onClose = onClose || null;
 this.dragging = false;
 this.hidden = false;
 this.minYear = 1970;
 this.maxYear = 2050;
 this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
 this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
 this.isPopup = true;
 this.weekNumbers = true;
 this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
 this.showsOtherMonths = false;
 this.dateStr = dateStr;
 this.ar_days = null;
 this.showsTime = false;
 this.time24 = true;
 this.yearStep = 2;
 this.hiliteToday = true;
 this.multiple = null;
 // HTML elements
 this.table = null;
 this.element = null;
 this.tbody = null;
 this.firstdayname = null;
 // Combo boxes
 this.monthsCombo = null;
 this.yearsCombo = null;
 this.hilitedMonth = null;
 this.activeMonth = null;
 this.hilitedYear = null;
 this.activeYear = null;
 // Information
 this.dateClicked = false;

 // one-time initializations
 if (typeof Calendar._SDN == "undefined") {
 // table of short day names
 if (typeof Calendar._SDN_len == "undefined")
 Calendar._SDN_len = 3;
 var ar = new Array();
 for (var i = 8; i > 0;) {
 ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
 }
 Calendar._SDN = ar;
 // table of short month names
 if (typeof Calendar._SMN_len == "undefined")
 Calendar._SMN_len = 3;
 ar = new Array();
 for (var i = 12; i > 0;) {
 ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
 }
 Calendar._SMN = ar;
 }
};

// ** constants

/// "static", needed for event handlers.
Calendar._C = null;

/// detect a special case of "web browser"
Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
 !/opera/i.test(navigator.userAgent) );

Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

/// detect Opera browser
Calendar.is_opera = /opera/i.test(navigator.userAgent);

/// detect KHTML-based browsers
Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);

/// detect Gecko browsers
Calendar.is_gecko = navigator.userAgent.match(/gecko/i);

// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
// library, at some point.

// Returns CSS property for element
Calendar.getStyle = function(element, style) {
 if (element.currentStyle) {
 var y = element.currentStyle[style];
 } else if (window.getComputedStyle) {
 var y = document.defaultView.getComputedStyle(element,null).getPropertyValue(style);
 }

 return y;
};

/*
 * Different ways to find element's absolute position
 */
Calendar.getAbsolutePos = function(element) {

 var res = new Object();
 res.x = 0; res.y = 0;

 // variant 1 (working best, copy-paste from prototype library)
 do {
 res.x += element.offsetLeft || 0;
 res.y += element.offsetTop || 0;
 element = element.offsetParent;
 if (element) {
 if (element.tagName.toUpperCase() == 'BODY') break;
 var p = Calendar.getStyle(element, 'position');
 if (p !== 'static') break;
 }
 } while (element);

 return res;

 // variant 2 (good solution, but lost in IE8)
 if (element !== null) {
 res.x = element.offsetLeft;
 res.y = element.offsetTop;

 var offsetParent = element.offsetParent;
 var parentNode = element.parentNode;

 while (offsetParent !== null) {
 res.x += offsetParent.offsetLeft;
 res.y += offsetParent.offsetTop;

 if (offsetParent != document.body && offsetParent != document.documentElement) {
 res.x -= offsetParent.scrollLeft;
 res.y -= offsetParent.scrollTop;
 }
 //next lines are necessary to support FireFox problem with offsetParent
 if (Calendar.is_gecko) {
 while (offsetParent != parentNode && parentNode !== null) {
 res.x -= parentNode.scrollLeft;
 res.y -= parentNode.scrollTop;
 parentNode = parentNode.parentNode;
 }
 }
 parentNode = offsetParent.parentNode;
 offsetParent = offsetParent.offsetParent;
 }
 }
 return res;

 // variant 2 (not working)

// var SL = 0, ST = 0;
// var is_div = /^div$/i.test(el.tagName);
// if (is_div && el.scrollLeft)
// SL = el.scrollLeft;
// if (is_div && el.scrollTop)
// ST = el.scrollTop;
// var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
// if (el.offsetParent) {
// var tmp = this.getAbsolutePos(el.offsetParent);
// r.x += tmp.x;
// r.y += tmp.y;
// }
// return r;
};

Calendar.isRelated = function (el, evt) {
 var related = evt.relatedTarget;
 if (!related) {
 var type = evt.type;
 if (type == "mouseover") {
 related = evt.fromElement;
 } else if (type == "mouseout") {
 related = evt.toElement;
 }
 }
 while (related) {
 if (related == el) {
 return true;
 }
 related = related.parentNode;
 }
 return false;
};

Calendar.removeClass = function(el, className) {
 if (!(el && el.className)) {
 return;
 }
 var cls = el.className.split(" ");
 var ar = new Array();
 for (var i = cls.length; i > 0;) {
 if (cls[--i] != className) {
 ar[ar.length] = cls[i];
 }
 }
 el.className = ar.join(" ");
};

Calendar.addClass = function(el, className) {
 Calendar.removeClass(el, className);
 el.className += " " + className;
};

// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
Calendar.getElement = function(ev) {
 var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
 while (f.nodeType != 1 || /^div$/i.test(f.tagName))
 f = f.parentNode;
 return f;
};

Calendar.getTargetElement = function(ev) {
 var f = Calendar.is_ie ? window.event.srcElement : ev.target;
 while (f.nodeType != 1)
 f = f.parentNode;
 return f;
};

Calendar.stopEvent = function(ev) {
 ev || (ev = window.event);
 if (Calendar.is_ie) {
 ev.cancelBubble = true;
 ev.returnValue = false;
 } else {
 ev.preventDefault();
 ev.stopPropagation();
 }
 return false;
};

Calendar.addEvent = function(el, evname, func) {
 if (el.attachEvent) { // IE
 el.attachEvent("on" + evname, func);
 } else if (el.addEventListener) { // Gecko / W3C
 el.addEventListener(evname, func, true);
 } else {
 el["on" + evname] = func;
 }
};

Calendar.removeEvent = function(el, evname, func) {
 if (el.detachEvent) { // IE
 el.detachEvent("on" + evname, func);
 } else if (el.removeEventListener) { // Gecko / W3C
 el.removeEventListener(evname, func, true);
 } else {
 el["on" + evname] = null;
 }
};

Calendar.createElement = function(type, parent) {
 var el = null;
 if (document.createElementNS) {
 // use the XHTML namespace; IE won't normally get here unless
 // _they_ "fix" the DOM2 implementation.
 el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
 } else {
 el = document.createElement(type);
 }
 if (typeof parent != "undefined") {
 parent.appendChild(el);
 }
 return el;
};

// END: UTILITY FUNCTIONS

// BEGIN: CALENDAR STATIC FUNCTIONS

/** Internal -- adds a set of events to make some element behave like a button. */
Calendar._add_evs = function(el) {
 with (Calendar) {
 addEvent(el, "mouseover", dayMouseOver);
 addEvent(el, "mousedown", dayMouseDown);
 addEvent(el, "mouseout", dayMouseOut);
 if (is_ie) {
 addEvent(el, "dblclick", dayMouseDblClick);
 el.setAttribute("unselectable", true);
 }
 }
};

Calendar.findMonth = function(el) {
 if (typeof el.month != "undefined") {
 return el;
 } else if (typeof el.parentNode.month != "undefined") {
 return el.parentNode;
 }
 return null;
};

Calendar.findYear = function(el) {
 if (typeof el.year != "undefined") {
 return el;
 } else if (typeof el.parentNode.year != "undefined") {
 return el.parentNode;
 }
 return null;
};

Calendar.showMonthsCombo = function () {
 var cal = Calendar._C;
 if (!cal) {
 return false;
 }
 var cal = cal;
 var cd = cal.activeDiv;
 var mc = cal.monthsCombo;
 if (cal.hilitedMonth) {
 Calendar.removeClass(cal.hilitedMonth, "hilite");
 }
 if (cal.activeMonth) {
 Calendar.removeClass(cal.activeMonth, "active");
 }
 var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
 Calendar.addClass(mon, "active");
 cal.activeMonth = mon;
 var s = mc.style;
 s.display = "block";
 if (cd.navtype < 0)
 s.left = cd.offsetLeft + "px";
 else {
 var mcw = mc.offsetWidth;
 if (typeof mcw == "undefined")
 // Konqueror brain-dead techniques
 mcw = 50;
 s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
 }
 s.top = (cd.offsetTop + cd.offsetHeight) + "px";
};

Calendar.showYearsCombo = function (fwd) {
 var cal = Calendar._C;
 if (!cal) {
 return false;
 }
 var cal = cal;
 var cd = cal.activeDiv;
 var yc = cal.yearsCombo;
 if (cal.hilitedYear) {
 Calendar.removeClass(cal.hilitedYear, "hilite");
 }
 if (cal.activeYear) {
 Calendar.removeClass(cal.activeYear, "active");
 }
 cal.activeYear = null;
 var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
 var yr = yc.firstChild;
 var show = false;
 for (var i = 12; i > 0; --i) {
 if (Y >= cal.minYear && Y <= cal.maxYear) {
 yr.innerHTML = Y;
 yr.year = Y;
 yr.style.display = "block";
 show = true;
 } else {
 yr.style.display = "none";
 }
 yr = yr.nextSibling;
 Y += fwd ? cal.yearStep : -cal.yearStep;
 }
 if (show) {
 var s = yc.style;
 s.display = "block";
 if (cd.navtype < 0)
 s.left = cd.offsetLeft + "px";
 else {
 var ycw = yc.offsetWidth;
 if (typeof ycw == "undefined")
 // Konqueror brain-dead techniques
 ycw = 50;
 s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
 }
 s.top = (cd.offsetTop + cd.offsetHeight) + "px";
 }
};

// event handlers

Calendar.tableMouseUp = function(ev) {
 var cal = Calendar._C;
 if (!cal) {
 return false;
 }
 if (cal.timeout) {
 clearTimeout(cal.timeout);
 }
 var el = cal.activeDiv;
 if (!el) {
 return false;
 }
 var target = Calendar.getTargetElement(ev);
 ev || (ev = window.event);
 Calendar.removeClass(el, "active");
 if (target == el || target.parentNode == el) {
 Calendar.cellClick(el, ev);
 }
 var mon = Calendar.findMonth(target);
 var date = null;
 if (mon) {
 date = new CalendarDateObject(cal.date);
 if (mon.month != date.getMonth()) {
 date.setMonth(mon.month);
 cal.setDate(date);
 cal.dateClicked = false;
 cal.callHandler();
 }
 } else {
 var year = Calendar.findYear(target);
 if (year) {
 date = new CalendarDateObject(cal.date);
 if (year.year != date.getFullYear()) {
 date.setFullYear(year.year);
 cal.setDate(date);
 cal.dateClicked = false;
 cal.callHandler();
 }
 }
 }
 with (Calendar) {
 removeEvent(document, "mouseup", tableMouseUp);
 removeEvent(document, "mouseover", tableMouseOver);
 removeEvent(document, "mousemove", tableMouseOver);
 cal._hideCombos();
 _C = null;
 return stopEvent(ev);
 }
};

Calendar.tableMouseOver = function (ev) {
 var cal = Calendar._C;
 if (!cal) {
 return;
 }
 var el = cal.activeDiv;
 var target = Calendar.getTargetElement(ev);
 if (target == el || target.parentNode == el) {
 Calendar.addClass(el, "hilite active");
 Calendar.addClass(el.parentNode, "rowhilite");
 } else {
 if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
 Calendar.removeClass(el, "active");
 Calendar.removeClass(el, "hilite");
 Calendar.removeClass(el.parentNode, "rowhilite");
 }
 ev || (ev = window.event);
 if (el.navtype == 50 && target != el) {
 var pos = Calendar.getAbsolutePos(el);
 var w = el.offsetWidth;
 var x = ev.clientX;
 var dx;
 var decrease = true;
 if (x > pos.x + w) {
 dx = x - pos.x - w;
 decrease = false;
 } else
 dx = pos.x - x;

 if (dx < 0) dx = 0;
 var range = el._range;
 var current = el._current;
 var count = Math.floor(dx / 10) % range.length;
 for (var i = range.length; --i >= 0;)
 if (range[i] == current)
 break;
 while (count-- > 0)
 if (decrease) {
 if (--i < 0)
 i = range.length - 1;
 } else if ( ++i >= range.length )
 i = 0;
 var newval = range[i];
 el.innerHTML = newval;

 cal.onUpdateTime();
 }
 var mon = Calendar.findMonth(target);
 if (mon) {
 if (mon.month != cal.date.getMonth()) {
 if (cal.hilitedMonth) {
 Calendar.removeClass(cal.hilitedMonth, "hilite");
 }
 Calendar.addClass(mon, "hilite");
 cal.hilitedMonth = mon;
 } else if (cal.hilitedMonth) {
 Calendar.removeClass(cal.hilitedMonth, "hilite");
 }
 } else {
 if (cal.hilitedMonth) {
 Calendar.removeClass(cal.hilitedMonth, "hilite");
 }
 var year = Calendar.findYear(target);
 if (year) {
 if (year.year != cal.date.getFullYear()) {
 if (cal.hilitedYear) {
 Calendar.removeClass(cal.hilitedYear, "hilite");
 }
 Calendar.addClass(year, "hilite");
 cal.hilitedYear = year;
 } else if (cal.hilitedYear) {
 Calendar.removeClass(cal.hilitedYear, "hilite");
 }
 } else if (cal.hilitedYear) {
 Calendar.removeClass(cal.hilitedYear, "hilite");
 }
 }
 return Calendar.stopEvent(ev);
};

Calendar.tableMouseDown = function (ev) {
 if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
 return Calendar.stopEvent(ev);
 }
};

Calendar.calDragIt = function (ev) {
 var cal = Calendar._C;
 if (!(cal && cal.dragging)) {
 return false;
 }
 var posX;
 var posY;
 if (Calendar.is_ie) {
 posY = window.event.clientY + document.body.scrollTop;
 posX = window.event.clientX + document.body.scrollLeft;
 } else {
 posX = ev.pageX;
 posY = ev.pageY;
 }
 cal.hideShowCovered();
 var st = cal.element.style;
 st.left = (posX - cal.xOffs) + "px";
 st.top = (posY - cal.yOffs) + "px";
 return Calendar.stopEvent(ev);
};

Calendar.calDragEnd = function (ev) {
 var cal = Calendar._C;
 if (!cal) {
 return false;
 }
 cal.dragging = false;
 with (Calendar) {
 removeEvent(document, "mousemove", calDragIt);
 removeEvent(document, "mouseup", calDragEnd);
 tableMouseUp(ev);
 }
 cal.hideShowCovered();
};

Calendar.dayMouseDown = function(ev) {
 var el = Calendar.getElement(ev);
 if (el.disabled) {
 return false;
 }
 var cal = el.calendar;
 cal.activeDiv = el;
 Calendar._C = cal;
 if (el.navtype != 300) with (Calendar) {
 if (el.navtype == 50) {
 el._current = el.innerHTML;
 addEvent(document, "mousemove", tableMouseOver);
 } else
 addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
 addClass(el, "hilite active");
 addEvent(document, "mouseup", tableMouseUp);
 } else if (cal.isPopup) {
 cal._dragStart(ev);
 }
 if (el.navtype == -1 || el.navtype == 1) {
 if (cal.timeout) clearTimeout(cal.timeout);
 cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
 } else if (el.navtype == -2 || el.navtype == 2) {
 if (cal.timeout) clearTimeout(cal.timeout);
 cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
 } else {
 cal.timeout = null;
 }
 return Calendar.stopEvent(ev);
};

Calendar.dayMouseDblClick = function(ev) {
 Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
 if (Calendar.is_ie) {
 document.selection.empty();
 }
};

Calendar.dayMouseOver = function(ev) {
 var el = Calendar.getElement(ev);
 if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
 return false;
 }
 if (el.ttip) {
 if (el.ttip.substr(0, 1) == "_") {
 el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
 }
 el.calendar.tooltips.innerHTML = el.ttip;
 }
 if (el.navtype != 300) {
 Calendar.addClass(el, "hilite");
 if (el.caldate) {
 Calendar.addClass(el.parentNode, "rowhilite");
 }
 }
 return Calendar.stopEvent(ev);
};

Calendar.dayMouseOut = function(ev) {
 with (Calendar) {
 var el = getElement(ev);
 if (isRelated(el, ev) || _C || el.disabled)
 return false;
 removeClass(el, "hilite");
 if (el.caldate)
 removeClass(el.parentNode, "rowhilite");
 if (el.calendar)
 el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
 return stopEvent(ev);
 }
};

/**
 * A generic "click" handler :) handles all types of buttons defined in this
 * calendar.
 */
Calendar.cellClick = function(el, ev) {

 var cal = el.calendar;
 var closing = false;
 var newdate = false;
 var date = null;
 if (typeof el.navtype == "undefined") {
 if (cal.currentDateEl) {
 Calendar.removeClass(cal.currentDateEl, "selected");
 Calendar.addClass(el, "selected");
 closing = (cal.currentDateEl == el);
 if (!closing) {
 cal.currentDateEl = el;
 }
 }
 cal.date.setDateOnly(el.caldate);
 date = cal.date;
 var other_month = !(cal.dateClicked = !el.otherMonth);
 if (!other_month && !cal.currentDateEl)
 cal._toggleMultipleDate(new CalendarDateObject(date));
 else
 newdate = !el.disabled;
 // a date was clicked
 if (other_month)
 cal._init(cal.firstDayOfWeek, date);
 } else {
 if (el.navtype == 200) {
 Calendar.removeClass(el, "hilite");
 cal.callCloseHandler();
 return;
 }
 date = new CalendarDateObject(cal.date);
 if (el.navtype == 0)
 date.setDateOnly(new CalendarDateObject()); // TODAY
 // unless "today" was clicked, we assume no date was clicked so
 // the selected handler will know not to close the calenar when
 // in single-click mode.
 // cal.dateClicked = (el.navtype == 0);
 cal.dateClicked = false;
 var year = date.getFullYear();
 var mon = date.getMonth();
 function setMonth(m) {
 var day = date.getDate();
 var max = date.getMonthDays(m);
 if (day > max) {
 date.setDate(max);
 }
 date.setMonth(m);
 };
 switch (el.navtype) {
 case 400:
 Calendar.removeClass(el, "hilite");
 var text = Calendar._TT["ABOUT"];
 if (typeof text != "undefined") {
 text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
 } else {
 // FIXME: this should be removed as soon as lang files get updated!
 text = "Help and about box text is not translated into this language.\n" +
 "If you know this language and you feel generous please update\n" +
 "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
 "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution ;-)\n\n" +
 "Thank you!\n" +
 "http://dynarch.com/mishoo/calendar.epl\n";
 }
 alert(text);
 return;
 case -2:
 if (year > cal.minYear) {
 date.setFullYear(year - 1);
 }
 break;
 case -1:
 if (mon > 0) {
 setMonth(mon - 1);
 } else if (year-- > cal.minYear) {
 date.setFullYear(year);
 setMonth(11);
 }
 break;
 case 1:
 if (mon < 11) {
 setMonth(mon + 1);
 } else if (year < cal.maxYear) {
 date.setFullYear(year + 1);
 setMonth(0);
 }
 break;
 case 2:
 if (year < cal.maxYear) {
 date.setFullYear(year + 1);
 }
 break;
 case 100:
 cal.setFirstDayOfWeek(el.fdow);
 return;
 case 50:
 var range = el._range;
 var current = el.innerHTML;
 for (var i = range.length; --i >= 0;)
 if (range[i] == current)
 break;
 if (ev && ev.shiftKey) {
 if (--i < 0)
 i = range.length - 1;
 } else if ( ++i >= range.length )
 i = 0;
 var newval = range[i];
 el.innerHTML = newval;
 cal.onUpdateTime();
 return;
 case 0:
 // TODAY will bring us here
 if ((typeof cal.getDateStatus == "function") &&
 cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
 return false;
 }
 break;
 }
 if (!date.equalsTo(cal.date)) {
 cal.setDate(date);
 newdate = true;
 } else if (el.navtype == 0)
 newdate = closing = true;
 }
 if (newdate) {
 ev && cal.callHandler();
 }
 if (closing) {
 Calendar.removeClass(el, "hilite");
 ev && cal.callCloseHandler();
 }
};

// END: CALENDAR STATIC FUNCTIONS

// BEGIN: CALENDAR OBJECT FUNCTIONS

/**
 * This function creates the calendar inside the given parent. If _par is
 * null than it creates a popup calendar inside the BODY element. If _par is
 * an element, be it BODY, then it creates a non-popup calendar (still
 * hidden). Some properties need to be set before calling this function.
 */
Calendar.prototype.create = function (_par) {
 var parent = null;
 if (! _par) {
 // default parent is the document body, in which case we create
 // a popup calendar.
 parent = document.getElementsByTagName("body")[0];
 this.isPopup = true;
 } else {
 parent = _par;
 this.isPopup = false;
 }
 this.date = this.dateStr ? new CalendarDateObject(this.dateStr) : new CalendarDateObject();

 var table = Calendar.createElement("table");
 this.table = table;
 table.cellSpacing = 0;
 table.cellPadding = 0;
 table.calendar = this;
 Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);

 var div = Calendar.createElement("div");
 this.element = div;
 div.className = "calendar";
 if (this.isPopup) {
 div.style.position = "absolute";
 div.style.display = "none";
 }
 div.appendChild(table);

 var thead = Calendar.createElement("thead", table);
 var cell = null;
 var row = null;

 var cal = this;
 var hh = function (text, cs, navtype) {
 cell = Calendar.createElement("td", row);
 cell.colSpan = cs;
 cell.className = "button";
 if (navtype != 0 && Math.abs(navtype) <= 2)
 cell.className += " nav";
 Calendar._add_evs(cell);
 cell.calendar = cal;
 cell.navtype = navtype;
 cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
 return cell;
 };

 row = Calendar.createElement("tr", thead);
 var title_length = 6;
 (this.isPopup) && --title_length;
 (this.weekNumbers) && ++title_length;

 hh("?", 1, 400).ttip = Calendar._TT["INFO"];
 this.title = hh("", title_length, 300);
 this.title.className = "title";
 if (this.isPopup) {
 this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
 this.title.style.cursor = "move";
 hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
 }

 row = Calendar.createElement("tr", thead);
 row.className = "headrow";

 this._nav_py = hh("&#x00ab;", 1, -2);
 this._nav_py.ttip = Calendar._TT["PREV_YEAR"];

 this._nav_pm = hh("&#x2039;", 1, -1);
 this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];

 this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
 this._nav_now.ttip = Calendar._TT["GO_TODAY"];

 this._nav_nm = hh("&#x203a;", 1, 1);
 this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];

 this._nav_ny = hh("&#x00bb;", 1, 2);
 this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];

 // day names
 row = Calendar.createElement("tr", thead);
 row.className = "daynames";
 if (this.weekNumbers) {
 cell = Calendar.createElement("td", row);
 cell.className = "name wn";
 cell.innerHTML = Calendar._TT["WK"];
 }
 for (var i = 7; i > 0; --i) {
 cell = Calendar.createElement("td", row);
 if (!i) {
 cell.navtype = 100;
 cell.calendar = this;
 Calendar._add_evs(cell);
 }
 }
 this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
 this._displayWeekdays();

 var tbody = Calendar.createElement("tbody", table);
 this.tbody = tbody;

 for (i = 6; i > 0; --i) {
 row = Calendar.createElement("tr", tbody);
 if (this.weekNumbers) {
 cell = Calendar.createElement("td", row);
 }
 for (var j = 7; j > 0; --j) {
 cell = Calendar.createElement("td", row);
 cell.calendar = this;
 Calendar._add_evs(cell);
 }
 }

 if (this.showsTime) {
 row = Calendar.createElement("tr", tbody);
 row.className = "time";

 cell = Calendar.createElement("td", row);
 cell.className = "time";
 cell.colSpan = 2;
 cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";

 cell = Calendar.createElement("td", row);
 cell.className = "time";
 cell.colSpan = this.weekNumbers ? 4 : 3;

 (function(){
 function makeTimePart(className, init, range_start, range_end) {
 var part = Calendar.createElement("span", cell);
 part.className = className;
 part.innerHTML = init;
 part.calendar = cal;
 part.ttip = Calendar._TT["TIME_PART"];
 part.navtype = 50;
 part._range = [];
 if (typeof range_start != "number")
 part._range = range_start;
 else {
 for (var i = range_start; i <= range_end; ++i) {
 var txt;
 if (i < 10 && range_end >= 10) txt = '0' + i;
 else txt = '' + i;
 part._range[part._range.length] = txt;
 }
 }
 Calendar._add_evs(part);
 return part;
 };
 var hrs = cal.date.getHours();
 var mins = cal.date.getMinutes();
 var t12 = !cal.time24;
 var pm = (hrs > 12);
 if (t12 && pm) hrs -= 12;
 var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
 var span = Calendar.createElement("span", cell);
 span.innerHTML = ":";
 span.className = "colon";
 var M = makeTimePart("minute", mins, 0, 59);
 var AP = null;
 cell = Calendar.createElement("td", row);
 cell.className = "time";
 cell.colSpan = 2;
 if (t12)
 AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
 else
 cell.innerHTML = "&nbsp;";

 cal.onSetTime = function() {
 var pm, hrs = this.date.getHours(),
 mins = this.date.getMinutes();
 if (t12) {
 pm = (hrs >= 12);
 if (pm) hrs -= 12;
 if (hrs == 0) hrs = 12;
 AP.innerHTML = pm ? "pm" : "am";
 }
 H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
 M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
 };

 cal.onUpdateTime = function() {
 var date = this.date;
 var h = parseInt(H.innerHTML, 10);
 if (t12) {
 if (/pm/i.test(AP.innerHTML) && h < 12)
 h += 12;
 else if (/am/i.test(AP.innerHTML) && h == 12)
 h = 0;
 }
 var d = date.getDate();
 var m = date.getMonth();
 var y = date.getFullYear();
 date.setHours(h);
 date.setMinutes(parseInt(M.innerHTML, 10));
 date.setFullYear(y);
 date.setMonth(m);
 date.setDate(d);
 this.dateClicked = false;
 this.callHandler();
 };
 })();
 } else {
 this.onSetTime = this.onUpdateTime = function() {};
 }

 var tfoot = Calendar.createElement("tfoot", table);

 row = Calendar.createElement("tr", tfoot);
 row.className = "footrow";

 cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
 cell.className = "ttip";
 if (this.isPopup) {
 cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
 cell.style.cursor = "move";
 }
 this.tooltips = cell;

 div = Calendar.createElement("div", this.element);
 this.monthsCombo = div;
 div.className = "combo";
 for (i = 0; i < Calendar._MN.length; ++i) {
 var mn = Calendar.createElement("div");
 mn.className = Calendar.is_ie ? "label-IEfix" : "label";
 mn.month = i;
 mn.innerHTML = Calendar._SMN[i];
 div.appendChild(mn);
 }

 div = Calendar.createElement("div", this.element);
 this.yearsCombo = div;
 div.className = "combo";
 for (i = 12; i > 0; --i) {
 var yr = Calendar.createElement("div");
 yr.className = Calendar.is_ie ? "label-IEfix" : "label";
 div.appendChild(yr);
 }

 this._init(this.firstDayOfWeek, this.date);
 parent.appendChild(this.element);
};

/** keyboard navigation, only for popup calendars */
Calendar._keyEvent = function(ev) {
 var cal = window._dynarch_popupCalendar;
 if (!cal || cal.multiple)
 return false;
 (Calendar.is_ie) && (ev = window.event);
 var act = (Calendar.is_ie || ev.type == "keypress"),
 K = ev.keyCode;
 if (ev.ctrlKey) {
 switch (K) {
 case 37: // KEY left
 act && Calendar.cellClick(cal._nav_pm);
 break;
 case 38: // KEY up
 act && Calendar.cellClick(cal._nav_py);
 break;
 case 39: // KEY right
 act && Calendar.cellClick(cal._nav_nm);
 break;
 case 40: // KEY down
 act && Calendar.cellClick(cal._nav_ny);
 break;
 default:
 return false;
 }
 } else switch (K) {
 case 32: // KEY space (now)
 Calendar.cellClick(cal._nav_now);
 break;
 case 27: // KEY esc
 act && cal.callCloseHandler();
 break;
 case 37: // KEY left
 case 38: // KEY up
 case 39: // KEY right
 case 40: // KEY down
 if (act) {
 var prev, x, y, ne, el, step;
 prev = K == 37 || K == 38;
 step = (K == 37 || K == 39) ? 1 : 7;
 function setVars() {
 el = cal.currentDateEl;
 var p = el.pos;
 x = p & 15;
 y = p >> 4;
 ne = cal.ar_days[y][x];
 };setVars();
 function prevMonth() {
 var date = new CalendarDateObject(cal.date);
 date.setDate(date.getDate() - step);
 cal.setDate(date);
 };
 function nextMonth() {
 var date = new CalendarDateObject(cal.date);
 date.setDate(date.getDate() + step);
 cal.setDate(date);
 };
 while (1) {
 switch (K) {
 case 37: // KEY left
 if (--x >= 0)
 ne = cal.ar_days[y][x];
 else {
 x = 6;
 K = 38;
 continue;
 }
 break;
 case 38: // KEY up
 if (--y >= 0)
 ne = cal.ar_days[y][x];
 else {
 prevMonth();
 setVars();
 }
 break;
 case 39: // KEY right
 if (++x < 7)
 ne = cal.ar_days[y][x];
 else {
 x = 0;
 K = 40;
 continue;
 }
 break;
 case 40: // KEY down
 if (++y < cal.ar_days.length)
 ne = cal.ar_days[y][x];
 else {
 nextMonth();
 setVars();
 }
 break;
 }
 break;
 }
 if (ne) {
 if (!ne.disabled)
 Calendar.cellClick(ne);
 else if (prev)
 prevMonth();
 else
 nextMonth();
 }
 }
 break;
 case 13: // KEY enter
 if (act)
 Calendar.cellClick(cal.currentDateEl, ev);
 break;
 default:
 return false;
 }
 return Calendar.stopEvent(ev);
};

/**
 * (RE)Initializes the calendar to the given date and firstDayOfWeek
 */
Calendar.prototype._init = function (firstDayOfWeek, date) {
 var today = new CalendarDateObject(),
 TY = today.getFullYear(),
 TM = today.getMonth(),
 TD = today.getDate();
 this.table.style.visibility = "hidden";
 var year = date.getFullYear();
 if (year < this.minYear) {
 year = this.minYear;
 date.setFullYear(year);
 } else if (year > this.maxYear) {
 year = this.maxYear;
 date.setFullYear(year);
 }
 this.firstDayOfWeek = firstDayOfWeek;
 this.date = new CalendarDateObject(date);
 var month = date.getMonth();
 var mday = date.getDate();
 var no_days = date.getMonthDays();
 // calendar voodoo for computing the first day that would actually be
 // displayed in the calendar, even if it's from the previous month.
 // WARNING: this is magic. ;-)
 date.setDate(1);
 var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
 if (day1 < 0)
 day1 += 7;
 (day1 == 0)?date.setDate(day1) : date.setDate(-day1);
 date.setDate(date.getDate() + 1);
 var row = this.tbody.firstChild;
 var MN = Calendar._SMN[month];
 var ar_days = this.ar_days = new Array();
 var weekend = Calendar._TT["WEEKEND"];
 var dates = this.multiple ? (this.datesCells = {}) : null;
 for (var i = 0; i < 6; ++i, row = row.nextSibling) {
 var cell = row.firstChild;
 if (this.weekNumbers) {
 cell.className = "day wn";
 cell.innerHTML = date.getWeekNumber();
 cell = cell.nextSibling;
 }
 row.className = "daysrow";
 var hasdays = false, iday, dpos = ar_days[i] = [];
 for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
 iday = date.getDate();
 var wday = date.getDay();
 cell.className = "day";
 cell.pos = i << 4 | j;
 dpos[j] = cell;
 var current_month = (date.getMonth() == month);
 if (!current_month) {
 if (this.showsOtherMonths) {
 cell.className += " othermonth";
 cell.otherMonth = true;
 } else {
 cell.className = "emptycell";
 cell.innerHTML = "&nbsp;";
 cell.disabled = true;
 continue;
 }
 } else {
 cell.otherMonth = false;
 hasdays = true;
 }
 cell.disabled = false;
 cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
 if (dates)
 dates[date.print("%Y%m%d")] = cell;
 if (this.getDateStatus) {
 this.getDateStatus.bind(this); // To bind with calendar object rather than window
 var status = this.getDateStatus(date, year, month, iday);
 
 if (this.getDateToolTip) {
 var toolTip = this.getDateToolTip(date, year, month, iday);
 if (toolTip)
 cell.title = toolTip;
 }
 if (status === true) {
 cell.className += " disabled";
 cell.disabled = true;
 } else {
 if (/disabled/i.test(status))
 cell.disabled = true;
 cell.className += " " + status;
 }
 }
 if (!cell.disabled) {
 cell.caldate = new CalendarDateObject(date);
 cell.ttip = "_";
 if (!this.multiple && current_month
 && iday == mday && this.hiliteToday) {
 cell.className += " selected";
 this.currentDateEl = cell;
 }
 if (date.getFullYear() == TY &&
 date.getMonth() == TM &&
 iday == TD) {
 cell.className += " today";
 cell.ttip += Calendar._TT["PART_TODAY"];
 }
 if (weekend.indexOf(wday.toString()) != -1)
 cell.className += cell.otherMonth ? " oweekend" : " weekend";
 }
 }
 if (!(hasdays || this.showsOtherMonths))
 row.className = "emptyrow";
 }
 this.title.innerHTML = Calendar._MN[month] + ", " + year;
 this.onSetTime();
 this.table.style.visibility = "visible";
 this._initMultipleDates();
 // PROFILE
 // this.tooltips.innerHTML = "Generated in " + ((new CalendarDateObject()) - today) + " ms";
};

Calendar.prototype._initMultipleDates = function() {
 if (this.multiple) {
 for (var i in this.multiple) {
 var cell = this.datesCells[i];
 var d = this.multiple[i];
 if (!d)
 continue;
 if (cell)
 cell.className += " selected";
 }
 }
};

Calendar.prototype._toggleMultipleDate = function(date) {
 if (this.multiple) {
 var ds = date.print("%Y%m%d");
 var cell = this.datesCells[ds];
 if (cell) {
 var d = this.multiple[ds];
 if (!d) {
 Calendar.addClass(cell, "selected");
 this.multiple[ds] = date;
 } else {
 Calendar.removeClass(cell, "selected");
 delete this.multiple[ds];
 }
 }
 }
};

Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
 this.getDateToolTip = unaryFunction;
};

/**
 * Calls _init function above for going to a certain date (but only if the
 * date is different than the currently selected one).
 */
Calendar.prototype.setDate = function (date) {
/*
 if((cal = window._dynarch_popupCalendar)){
 var min_date = cal.params.inputField.getAttribute('min_date');
 var max_date = cal.params.inputField.getAttribute('max_date');

 if(typeof (min_date) != "undefined"){
 var minDate = new Date(min_date);
 if(date < minDate){
 alert('Can not select date less then '+ min_date);
 return false;
 }
 }
 
 
 if(typeof(max_date) != "undefined"){
 var maxDate = new Date(max_date);
 if(date > maxDate){
 alert('Can not select date out '+ max_date);
 return false;
 }
 }
 }*/
 
 if (!date.equalsTo(this.date)) {
 this._init(this.firstDayOfWeek, date);
 }
};

/**
 * Refreshes the calendar. Useful if the "disabledHandler" function is
 * dynamic, meaning that the list of disabled date can change at runtime.
 * Just * call this function if you think that the list of disabled dates
 * should * change.
 */
Calendar.prototype.refresh = function () {
 this._init(this.firstDayOfWeek, this.date);
};

/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
 this._init(firstDayOfWeek, this.date);
 this._displayWeekdays();
};

/**
 * Allows customization of what dates are enabled. The "unaryFunction"
 * parameter must be a function object that receives the date (as a JS Date
 * object) and returns a boolean value. If the returned value is true then
 * the passed date will be marked as disabled.
 */
Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
 this.getDateStatus = unaryFunction;
};

/** Customization of allowed year range for the calendar. */
Calendar.prototype.setRange = function (a, z) {
 this.minYear = a;
 this.maxYear = z;
};

/** Calls the first user handler (selectedHandler). */
Calendar.prototype.callHandler = function () {
 if (this.onSelected) {
 this.onSelected(this, this.date.print(this.dateFormat));
 }
};

/** Calls the second user handler (closeHandler). */
Calendar.prototype.callCloseHandler = function () {
 if (this.onClose) {
 this.onClose(this);
 }
 this.hideShowCovered();
};

/** Removes the calendar object from the DOM tree and destroys it. */
Calendar.prototype.destroy = function () {
 var el = this.element.parentNode;
 el.removeChild(this.element);
 Calendar._C = null;
 window._dynarch_popupCalendar = null;
};

/**
 * Moves the calendar element to a different section in the DOM tree (changes
 * its parent).
 */
Calendar.prototype.reparent = function (new_parent) {
 var el = this.element;
 el.parentNode.removeChild(el);
 new_parent.appendChild(el);
};

// This gets called when the user presses a mouse button anywhere in the
// document, if the calendar is shown. If the click was outside the open
// calendar this function closes it.
Calendar._checkCalendar = function(ev) {
 var calendar = window._dynarch_popupCalendar;
 if (!calendar) {
 return false;
 }
 var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
 for (; el != null && el != calendar.element; el = el.parentNode);
 if (el == null) {
 // calls closeHandler which should hide the calendar.
 window._dynarch_popupCalendar.callCloseHandler();
 return Calendar.stopEvent(ev);
 }
};

/** Shows the calendar. */
Calendar.prototype.show = function () {
 var rows = this.table.getElementsByTagName("tr");
 for (var i = rows.length; i > 0;) {
 var row = rows[--i];
 Calendar.removeClass(row, "rowhilite");
 var cells = row.getElementsByTagName("td");
 for (var j = cells.length; j > 0;) {
 var cell = cells[--j];
 Calendar.removeClass(cell, "hilite");
 Calendar.removeClass(cell, "active");
 }
 }
 this.element.style.display = "block";
 this.hidden = false;
 if (this.isPopup) {
 window._dynarch_popupCalendar = this;
 Calendar.addEvent(document, "keydown", Calendar._keyEvent);
 Calendar.addEvent(document, "keypress", Calendar._keyEvent);
 Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
 }
 this.hideShowCovered();
};

/**
 * Hides the calendar. Also removes any "hilite" from the class of any TD
 * element.
 */
Calendar.prototype.hide = function () {
 if (this.isPopup) {
 Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
 Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
 Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
 }
 this.element.style.display = "none";
 this.hidden = true;
 this.hideShowCovered();
};

/**
 * Shows the calendar at a given absolute position (beware that, depending on
 * the calendar element style -- position property -- this might be relative
 * to the parent's containing rectangle).
 */
Calendar.prototype.showAt = function (x, y) {
 var s = this.element.style;
 s.left = x + "px";
 s.top = y + "px";
 this.show();
};

/** Shows the calendar near a given element. */
Calendar.prototype.showAtElement = function (el, opts) {
 var self = this;
 var p = Calendar.getAbsolutePos(el);
 if (!opts || typeof opts != "string") {
 this.showAt(p.x, p.y + el.offsetHeight);
 return true;
 }
 function fixPosition(box) {
 if (box.x < 0)
 box.x = 0;
 if (box.y < 0)
 box.y = 0;
 var cp = document.createElement("div");
 var s = cp.style;
 s.position = "absolute";
 s.right = s.bottom = s.width = s.height = "0px";
 document.body.appendChild(cp);
 var br = Calendar.getAbsolutePos(cp);
 document.body.removeChild(cp);
 if (Calendar.is_ie) {
 br.y += document.body.scrollTop;
 br.x += document.body.scrollLeft;
 } else {
 br.y += window.scrollY;
 br.x += window.scrollX;
 }
 var tmp = box.x + box.width - br.x;
 if (tmp > 0) box.x -= tmp;
 tmp = box.y + box.height - br.y;
 if (tmp > 0) box.y -= tmp;
 };
 this.element.style.display = "block";
 Calendar.continuation_for_the_fucking_khtml_browser = function() {
 var w = self.element.offsetWidth;
 var h = self.element.offsetHeight;
 self.element.style.display = "none";
 var valign = opts.substr(0, 1);
 var halign = "l";
 if (opts.length > 1) {
 halign = opts.substr(1, 1);
 }
 // vertical alignment
 switch (valign) {
 case "T": p.y -= h; break;
 case "B": p.y += el.offsetHeight; break;
 case "C": p.y += (el.offsetHeight - h) / 2; break;
 case "t": p.y += el.offsetHeight - h; break;
 case "b": break; // already there
 }
 // horizontal alignment
 switch (halign) {
 case "L": p.x -= w; break;
 case "R": p.x += el.offsetWidth; break;
 case "C": p.x += (el.offsetWidth - w) / 2; break;
 case "l": p.x += el.offsetWidth - w; break;
 case "r": break; // already there
 }
 p.width = w;
 p.height = h + 40;
 self.monthsCombo.style.display = "none";
 fixPosition(p);
 self.showAt(p.x, p.y);
 };
 if (Calendar.is_khtml)
 setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
 else
 Calendar.continuation_for_the_fucking_khtml_browser();
};

/** Customizes the date format. */
Calendar.prototype.setDateFormat = function (str) {
 this.dateFormat = str;
};

/** Customizes the tooltip date format. */
Calendar.prototype.setTtDateFormat = function (str) {
 this.ttDateFormat = str;
};

/**
 * Tries to identify the date represented in a string. If successful it also
 * calls this.setDate which moves the calendar to the given date.
 */
Calendar.prototype.parseDate = function(str, fmt) {
 if (!fmt)
 fmt = this.dateFormat;
 this.setDate(Date.parseDate(str, fmt));
};

Calendar.prototype.hideShowCovered = function () {
 if (!Calendar.is_ie && !Calendar.is_opera)
 return;
 function getVisib(obj){
 var value = obj.style.visibility;
 if (!value) {
 if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
 if (!Calendar.is_khtml)
 value = document.defaultView.
 getComputedStyle(obj, "").getPropertyValue("visibility");
 else
 value = '';
 } else if (obj.currentStyle) { // IE
 value = obj.currentStyle.visibility;
 } else
 value = '';
 }
 return value;
 };

 var tags = new Array("applet", "iframe", "select");
 var el = this.element;

 var p = Calendar.getAbsolutePos(el);
 var EX1 = p.x;
 var EX2 = el.offsetWidth + EX1;
 var EY1 = p.y;
 var EY2 = el.offsetHeight + EY1;

 for (var k = tags.length; k > 0; ) {
 var ar = document.getElementsByTagName(tags[--k]);
 var cc = null;

 for (var i = ar.length; i > 0;) {
 cc = ar[--i];

 p = Calendar.getAbsolutePos(cc);
 var CX1 = p.x;
 var CX2 = cc.offsetWidth + CX1;
 var CY1 = p.y;
 var CY2 = cc.offsetHeight + CY1;

 if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
 if (!cc.__msh_save_visibility) {
 cc.__msh_save_visibility = getVisib(cc);
 }
 cc.style.visibility = cc.__msh_save_visibility;
 } else {
 if (!cc.__msh_save_visibility) {
 cc.__msh_save_visibility = getVisib(cc);
 }
 cc.style.visibility = "hidden";
 }
 }
 }
};

/** Internal function; it displays the bar with the names of the weekday. */
Calendar.prototype._displayWeekdays = function () {
 var fdow = this.firstDayOfWeek;
 var cell = this.firstdayname;
 var weekend = Calendar._TT["WEEKEND"];
 for (var i = 0; i < 7; ++i) {
 cell.className = "day name";
 var realday = (i + fdow) % 7;
 if (i) {
 cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
 cell.navtype = 100;
 cell.calendar = this;
 cell.fdow = realday;
 Calendar._add_evs(cell);
 }
 if (weekend.indexOf(realday.toString()) != -1) {
 Calendar.addClass(cell, "weekend");
 }
 cell.innerHTML = Calendar._SDN[(i + fdow) % 7];
 cell = cell.nextSibling;
 }
};

/** Internal function. Hides all combo boxes that might be displayed. */
Calendar.prototype._hideCombos = function () {
 this.monthsCombo.style.display = "none";
 this.yearsCombo.style.display = "none";
};

/** Internal function. Starts dragging the element. */
Calendar.prototype._dragStart = function (ev) {
 if (this.dragging) {
 return;
 }
 this.dragging = true;
 var posX;
 var posY;
 if (Calendar.is_ie) {
 posY = window.event.clientY + document.body.scrollTop;
 posX = window.event.clientX + document.body.scrollLeft;
 } else {
 posY = ev.clientY + window.scrollY;
 posX = ev.clientX + window.scrollX;
 }
 var st = this.element.style;
 this.xOffs = posX - parseInt(st.left);
 this.yOffs = posY - parseInt(st.top);
 with (Calendar) {
 addEvent(document, "mousemove", calDragIt);
 addEvent(document, "mouseup", calDragEnd);
 }
};

// BEGIN: DATE OBJECT PATCHES

/** Adds the number of days array to the Date object. */
Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

/** Constants used for time computations */
Date.SECOND = 1000 /* milliseconds */;
Date.MINUTE = 60 * Date.SECOND;
Date.HOUR = 60 * Date.MINUTE;
Date.DAY = 24 * Date.HOUR;
Date.WEEK = 7 * Date.DAY;

Date.parseDate = function(str, fmt) {
 var today = new CalendarDateObject();
 var y = 0;
 var m = -1;
 var d = 0;

 // translate date into en_US, because split() cannot parse non-latin stuff
 var a = str;
 var i;
 for (i = 0; i < Calendar._MN.length; i++) {
 a = a.replace(Calendar._MN[i], enUS.m.wide[i]);
 }
 for (i = 0; i < Calendar._SMN.length; i++) {
 a = a.replace(Calendar._SMN[i], enUS.m.abbr[i]);
 }
 a = a.replace(Calendar._am, 'am');
 a = a.replace(Calendar._am.toLowerCase(), 'am');
 a = a.replace(Calendar._pm, 'pm');
 a = a.replace(Calendar._pm.toLowerCase(), 'pm');

 a = a.split(/\W+/);

 var b = fmt.match(/%./g);
 var i = 0, j = 0;
 var hr = 0;
 var min = 0;
 for (i = 0; i < a.length; ++i) {
 if (!a[i])
 continue;
 switch (b[i]) {
 case "%d":
 case "%e":
 d = parseInt(a[i], 10);
 break;

 case "%m":
 m = parseInt(a[i], 10) - 1;
 break;

 case "%Y":
 case "%y":
 y = parseInt(a[i], 10);
 (y < 100) && (y += (y > 29) ? 1900 : 2000);
 break;

 case "%b":
 for (j = 0; j < 12; ++j) {
 if (enUS.m.abbr[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
 }
 break;

 case "%B":
 for (j = 0; j < 12; ++j) {
 if (enUS.m.wide[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
 }
 break;

 case "%H":
 case "%I":
 case "%k":
 case "%l":
 hr = parseInt(a[i], 10);
 break;

 case "%P":
 case "%p":
 if (/pm/i.test(a[i]) && hr < 12)
 hr += 12;
 else if (/am/i.test(a[i]) && hr >= 12)
 hr -= 12;
 break;

 case "%M":
 min = parseInt(a[i], 10);
 break;
 }
 }
 if (isNaN(y)) y = today.getFullYear();
 if (isNaN(m)) m = today.getMonth();
 if (isNaN(d)) d = today.getDate();
 if (isNaN(hr)) hr = today.getHours();
 if (isNaN(min)) min = today.getMinutes();
 if (y != 0 && m != -1 && d != 0)
 return new CalendarDateObject(y, m, d, hr, min, 0);
 y = 0; m = -1; d = 0;
 for (i = 0; i < a.length; ++i) {
 if (a[i].search(/[a-zA-Z]+/) != -1) {
 var t = -1;
 for (j = 0; j < 12; ++j) {
 if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
 }
 if (t != -1) {
 if (m != -1) {
 d = m+1;
 }
 m = t;
 }
 } else if (parseInt(a[i], 10) <= 12 && m == -1) {
 m = a[i]-1;
 } else if (parseInt(a[i], 10) > 31 && y == 0) {
 y = parseInt(a[i], 10);
 (y < 100) && (y += (y > 29) ? 1900 : 2000);
 } else if (d == 0) {
 d = a[i];
 }
 }
 if (y == 0)
 y = today.getFullYear();
 if (m != -1 && d != 0)
 return new CalendarDateObject(y, m, d, hr, min, 0);
 return today;
};

/** Returns the number of days in the current month */
Date.prototype.getMonthDays = function(month) {
 var year = this.getFullYear();
 if (typeof month == "undefined") {
 month = this.getMonth();
 }
 if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
 return 29;
 } else {
 return Date._MD[month];
 }
};

/** Returns the number of day in the year. */
Date.prototype.getDayOfYear = function() {
 var now = new CalendarDateObject(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
 var then = new CalendarDateObject(this.getFullYear(), 0, 0, 0, 0, 0);
 var time = now - then;
 return Math.floor(time / Date.DAY);
};

/** Returns the number of the week in year, as defined in ISO 8601. */
Date.prototype.getWeekNumber = function() {
 var d = new CalendarDateObject(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
 var DoW = d.getDay();
 d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
 var ms = d.valueOf(); // GMT
 d.setMonth(0);
 d.setDate(4); // Thu in Week 1
 return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};

/** Checks date and time equality */
Date.prototype.equalsTo = function(date) {
 return ((this.getFullYear() == date.getFullYear()) &&
 (this.getMonth() == date.getMonth()) &&
 (this.getDate() == date.getDate()) &&
 (this.getHours() == date.getHours()) &&
 (this.getMinutes() == date.getMinutes()));
};

/** Set only the year, month, date parts (keep existing time) */
Date.prototype.setDateOnly = function(date) {
 var tmp = new CalendarDateObject(date);
 this.setDate(1);
 this.setFullYear(tmp.getFullYear());
 this.setMonth(tmp.getMonth());
 this.setDate(tmp.getDate());
};

/** Prints the date in a string according to the given format. */
Date.prototype.print = function (str) {
 var m = this.getMonth();
 var d = this.getDate();
 var y = this.getFullYear();
 var wn = this.getWeekNumber();
 var w = this.getDay();
 var s = {};
 var hr = this.getHours();
 var pm = (hr >= 12);
 var ir = (pm) ? (hr - 12) : hr;
 var dy = this.getDayOfYear();
 if (ir == 0)
 ir = 12;
 var min = this.getMinutes();
 var sec = this.getSeconds();
 s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
 s["%A"] = Calendar._DN[w]; // full weekday name
 s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
 s["%B"] = Calendar._MN[m]; // full month name
 // FIXME: %c : preferred date and time representation for the current locale
 s["%C"] = 1 + Math.floor(y / 100); // the century number
 s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
 s["%e"] = d; // the day of the month (range 1 to 31)
 // FIXME: %D : american date style: %m/%d/%y
 // FIXME: %E, %F, %G, %g, %h (man strftime)
 s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
 s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
 s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
 s["%k"] = hr; // hour, range 0 to 23 (24h format)
 s["%l"] = ir; // hour, range 1 to 12 (12h format)
 s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
 s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
 s["%n"] = "\n"; // a newline character
 s["%p"] = pm ? Calendar._pm.toUpperCase() : Calendar._am.toUpperCase();
 s["%P"] = pm ? Calendar._pm.toLowerCase() : Calendar._am.toLowerCase();
 // FIXME: %r : the time in am/pm notation %I:%M:%S %p
 // FIXME: %R : the time in 24-hour notation %H:%M
 s["%s"] = Math.floor(this.getTime() / 1000);
 s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
 s["%t"] = "\t"; // a tab character
 // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
 s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
 s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
 s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
 // FIXME: %x : preferred date representation for the current locale without the time
 // FIXME: %X : preferred time representation for the current locale without the date
 s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
 s["%Y"] = y; // year with the century
 s["%%"] = "%"; // a literal '%' character

 var re = /%./g;
 if (!Calendar.is_ie5 && !Calendar.is_khtml)
 return str.replace(re, function (par) { return s[par] || par; });

 var a = str.match(re);
 for (var i = 0; i < a.length; i++) {
 var tmp = s[a[i]];
 if (tmp) {
 re = new RegExp(a[i], 'g');
 str = str.replace(re, tmp);
 }
 }

 return str;
};

Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
Date.prototype.setFullYear = function(y) {
 var d = new CalendarDateObject(this);
 d.__msh_oldSetFullYear(y);
 if (d.getMonth() != this.getMonth())
 this.setDate(28);
 this.__msh_oldSetFullYear(y);
};

CalendarDateObject.prototype = new Date();
CalendarDateObject.prototype.constructor = CalendarDateObject;
CalendarDateObject.prototype.parent = Date.prototype;
function CalendarDateObject() {
 var dateObj;
 if (arguments.length > 1) {
 dateObj = eval("new this.parent.constructor("+Array.prototype.slice.call(arguments).join(",")+");");
 } else if (arguments.length > 0) {
 dateObj = new this.parent.constructor(arguments[0]);
 } else {
 dateObj = new this.parent.constructor();
 if (typeof(CalendarDateObject._LOCAL_TIMZEONE_OFFSET_SECONDS) != "undefined") {
 dateObj.setTime(dateObj.getTime()+(CalendarDateObject._LOCAL_TIMZEONE_OFFSET_SECONDS - dateObj.getTimezoneOffset())*1000);
 }
 }
 return dateObj;
}

// END: DATE OBJECT PATCHES

// global object that remembers the calendar
window._dynarch_popupCalendar = null;
federalHolidayList = new Array();
federalHolidayList.push('01/01'); //Thursday, January 1 - New Year's Day
federalHolidayList.push('01/19'); //Monday, January 19 - Martin Luther King Jr's Birthday
federalHolidayList.push('02/16'); //Monday, February 16 - Washington's Birthday (President's Day)
federalHolidayList.push('05/25'); //Monday, May 25 - Memorial Day
federalHolidayList.push('07/04'); //Saturday, July 4 - Independence Day
federalHolidayList.push('09/07'); //Monday, September 7 - Labor Day
federalHolidayList.push('10/12'); //Monday, October 12 - Columbus Day
federalHolidayList.push('11/11'); //Wednesday, November 11 - Veterans Day
federalHolidayList.push('11/26'); //Thursday, November 26 - Thanksgiving Day
federalHolidayList.push('12/25'); //Friday, December 25 - Christmas Day

Calendar.federalHolidayList = federalHolidayList;

disableNonRangedDates = function(date){

 if((cal = window._dynarch_popupCalendar) || (cal = this)){

 var min_date = cal.params.inputField.getAttribute('min_date');
 var max_date = cal.params.inputField.getAttribute('max_date');

 if(typeof (min_date) != "undefined"){
 var minDate = new Date(min_date);
 if(date < minDate){
 return true;
 }
 
 }
 
 if(typeof(max_date) != "undefined"){
 var maxDate = new Date(max_date);
 if(date > maxDate){
 return true;
 }
 }
 
 }
 
 if(date.getDay() == 0){
 return true;
 }
 
 if(Calendar.federalHolidayList.inArray(date.toStringFormat("{m}/{d}"))){
 return true;
 }
 
 if(date.getDay() == 1){//MOnday
 var y = new Date(date);
 y.setDate(date.getDate()-1);
 if(Calendar.federalHolidayList.inArray(y.toStringFormat("{m}/{d}"))){
 return true;
 } 
 
 }

 var t = new Date(); today = new Date(t.getFullYear(), t.getMonth() , t.getDate());

 if(date < today){
 return true;
 }

 return false;
}

Array.prototype.inArray = function(val){
 for(var i=0, l= this.length; i<l; i++){
 if(this[i] === val){
 return true;
 }
 }
 return false;
}

Date.prototype.toStringFormat = function(str){

 var val = new Array();
 var valArr = this.toString().split(" ");
 var valAr2 = valArr[4].split(':');
 
 val['{D}' ] = valArr[0];
 val['{M}' ] = valArr[1];
 val['{d}' ] = valArr[2];
 val['{Y}' ] = valArr[3];
 val['{H}' ] = valAr2[0];
 val['{i}' ] = valAr2[1];
 val['{s}' ] = valAr2[2];
 val['{GM}'] = valArr[5]; 
 val['{TZ}'] = this.toString().match(/\([^)]*\)/);
 val['{m}' ] = (this.getMonth() <10)?"0"+(this.getMonth()+1):this.getMonth();
 val['{n}' ] = this.getMonth() +1;
 var resKey = str.match(/{[a-zA-Z]}/g);
 for(var i=0, l=resKey.length; i<l; i++){
 if(typeof(val[resKey[i]]) != "undefined"){
 str = str.replace(resKey[i], val[resKey[i]])
 }
 }
 
 return str;
}
new Date().toStringFormat("{m}/{d}")

/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
 * ---------------------------------------------------------------------------
 *
 * The DHTML Calendar
 *
 * Details and latest version at:
 * http://dynarch.com/mishoo/calendar.epl
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 *
 * This file defines helper functions for setting up the calendar. They are
 * intended to help non-programmers get a working calendar on their site
 * quickly. This script should not be seen as part of the calendar. It just
 * shows you what one can do with the calendar, while in the same time
 * providing a quick and simple method for setting it up. If you need
 * exhaustive customization of the calendar creation process feel free to
 * modify this code to suit your needs (this is recommended and much better
 * than modifying calendar.js itself).
 */
 Calendar.setup=function(params){function param_default(pname,def){if(typeof params[pname]=="undefined"){params[pname]=def;}};param_default("inputField",null);param_default("displayArea",null);param_default("button",null);param_default("eventName","click");param_default("ifFormat","%Y/%m/%d");param_default("daFormat","%Y/%m/%d");param_default("singleClick",true);param_default("disableFunc",null);param_default("dateStatusFunc",params["disableFunc"]);param_default("dateText",null);param_default("firstDay",null);param_default("align","Br");param_default("range",[1900,2999]);param_default("weekNumbers",true);param_default("flat",null);param_default("flatCallback",null);param_default("onSelect",null);param_default("onClose",null);param_default("onUpdate",null);param_default("date",null);param_default("showsTime",false);param_default("timeFormat","24");param_default("electric",true);param_default("step",2);param_default("position",null);param_default("cache",false);param_default("showOthers",false);param_default("multiple",null);var tmp=["inputField","displayArea","button"];for(var i in tmp){if(typeof params[tmp[i]]=="string"){params[tmp[i]]=document.getElementById(params[tmp[i]]);}}if(!(params.flat||params.multiple||params.inputField||params.displayArea||params.button)){alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code");return false;}function onSelect(cal){var p=cal.params;var update=(cal.dateClicked||p.electric);if(update&&p.inputField){p.inputField.value=cal.date.print(p.ifFormat);if(typeof p.inputField.onchange=="function")p.inputField.onchange();}if(update&&p.displayArea)p.displayArea.innerHTML=cal.date.print(p.daFormat);if(update&&typeof p.onUpdate=="function")p.onUpdate(cal);if(update&&p.flat){if(typeof p.flatCallback=="function")p.flatCallback(cal);}if(update&&p.singleClick&&cal.dateClicked)cal.callCloseHandler();};if(params.flat!=null){if(typeof params.flat=="string")params.flat=document.getElementById(params.flat);if(!params.flat){alert("Calendar.setup:\n Flat specified but can't find parent.");return false;}var cal=new Calendar(params.firstDay,params.date,params.onSelect||onSelect);cal.showsOtherMonths=params.showOthers;cal.showsTime=params.showsTime;cal.time24=(params.timeFormat=="24");cal.params=params;cal.weekNumbers=params.weekNumbers;cal.setRange(params.range[0],params.range[1]);cal.setDateStatusHandler(params.dateStatusFunc);cal.getDateText=params.dateText;if(params.ifFormat){cal.setDateFormat(params.ifFormat);}if(params.inputField&&typeof params.inputField.value=="string"){cal.parseDate(params.inputField.value);}cal.create(params.flat);cal.show();return false;}var triggerEl=params.button||params.displayArea||params.inputField;triggerEl["on"+params.eventName]=function(){var dateEl=params.inputField||params.displayArea;var dateFmt=params.inputField?params.ifFormat:params.daFormat;var mustCreate=false;var cal=window.calendar;if(dateEl)params.date=Date.parseDate(dateEl.value||dateEl.innerHTML,dateFmt);if(!(cal&&params.cache)){window.calendar=cal=new Calendar(params.firstDay,params.date,params.onSelect||onSelect,params.onClose||function(cal){cal.hide();});cal.showsTime=params.showsTime;cal.time24=(params.timeFormat=="24");cal.weekNumbers=params.weekNumbers;mustCreate=true;}else{if(params.date)cal.setDate(params.date);cal.hide();}if(params.multiple){cal.multiple={};for(var i=params.multiple.length;--i>=0;){var d=params.multiple[i];var ds=d.print("%Y%m%d");cal.multiple[ds]=d;}}cal.showsOtherMonths=params.showOthers;cal.yearStep=params.step;cal.setRange(params.range[0],params.range[1]);cal.params=params;cal.setDateStatusHandler(params.dateStatusFunc);cal.getDateText=params.dateText;cal.setDateFormat(dateFmt);if(mustCreate)cal.create();cal.refresh();if(!params.position)cal.showAtElement(params.button||params.displayArea||params.inputField,params.align);else cal.showAt(params.position[0],params.position[1]);return false;};return cal;};
var SIM_POPUP = Class.create();
SIM_POPUP.prototype = {
 popUpWindow : null,
 popUpBox: null,
 
 initialize: function() {
 
 if(!$('sim_popup_window')){
 
 var a = document.createElement('div');
 a.innerHTML = '<div id="sim_popup_window" ></div>'
 + '<div id="sim_popup_box" style="position:absolute; left:0; top:0; border:2px solid #E7E4D4; overflow-x:hidden;">'
 + '<div style="float:right; padding-right:10px;" id="sim_popup_close">'
 + '<img src="'+CLOSE_IMG+'" alt="Close" />'
 + '</div>'
 + '<div id="sim_popup_content"></div>'
 + '</div>'; 
 document.body.appendChild(a); 
 }
 
 this.popUpWindow = $('sim_popup_window');
 this.popUpBox = $('sim_popup_box');

 Element.hide('sim_popup_window');
 Element.hide('sim_popup_box');
 
 $('sim_popup_close').observe('click', this.close.bind(this));

 },
 showPopUpDiv :function (htm){
 if(htm){
 this.setPopupContent(htm);
 }
 this.initPopUp(); 
 },
 setPopupContent : function (htm){
 $('sim_popup_content').innerHTML = htm;
 //this.initPopUpBox();
 },
 
 getDocHeight : function ()
 {
 var D = document;
 return Math.max
 (
 Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
 Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
 Math.max(D.body.clientHeight, D.documentElement.clientHeight)
 );
 },
 
 initPopUp : function(){
 var wH = document.viewport.getHeight();
 var wW = document.viewport.getWidth();
 
 var tWH = this.getDocHeight();
 
 this.popUpWindow.setStyle({'height':tWH+'px'});
 this.popUpWindow.setStyle({'width' :wW +'px'});
 
 this.toggleSelectsUnderBlock(this.popUpWindow, false);
 
 Element.hide('sim_popup_box');
 Element.show('sim_popup_window');
 
 },
 initPopUpBox : function (h, w , l , t){
 
 h = (h)? h : 200 ; 
 w = (w)? w : 400 ; 

/* if(!l){
 l = (window.innerWidth - w)/2 
 + document.viewport.getScrollOffsets()[0]; 
 }
 
 if(!t){
 t = (window.innerHeight - h)/2 
 + document.viewport.getScrollOffsets()[1]; 
 }*/

 if(!l){
 l = (document.viewport.getWidth() - w)/2 
 + document.viewport.getScrollOffsets()[0]; 
 }
 
 if(!t){
 t = (document.viewport.getHeight() - h)/2 
 + document.viewport.getScrollOffsets()[1]; 
 }
 
 if(h !='auto'){
 h = h + "px";
 }
 
 this.popUpBox.setStyle({'height':h});
 this.popUpBox.setStyle({'width' :w +'px'});
 this.popUpBox.setStyle({'left' :l +'px'});
 this.popUpBox.setStyle({'top' :t +'px'});
 this.popUpBox.setStyle({'di' :t +'px'});
 
 Element.show('sim_popup_box');

 },
 close : function (){
 this.toggleSelectsUnderBlock(this.popUpWindow, true);
 Element.hide('sim_popup_window');
 Element.hide('sim_popup_box');
 
 },
 toggleSelectsUnderBlock : function(block, flag){

 if(Prototype.Browser.IE){
 var selects = document.getElementsByTagName("select");
 for(var i=0; i<selects.length; i++){
 if(flag){
 if(selects[i].needShowOnSuccess){
 selects[i].needShowOnSuccess = false;
 selects[i].style.visibility = '';
 }
 }else{
 if(Element.visible(selects[i])){
 //selects[i].style.visibility = 'hidden';
 //selects[i].needShowOnSuccess = true;
 }
 }
 }
 }
}

};

function forgotUsernamePopup(){
 
 var popUp = new SIM_POPUP();
 popUp.showPopUpDiv('<div style="padding:10px 50px; font-size:16px;">Loading ...</div>');
 popUp.initPopUpBox(300, 636);
 
 var ajaxReq = new Ajax.Request(FU_APP_URL, {
 method : 'get' ,
 evalJS : 'force' , 
 onSuccess: function(res) {
 popUp.setPopupContent(res.responseText);
 res.responseText.evalScripts();
 }
 });

}

function showForgotPasswordPopup (){
 
 var popUp = new SIM_POPUP();
 popUp.showPopUpDiv('<div style="padding:10px 50px; font-size:16px;">Loading ...</div>');
 popUp.initPopUpBox(300, 636);
 
 var ajaxReq = new Ajax.Request(FP_APP_URL, {
 method : 'get' ,
 evalJS : 'force' , 
 onSuccess: function(res) {
 popUp.setPopupContent(res.responseText);
 res.responseText.evalScripts();
 }
 });

}

function showForgotUsernamePost(){ 
 if(fp_dataForm.validator.validate()){
 var postBody=$('form-validate-forgotusername').serialize();
 fp_popUp = new SIM_POPUP();
 fp_popUp.showPopUpDiv('<div style="padding:10px 50px; font-size:16px;">Loading ...</div>');
 fp_popUp.initPopUpBox(300, 636);
 var ajaxReq = new Ajax.Request(FU_POST_URL, {
 method : 'post' ,
 evalJS : 'force' , 
 postBody: postBody,
 onSuccess: function(res) { 
 fp_popUp.setPopupContent(res.responseText);
 res.responseText.evalScripts();
 }
 });
 }
}

showForgotPasswordPost=function(){ 
 if(fp_dataForm.validator.validate()){ 
 var postBody=$('form-validate').serialize();
 fp_popUp = new SIM_POPUP();
 fp_popUp.showPopUpDiv('<div style="padding:10px 50px; font-size:16px;">Loading ...</div>');
 fp_popUp.initPopUpBox(300, 636);
 var ajaxReq = new Ajax.Request(FP_POST_URL, {
 method : 'post' ,
 evalJS : 'force' , 
 postBody: postBody,
 onSuccess: function(res) { 
 fp_popUp.setPopupContent(res.responseText);
 res.responseText.evalScripts();
 
 }
 });
 
 }

}

var idleFlag = false;

logout=function() {
 if(idleFlag) {
 return false;
 }
 popUp.close();
 window.location = BASE_URL_PATH + "customer/account/logout";
}

login=function() {
 idleFlag = true;
 new Ajax.Request(BASE_URL_PATH+"chklogin/index/index", {
 method : 'get' ,
 onSuccess: function(res) {
 if(res.responseText=="yes") {
 flag = true;
 popUp.close();
 }
 else if(res.responseText=="no") {
 document.getElementById('getMe').innerHTML='Sorry, You have taken too long. you are already logged out';
 window.location = BASE_URL_PATH + "customer/account/login";
 }
 }
 });
}
 
var albox = '<div style="margin:0; padding:0 0 0 10px;">'+
 '<div class="reg-from-top1"></div>'+
 '<div class="reg-from-middle2" style="height:190px;">'+
 '<div class="head">'+
 '<h4 class="sifrtitle sIFR-replaced"><?php echo $this->Warning !</h4>'+
 '</div>'+
 '<form action="" method="post" id="form-validate" onsubmit="javascript:return false;">'+
 '<p style="padding-right:38px;" id="getMe"> You are going to be logged out of the system within 5 mins. Do you want to be logged out? </p>'+ 
 '<div class="button-set" style="border-top:none; padding-right:45px;">'+ 
 '<input type="image" src='+IMG_URL+'/btnlogout.png onclick="javascript:logout();" />'+
 '<input type="image" src='+IMG_URL+'/btn-stay-loggedin.png onclick="javascript:login();" />'+
 '</div>'+
 
 '</form>'+
 '</div>'+
 '<div class="login-form-bottom1" ></div>'+
 '</div>';




var inter = 1*60*1000;
var url = BASE_URL_PATH + 'time_test/time.php';
var MSG_URL = BASE_URL_PATH + "chklogin/index/message/" ;
var popUp = null; 
function idlePop() {
 flag = false;
 setInterval('logout()', 300000);
 popUp = new SIM_POPUP();
 popUp.showPopUpDiv(albox);
 popUp.initPopUpBox(300, 620);
}


var flag = true;
document.observe("dom:loaded", function() {
 if(sess=="set") {
 window.setInterval( function() {
 if(!flag || (BASE_STORE != CUR_STORE)) {
 return false;
 }
 
 new Ajax.Request( url + '/j/'+Math.random(), {
 method: 'get', 
 onSuccess: function(transport) {
 var json = transport.responseText.evalJSON();
 if(json.logged_in == "yes") {
 if(json.popup == "yes") {
 inter = 24*60*60*1000;
 idlePop();
 }
 }
 
 if(json.logged_in == "no") {
 inter = 24*60*60*1000;
 window.location = BASE_URL_PATH + 'customer/account/logout';
 }
 }
 });
 },inter);
 }
});
