

/*  Prototype JavaScript framework, version 1.6.0.3
*  (c) 2005-2008 Sam Stephenson
*
*  Prototype is freely distributable under the terms of an MIT-style license.
*  For details, see the Prototype web site: http://www.prototypejs.org/
*
*--------------------------------------------------------------------------*/
var Prototype = {
Version: '1.6.0.3',
Browser: {
IE:     !!(window.attachEvent &&
navigator.userAgent.indexOf('Opera') === -1),
Opera:  navigator.userAgent.indexOf('Opera') > -1,
WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
navigator.userAgent.indexOf('KHTML') === -1,
MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
},
BrowserFeatures: {
XPath: !!document.evaluate,
SelectorsAPI: !!document.querySelector,
ElementExtensions: !!window.HTMLElement,
SpecificElementExtensions:
document.createElement('div')['__proto__'] &&
document.createElement('div')['__proto__'] !==
document.createElement('form')['__proto__']
},
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
emptyFunction: function() { },
K: function(x) { return x }
};
if (Prototype.Browser.MobileSafari)
Prototype.BrowserFeatures.SpecificElementExtensions = false;
/* Based on Alex Arnell's inheritance implementation. */
var Class = {
create: function() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
this.initialize.apply(this, arguments);
}
Object.extend(klass, Class.Methods);
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
var subclass = function() { };
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
return klass;
}
};
Class.Methods = {
addMethods: function(source) {
var ancestor   = this.superclass && this.superclass.prototype;
var properties = Object.keys(source);
if (!Object.keys({ toString: true }).length)
properties.push("toString", "valueOf");
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
value.argumentNames().first() == "$super") {
var method = value;
value = (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;
}
};
var Abstract = { };
Object.extend = function(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
};
Object.extend(Object, {
inspect: function(object) {
try {
if (Object.isUndefined(object)) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : String(object);
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
},
toJSON: function(object) {
var type = typeof object;
switch (type) {
case 'undefined':
case 'function':
case 'unknown': return;
case 'boolean': return object.toString();
}
if (object === null) return 'null';
if (object.toJSON) return object.toJSON();
if (Object.isElement(object)) return;
var results = [];
for (var property in object) {
var value = Object.toJSON(object[property]);
if (!Object.isUndefined(value))
results.push(property.toJSON() + ': ' + value);
}
return '{' + results.join(', ') + '}';
},
toQueryString: function(object) {
return $H(object).toQueryString();
},
toHTML: function(object) {
return object && object.toHTML ? object.toHTML() : String.interpret(object);
},
keys: function(object) {
var keys = [];
for (var property in object)
keys.push(property);
return keys;
},
values: function(object) {
var values = [];
for (var property in object)
values.push(object[property]);
return values;
},
clone: function(object) {
return Object.extend({ }, object);
},
isElement: function(object) {
return !!(object && object.nodeType == 1);
},
isArray: function(object) {
return object != null && typeof object == "object" &&
'splice' in object && 'join' in object;
},
isHash: function(object) {
return object instanceof Hash;
},
isFunction: function(object) {
return typeof object == "function";
},
isString: function(object) {
return typeof object == "string";
},
isNumber: function(object) {
return typeof object == "number";
},
isUndefined: function(object) {
return typeof object == "undefined";
}
});
Object.extend(Function.prototype, {
argumentNames: function() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
},
bind: function() {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
},
bindAsEventListener: function() {
var __method = this, args = $A(arguments), object = args.shift();
return function(event) {
return __method.apply(object, [event || window.event].concat(args));
}
},
curry: function() {
if (!arguments.length) return this;
var __method = this, args = $A(arguments);
return function() {
return __method.apply(this, args.concat($A(arguments)));
}
},
delay: function() {
var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
},
defer: function() {
var args = [0.01].concat($A(arguments));
return this.delay.apply(this, args);
},
wrap: function(wrapper) {
var __method = this;
return function() {
return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
}
},
methodize: function() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
return __method.apply(null, [this].concat($A(arguments)));
};
}
});
Date.prototype.toJSON = function() {
return '"' + this.getUTCFullYear() + '-' +
(this.getUTCMonth() + 1).toPaddedString(2) + '-' +
this.getUTCDate().toPaddedString(2) + 'T' +
this.getUTCHours().toPaddedString(2) + ':' +
this.getUTCMinutes().toPaddedString(2) + ':' +
this.getUTCSeconds().toPaddedString(2) + 'Z"';
};
var Try = {
these: function() {
var returnValue;
for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) { }
}
return returnValue;
}
};
RegExp.prototype.match = RegExp.prototype.test;
RegExp.escape = function(str) {
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
/*--------------------------------------------------------------------------*/
var PeriodicalExecuter = Class.create({
initialize: function(callback, frequency) {
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
this.registerCallback();
},
registerCallback: function() {
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
execute: function() {
this.callback(this);
},
stop: function() {
if (!this.timer) return;
clearInterval(this.timer);
this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = true;
this.execute();
} finally {
this.currentlyExecuting = false;
}
}
}
});
Object.extend(String, {
interpret: function(value) {
return value == null ? '' : String(value);
},
specialChar: {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'\\': '\\\\'
}
});
Object.extend(String.prototype, {
gsub: function(pattern, replacement) {
var result = '', source = this, match;
replacement = arguments.callee.prepareReplacement(replacement);
while (source.length > 0) {
if (match = source.match(pattern)) {
result += source.slice(0, match.index);
result += String.interpret(replacement(match));
source  = source.slice(match.index + match[0].length);
} else {
result += source, source = '';
}
}
return result;
},
sub: function(pattern, replacement, count) {
replacement = this.gsub.prepareReplacement(replacement);
count = Object.isUndefined(count) ? 1 : count;
return this.gsub(pattern, function(match) {
if (--count < 0) return match[0];
return replacement(match);
});
},
scan: function(pattern, iterator) {
this.gsub(pattern, iterator);
return String(this);
},
truncate: function(length, truncation) {
length = length || 30;
truncation = Object.isUndefined(truncation) ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : String(this);
},
strip: function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
},
stripTags: function() {
return this.replace(/<\/?[^>]+>/gi, '');
},
stripScripts: function() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
},
extractScripts: function() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag) {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
},
evalScripts: function() {
return this.extractScripts().map(function(script) { return eval(script) });
},
escapeHTML: function() {
var self = arguments.callee;
self.text.data = this;
return self.div.innerHTML;
},
unescapeHTML: function() {
var div = new Element('div');
div.innerHTML = this.stripTags();
return div.childNodes[0] ? (div.childNodes.length > 1 ?
$A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
div.childNodes[0].nodeValue) : '';
},
toQueryParams: function(separator) {
var match = this.strip().match(/([^?#]*)(#.*)?$/);
if (!match) return { };
return match[1].split(separator || '&').inject({ }, function(hash, pair) {
if ((pair = pair.split('='))[0]) {
var key = decodeURIComponent(pair.shift());
var value = pair.length > 1 ? pair.join('=') : pair[0];
if (value != undefined) value = decodeURIComponent(value);
if (key in hash) {
if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
hash[key].push(value);
}
else hash[key] = value;
}
return hash;
});
},
toArray: function() {
return this.split('');
},
succ: function() {
return this.slice(0, this.length - 1) +
String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
},
times: function(count) {
return count < 1 ? '' : new Array(count + 1).join(this);
},
camelize: function() {
var parts = this.split('-'), len = parts.length;
if (len == 1) return parts[0];
var camelized = this.charAt(0) == '-'
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
: parts[0];
for (var i = 1; i < len; i++)
camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
return camelized;
},
capitalize: function() {
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
},
underscore: function() {
return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
},
dasherize: function() {
return this.gsub(/_/,'-');
},
inspect: function(useDoubleQuotes) {
var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
var character = String.specialChar[match[0]];
return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
});
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
},
toJSON: function() {
return this.inspect(true);
},
unfilterJSON: function(filter) {
return this.sub(filter || Prototype.JSONFilter, '#{1}');
},
isJSON: function() {
var str = this;
if (str.blank()) return false;
str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
},
evalJSON: function(sanitize) {
var json = this.unfilterJSON();
try {
if (!sanitize || json.isJSON()) return eval('(' + json + ')');
} catch (e) { }
throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
},
include: function(pattern) {
return this.indexOf(pattern) > -1;
},
startsWith: function(pattern) {
return this.indexOf(pattern) === 0;
},
endsWith: function(pattern) {
var d = this.length - pattern.length;
return d >= 0 && this.lastIndexOf(pattern) === d;
},
empty: function() {
return this == '';
},
blank: function() {
return /^\s*$/.test(this);
},
interpolate: function(object, pattern) {
return new Template(this, pattern).evaluate(object);
}
});
if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
escapeHTML: function() {
return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
},
unescapeHTML: function() {
return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
}
});
String.prototype.gsub.prepareReplacement = function(replacement) {
if (Object.isFunction(replacement)) return replacement;
var template = new Template(replacement);
return function(match) { return template.evaluate(match) };
};
String.prototype.parseQuery = String.prototype.toQueryParams;
Object.extend(String.prototype.escapeHTML, {
div:  document.createElement('div'),
text: document.createTextNode('')
});
String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
var Template = Class.create({
initialize: function(template, pattern) {
this.template = template.toString();
this.pattern = pattern || Template.Pattern;
},
evaluate: function(object) {
if (Object.isFunction(object.toTemplateReplacements))
object = object.toTemplateReplacements();
return this.template.gsub(this.pattern, function(match) {
if (object == null) return '';
var before = match[1] || '';
if (before == '\\') return match[2];
var ctx = object, expr = match[3];
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
match = pattern.exec(expr);
if (match == null) return before;
while (match != null) {
var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
ctx = ctx[comp];
if (null == ctx || '' == match[3]) break;
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
match = pattern.exec(expr);
}
return before + String.interpret(ctx);
});
}
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
var $break = { };
var Enumerable = {
each: function(iterator, context) {
var index = 0;
try {
this._each(function(value) {
iterator.call(context, value, index++);
});
} catch (e) {
if (e != $break) throw e;
}
return this;
},
eachSlice: function(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);
},
all: function(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;
},
any: function(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;
},
collect: function(iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
results.push(iterator.call(context, value, index));
});
return results;
},
detect: function(iterator, context) {
var result;
this.each(function(value, index) {
if (iterator.call(context, value, index)) {
result = value;
throw $break;
}
});
return result;
},
findAll: function(iterator, context) {
var results = [];
this.each(function(value, index) {
if (iterator.call(context, value, index))
results.push(value);
});
return results;
},
grep: function(filter, iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
if (Object.isString(filter))
filter = new RegExp(filter);
this.each(function(value, index) {
if (filter.match(value))
results.push(iterator.call(context, value, index));
});
return results;
},
include: function(object) {
if (Object.isFunction(this.indexOf))
if (this.indexOf(object) != -1) return true;
var found = false;
this.each(function(value) {
if (value == object) {
found = true;
throw $break;
}
});
return found;
},
inGroupsOf: function(number, fillWith) {
fillWith = Object.isUndefined(fillWith) ? null : fillWith;
return this.eachSlice(number, function(slice) {
while(slice.length < number) slice.push(fillWith);
return slice;
});
},
inject: function(memo, iterator, context) {
this.each(function(value, index) {
memo = iterator.call(context, memo, value, index);
});
return memo;
},
invoke: function(method) {
var args = $A(arguments).slice(1);
return this.map(function(value) {
return value[method].apply(value, args);
});
},
max: function(iterator, context) {
iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
value = iterator.call(context, value, index);
if (result == null || value >= result)
result = value;
});
return result;
},
min: function(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;
},
partition: function(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];
},
pluck: function(property) {
var results = [];
this.each(function(value) {
results.push(value[property]);
});
return results;
},
reject: function(iterator, context) {
var results = [];
this.each(function(value, index) {
if (!iterator.call(context, value, index))
results.push(value);
});
return results;
},
sortBy: function(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');
},
toArray: function() {
return this.map();
},
zip: function() {
var iterator = Prototype.K, args = $A(arguments);
if (Object.isFunction(args.last()))
iterator = args.pop();
var collections = [this].concat(args).map($A);
return this.map(function(value, index) {
return iterator(collections.pluck(index));
});
},
size: function() {
return this.toArray().length;
},
inspect: function() {
return '#<Enumerable:' + this.toArray().inspect() + '>';
}
};
Object.extend(Enumerable, {
map:     Enumerable.collect,
find:    Enumerable.detect,
select:  Enumerable.findAll,
filter:  Enumerable.findAll,
member:  Enumerable.include,
entries: Enumerable.toArray,
every:   Enumerable.all,
some:    Enumerable.any
});
function $A(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
if (Prototype.Browser.WebKit) {
$A = function(iterable) {
if (!iterable) return [];
// In Safari, only use the `toArray` method if it's not a NodeList.
// A NodeList is a function, has an function `item` property, and a numeric
// `length` property. Adapted from Google Doctype.
if (!(typeof iterable === 'function' && typeof iterable.length ===
'number' && typeof iterable.item === 'function') && iterable.toArray)
return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
};
}
Array.from = $A;
Object.extend(Array.prototype, Enumerable);
if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
Object.extend(Array.prototype, {
_each: function(iterator) {
for (var i = 0, length = this.length; i < length; i++)
iterator(this[i]);
},
clear: function() {
this.length = 0;
return this;
},
first: function() {
return this[0];
},
last: function() {
return this[this.length - 1];
},
compact: function() {
return this.select(function(value) {
return value != null;
});
},
flatten: function() {
return this.inject([], function(array, value) {
return array.concat(Object.isArray(value) ?
value.flatten() : [value]);
});
},
without: function() {
var values = $A(arguments);
return this.select(function(value) {
return !values.include(value);
});
},
reverse: function(inline) {
return (inline !== false ? this : this.toArray())._reverse();
},
reduce: function() {
return this.length > 1 ? this : this[0];
},
uniq: function(sorted) {
return this.inject([], function(array, value, index) {
if (0 == index || (sorted ? array.last() != value : !array.include(value)))
array.push(value);
return array;
});
},
intersect: function(array) {
return this.uniq().findAll(function(item) {
return array.detect(function(value) { return item === value });
});
},
clone: function() {
return [].concat(this);
},
size: function() {
return this.length;
},
inspect: function() {
return '[' + this.map(Object.inspect).join(', ') + ']';
},
toJSON: function() {
var results = [];
this.each(function(object) {
var value = Object.toJSON(object);
if (!Object.isUndefined(value)) results.push(value);
});
return '[' + results.join(', ') + ']';
}
});
// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
Array.prototype._each = Array.prototype.forEach;
if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
i || (i = 0);
var length = this.length;
if (i < 0) i = length + i;
for (; i < length; i++)
if (this[i] === item) return i;
return -1;
};
if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
var n = this.slice(0, i).reverse().indexOf(item);
return (n < 0) ? n : i - n - 1;
};
Array.prototype.toArray = Array.prototype.clone;
function $w(string) {
if (!Object.isString(string)) return [];
string = string.strip();
return string ? string.split(/\s+/) : [];
}
if (Prototype.Browser.Opera){
Array.prototype.concat = function() {
var array = [];
for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
for (var i = 0, length = arguments.length; i < length; i++) {
if (Object.isArray(arguments[i])) {
for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
array.push(arguments[i][j]);
} else {
array.push(arguments[i]);
}
}
return array;
};
}
Object.extend(Number.prototype, {
toColorPart: function() {
return this.toPaddedString(2, 16);
},
succ: function() {
return this + 1;
},
times: function(iterator, context) {
$R(0, this, true).each(iterator, context);
return this;
},
toPaddedString: function(length, radix) {
var string = this.toString(radix || 10);
return '0'.times(length - string.length) + string;
},
toJSON: function() {
return isFinite(this) ? this.toString() : 'null';
}
});
$w('abs round ceil floor').each(function(method){
Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
return new Hash(object);
};
var Hash = Class.create(Enumerable, (function() {
function toQueryPair(key, value) {
if (Object.isUndefined(value)) return key;
return key + '=' + encodeURIComponent(String.interpret(value));
}
return {
initialize: function(object) {
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
},
_each: function(iterator) {
for (var key in this._object) {
var value = this._object[key], pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
},
set: function(key, value) {
return this._object[key] = value;
},
get: function(key) {
// simulating poorly supported hasOwnProperty
if (this._object[key] !== Object.prototype[key])
return this._object[key];
},
unset: function(key) {
var value = this._object[key];
delete this._object[key];
return value;
},
toObject: function() {
return Object.clone(this._object);
},
keys: function() {
return this.pluck('key');
},
values: function() {
return this.pluck('value');
},
index: function(value) {
var match = this.detect(function(pair) {
return pair.value === value;
});
return match && match.key;
},
merge: function(object) {
return this.clone().update(object);
},
update: function(object) {
return new Hash(object).inject(this, function(result, pair) {
result.set(pair.key, pair.value);
return result;
});
},
toQueryString: function() {
return this.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('&');
},
inspect: function() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
},
toJSON: function() {
return Object.toJSON(this.toObject());
},
clone: function() {
return new Hash(this);
}
}
})());
Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
initialize: function(start, end, exclusive) {
this.start = start;
this.end = end;
this.exclusive = exclusive;
},
_each: function(iterator) {
var value = this.start;
while (this.include(value)) {
iterator(value);
value = value.succ();
}
},
include: function(value) {
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
});
var $R = function(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
};
var Ajax = {
getTransport: function() {
return Try.these(
function() {return new XMLHttpRequest()},
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
) || false;
},
activeRequestCount: 0
};
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
register: function(responder) {
if (!this.include(responder))
this.responders.push(responder);
},
unregister: function(responder) {
this.responders = this.responders.without(responder);
},
dispatch: function(callback, request, transport, json) {
this.each(function(responder) {
if (Object.isFunction(responder[callback])) {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) { }
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate:   function() { Ajax.activeRequestCount++ },
onComplete: function() { Ajax.activeRequestCount-- }
});
Ajax.Base = Class.create({
initialize: function(options) {
this.options = {
method:       'post',
asynchronous: true,
contentType:  'application/x-www-form-urlencoded',
encoding:     'UTF-8',
parameters:   '',
evalJSON:     true,
evalJS:       true
};
Object.extend(this.options, options || { });
this.options.method = this.options.method.toLowerCase();
if (Object.isString(this.options.parameters))
this.options.parameters = this.options.parameters.toQueryParams();
else if (Object.isHash(this.options.parameters))
this.options.parameters = this.options.parameters.toObject();
}
});
Ajax.Request = Class.create(Ajax.Base, {
_complete: false,
initialize: function($super, url, options) {
$super(options);
this.transport = Ajax.getTransport();
this.request(url);
},
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = Object.clone(this.options.parameters);
if (!['get', 'post'].include(this.method)) {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
this.parameters = params;
if (params = Object.toQueryString(params)) {
// when GET, append parameters to URL
if (this.method == 'get')
this.url += (this.url.include('?') ? '&' : '?') + params;
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
params += '&_=';
}
try {
var response = new Ajax.Response(this);
if (this.options.onCreate) this.options.onCreate(response);
Ajax.Responders.dispatch('onCreate', this, response);
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
this.body = this.method == 'post' ? (this.options.postBody || params) : null;
this.transport.send(this.body);
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
},
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete))
this.respondToReadyState(this.transport.readyState);
},
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
};
if (this.method == 'post') {
headers['Content-type'] = this.options.contentType +
(this.options.encoding ? '; charset=' + this.options.encoding : '');
/* Force "Connection: close" for older Mozilla browsers to work
* around a bug where XMLHttpRequest sends an incorrect
* Content-length header. See Mozilla Bugzilla #246651.
*/
if (this.transport.overrideMimeType &&
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
headers['Connection'] = 'close';
}
// user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
if (Object.isFunction(extras.push))
for (var i = 0, length = extras.length; i < length; i += 2)
headers[extras[i]] = extras[i+1];
else
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
}
for (var name in headers)
this.transport.setRequestHeader(name, headers[name]);
},
success: function() {
var status = this.getStatus();
return !status || (status >= 200 && status < 300);
},
getStatus: function() {
try {
return this.transport.status || 0;
} catch (e) { return 0 }
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
if (state == 'Complete') {
try {
this._complete = true;
(this.options['on' + response.status]
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(response, response.headerJSON);
} catch (e) {
this.dispatchException(e);
}
var contentType = response.getHeader('Content-type');
if (this.options.evalJS == 'force'
|| (this.options.evalJS && this.isSameOrigin() && contentType
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();
}
try {
(this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
isSameOrigin: function() {
var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
protocol: location.protocol,
domain: document.domain,
port: location.port ? ':' + location.port : ''
}));
},
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name) || null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval((this.transport.responseText || '').unfilterJSON());
} catch (e) {
this.dispatchException(e);
}
},
dispatchException: function(exception) {
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Ajax.Response = Class.create({
initialize: function(request){
this.request = request;
var transport  = this.transport  = request.transport,
readyState = this.readyState = transport.readyState;
if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
this.status       = this.getStatus();
this.statusText   = this.getStatusText();
this.responseText = String.interpret(transport.responseText);
this.headerJSON   = this._getHeaderJSON();
}
if(readyState == 4) {
var xml = transport.responseXML;
this.responseXML  = Object.isUndefined(xml) ? null : xml;
this.responseJSON = this._getResponseJSON();
}
},
status:      0,
statusText: '',
getStatus: Ajax.Request.prototype.getStatus,
getStatusText: function() {
try {
return this.transport.statusText || '';
} catch (e) { return '' }
},
getHeader: Ajax.Request.prototype.getHeader,
getAllHeaders: function() {
try {
return this.getAllResponseHeaders();
} catch (e) { return null }
},
getResponseHeader: function(name) {
return this.transport.getResponseHeader(name);
},
getAllResponseHeaders: function() {
return this.transport.getAllResponseHeaders();
},
_getHeaderJSON: function() {
var json = this.getHeader('X-JSON');
if (!json) return null;
json = decodeURIComponent(escape(json));
try {
return json.evalJSON(this.request.options.sanitizeJSON ||
!this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
},
_getResponseJSON: function() {
var options = this.request.options;
if (!options.evalJSON || (options.evalJSON != 'force' &&
!(this.getHeader('Content-type') || '').include('application/json')) ||
this.responseText.blank())
return null;
try {
return this.responseText.evalJSON(options.sanitizeJSON ||
!this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
}
});
Ajax.Updater = Class.create(Ajax.Request, {
initialize: function($super, container, url, options) {
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
};
options = Object.clone(options);
var onComplete = options.onComplete;
options.onComplete = (function(response, json) {
this.updateContent(response.responseText);
if (Object.isFunction(onComplete)) onComplete(response, json);
}).bind(this);
$super(url, options);
},
updateContent: function(responseText) {
var receiver = this.container[this.success() ? 'success' : 'failure'],
options = this.options;
if (!options.evalScripts) responseText = responseText.stripScripts();
if (receiver = $(receiver)) {
if (options.insertion) {
if (Object.isString(options.insertion)) {
var insertion = { }; insertion[options.insertion] = responseText;
receiver.insert(insertion);
}
else options.insertion(receiver, responseText);
}
else receiver.update(responseText);
}
}
});
Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
initialize: function($super, container, url, options) {
$super(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = { };
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
updateComplete: function(response) {
if (this.options.decay) {
this.decay = (response.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = response.responseText;
}
this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
elements.push($(arguments[i]));
return elements;
}
if (Object.isString(element))
element = document.getElementById(element);
return Element.extend(element);
}
if (Prototype.BrowserFeatures.XPath) {
document._getElementsByXPath = function(expression, parentElement) {
var results = [];
var query = document.evaluate(expression, $(parentElement) || document,
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0, length = query.snapshotLength; i < length; i++)
results.push(Element.extend(query.snapshotItem(i)));
return results;
};
}
/*--------------------------------------------------------------------------*/
if (!window.Node) var Node = { };
if (!Node.ELEMENT_NODE) {
// DOM level 2 ECMAScript Language Binding
Object.extend(Node, {
ELEMENT_NODE: 1,
ATTRIBUTE_NODE: 2,
TEXT_NODE: 3,
CDATA_SECTION_NODE: 4,
ENTITY_REFERENCE_NODE: 5,
ENTITY_NODE: 6,
PROCESSING_INSTRUCTION_NODE: 7,
COMMENT_NODE: 8,
DOCUMENT_NODE: 9,
DOCUMENT_TYPE_NODE: 10,
DOCUMENT_FRAGMENT_NODE: 11,
NOTATION_NODE: 12
});
}
(function() {
var element = this.Element;
this.Element = function(tagName, attributes) {
attributes = attributes || { };
tagName = tagName.toLowerCase();
var cache = Element.cache;
if (Prototype.Browser.IE && attributes.name) {
tagName = '<' + tagName + ' name="' + attributes.name + '">';
delete attributes.name;
return Element.writeAttribute(document.createElement(tagName), attributes);
}
if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
};
Object.extend(this.Element, element || { });
if (element) this.Element.prototype = element.prototype;
}).call(window);
Element.cache = { };
Element.Methods = {
visible: function(element) {
return $(element).style.display != 'none';
},
toggle: function(element) {
element = $(element);
Element[Element.visible(element) ? 'hide' : 'show'](element);
return element;
},
hide: function(element) {
element = $(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(element, content) {
element = $(element);
if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) return element.update().insert(content);
content = Object.toHTML(content);
element.innerHTML = content.stripScripts();
content.evalScripts.bind(content).defer();
return element;
},
replace: function(element, content) {
element = $(element);
if (content && content.toElement) content = content.toElement();
else if (!Object.isElement(content)) {
content = Object.toHTML(content);
var range = element.ownerDocument.createRange();
range.selectNode(element);
content.evalScripts.bind(content).defer();
content = range.createContextualFragment(content.stripScripts());
}
element.parentNode.replaceChild(content, element);
return element;
},
insert: function(element, insertions) {
element = $(element);
if (Object.isString(insertions) || Object.isNumber(insertions) ||
Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
insertions = {bottom:insertions};
var content, insert, tagName, childNodes;
for (var position in insertions) {
content  = insertions[position];
position = position.toLowerCase();
insert = Element._insertionTranslations[position];
if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) {
insert(element, content);
continue;
}
content = Object.toHTML(content);
tagName = ((position == 'before' || position == 'after')
? element.parentNode : element).tagName.toUpperCase();
childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
if (position == 'top' || position == 'after') childNodes.reverse();
childNodes.each(insert.curry(element));
content.evalScripts.bind(content).defer();
}
return element;
},
wrap: function(element, wrapper, attributes) {
element = $(element);
if (Object.isElement(wrapper))
$(wrapper).writeAttribute(attributes || { });
else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
else wrapper = new Element('div', wrapper);
if (element.parentNode)
element.parentNode.replaceChild(wrapper, element);
wrapper.appendChild(element);
return wrapper;
},
inspect: function(element) {
element = $(element);
var result = '<' + element.tagName.toLowerCase();
$H({'id': 'id', 'className': 'class'}).each(function(pair) {
var property = pair.first(), attribute = pair.last();
var value = (element[property] || '').toString();
if (value) result += ' ' + attribute + '=' + value.inspect(true);
});
return result + '>';
},
recursivelyCollect: function(element, property) {
element = $(element);
var elements = [];
while (element = element[property])
if (element.nodeType == 1)
elements.push(Element.extend(element));
return elements;
},
ancestors: function(element) {
return $(element).recursivelyCollect('parentNode');
},
descendants: function(element) {
return $(element).select("*");
},
firstDescendant: function(element) {
element = $(element).firstChild;
while (element && element.nodeType != 1) element = element.nextSibling;
return $(element);
},
immediateDescendants: function(element) {
if (!(element = $(element).firstChild)) return [];
while (element && element.nodeType != 1) element = element.nextSibling;
if (element) return [element].concat($(element).nextSiblings());
return [];
},
previousSiblings: function(element) {
return $(element).recursivelyCollect('previousSibling');
},
nextSiblings: function(element) {
return $(element).recursivelyCollect('nextSibling');
},
siblings: function(element) {
element = $(element);
return element.previousSiblings().reverse().concat(element.nextSiblings());
},
match: function(element, selector) {
if (Object.isString(selector))
selector = new Selector(selector);
return selector.match($(element));
},
up: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return $(element.parentNode);
var ancestors = element.ancestors();
return Object.isNumber(expression) ? ancestors[expression] :
Selector.findElement(ancestors, expression, index);
},
down: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return element.firstDescendant();
return Object.isNumber(expression) ? element.descendants()[expression] :
Element.select(element, expression)[index || 0];
},
previous: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
var previousSiblings = element.previousSiblings();
return Object.isNumber(expression) ? previousSiblings[expression] :
Selector.findElement(previousSiblings, expression, index);
},
next: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
var nextSiblings = element.nextSiblings();
return Object.isNumber(expression) ? nextSiblings[expression] :
Selector.findElement(nextSiblings, expression, index);
},
select: function() {
var args = $A(arguments), element = $(args.shift());
return Selector.findChildElements(element, args);
},
adjacent: function() {
var args = $A(arguments), element = $(args.shift());
return Selector.findChildElements(element.parentNode, args).without(element);
},
identify: function(element) {
element = $(element);
var id = element.readAttribute('id'), self = arguments.callee;
if (id) return id;
do { id = 'anonymous_element_' + self.counter++ } while ($(id));
element.writeAttribute('id', id);
return id;
},
readAttribute: function(element, name) {
element = $(element);
if (Prototype.Browser.IE) {
var t = Element._attributeTranslations.read;
if (t.values[name]) return t.values[name](element, name);
if (t.names[name]) name = t.names[name];
if (name.include(':')) {
return (!element.attributes || !element.attributes[name]) ? null :
element.attributes[name].value;
}
}
return element.getAttribute(name);
},
writeAttribute: function(element, name, value) {
element = $(element);
var attributes = { }, t = Element._attributeTranslations.write;
if (typeof name == 'object') attributes = name;
else attributes[name] = Object.isUndefined(value) ? true : value;
for (var attr in attributes) {
name = t.names[attr] || attr;
value = attributes[attr];
if (t.values[attr]) name = t.values[attr](element, value);
if (value === false || value === null)
element.removeAttribute(name);
else if (value === true)
element.setAttribute(name, name);
else element.setAttribute(name, value);
}
return element;
},
getHeight: function(element) {
return $(element).getDimensions().height;
},
getWidth: function(element) {
return $(element).getDimensions().width;
},
classNames: function(element) {
return new Element.ClassNames(element);
},
hasClassName: function(element, className) {
if (!(element = $(element))) return;
var elementClassName = element.className;
return (elementClassName.length > 0 && (elementClassName == className ||
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
},
addClassName: function(element, className) {
if (!(element = $(element))) return;
if (!element.hasClassName(className))
element.className += (element.className ? ' ' : '') + className;
return element;
},
removeClassName: function(element, className) {
if (!(element = $(element))) return;
element.className = element.className.replace(
new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
return element;
},
toggleClassName: function(element, className) {
if (!(element = $(element))) return;
return element[element.hasClassName(className) ?
'removeClassName' : 'addClassName'](className);
},
// removes whitespace-only text node children
cleanWhitespace: function(element) {
element = $(element);
var node = element.firstChild;
while (node) {
var nextNode = node.nextSibling;
if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
element.removeChild(node);
node = nextNode;
}
return element;
},
empty: function(element) {
return $(element).innerHTML.blank();
},
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
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();
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('display');
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
makePositioned: function(element) {
element = $(element);
var pos = Element.getStyle(element, 'position');
if (pos == 'static' || !pos) {
element._madePositioned = true;
element.style.position = 'relative';
// Opera returns the offset relative to the positioning context, when an
// element is position relative but top and left have not been defined
if (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('position') == 'absolute') return element;
// Position.prepare(); // To be done manually by Scripty when it needs it.
var offsets = element.positionedOffset();
var top     = offsets[1];
var left    = offsets[0];
var width   = element.clientWidth;
var height  = element.clientHeight;
element._originalLeft   = left - parseFloat(element.style.left  || 0);
element._originalTop    = top  - parseFloat(element.style.top || 0);
element._originalWidth  = element.style.width;
element._originalHeight = element.style.height;
element.style.position = 'absolute';
element.style.top    = top + 'px';
element.style.left   = left + 'px';
element.style.width  = width + 'px';
element.style.height = height + 'px';
return element;
},
relativize: function(element) {
element = $(element);
if (element.getStyle('position') == 'relative') return element;
// Position.prepare(); // To be done manually by Scripty when it needs it.
element.style.position = 'relative';
var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
element.style.top    = top + 'px';
element.style.left   = left + 'px';
element.style.height = element._originalHeight;
element.style.width  = element._originalWidth;
return element;
},
cumulativeScrollOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.scrollTop  || 0;
valueL += element.scrollLeft || 0;
element = element.parentNode;
} while (element);
return Element._returnOffset(valueL, valueT);
},
getOffsetParent: function(element) {
if (element.offsetParent) return $(element.offsetParent);
if (element == document.body) return $(element);
while ((element = element.parentNode) && element != document.body)
if (Element.getStyle(element, 'position') != 'static')
return $(element);
return $(document.body);
},
viewportOffset: function(forElement) {
var valueT = 0, valueL = 0;
var element = forElement;
do {
valueT += element.offsetTop  || 0;
valueL += element.offsetLeft || 0;
// Safari fix
if (element.offsetParent == document.body &&
Element.getStyle(element, 'position') == 'absolute') break;
} while (element = element.offsetParent);
element = forElement;
do {
if (!Prototype.Browser.Opera || (element.tagName && (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] || { });
// find page position of source
source = $(source);
var p = source.viewportOffset();
// find coordinate system to use
element = $(element);
var delta = [0, 0];
var parent = null;
// delta [0,0] will do fine with position: fixed elements,
// position:absolute needs offsetParent deltas
if (Element.getStyle(element, 'position') == 'absolute') {
parent = element.getOffsetParent();
delta = parent.viewportOffset();
}
// correct by body offsets (fixes Safari)
if (parent == document.body) {
delta[0] -= document.body.offsetLeft;
delta[1] -= document.body.offsetTop;
}
// set position
if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
if (options.setHeight) element.style.height = source.offsetHeight + 'px';
return element;
}
};
Element.Methods.identify.counter = 1;
Object.extend(Element.Methods, {
getElementsBySelector: Element.Methods.select,
childElements: Element.Methods.immediateDescendants
});
Element._attributeTranslations = {
write: {
names: {
className: 'class',
htmlFor:   'for'
},
values: { }
}
};
if (Prototype.Browser.Opera) {
Element.Methods.getStyle = Element.Methods.getStyle.wrap(
function(proceed, element, style) {
switch (style) {
case 'left': case 'top': case 'right': case 'bottom':
if (proceed(element, 'position') === 'static') return null;
case 'height': case 'width':
// returns '0px' for hidden elements; we want it to return null
if (!Element.visible(element)) return null;
// returns the border-box dimensions rather than the content-box
// dimensions, so we subtract padding and borders from the value
var dim = parseInt(proceed(element, style), 10);
if (dim !== element['offset' + style.capitalize()])
return dim + 'px';
var properties;
if (style === 'height') {
properties = ['border-top-width', 'padding-top',
'padding-bottom', 'border-bottom-width'];
}
else {
properties = ['border-left-width', 'padding-left',
'padding-right', 'border-right-width'];
}
return properties.inject(dim, function(memo, property) {
var val = proceed(element, property);
return val === null ? memo : memo - parseInt(val, 10);
}) + 'px';
default: return proceed(element, style);
}
}
);
Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
function(proceed, element, attribute) {
if (attribute === 'title') return element.title;
return proceed(element, attribute);
}
);
}
else if (Prototype.Browser.IE) {
// IE doesn't report offsets correctly for static elements, so we change them
// to "relative" to get the values, then change them back.
Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
function(proceed, element) {
element = $(element);
// IE throws an error if element is not in document
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);
// Trigger hasLayout on the offset parent so that IE6 reports
// accurate offsetTop and offsetLeft values for position: fixed.
var offsetParent = element.getOffsetParent();
if (offsetParent && offsetParent.getStyle('position') === 'fixed')
offsetParent.setStyle({ zoom: 1 });
element.setStyle({ position: 'relative' });
var value = proceed(element);
element.setStyle({ position: position });
return value;
}
);
});
Element.Methods.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 = {
read: {
names: {
'class': 'className',
'for':   'htmlFor'
},
values: {
_getAttr: function(element, attribute) {
return element.getAttribute(attribute, 2);
},
_getAttrNode: function(element, attribute) {
var node = element.getAttributeNode(attribute);
return node ? node.value : "";
},
_getEv: function(element, attribute) {
attribute = element.getAttribute(attribute);
return attribute ? attribute.toString().slice(23, -2) : null;
},
_flag: function(element, attribute) {
return $(element).hasAttribute(attribute) ? attribute : null;
},
style: function(element) {
return element.style.cssText.toLowerCase();
},
title: function(element) {
return element.title;
}
}
}
};
Element._attributeTranslations.write = {
names: Object.extend({
cellpadding: 'cellPadding',
cellspacing: 'cellSpacing'
}, Element._attributeTranslations.read.names),
values: {
checked: function(element, value) {
element.checked = !!value;
},
style: function(element, value) {
element.style.cssText = value ? value : '';
}
}
};
Element._attributeTranslations.has = {};
$w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
Element._attributeTranslations.has[attr.toLowerCase()] = attr;
});
(function(v) {
Object.extend(v, {
href:        v._getAttr,
src:         v._getAttr,
type:        v._getAttr,
action:      v._getAttrNode,
disabled:    v._flag,
checked:     v._flag,
readonly:    v._flag,
multiple:    v._flag,
onload:      v._getEv,
onunload:    v._getEv,
onclick:     v._getEv,
ondblclick:  v._getEv,
onmousedown: v._getEv,
onmouseup:   v._getEv,
onmouseover: v._getEv,
onmousemove: v._getEv,
onmouseout:  v._getEv,
onfocus:     v._getEv,
onblur:      v._getEv,
onkeypress:  v._getEv,
onkeydown:   v._getEv,
onkeyup:     v._getEv,
onsubmit:    v._getEv,
onreset:     v._getEv,
onselect:    v._getEv,
onchange:    v._getEv
});
})(Element._attributeTranslations.read.values);
}
else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
Element.Methods.setOpacity = function(element, value) {
element = $(element);
element.style.opacity = (value == 1) ? 0.999999 :
(value === '') ? '' : (value < 0.00001) ? 0 : value;
return element;
};
}
else if (Prototype.Browser.WebKit) {
Element.Methods.setOpacity = function(element, value) {
element = $(element);
element.style.opacity = (value == 1 || value === '') ? '' :
(value < 0.00001) ? 0 : value;
if (value == 1)
if(element.tagName.toUpperCase() == 'IMG' && element.width) {
element.width++; element.width--;
} else try {
var n = document.createTextNode(' ');
element.appendChild(n);
element.removeChild(n);
} catch (e) { }
return element;
};
// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Element#cumulativeOffset for
// KHTML/WebKit only.
Element.Methods.cumulativeOffset = function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop  || 0;
valueL += element.offsetLeft || 0;
if (element.offsetParent == document.body)
if (Element.getStyle(element, 'position') == 'absolute') break;
element = element.offsetParent;
} while (element);
return Element._returnOffset(valueL, valueT);
};
}
if (Prototype.Browser.IE || Prototype.Browser.Opera) {
// IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
Element.Methods.update = function(element, content) {
element = $(element);
if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) return element.update().insert(content);
content = Object.toHTML(content);
var tagName = element.tagName.toUpperCase();
if (tagName in Element._insertionTranslations.tags) {
$A(element.childNodes).each(function(node) { element.removeChild(node) });
Element._getContentFromAnonymousElement(tagName, content.stripScripts())
.each(function(node) { element.appendChild(node) });
}
else element.innerHTML = content.stripScripts();
content.evalScripts.bind(content).defer();
return element;
};
}
if ('outerHTML' in document.createElement('div')) {
Element.Methods.replace = function(element, content) {
element = $(element);
if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) {
element.parentNode.replaceChild(content, element);
return element;
}
content = Object.toHTML(content);
var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
if (Element._insertionTranslations.tags[tagName]) {
var nextSibling = element.next();
var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
parent.removeChild(element);
if (nextSibling)
fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
else
fragments.each(function(node) { parent.appendChild(node) });
}
else element.outerHTML = content.stripScripts();
content.evalScripts.bind(content).defer();
return element;
};
}
Element._returnOffset = function(l, t) {
var result = [l, t];
result.left = l;
result.top = t;
return result;
};
Element._getContentFromAnonymousElement = function(tagName, html) {
var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
if (t) {
div.innerHTML = t[0] + html + t[1];
t[2].times(function() { div = div.firstChild });
} else div.innerHTML = html;
return $A(div.childNodes);
};
Element._insertionTranslations = {
before: function(element, node) {
element.parentNode.insertBefore(node, element);
},
top: function(element, node) {
element.insertBefore(node, element.firstChild);
},
bottom: function(element, node) {
element.appendChild(node);
},
after: function(element, node) {
element.parentNode.insertBefore(node, element.nextSibling);
},
tags: {
TABLE:  ['<table>',                '</table>',                   1],
TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
SELECT: ['<select>',               '</select>',                  1]
}
};
(function() {
Object.extend(this.tags, {
THEAD: this.tags.TBODY,
TFOOT: this.tags.TBODY,
TH:    this.tags.TD
});
}).call(Element._insertionTranslations);
Element.Methods.Simulated = {
hasAttribute: function(element, attribute) {
attribute = Element._attributeTranslations.has[attribute] || attribute;
var node = $(element).getAttributeNode(attribute);
return !!(node && node.specified);
}
};
Element.Methods.ByTag = { };
Object.extend(Element, Element.Methods);
if (!Prototype.BrowserFeatures.ElementExtensions &&
document.createElement('div')['__proto__']) {
window.HTMLElement = { };
window.HTMLElement.prototype = document.createElement('div')['__proto__'];
Prototype.BrowserFeatures.ElementExtensions = true;
}
Element.extend = (function() {
if (Prototype.BrowserFeatures.SpecificElementExtensions)
return Prototype.K;
var Methods = { }, ByTag = Element.Methods.ByTag;
var extend = Object.extend(function(element) {
if (!element || element._extendedByPrototype ||
element.nodeType != 1 || element == window) return element;
var methods = Object.clone(Methods),
tagName = element.tagName.toUpperCase(), property, value;
// extend methods for specific tags
if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
for (property in methods) {
value = methods[property];
if (Object.isFunction(value) && !(property in element))
element[property] = value.methodize();
}
element._extendedByPrototype = Prototype.emptyFunction;
return element;
}, {
refresh: function() {
// extend methods for all tags (Safari doesn't need this)
if (!Prototype.BrowserFeatures.ElementExtensions) {
Object.extend(Methods, Element.Methods);
Object.extend(Methods, Element.Methods.Simulated);
}
}
});
extend.refresh();
return extend;
})();
Element.hasAttribute = function(element, attribute) {
if (element.hasAttribute) return element.hasAttribute(attribute);
return Element.Methods.Simulated.hasAttribute(element, attribute);
};
Element.addMethods = function(methods) {
var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
if (!methods) {
Object.extend(Form, Form.Methods);
Object.extend(Form.Element, Form.Element.Methods);
Object.extend(Element.Methods.ByTag, {
"FORM":     Object.clone(Form.Methods),
"INPUT":    Object.clone(Form.Element.Methods),
"SELECT":   Object.clone(Form.Element.Methods),
"TEXTAREA": Object.clone(Form.Element.Methods)
});
}
if (arguments.length == 2) {
var tagName = methods;
methods = arguments[1];
}
if (!tagName) Object.extend(Element.Methods, methods || { });
else {
if (Object.isArray(tagName)) tagName.each(extend);
else extend(tagName);
}
function extend(tagName) {
tagName = tagName.toUpperCase();
if (!Element.Methods.ByTag[tagName])
Element.Methods.ByTag[tagName] = { };
Object.extend(Element.Methods.ByTag[tagName], methods);
}
function copy(methods, destination, onlyIfAbsent) {
onlyIfAbsent = onlyIfAbsent || false;
for (var property in methods) {
var value = methods[property];
if (!Object.isFunction(value)) continue;
if (!onlyIfAbsent || !(property in destination))
destination[property] = value.methodize();
}
}
function findDOMClass(tagName) {
var klass;
var trans = {
"OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
"FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
"DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
"H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
"INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
"TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
"TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
"TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
"FrameSet", "IFRAME": "IFrame"
};
if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
if (window[klass]) return window[klass];
klass = 'HTML' + tagName + 'Element';
if (window[klass]) return window[klass];
klass = 'HTML' + tagName.capitalize() + 'Element';
if (window[klass]) return window[klass];
window[klass] = { };
window[klass].prototype = document.createElement(tagName)['__proto__'];
return window[klass];
}
if (F.ElementExtensions) {
copy(Element.Methods, HTMLElement.prototype);
copy(Element.Methods.Simulated, HTMLElement.prototype, true);
}
if (F.SpecificElementExtensions) {
for (var tag in Element.Methods.ByTag) {
var klass = findDOMClass(tag);
if (Object.isUndefined(klass)) continue;
copy(T[tag], klass.prototype);
}
}
Object.extend(Element, Element.Methods);
delete Element.ByTag;
if (Element.extend.refresh) Element.extend.refresh();
Element.cache = { };
};
document.viewport = {
getDimensions: function() {
var dimensions = { }, B = Prototype.Browser;
$w('width height').each(function(d) {
var D = d.capitalize();
if (B.WebKit && !document.evaluate) {
// Safari <3.0 needs self.innerWidth/Height
dimensions[d] = self['inner' + D];
} else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
// Opera <9.5 needs document.body.clientWidth/Height
dimensions[d] = document.body['client' + D]
} else {
dimensions[d] = document.documentElement['client' + D];
}
});
return dimensions;
},
getWidth: function() {
return this.getDimensions().width;
},
getHeight: function() {
return this.getDimensions().height;
},
getScrollOffsets: function() {
return Element._returnOffset(
window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
}
};
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
* part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
* license.  Please see http://www.yui-ext.com/ for more information. */
var Selector = Class.create({
initialize: function(expression) {
this.expression = expression.strip();
if (this.shouldUseSelectorsAPI()) {
this.mode = 'selectorsAPI';
} else if (this.shouldUseXPath()) {
this.mode = 'xpath';
this.compileXPathMatcher();
} else {
this.mode = "normal";
this.compileMatcher();
}
},
shouldUseXPath: function() {
if (!Prototype.BrowserFeatures.XPath) return false;
var e = this.expression;
// Safari 3 chokes on :*-of-type and :empty
if (Prototype.Browser.WebKit &&
(e.include("-of-type") || e.include(":empty")))
return false;
// XPath can't do namespaced attributes, nor can it read
// the "checked" property from DOM nodes
if ((/(\[[\w-]*?:|:checked)/).test(e))
return false;
return true;
},
shouldUseSelectorsAPI: function() {
if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
if (!Selector._div) Selector._div = new Element('div');
// Make sure the browser treats the selector as valid. Test on an
// isolated element to minimize cost of this check.
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;
if (Selector._cache[e]) {
this.matcher = Selector._cache[e];
return;
}
this.matcher = ["this.matcher = function(root) {",
"var r = root, h = Selector.handlers, c = false, n;"];
while (e && le != e && (/\S/).test(e)) {
le = e;
for (var i in ps) {
p = ps[i];
if (m = e.match(p)) {
this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
new Template(c[i]).evaluate(m));
e = e.replace(m[0], '');
break;
}
}
}
this.matcher.push("return h.unique(n);\n}");
eval(this.matcher.join('\n'));
Selector._cache[this.expression] = this.matcher;
},
compileXPathMatcher: function() {
var e = this.expression, ps = Selector.patterns,
x = Selector.xpath, le, m;
if (Selector._cache[e]) {
this.xpath = Selector._cache[e]; return;
}
this.matcher = ['.//*'];
while (e && le != e && (/\S/).test(e)) {
le = e;
for (var i in ps) {
if (m = e.match(ps[i])) {
this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
new Template(x[i]).evaluate(m));
e = e.replace(m[0], '');
break;
}
}
}
this.xpath = this.matcher.join('');
Selector._cache[this.expression] = this.xpath;
},
findElements: function(root) {
root = root || document;
var e = this.expression, results;
switch (this.mode) {
case 'selectorsAPI':
// querySelectorAll queries document-wide, then filters to descendants
// of the context element. That's not what we want.
// Add an explicit context to the selector if necessary.
if (root !== document) {
var oldId = root.id, id = $(root).identify();
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;
while (e && le !== e && (/\S/).test(e)) {
le = e;
for (var i in ps) {
p = ps[i];
if (m = e.match(p)) {
// use the Selector.assertions methods unless the selector
// is too complex.
if (as[i]) {
this.tokens.push([i, Object.clone(m)]);
e = e.replace(m[0], '');
} else {
// reluctantly do a document-wide search
// and look for a match in the array
return this.findElements(document).include(element);
}
}
}
}
var match = true, name, matches;
for (var i = 0, token; token = this.tokens[i]; i++) {
name = token[0], matches = token[1];
if (!Selector.assertions[name](element, matches)) {
match = false; break;
}
}
return match;
},
toString: function() {
return this.expression;
},
inspect: function() {
return "#<Selector:" + this.expression.inspect() + ">";
}
});
Object.extend(Selector, {
_cache: { },
xpath: {
descendant:   "//*",
child:        "/*",
adjacent:     "/following-sibling::*[1]",
laterSibling: '/following-sibling::*',
tagName:      function(m) {
if (m[1] == '*') return '';
return "[local-name()='" + m[1].toLowerCase() +
"' or local-name()='" + m[1].toUpperCase() + "']";
},
className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
id:           "[@id='#{1}']",
attrPresence: function(m) {
m[1] = m[1].toLowerCase();
return new Template("[@#{1}]").evaluate(m);
},
attr: function(m) {
m[1] = m[1].toLowerCase();
m[3] = m[5] || m[6];
return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
},
pseudo: function(m) {
var h = Selector.xpath.pseudos[m[1]];
if (!h) return '';
if (Object.isFunction(h)) return h(m);
return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
},
operators: {
'=':  "[@#{1}='#{3}']",
'!=': "[@#{1}!='#{3}']",
'^=': "[starts-with(@#{1}, '#{3}')]",
'$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
'*=': "[contains(@#{1}, '#{3}')]",
'~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
'|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
},
pseudos: {
'first-child': '[not(preceding-sibling::*)]',
'last-child':  '[not(following-sibling::*)]',
'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
'empty':       "[count(*) = 0 and (count(text()) = 0)]",
'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;
var exclusion = [];
while (e && le != e && (/\S/).test(e)) {
le = e;
for (var i in p) {
if (m = e.match(p[i])) {
v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
exclusion.push("(" + v.substring(1, v.length - 1) + ")");
e = e.replace(m[0], '');
break;
}
}
}
return "[not(" + exclusion.join(" and ") + ")]";
},
'nth-child':      function(m) {
return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
},
'nth-last-child': function(m) {
return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
},
'nth-of-type':    function(m) {
return Selector.xpath.pseudos.nth("position() ", m);
},
'nth-last-of-type': function(m) {
return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
},
'first-of-type':  function(m) {
m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
},
'last-of-type':   function(m) {
m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
},
'only-of-type':   function(m) {
var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
},
nth: function(fragment, m) {
var mm, formula = m[6], predicate;
if (formula == 'even') formula = '2n+0';
if (formula == 'odd')  formula = '2n+1';
if (mm = formula.match(/^(\d+)$/)) // digit only
return '[' + fragment + "= " + mm[1] + ']';
if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
if (mm[1] == "-") mm[1] = -1;
var a = mm[1] ? Number(mm[1]) : 1;
var b = mm[2] ? Number(mm[2]) : 0;
predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
"((#{fragment} - #{b}) div #{a} >= 0)]";
return new Template(predicate).evaluate({
fragment: fragment, a: a, b: b });
}
}
}
},
criteria: {
tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
className:    'n = h.className(n, r, "#{1}", c);    c = false;',
id:           'n = h.id(n, r, "#{1}", c);           c = false;',
attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
attr: function(m) {
m[3] = (m[5] || m[6]);
return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
},
pseudo: function(m) {
if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
},
descendant:   'c = "descendant";',
child:        'c = "child";',
adjacent:     'c = "adjacent";',
laterSibling: 'c = "laterSibling";'
},
patterns: {
// combinators must be listed first
// (and descendant needs to be last combinator)
laterSibling: /^\s*~\s*/,
child:        /^\s*>\s*/,
adjacent:     /^\s*\+\s*/,
descendant:   /^\s/,
// selectors follow
tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
id:           /^#([\w\-\*]+)(\b|$)/,
className:    /^\.([\w\-\*]+)(\b|$)/,
pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
},
// for Selector.match and Element#match
assertions: {
tagName: function(element, matches) {
return matches[1].toUpperCase() == element.tagName.toUpperCase();
},
className: function(element, matches) {
return Element.hasClassName(element, matches[1]);
},
id: function(element, matches) {
return element.id === matches[1];
},
attrPresence: function(element, matches) {
return Element.hasAttribute(element, matches[1]);
},
attr: function(element, matches) {
var nodeValue = Element.readAttribute(element, matches[1]);
return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
}
},
handlers: {
// UTILITY FUNCTIONS
// joins two collections
concat: function(a, b) {
for (var i = 0, node; node = b[i]; i++)
a.push(node);
return a;
},
// marks an array of nodes for counting
mark: function(nodes) {
var _true = Prototype.emptyFunction;
for (var i = 0, node; node = nodes[i]; i++)
node._countedByPrototype = _true;
return nodes;
},
unmark: function(nodes) {
for (var i = 0, node; node = nodes[i]; i++)
node._countedByPrototype = undefined;
return nodes;
},
// mark each child node with its position (for nth calls)
// "ofType" flag indicates whether we're indexing for nth-of-type
// rather than nth-child
index: function(parentNode, reverse, ofType) {
parentNode._countedByPrototype = Prototype.emptyFunction;
if (reverse) {
for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
var node = nodes[i];
if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
} else {
for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
},
// filters out duplicates and extends all nodes
unique: function(nodes) {
if (nodes.length == 0) return nodes;
var results = [], n;
for (var i = 0, l = nodes.length; i < l; i++)
if (!(n = nodes[i])._countedByPrototype) {
n._countedByPrototype = Prototype.emptyFunction;
results.push(Element.extend(n));
}
return Selector.handlers.unmark(results);
},
// COMBINATOR FUNCTIONS
descendant: function(nodes) {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++)
h.concat(results, node.getElementsByTagName('*'));
return results;
},
child: function(nodes) {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++) {
for (var j = 0, child; child = node.childNodes[j]; j++)
if (child.nodeType == 1 && child.tagName != '!') results.push(child);
}
return results;
},
adjacent: function(nodes) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
var next = this.nextElementSibling(node);
if (next) results.push(next);
}
return results;
},
laterSibling: function(nodes) {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++)
h.concat(results, Element.nextSiblings(node));
return results;
},
nextElementSibling: function(node) {
while (node = node.nextSibling)
if (node.nodeType == 1) return node;
return null;
},
previousElementSibling: function(node) {
while (node = node.previousSibling)
if (node.nodeType == 1) return node;
return null;
},
// TOKEN FUNCTIONS
tagName: function(nodes, root, tagName, combinator) {
var uTagName = tagName.toUpperCase();
var results = [], h = Selector.handlers;
if (nodes) {
if (combinator) {
// fastlane for ordinary descendant combinators
if (combinator == "descendant") {
for (var i = 0, node; node = nodes[i]; i++)
h.concat(results, node.getElementsByTagName(tagName));
return results;
} else nodes = this[combinator](nodes);
if (tagName == "*") return nodes;
}
for (var i = 0, node; node = nodes[i]; i++)
if (node.tagName.toUpperCase() === uTagName) results.push(node);
return results;
} else return root.getElementsByTagName(tagName);
},
id: function(nodes, root, id, combinator) {
var targetNode = $(id), h = Selector.handlers;
if (!targetNode) return [];
if (!nodes && root == document) return [targetNode];
if (nodes) {
if (combinator) {
if (combinator == 'child') {
for (var i = 0, node; node = nodes[i]; i++)
if (targetNode.parentNode == node) return [targetNode];
} else if (combinator == 'descendant') {
for (var i = 0, node; node = nodes[i]; i++)
if (Element.descendantOf(targetNode, node)) return [targetNode];
} else if (combinator == 'adjacent') {
for (var i = 0, node; node = nodes[i]; i++)
if (Selector.handlers.previousElementSibling(targetNode) == node)
return [targetNode];
} else nodes = h[combinator](nodes);
}
for (var i = 0, node; node = nodes[i]; i++)
if (node == targetNode) return [targetNode];
return [];
}
return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
},
className: function(nodes, root, className, combinator) {
if (nodes && combinator) nodes = this[combinator](nodes);
return Selector.handlers.byClassName(nodes, root, className);
},
byClassName: function(nodes, root, className) {
if (!nodes) nodes = Selector.handlers.descendant([root]);
var needle = ' ' + className + ' ';
for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
nodeClassName = node.className;
if (nodeClassName.length == 0) continue;
if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
results.push(node);
}
return results;
},
attrPresence: function(nodes, root, attr, combinator) {
if (!nodes) nodes = root.getElementsByTagName("*");
if (nodes && combinator) nodes = this[combinator](nodes);
var results = [];
for (var i = 0, node; node = nodes[i]; i++)
if (Element.hasAttribute(node, attr)) results.push(node);
return results;
},
attr: function(nodes, root, attr, value, operator, combinator) {
if (!nodes) nodes = root.getElementsByTagName("*");
if (nodes && combinator) nodes = this[combinator](nodes);
var handler = Selector.operators[operator], results = [];
for (var i = 0, node; node = nodes[i]; i++) {
var nodeValue = Element.readAttribute(node, attr);
if (nodeValue === null) continue;
if (handler(nodeValue, value)) results.push(node);
}
return results;
},
pseudo: function(nodes, name, value, root, combinator) {
if (nodes && combinator) nodes = this[combinator](nodes);
if (!nodes) nodes = root.getElementsByTagName("*");
return Selector.pseudos[name](nodes, value, root);
}
},
pseudos: {
'first-child': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
if (Selector.handlers.previousElementSibling(node)) continue;
results.push(node);
}
return results;
},
'last-child': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
if (Selector.handlers.nextElementSibling(node)) continue;
results.push(node);
}
return results;
},
'only-child': function(nodes, value, root) {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++)
if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
results.push(node);
return results;
},
'nth-child':        function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root);
},
'nth-last-child':   function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root, true);
},
'nth-of-type':      function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root, false, true);
},
'nth-last-of-type': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root, true, true);
},
'first-of-type':    function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, "1", root, false, true);
},
'last-of-type':     function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, "1", root, true, true);
},
'only-of-type':     function(nodes, formula, root) {
var p = Selector.pseudos;
return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
},
// handles the an+b logic
getIndices: function(a, b, total) {
if (a == 0) return b > 0 ? [b] : [];
return $R(1, total).inject([], function(memo, i) {
if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
return memo;
});
},
// handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
nth: function(nodes, formula, root, reverse, ofType) {
if (nodes.length == 0) return [];
if (formula == 'even') formula = '2n+0';
if (formula == 'odd')  formula = '2n+1';
var h = Selector.handlers, results = [], indexed = [], m;
h.mark(nodes);
for (var i = 0, node; node = nodes[i]; i++) {
if (!node.parentNode._countedByPrototype) {
h.index(node.parentNode, reverse, ofType);
indexed.push(node.parentNode);
}
}
if (formula.match(/^\d+$/)) { // just a number
formula = Number(formula);
for (var i = 0, node; node = nodes[i]; i++)
if (node.nodeIndex == formula) results.push(node);
} else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
if (m[1] == "-") m[1] = -1;
var a = m[1] ? Number(m[1]) : 1;
var b = m[2] ? Number(m[2]) : 0;
var indices = Selector.pseudos.getIndices(a, b, nodes.length);
for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
for (var j = 0; j < l; j++)
if (node.nodeIndex == indices[j]) results.push(node);
}
}
h.unmark(nodes);
h.unmark(indexed);
return results;
},
'empty': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
// IE treats comments as element nodes
if (node.tagName == '!' || node.firstChild) 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.endsWith(v); },
'*=': function(nv, v) { return nv.include(v); },
'~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
'|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
'-').include('-' + (v || "").toUpperCase() + '-'); }
},
split: function(expression) {
var expressions = [];
expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
expressions.push(m[1].strip());
});
return expressions;
},
matchElements: function(elements, expression) {
var matches = $$(expression), h = Selector.handlers;
h.mark(matches);
for (var i = 0, results = [], element; element = elements[i]; i++)
if (element._countedByPrototype) results.push(element);
h.unmark(matches);
return results;
},
findElement: function(elements, expression, index) {
if (Object.isNumber(expression)) {
index = expression; expression = false;
}
return Selector.matchElements(elements, expression || '*')[index || 0];
},
findChildElements: function(element, expressions) {
expressions = Selector.split(expressions.join(','));
var results = [], h = Selector.handlers;
for (var i = 0, l = expressions.length, selector; i < l; i++) {
selector = new Selector(expressions[i].strip());
h.concat(results, selector.findElements(element));
}
return (l > 1) ? h.unique(results) : results;
}
});
if (Prototype.Browser.IE) {
Object.extend(Selector.handlers, {
// IE returns comment nodes on getElementsByTagName("*").
// Filter them out.
concat: function(a, b) {
for (var i = 0, node; node = b[i]; i++)
if (node.tagName !== "!") a.push(node);
return a;
},
// IE improperly serializes _countedByPrototype in (inner|outer)HTML.
unmark: function(nodes) {
for (var i = 0, node; node = nodes[i]; i++)
node.removeAttribute('_countedByPrototype');
return nodes;
}
});
}
function $$() {
return Selector.findChildElements(document, $A(arguments));
}
var Form = {
reset: function(form) {
$(form).reset();
return form;
},
serializeElements: function(elements, options) {
if (typeof options != 'object') options = { hash: !!options };
else if (Object.isUndefined(options.hash)) options.hash = true;
var key, value, submitted = false, submit = options.submit;
var data = elements.inject({ }, function(result, element) {
if (!element.disabled && element.name) {
key = element.name; value = $(element).getValue();
if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
submit !== false && (!submit || key == submit) && (submitted = true)))) {
if (key in result) {
// a key is already present; construct an array of values
if (!Object.isArray(result[key])) result[key] = [result[key]];
result[key].push(value);
}
else result[key] = value;
}
}
return result;
});
return options.hash ? data : Object.toQueryString(data);
}
};
Form.Methods = {
serialize: function(form, options) {
return Form.serializeElements(Form.getElements(form), options);
},
getElements: function(form) {
return $A($(form).getElementsByTagName('*')).inject([],
function(elements, child) {
if (Form.Element.Serializers[child.tagName.toLowerCase()])
elements.push(Element.extend(child));
return elements;
}
);
},
getInputs: function(form, typeName, name) {
form = $(form);
var inputs = form.getElementsByTagName('input');
if (!typeName && !name) return $A(inputs).map(Element.extend);
for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
var input = inputs[i];
if ((typeName && input.type != typeName) || (name && input.name != name))
continue;
matchingInputs.push(Element.extend(input));
}
return matchingInputs;
},
disable: function(form) {
form = $(form);
Form.getElements(form).invoke('disable');
return form;
},
enable: function(form) {
form = $(form);
Form.getElements(form).invoke('enable');
return form;
},
findFirstElement: function(form) {
var elements = $(form).getElements().findAll(function(element) {
return 'hidden' != element.type && !element.disabled;
});
var firstByIndex = elements.findAll(function(element) {
return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
}).sortBy(function(element) { return element.tabIndex }).first();
return firstByIndex ? firstByIndex : elements.find(function(element) {
return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
});
},
focusFirstElement: function(form) {
form = $(form);
form.findFirstElement().activate();
return form;
},
request: function(form, options) {
form = $(form), options = Object.clone(options || { });
var params = options.parameters, action = form.readAttribute('action') || '';
if (action.blank()) action = window.location.href;
options.parameters = form.serialize(true);
if (params) {
if (Object.isString(params)) params = params.toQueryParams();
Object.extend(options.parameters, params);
}
if (form.hasAttribute('method') && !options.method)
options.method = form.method;
return new Ajax.Request(action, options);
}
};
/*--------------------------------------------------------------------------*/
Form.Element = {
focus: function(element) {
$(element).focus();
return element;
},
select: function(element) {
$(element).select();
return element;
}
};
Form.Element.Methods = {
serialize: function(element) {
element = $(element);
if (!element.disabled && element.name) {
var value = element.getValue();
if (value != undefined) {
var pair = { };
pair[element.name] = value;
return Object.toQueryString(pair);
}
}
return '';
},
getValue: function(element) {
element = $(element);
var method = element.tagName.toLowerCase();
return Form.Element.Serializers[method](element);
},
setValue: function(element, value) {
element = $(element);
var method = element.tagName.toLowerCase();
Form.Element.Serializers[method](element, value);
return element;
},
clear: function(element) {
$(element).value = '';
return element;
},
present: function(element) {
return $(element).value != '';
},
activate: function(element) {
element = $(element);
try {
element.focus();
if (element.select && (element.tagName.toLowerCase() != 'input' ||
!['button', 'reset', 'submit'].include(element.type)))
element.select();
} catch (e) { }
return element;
},
disable: function(element) {
element = $(element);
element.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) {
// extend element because hasAttribute may not be native
return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
}
};
/*--------------------------------------------------------------------------*/
Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
initialize: function($super, element, frequency, callback) {
$super(callback, frequency);
this.element   = $(element);
this.lastValue = this.getValue();
},
execute: function() {
var value = this.getValue();
if (Object.isString(this.lastValue) && Object.isString(value) ?
this.lastValue != value : String(this.lastValue) != String(value)) {
this.callback(this.element, value);
this.lastValue = value;
}
}
});
Form.Element.Observer = Class.create(Abstract.TimedObserver, {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.Observer = Class.create(Abstract.TimedObserver, {
getValue: function() {
return Form.serialize(this.element);
}
});
/*--------------------------------------------------------------------------*/
Abstract.EventObserver = Class.create({
initialize: function(element, callback) {
this.element  = $(element);
this.callback = callback;
this.lastValue = this.getValue();
if (this.element.tagName.toLowerCase() == 'form')
this.registerFormCallbacks();
else
this.registerCallback(this.element);
},
onElementEvent: function() {
var value = this.getValue();
if (this.lastValue != value) {
this.callback(this.element, value);
this.lastValue = value;
}
},
registerFormCallbacks: function() {
Form.getElements(this.element).each(this.registerCallback, this);
},
registerCallback: function(element) {
if (element.type) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':
Event.observe(element, 'click', this.onElementEvent.bind(this));
break;
default:
Event.observe(element, 'change', this.onElementEvent.bind(this));
break;
}
}
}
});
Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.EventObserver = Class.create(Abstract.EventObserver, {
getValue: function() {
return Form.serialize(this.element);
}
});
if (!window.Event) var Event = { };
Object.extend(Event, {
KEY_BACKSPACE: 8,
KEY_TAB:       9,
KEY_RETURN:   13,
KEY_ESC:      27,
KEY_LEFT:     37,
KEY_UP:       38,
KEY_RIGHT:    39,
KEY_DOWN:     40,
KEY_DELETE:   46,
KEY_HOME:     36,
KEY_END:      35,
KEY_PAGEUP:   33,
KEY_PAGEDOWN: 34,
KEY_INSERT:   45,
cache: { },
relatedTarget: function(event) {
var element;
switch(event.type) {
case 'mouseover': element = event.fromElement; break;
case 'mouseout':  element = event.toElement;   break;
default: return null;
}
return Element.extend(element);
}
});
Event.Methods = (function() {
var isButton;
if (Prototype.Browser.IE) {
var buttonMap = { 0: 1, 1: 4, 2: 2 };
isButton = function(event, code) {
return event.button == buttonMap[code];
};
} else if (Prototype.Browser.WebKit) {
isButton = function(event, code) {
switch (code) {
case 0: return event.which == 1 && !event.metaKey;
case 1: return event.which == 1 && event.metaKey;
default: return false;
}
};
} else {
isButton = function(event, code) {
return event.which ? (event.which === code + 1) : (event.button === code);
};
}
return {
isLeftClick:   function(event) { return isButton(event, 0) },
isMiddleClick: function(event) { return isButton(event, 1) },
isRightClick:  function(event) { return isButton(event, 2) },
element: function(event) {
event = Event.extend(event);
var node          = event.target,
type          = event.type,
currentTarget = event.currentTarget;
if (currentTarget && currentTarget.tagName) {
// Firefox screws up the "click" event when moving between radio buttons
// via arrow keys. It also screws up the "load" and "error" events on images,
// reporting the document as the target instead of the original image.
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);
},
findElement: function(event, expression) {
var element = Event.element(event);
if (!expression) return element;
var elements = [element].concat(element.ancestors());
return Selector.findElement(elements, expression, 0);
},
pointer: function(event) {
var docElement = document.documentElement,
body = document.body || { scrollLeft: 0, scrollTop: 0 };
return {
x: event.pageX || (event.clientX +
(docElement.scrollLeft || body.scrollLeft) -
(docElement.clientLeft || 0)),
y: event.pageY || (event.clientY +
(docElement.scrollTop || body.scrollTop) -
(docElement.clientTop || 0))
};
},
pointerX: function(event) { return Event.pointer(event).x },
pointerY: function(event) { return Event.pointer(event).y },
stop: function(event) {
Event.extend(event);
event.preventDefault();
event.stopPropagation();
event.stopped = true;
}
};
})();
Event.extend = (function() {
var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
m[name] = Event.Methods[name].methodize();
return m;
});
if (Prototype.Browser.IE) {
Object.extend(methods, {
stopPropagation: function() { this.cancelBubble = true },
preventDefault:  function() { this.returnValue = false },
inspect: function() { return "[object Event]" }
});
return function(event) {
if (!event) return false;
if (event._extendedByPrototype) return event;
event._extendedByPrototype = Prototype.emptyFunction;
var pointer = Event.pointer(event);
Object.extend(event, {
target: event.srcElement,
relatedTarget: Event.relatedTarget(event),
pageX:  pointer.x,
pageY:  pointer.y
});
return Object.extend(event, methods);
};
} else {
Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
Object.extend(Event.prototype, methods);
return Prototype.K;
}
})();
Object.extend(Event, (function() {
var cache = Event.cache;
function getEventID(element) {
if (element._prototypeEventID) return element._prototypeEventID[0];
arguments.callee.id = arguments.callee.id || 1;
return element._prototypeEventID = [++arguments.callee.id];
}
function getDOMEventName(eventName) {
if (eventName && eventName.include(':')) return "dataavailable";
return eventName;
}
function getCacheForID(id) {
return cache[id] = cache[id] || { };
}
function getWrappersForEventName(id, eventName) {
var c = getCacheForID(id);
return c[eventName] = c[eventName] || [];
}
function createWrapper(element, eventName, handler) {
var id = getEventID(element);
var c = getWrappersForEventName(id, eventName);
if (c.pluck("handler").include(handler)) return false;
var wrapper = function(event) {
if (!Event || !Event.extend ||
(event.eventName && event.eventName != eventName))
return false;
Event.extend(event);
handler.call(element, event);
};
wrapper.handler = handler;
c.push(wrapper);
return wrapper;
}
function findWrapper(id, eventName, handler) {
var c = getWrappersForEventName(id, eventName);
return c.find(function(wrapper) { return wrapper.handler == handler });
}
function destroyWrapper(id, eventName, handler) {
var c = getCacheForID(id);
if (!c[eventName]) return false;
c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
}
function destroyCache() {
for (var id in cache)
for (var eventName in cache[id])
cache[id][eventName] = null;
}
// Internet Explorer needs to remove event handlers on page unload
// in order to avoid memory leaks.
if (window.attachEvent) {
window.attachEvent("onunload", destroyCache);
}
// Safari has a dummy event handler on page unload so that it won't
// use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
// object when page is returned to via the back button using its bfcache.
if (Prototype.Browser.WebKit) {
window.addEventListener('unload', Prototype.emptyFunction, false);
}
return {
observe: function(element, eventName, handler) {
element = $(element);
var name = getDOMEventName(eventName);
var wrapper = createWrapper(element, eventName, handler);
if (!wrapper) return element;
if (element.addEventListener) {
element.addEventListener(name, wrapper, false);
} else {
element.attachEvent("on" + name, wrapper);
}
return element;
},
stopObserving: function(element, eventName, handler) {
element = $(element);
var id = getEventID(element), name = getDOMEventName(eventName);
if (!handler && eventName) {
getWrappersForEventName(id, eventName).each(function(wrapper) {
element.stopObserving(eventName, wrapper.handler);
});
return element;
} else if (!eventName) {
Object.keys(getCacheForID(id)).each(function(eventName) {
element.stopObserving(eventName);
});
return element;
}
var wrapper = findWrapper(id, eventName, handler);
if (!wrapper) return element;
if (element.removeEventListener) {
element.removeEventListener(name, wrapper, false);
} else {
element.detachEvent("on" + name, wrapper);
}
destroyWrapper(id, eventName, handler);
return element;
},
fire: function(element, eventName, memo) {
element = $(element);
if (element == document && document.createEvent && !element.dispatchEvent)
element = document.documentElement;
var event;
if (document.createEvent) {
event = document.createEvent("HTMLEvents");
event.initEvent("dataavailable", true, true);
} else {
event = document.createEventObject();
event.eventType = "ondataavailable";
}
event.eventName = eventName;
event.memo = memo || { };
if (document.createEvent) {
element.dispatchEvent(event);
} else {
element.fireEvent(event.eventType, event);
}
return Event.extend(event);
}
};
})());
Object.extend(Event, Event.Methods);
Element.addMethods({
fire:          Event.fire,
observe:       Event.observe,
stopObserving: Event.stopObserving
});
Object.extend(document, {
fire:          Element.Methods.fire.methodize(),
observe:       Element.Methods.observe.methodize(),
stopObserving: Element.Methods.stopObserving.methodize(),
loaded:        false
});
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards and John Resig. */
var timer;
function fireContentLoadedEvent() {
if (document.loaded) return;
if (timer) window.clearInterval(timer);
document.fire("dom:loaded");
document.loaded = true;
}
if (document.addEventListener) {
if (Prototype.Browser.WebKit) {
timer = window.setInterval(function() {
if (/loaded|complete/.test(document.readyState))
fireContentLoadedEvent();
}, 0);
Event.observe(window, "load", fireContentLoadedEvent);
} else {
document.addEventListener("DOMContentLoaded",
fireContentLoadedEvent, false);
}
} else {
document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
$("__onDOMContentLoaded").onreadystatechange = function() {
if (this.readyState == "complete") {
this.onreadystatechange = null;
fireContentLoadedEvent();
}
};
}
})();
/*------------------------------- DEPRECATED -------------------------------*/
Hash.toQueryString = Object.toQueryString;
var Toggle = { display: Element.toggle };
Element.Methods.childOf = Element.Methods.descendantOf;
var Insertion = {
Before: function(element, content) {
return Element.insert(element, {before:content});
},
Top: function(element, content) {
return Element.insert(element, {top:content});
},
Bottom: function(element, content) {
return Element.insert(element, {bottom:content});
},
After: function(element, content) {
return Element.insert(element, {after:content});
}
};
var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
// set to true if needed, warning: firefox performance problems
// NOT neeeded for page scrolling, only if draggable contained in
// scrollable elements
includeScrollOffsets: false,
// must be called before calling withinIncludingScrolloffset, every time the
// page is scrolled
prepare: function() {
this.deltaX =  window.pageXOffset
|| document.documentElement.scrollLeft
|| document.body.scrollLeft
|| 0;
this.deltaY =  window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop
|| 0;
},
// caches x/y coordinate pair to use with overlap
within: function(element, x, y) {
if (this.includeScrollOffsets)
return this.withinIncludingScrolloffsets(element, x, y);
this.xcomp = x;
this.ycomp = y;
this.offset = Element.cumulativeOffset(element);
return (y >= this.offset[1] &&
y <  this.offset[1] + element.offsetHeight &&
x >= this.offset[0] &&
x <  this.offset[0] + element.offsetWidth);
},
withinIncludingScrolloffsets: function(element, x, y) {
var offsetcache = Element.cumulativeScrollOffset(element);
this.xcomp = x + offsetcache[0] - this.deltaX;
this.ycomp = y + offsetcache[1] - this.deltaY;
this.offset = Element.cumulativeOffset(element);
return (this.ycomp >= this.offset[1] &&
this.ycomp <  this.offset[1] + element.offsetHeight &&
this.xcomp >= this.offset[0] &&
this.xcomp <  this.offset[0] + element.offsetWidth);
},
// within must be called directly before
overlap: function(mode, element) {
if (!mode) return 0;
if (mode == 'vertical')
return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
element.offsetHeight;
if (mode == 'horizontal')
return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
element.offsetWidth;
},
// Deprecation layer -- use newer Element methods now (1.5.2).
cumulativeOffset: Element.Methods.cumulativeOffset,
positionedOffset: Element.Methods.positionedOffset,
absolutize: function(element) {
Position.prepare();
return Element.absolutize(element);
},
relativize: function(element) {
Position.prepare();
return Element.relativize(element);
},
realOffset: Element.Methods.cumulativeScrollOffset,
offsetParent: Element.Methods.getOffsetParent,
page: Element.Methods.viewportOffset,
clone: function(source, target, options) {
options = options || { };
return Element.clonePosition(target, source, options);
}
};
/*--------------------------------------------------------------------------*/
if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
function iter(name) {
return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
}
instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
function(element, className) {
className = className.toString().strip();
var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
} : function(element, className) {
className = className.toString().strip();
var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
if (!classNames && !className) return elements;
var nodes = $(element).getElementsByTagName('*');
className = ' ' + className + ' ';
for (var i = 0, child, cn; child = nodes[i]; i++) {
if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
(classNames && classNames.all(function(name) {
return !name.toString().blank() && cn.include(' ' + name + ' ');
}))))
elements.push(Element.extend(child));
}
return elements;
};
return function(className, parentElement) {
return $(parentElement || document.body).getElementsByClassName(className);
};
}(Element.Methods);
/*--------------------------------------------------------------------------*/
Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
initialize: function(element) {
this.element = $(element);
},
_each: function(iterator) {
this.element.className.split(/\s+/).select(function(name) {
return name.length > 0;
})._each(iterator);
},
set: function(className) {
this.element.className = className;
},
add: function(classNameToAdd) {
if (this.include(classNameToAdd)) return;
this.set($A(this).concat(classNameToAdd).join(' '));
},
remove: function(classNameToRemove) {
if (!this.include(classNameToRemove)) return;
this.set($A(this).without(classNameToRemove).join(' '));
},
toString: function() {
return $A(this).join(' ');
}
};
Object.extend(Element.ClassNames.prototype, Enumerable);
/*--------------------------------------------------------------------------*/
Element.addMethods();

// script.aculo.us scriptaculous.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For details, see the script.aculo.us web site: http://script.aculo.us/
var Scriptaculous = {
Version: '1.8.2',
require: function(libraryName) {
// inserting via DOM fails in Safari 2.0, so brute force approach
document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
},
REQUIRED_PROTOTYPE: '1.6.0.3',
load: function() {
function convertVersionString(versionString) {
var v = versionString.replace(/_.*|\./g, '');
v = parseInt(v + '0'.times(4-v.length));
return versionString.indexOf('_') > -1 ? v-1 : v;
}
if((typeof Prototype=='undefined') ||
(typeof Element == 'undefined') ||
(typeof Element.Methods=='undefined') ||
(convertVersionString(Prototype.Version) <
convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
throw("script.aculo.us requires the Prototype JavaScript framework >= " +
Scriptaculous.REQUIRED_PROTOTYPE);
var js = /scriptaculous\.js(\?.*)?$/;
$$('head script[src]').findAll(function(s) {
return s.src.match(js);
}).each(function(s) {
var path = s.src.replace(js, ''),
includes = s.src.match(/\?.*load=([a-z,]*)/);
(includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
function(include) { Scriptaculous.require(path+include+'.js') });
});
}
};
Scriptaculous.load();

// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
// Copyright (c) 2005-2008 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) {
element = $(element);
effect = (effect || 'appear').toLowerCase();
var options = Object.extend({
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
}, arguments[2] || { });
Effect[element.visible() ?
Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
}
};
Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
/* ------------- core effects ------------- */
Effect.ScopedQueue = Class.create(Enumerable, {
initialize: function() {
this.effects  = [];
this.interval = null;
},
_each: function(iterator) {
this.effects._each(iterator);
},
add: function(effect) {
var timestamp = new Date().getTime();
var position = Object.isString(effect.options.queue) ?
effect.options.queue : effect.options.queue.position;
switch(position) {
case 'front':
// move unstarted effects after this effect
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
e.startOn  += effect.finishOn;
e.finishOn += effect.finishOn;
});
break;
case 'with-last':
timestamp = this.effects.pluck('startOn').max() || timestamp;
break;
case 'end':
// start effect after last queued effect has finished
timestamp = this.effects.pluck('finishOn').max() || timestamp;
break;
}
effect.startOn  += timestamp;
effect.finishOn += timestamp;
if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
this.effects.push(effect);
if (!this.interval)
this.interval = setInterval(this.loop.bind(this), 15);
},
remove: function(effect) {
this.effects = this.effects.reject(function(e) { return e==effect });
if (this.effects.length == 0) {
clearInterval(this.interval);
this.interval = null;
}
},
loop: function() {
var timePos = new Date().getTime();
for(var i=0, len=this.effects.length;i<len;i++)
this.effects[i] && this.effects[i].loop(timePos);
}
});
Effect.Queues = {
instances: $H(),
get: function(queueName) {
if (!Object.isString(queueName)) return queueName;
return this.instances.get(queueName) ||
this.instances.set(queueName, new Effect.ScopedQueue());
}
};
Effect.Queue = Effect.Queues.get('global');
Effect.Base = Class.create({
position: null,
start: function(options) {
function codeForEvent(options,eventName){
return (
(options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
(options[eventName] ? 'this.options.'+eventName+'(this);' : '')
);
}
if (options && options.transition === false) options.transition = Effect.Transitions.linear;
this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
this.currentFrame = 0;
this.state        = 'idle';
this.startOn      = this.options.delay*1000;
this.finishOn     = this.startOn+(this.options.duration*1000);
this.fromToDelta  = this.options.to-this.options.from;
this.totalTime    = this.finishOn-this.startOn;
this.totalFrames  = this.options.fps*this.options.duration;
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);

// Copyright (c) 2005-2008 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  = Position.cumulativeOffset(this.track); 
this.event = event;
this.setValue(this.translateToValue( 
(this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
));
var offsets  = Position.cumulativeOffset(this.activeHandle);
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  = Position.cumulativeOffset(this.activeHandle);
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 = Position.cumulativeOffset(this.track);
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;
}
});


/**
*  Prototype Xtensions 
*  
*  @author    Simon Martins
*  @copyright (c) 2008 Netatoo SARL <http://www.netatoo.fr>
*  @license   MIT License <http://www.prototypextensions.com/#main=license>
* 
*  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.
*
*/
/**
* Prototype.X requirements
*/
Prototype.X = {
Version   : '0.1.2',
Required  : {
Prototype : '1.6.0.2',
Scripty   : '1.8.1'
},
Require   : function() {
if(typeof Prototype       == 'undefined' 
|| typeof Element         == 'undefined'
|| typeof Element.Methods == 'undefined'
|| this.convertVersionString(Prototype.Version  || 0) < 
this.convertVersionString(this.Required.Prototype)
) {
throw( "Prototype.Xtensions require Prototype.js >= " 
+ this.Required.Prototype );
} 
if(typeof Scriptaculous   == 'undefined' 
|| typeof Effect          == 'undefined'
|| this.convertVersionString(Scriptaculous.Version || 0) < 
this.convertVersionString(this.Required.Scripty)
) {
throw( "Prototype.Xtensions require Scriptaculous >= " 
+ this.Required.Scripty );
}
},
convertVersionString: function (versionString) {
var v = versionString.replace(/_.*|\./g, '');
v = parseInt(v + '0'.times(4-v.length));
return versionString.indexOf('_') > -1 ? v-1 : v;
}
};
Prototype.X.Require();
/**
* Create registry for self components
*/
Prototype.X.Registry = {};
/**
* Prototype.X.Browser
*
* @desc Used to retrieve the browser version
*/
(function() {
var nav       = navigator;
var userAgent = ua = navigator.userAgent;
var v         = nav.appVersion;
var version   = parseFloat(v);
Prototype.X.Browser = {
IE      : (Prototype.Browser.IE)    ? parseFloat(v.split("MSIE ")[1]) || 0 : 0,
Firefox : (Prototype.Browser.Gecko) ? parseFloat(ua.split("Firefox/")[1]) || 0 : 0,
Camino  : (Prototype.Browser.Gecko) ? parseFloat(ua.split("Camino/")[1]) || 0 : 0,
Flock   : (Prototype.Browser.Gecko) ? parseFloat(ua.split("Flock/")[1]) || 0 : 0,
Opera   : (Prototype.Browser.Opera) ? version : 0,
AIR     : (ua.indexOf("AdobeAIR") >= 0) ? 1 : 0,
Mozilla : (Prototype.Browser.Gecko || !this.Khtml) ? version : 0,
Khtml   : (v.indexOf("Konqueror") >= 0 && this.safari) ? version : 0,
Safari  : (function() {
var safari = Math.max(v.indexOf("WebKit"), v.indexOf("Safari"), 0);
return (safari) ? (
parseFloat(v.split("Version/")[1]) || ( ( parseFloat(v.substr(safari+7)) >= 419.3 ) ? 3 : 2 ) || 2
) : 0;
})()
};
})();
/**
* $unixTime
*
* @desc Easy recovering of UNIX timestamp
*/
$unixTime = function(inSeconds) { 
var time = new Date();
time = time.getTime();
if(inSeconds == true) time = time / 1000;
return time; 
};
/**
* String extension
*/
Object.extend(String.prototype, 
{
/**
* (string).ucfirst()
*
* @desc Make a string's first character uppercase
*/
ucfirst: function() {
return this.charAt(0).toUpperCase() + this.substring(1);
}
});
/**
* Number extension
*/
Object.extend(Number.prototype, 
{
/**
* (Number).hour()
*
* @desc Return the multiplication of 'Number' per hour
*/
minute: function() {
return this * 60;
},
/**
* (Number).hour()
*
* @desc Return the multiplication of 'Number' per hour
*/
hour: function() {
return this * 3600;
},
/**
* (Number).day()
*
* @desc Return the multiplication of 'Number' per day
*/
day: function() {
return this * 86400;
},
/**
* (Number).week()
*
* @desc Return the multiplication of 'Number' per week
*/
week: function() {
return this * 604800;
},
/**
* (Number).month()
*
* @desc Return the multiplication of 'Number' per month
*/
month: function() {
return this * 2629743.83;
},
/**
* (Number).year()
*
* @desc Return the multiplication of 'Number' per year
*/
year: function() {
return this * 31556926;
},
/**
* (Number).time()
*
* @desc Return the unix time + Number
*/
time: function() {
return (this.toMs()) + $unixTime();
},
/**
* (Number).toSeconds()
*
* @desc Convert milisecond to seconds
*/
toSeconds: function() {
return this * 1000;
},
/**
* (Number).toSeconds()
*
* @desc Convert seconds to miliseconds
*/
toMs: function() {
return this / 1000;
}
});
/**
* Class extension
*/
Object.extend(Class, 
{
/**
* Class.accessors
*
* @desc Provides a method setOptions() you can create 
* accessors for all options 'opt'.
* And method createAccessors
*/
accessors: {
/**
* createAccessors( object options )
*
* @desc Create accessors (get/set) for each options.
*/
createAccessors: function(options, entries) {
var entries = (!Object.isArray(entries)) ? Object.keys(entries) : entries;
entries.each(function(name) {
var cname = (name.camelize()).ucfirst();
(function(name, that){
that['get' + cname] = function(){ 
return options[name];
};
that['set' + cname] = function(value){ 
options[name] = value; 
return that; 
};
})(name, this);
}, this);
},
/**
* setOptions( object options )
*
* @desc Create accessors for each options (Included mechanism Object.extend)
*/
setOptions: function (defaultOptions, userOptions, accessorize) {
var access = (Object.isUndefined(accessorize)) ? false : accessorize;
if (!defaultOptions) defaultOptions = { };
Object.extend(defaultOptions, userOptions || { });
if(accessorize) this.createAccessors(defaultOptions, defaultOptions);
}
}
});
// -------------------------------------------------------------------
/**
* EventManager
* 
* @extend Class.accessors
* @desc Easly event manager for create custom event on your own class
*/
var EventManager = Class.create({
/**
* Initialize
*
* @desc Set scope and events hash
*/
initialize: function(scope) {
this.scope  = scope;
this.events = new Hash();
},
/**
* addListener
*
* @desc Add event observer
*/
addObserver: function(name) {
return this.events.set(name, new Hash());
},
/**
* observe
*
* @desc Add an callback for listener 'name'
*/
observe: function(name, callback) {
var observers = this.events.get(name);
if(!observers) observers = this.addObserver(name);
if(!Object.isFunction(callback)) {
throw('EventManager.observe : callback must be an js function');
}
var i = this.events.get(name).keys().length;
observers.set(i, callback.bind(this.scope));
return this;
},
/**
* notify
*
* @desc Launch all callbacks for listener 'name'
*/
notify: function(name) {
var observers = this.events.get(name);
if(observers) {
var args = $A(arguments).slice(1);
observers.each(function(callback) {
if(Object.isFunction(callback[1])) {
callback[1].apply(this.scope, args);
}
});
}
return this;
}
});
// -------------------------------------------------------------------
/**
* Cookie
* 
* @desc Set / Get / Unset cookie 
*/
var Cookie = {
/**
* Custom events
*
* @desc Instance of EventManager
*/
events: new EventManager(this),
/**
* isEnabled()
*
* @desc Return true if cookie is enabled, false else
*/
isEnabled: function() {
this.set('enabled', 'ok');
var test = this.get('enabled');
this.unset('enabled');
return (test == 'ok') ? true : false;
},
/**
* observe()
*
* @desc Observe custom event on Cookie
*/
observe: function(name, callback) {
this.events.observe(name, callback);
return this;
},
/**
* set()
*
* @desc Set a cookie
*/
set: function(name, value, options, noevents) {
var expire = '';
var o      = options || {};
var path   = (o.path)   ? '; path=' + o.path : '; path=/';
var domain = (o.domain) ? '; domain=' + o.domain : '';
var secure = (o.secure == true) ? '; secure' : '';
var date = new Date();
if(Object.isNumber(o)) {
date.setTime(date.getTime() + (o * 1000));
expire = '; expires=' + date.toGMTString();
} else if(o.expires) {
date.setTime(date.getTime() + (o.expires * 1000));
expire = '; expires=' + date.toGMTString();
}
document.cookie = name + "=" + value + expire + path + domain + secure;
if(!noevents) this.events.notify('set', name, value, options);
return this;
},
/**
* get()
*
* @desc Get a cookie
* @attribution http://www.quirksmode.org/js/cookies.html
*/
get: function(name) {
this.events.notify('get', name);
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++){
var c = ca[i];
while(c.charAt(0) == ' ')
c = c.substring(1,c.length);
if(c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length,c.length);
}
return null;
},
/**
* get()
*
* @desc Unset a cookie
*/
unset: function(name) {
this.events.notify('unset', name);
this.set(name, '', -1, true);
return this;
} 
};
// -------------------------------------------------------------------
/**
* History
*
* @desc Provides basic methods to manage the history browsing.
*/
var History = {
__altered: false,
__currentHash: null,
__previousHash: null,
__iframe: false,
__title: false,
/**
* init()
* @desc Initialize the hash. Call this method in first
*/
init: function() {
var inst  = this;
var hash  = location.hash.substring(1);
this.hash = $H(hash.toQueryParams());
this.__currentHash  = hash;
this.__previousHash = hash;
this.__title = document.title;
if(Prototype.Browser.IE && Prototype.X.Browser.IE < 8) {
document.observe('dom:loaded', function(e) {
if(!$('px-historyframe')) {
History.__iframe = new Element('iframe', {
name   : 'px-historyframe',
id     : 'px-historyframe',
src    : '',
width  : '0',
height : '0',
style  : {
visibility: 'hidden'
}
});
document.body.appendChild(History.__iframe);
History.setHashOnIframe(inst.hash.toQueryString());
}
});
}
},
/**
* set( string $name, string $value )
*
* @desc Set new value $value for parameter $name
*/
set: function($name, $value) {
this.__previousHash = this.hash.toQueryString();
this.hash.set($name, $value);
this.apply();
},
/**
* get( string $name )
*
* @desc Get value parameter $name
*/
get: function($name) {
return this.hash.get($name);
},
/**
* unset( string $name )
*
* @desc Unset parameter $name
*/
unset: function($name) {
this.hash.unset($name);
this.apply();
},
/**
* update()
*
* @desc Updates this.hash with the current hash
*/
update: function() {
this.__previousHash = this.hash.toQueryString();
var hash = window.location.hash.substring(1);
// If IE, look in the iframe if the hash is updated
if(Prototype.Browser.IE && Prototype.X.Browser.IE < 8 && this.__iframe ) {
var hashInFrame = this.getHashOnIframe();
if(hashInFrame != hash) {
hash = hashInFrame;
}
}
this.hash = $H(hash.toQueryParams());
this.__currentHash = hash;
},
/**
* apply()
*
* @desc Apply this.hash to location.hash
*/
apply: function() {
var newHash = this.hash.toQueryString();
// set new hash
window.location.hash = newHash;
// If IE, apply new hash to frame for history    
if(Prototype.Browser.IE && Prototype.X.Browser.IE < 8 && this.__iframe) {
if(this.__currentHash != newHash) 
{
this.setHashOnIframe(newHash);   
}
else if(newHash != this.getHashOnIframe()) 
{
this.setHashOnIframe(newHash);    
}
}
},
/**
* isAltered()
*
* @desc Return true if current hash is different of previous hash.
* this.__altered allows to force the dispatch.
*/
isAltered: function() {
if(this.__altered == true) {
return true;
}
this.__altered = false;
return (History.__currentHash != History.__previousHash);
},
/**
* setHashOnIframe()
*
* @use  For IE compatibility
* @desc Set hash value on iframe
*/
setHashOnIframe: function(hash) {
try {
var doc = History.__iframe.contentWindow.document;
doc.open();
doc.write('<html><body id="history">' + hash + '</body></html>');
doc.close();
} catch(e) {}
},
/**
* getHashOnIframe()
*
* @use  For IE compatibility
* @desc Get hash value on iframe
*/
getHashOnIframe: function() {
var doc = this.__iframe.contentWindow.document;
if (doc && doc.body.id == 'history') {
return doc.body.innerText;
} else {
return this.hash.toQueryString();
}
},
/**
* setTitle()
*
* @desc Set a new title for window
*/
setTitle: function(title) {
if(document.title) {
document.title = title;
}
},
/**
* getTitle()
*
* @desc Return current window title
*/
getTitle: function() {
return this.__title;
}
};
History.init();
/**
* History.Registry
*
* @desc Used to register a callback for a parameter
*/
History.Registry = 
{
/**
* @desc Hash
*/
hash : new Hash(),
/**
* set( string $config )
*
* @desc Set new value $historyId for parameter $config
*/
set: function($config) {
if(typeof($config) != 'object') {
throw('History.Registry.set : $config must be an javascript object');
} 
// id
if(!$config.id || !Object.isString($config.id)) {
throw('History.Registry.set : $config.id must be an string');
} 
// onChange
if(!$config.onStateChange || !Object.isFunction($config.onStateChange)) {
throw('History.Registry.set : $config.onStateChange '
+ 'must be an javascript callback function');
}
// defaultValue
if(!$config.defaultValue || !Object.isString($config.defaultValue)) {
$config.defaultValue = '';
}
this.hash.set($config.id, $config);
},
/**
* get( string $id )
*
* @desc Get value parameter $id
*/
get: function($id) {
return this.hash.get($id);
},
/**
* unset( string $id )
*
* @desc Unset parameter $id
*/
unset: function($id) {
this.hash.unset($id);
}
}
/**
* History.Observer
*
* @desc Used to perform actions defined in the registry, 
* according to the hash of the url.
*/
History.Observer = {
/**
* @desc Interval delay in seconds
*/
delay : 0.2,
/**
* @desc Interval timer instance
*/
interval : null,
/**
* @desc If interval is started : true, else false
*/
started : false,
/**
* start()
*
* @desc Start a interval timer
*/
start: function() {
if(this.started) return;
this.interval = new PeriodicalExecuter(History.Observer.dispatch, this.delay);
this.started = true;
},
/**
* stop()
*
* @desc Stop the interval timer
*/
stop: function() {
if(!this.started) return;
this.interval.stop();
this.started = false;
},
/**
* dispatch()
*
* @desc This method is called each time interval, 
* the dispatch of the registry is implemented only if 
* the hash has been amended (optimisiation)
*/
dispatch: function() {
// Update the hash
History.update();
// Dispatch only if location.hash has been altered
if(History.isAltered()) {
//if(console) console.log('pass');
History.hash.each(function(pair)  {
var registry = History.Registry.get(pair.key);
if(registry) {
registry.onStateChange.bind(History)( pair.value );
}
});
}
}
};
/**
* Ajax.History
*
* @desc Provides core methods to easily manage browsing history 
* with Ajax.History.Request / Updater.
*/
Ajax.History = {
/**
* @desc Allowed Ajax.History prefix (for validation)
*/
types : ['Request', 'Updater'],
/**
* observe( string $type, string $id, string $url, object $options )
*
* @desc This method helps manage the browsing history
*/
observe: function($type, $id, $url, $options) {
var getter         = Ajax.History.Registry.get($id);
var currentVersion = 0;
var output         = false;
// Type validation   
if(this.types.indexOf($type) == -1) {
throw('Ajax.History.observer() -> type ' + $type + ' is invalid !');
}      
// Registry management
if(getter == undefined) {
currentVersion = ($options.history.state) ? $options.history.state : 0;  
var hash = new Hash();
hash.set(currentVersion, $options);
Ajax.History.Registry.set($id, hash);
} else {
currentVersion = ($options.history.state) 
? $options.history.state : this.getCurrentVersion($id);
getter.set(currentVersion, $options);
}
// add handler on registry
this.addCallback($type, $id);
return currentVersion;
},
/**
* addCallback( string $type, string $id )
*
* @desc This method adds a state for request on History.Registry
*/
addCallback: function($type, $id) {
History.Observer.start();
// Set history altered state to true : force dispatch
History.__altered = true;
// Return void if registry is already set
if(!Object.isUndefined(History.Registry.get($id))) return;  
// Add this id to history registry
History.Registry.set({
id: $id,
onStateChange: function(state) {
var options = Ajax.History.Registry.get($id).get(state.toString());
var request = null;
if(Object.isUndefined(options)) return;
if(options.history.cache == true && options.history.__request) {
new Ajax.Cache(options.history.__request);
} else {
if($type == 'Request') {
request = new Ajax.Request(options.history.__url, options);
} else if($type == 'Updater') {
request = new Ajax.Updater(options.container, options.history.__url, options);
}
options.history.__request = request; 
}
History.__altered = false;
if (Object.isFunction(options.history.onStateChange)) {
options.history.onStateChange(state);
}
} 
}); 
},
/**
* getCurrentVersion( string $id )
*
* @desc This method returns the current state in history 
* (if the state is not defined)
*/
getCurrentVersion: function($id) {
var getter = Ajax.History.Registry.get($id);        
return Object.isUndefined(getter) ? 0 : getter.keys().length;
}
};
/**
* Ajax.History.Registry
*
* @desc Simple Hash to manage request history
*/
Ajax.History.Registry = new Hash();
/**
* Ajax.History.Features
*
* @desc It's default request options for Ajax.History.Request/Updater
*/
Ajax.History.Features = {
id              : null,    // set custom history value for this instance
state           : false,   // set custom state value for this instance
cache           : false,   // enable/disable history cache 
onStateChange   : null,    // this handler is called when history change
__url           : null,
__request       : null
};
/**
* Ajax.History.Request
*
* @desc Used to execute an Ajax.Request by integrating 
* the management of browsing history
*/
Ajax.History.Request = Class.create({
initialize: function($url, $options) {
this.options = {};
Object.extend(this.options, $options);
// Set historing only if option is actived
if(this.options.history) {
var tmpOpt = Object.clone(Ajax.History.Features);
Object.extend(tmpOpt, this.options.history);  
this.options.history = tmpOpt;
this.options.history.__url = $url;
// History id
if(Object.isUndefined($options.history.id)) {
throw('Ajax.History.Request error : you must define historyId');
} else {
var id = this.options.history.id;
}
// Enable history observer
var version = Ajax.History.observe('Request', id, $url, this.options);
// Set current version value for container
History.set(id, version);
} else {
return new Ajax.Request($url, this.options);
}
}
});
/**
* Ajax.History.Updater
*
* @desc Used to execute an Ajax.Updater by integrating 
* the management of browsing history
*/
Ajax.History.Updater = Class.create({
initialize: function($container, $url, $options) {
this.options = {};
Object.extend(this.options, $options);
// Set historing only if option is actived
if(this.options.history) {
var tmpOpt = Object.clone(Ajax.History.Features);
Object.extend(tmpOpt, this.options.history);  
this.options.history = tmpOpt;
this.options.history.__url = $url;
// History id
if(Object.isUndefined($options.history.id)) {
var id = (Object.isString($container)) ? $container : $container.id;
} else {
var id = this.options.history.id;
}
// Set $container to this.options (take it back then)
this.options.container = $container;
// Enable history observer
var version = Ajax.History.observe('Updater', id, $url, this.options);
// Set current version value for container
History.set(id, version);
} else {
return new Ajax.Updater($container, $url, this.options);
}
}
});
/**
* Ajax.Cache
*
* @desc Ajax.Cache can "simulate" an Ajax request from an 
* Ajax.Request/Updater made beforehand. 
*/
Ajax.Cache = Class.create(Ajax.Base, {
_complete: false,
initialize: function($super, request) {
$super(request.options);
request._complete = false;
this.transport = request.transport;
this.request(request.url);
return this;
},
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = Object.clone(this.options.parameters);
try {
var response = new Ajax.Response(this);
if (this.options.onCreate) this.options.onCreate(response);
Ajax.Responders.dispatch('onCreate', this, response);
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
}
});
Object.extend(Ajax.Cache.prototype, {
respondToReadyState : Ajax.Request.prototype.respondToReadyState,
onStateChange       : Ajax.Request.prototype.onStateChange,
success             : Ajax.Request.prototype.getStatus,
getStatus           : Ajax.Request.prototype.getStatus,
isSameOrigin        : Ajax.Request.prototype.isSameOrigin,
getHeader           : Ajax.Request.prototype.getHeader,
evalResponse        : Ajax.Request.prototype.evalResponse,
dispatchException   : Ajax.Request.prototype.dispatchException
});
/**
* Ajax.Request Extended
*
* @desc Just a small change: now Ajax.Request return self scope.
* It is required by Ajax.Cache
*/
Ajax.Request = Class.create(Ajax.Request, {
initialize: function($super, url, options) {
$super(url, options);
return this;
}
});
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
/**
* Ajax.Updater Extended
*
* @desc Just a small change: now Ajax.Updater return self scope
* It is required by Ajax.Cache
*/
Ajax.Updater = Class.create(Ajax.Updater, {
initialize: function($super, container, url, options) {
$super(container, url, options);
return this;
} 
});
// -------------------------------------------------------------------
/**
* Create registry namespace for tabs
*/
Prototype.X.Registry.Tabs = {};
/**
* Tabs( string|HTMLElement element, object options)
* 
* @extend Class.accessors
* @desc Tabs is a class providing a set of method to manage a tab system. 
* The purpose Tabs acting as the manager of objects TabsItem 
* each representing a tab.
*/
var Tabs = Class.create(Class.accessors, {
/**
* Initialize
*
* @desc Sets options and create tabs TabsItem.
*/
initialize: function(element, options) {
var inst = this;
// items registry
this.items = [ ];
// set options
this.options = {
element         : null,         // is parent element
elementId       : null,         // is parent element id
tabsId          : null,         // is uniq namespace id for tabs
tabsContainer   : null,         // is <ul> for tabs
domTabs         : {},           // is <ul> childs elements
requests        : {},
selectEvent     : 'click',      // is event type for select tabs
defaultActive   : 0,            // is index of default active tab
currentActive   : null,         // is index of current active tab
history         : true,         // is dynamic hash with query params (simulate back and forward)
sortable        : false,        // is sortable option (true : active sortable, false : inactive)
sortableOptions : {             // is sortable options
tag         : 'li', 
overlap     : 'horizontal', 
constraint  : 'horizontal'
},
deletable       : false 
},
this.setOptions(this.options, options, true);
// declare some options
this.setElement(element);
this.setElementId(this.getElement().id);
this.setTabsId(this.getElementId().camelize());
// declare some variable
var tabs   = this.getElement().firstDescendant().childElements();
this.setDomTabs(tabs);
this.setTabsContainer(this.getElement().firstDescendant());
// builds items
this.__buildItems();
// select default tab (or get active tab on history)
this.__selectByDefault();
},
// ----------------------------------------------
/**
* __buildItems()
* 
* @desc This method is called on init and she's private.
* It can create TabsItem instance for each tab.
*/
__buildItems: function() {
var inst   = this;
var panels = this.getElement().childElements();
var tabs   = this.getDomTabs();
// shift first child (tabs ul)
panels.shift();
// create and save tabs item on registry
tabs.each(function(elem, index) {
if(!inst.items[index]) {
var requestOptions = {};
var seekedOptions  = inst.options.requests[panels[index].id];
// get request options if exist
if(seekedOptions) {
requestOptions = seekedOptions;
}
inst.items[index] = new TabsItem(inst, {
item    : elem,
panel   : panels[index],
request : requestOptions,
effect  : inst.effect
});
}
});
},
/**
* __selectByDefault()
* 
* @desc This method is called on init and she's private.
* It allows you to select the tab defines default from 
* the option 'defaultActive', and from browsing history 
* if the option 'history' is activated.
*/
__selectByDefault: function() {
var inst = this;
// options.history
if(this.getHistory()) {
// start history observer
History.Observer.start();
// set handler for this instance
History.Registry.set({
id: this.getTabsId(),
onStateChange: function(tabName) {
var getter = inst.get(tabName, false);
if(getter) {
// fix for opera history management
if(Prototype.Browser.Opera)
getter.__select();
else
getter.select();
}
this.setTitle(this.getTitle() + ' (' + tabName.ucfirst() + ')');
}
});
var value = History.get(this.getTabsId());
var tabs  = this.get(value, false);
if(value && tabs) {
tabs.select();
} else {
this.get(this.getDefaultActive()).select();
}
} else {
this.get(this.getDefaultActive()).select();
}
},
/**
* makeSortable()
* 
* @desc This method can make the tabs sortable 
* (use of the component Sortable script.aculo.us)
*/  
makeSortable: function(options) {
if(this.getSortable()) return;
var opt = Object.extend(this.getSortableOptions(), options || {}); 
Sortable.create(this.getTabsContainer().identify(), opt);
this.setSortable(true)
},
// ----------------------------------------------
/**
* show()
* 
* @desc To show the tab through its index or its unique identifier. 
* It is an alias TabsItem.show() (syntax shorter)
*/ 
show: function(index, select) {   
var getter = this.get(index);
if(getter) {
getter.show(select);
}
},
/**
* show()
* 
* @desc To show the tab through its index or its unique identifier. 
* It is an alias TabsItem.show() (syntax shorter)
*/ 
hide: function(index) {   
var getter = this.get(index);
if(getter) {
getter.hide();
}
},
/**
* select()
* 
* @desc To select the tab through its index or its unique identifier. 
* It is an alias TabsItem.select () (syntax shorter)
*/ 
select: function(index) {   
var getter = this.get(index);
if(getter) {
getter.select();
}
},
/**
* selectFirst()
* 
* @desc To select the first tab (displayed)
*/
selectFirst: function() {   
this.items.each(function(elem, index) {
if(elem.getDisplay()) {
elem.select();
throw $break;
}
});
},
/**
* selectLast()
* 
* @desc To select the last tab (displayed)
*/
selectLast: function() {   
this.items.last().select();
},
/**
* unSelectAll()
* 
* @desc To unSelect all tabs
*/
unSelectAll: function(without) {
without = (Object.isUndefined(without)) ? false : without;
this.items.each(function(tabs, index) {
if(without) {
if(tabs != without) tabs.unSelect();
} else {
tabs.unSelect();
}
});
},
/**
* get()
* 
* @desc To get a tab (TabsItem instance) through its index or its unique identifier. 
* @return TabsItem
*/ 
get: function(index, throwHandler) {
var inst  = this;
var error = 'Error on Tabs.get() : index '+index+' is undefined ! ';
if(Object.isUndefined(throwHandler)) {
throwHandler = true;
} 
if(Object.isNumber(index)) {
if(!this.items[index]) {
if(throwHandler) throw(error);
else return false;
}
return this.items[index];
} else if(Object.isString(index)) {
var seeked  = false;
var itemOut = null;
this.getDomTabs().each(function(elem, id) {
if(inst.items[id]) {
var item   = inst.items[id];
var itemId = item.getPanel().id;
if(itemId == index) {
seeked = true;
itemOut = item;
}
}
});
if(!seeked) {
if(throwHandler) throw(error);
else return false;
}
return itemOut;
}
if(throwHandler) throw(error);
else return false;
}
});
/**
* TabsItem( object tabs, object options)
* 
* @extend Class.accessors
* @desc TabsItem is a class providing a set of method to handle each tab.
* In fact, each tab is an instance of TabsItem.
*/
var TabsItem = Class.create(Class.accessors, {
/**
* Initialize
*
* @desc Defines the options and make treatment on the tab.
*/
initialize: function(tabs, options) {
this.options = {
tabs    : null,
item    : null,
panel   : null,
display : true,
request : null,
loaded  : false,
titleElement : null,
selected : false,
iframe   : null
};
this.setOptions(this.options, options, true);
this.setTabs(tabs);
this.__getTitleElement();
this.__setEvents();
this.__voidAhref();
// custom events
this.events = new EventManager(this);
},
/**
* __setEvents()
*
* @desc This method is private.
*/
__setEvents: function() {
var inst = this;
var selectEvent = this.getTabs().getSelectEvent();
$(this.getItem()).observe(selectEvent, this.select.bindAsEventListener(this));
},
/**
* __getTitleElement()
*
* @desc This method is private.
*/
__getTitleElement: function() {
this.setTitleElement(this.getItem().firstDescendant());
},
/**
* __voidAhref()
*
* @desc This method is private.
*/
__voidAhref: function() {
var a = this.getItem().firstDescendant();
if(a.hasAttribute('href')) {
a.href = 'javascript:void(0)';
} 
},
/**
* observe()
*
* @desc Observe an custom event
*/
observe: function(eventName, callback) {
this.events.observe(eventName, callback);
return this;
},
/**
* getTitle()
*
* @desc Return the current title of tab.
*/
getTitle: function() {
return this.getTitleElement().innerHTML;
},
/**
* setTitle()
*
* @desc Set new title on tab
*/
setTitle: function(title) {
this.getTitleElement().update(title);
},
/**
* select()
*
* @desc Select the tab and make history if actived.
*/
select: function() {
// update hash if options.history as true
if(this.getTabs().getHistory() ) {
History.set(this.getTabs().getTabsId(), this.getPanel().id);
History.setTitle(History.getTitle() + ' (' + this.getPanel().id.ucfirst() + ')');
}
this.__select();
},
/**
* __select()
*
* @desc Select the tab without history treatments (fix for opera)
*/
__select: function () {
if(this.getTabs().getHistory() ) {
History.setTitle(History.getTitle() + ' (' + this.getPanel().id.ucfirst() + ')');
}
this.getTabs().unSelectAll(this);
this.getItem().className = 'tab-show';
this.getPanel().className = 'panel-show';
this.getPanel().show();
this.load();
this.events.notify('select', this);
this.setSelected(true);
},
/**
* unSelect()
*
* @desc Unselect the tab.
*/
unSelect: function() {
this.getItem().className = 'tab-hide';
this.getPanel().className = 'panel-hide';
//this.hide();
if(this.getSelected()) {
this.events.notify('unselect', this);
}
this.setSelected(false);
},
/**
* show()
*
* @desc Show the tab (with effect if actived)
*/
show: function(select) {
if(this.getDisplay()) return;
this.events.notify('show', this);
this.getItem().show();
this.getPanel().show();
this.setDisplay(true);
if(select === true) this.select();
},
/**
* hide()
*
* @desc Hide the tab (with effect if actived)
*/
hide: function() {
if(!this.getDisplay()) return;
this.events.notify('hide', this);
this.getItem().hide();
this.getPanel().hide();
this.setDisplay(false);
this.getTabs().selectFirst();
},
/**
* toggle()
*
* @desc Toggle for show/hide.
*/
toggle: function(select) {
if(!this.getDisplay()) {
return this.show(select);
}
this.hide();
},
/**
* load()
*
* @desc Load an Ajax request or an iframe in the panel of the tab.
*/
load: function(options) {
var inst = this;
var options = Object.extend(options || {}, this.getRequest().options || {});
if(this.getRequest().url != null && !this.getLoaded()) {
var loadType = this.getRequest().type;
// AJAX
if(loadType == 'ajax') {
// Default options
options = Object.clone(Object.extend({
onLoading: function(response, json, tabs) {
tabs.setTitle('Loading...');
}
}, options));
// onCreate
var onCreate = options.onCreate;
options.onCreate = (function(response) {
if (Object.isFunction(onCreate)) onCreate(response, inst);
}).bind(this);
// onSuccess
var onSuccess = options.onSuccess;
options.onSuccess = (function(response, json) {
inst.setLoaded(true);  
if (Object.isFunction(onSuccess)) onSuccess(response, json, inst);
}).bind(this);
// onFailure
var onFailure = options.onFailure;
options.onFailure = (function(response, json) {
if (Object.isFunction(onFailure)) onFailure(response, json, inst);
}).bind(this);
// onComplete
var onComplete = options.onComplete;
options.onComplete = (function(response, json) {
inst.setTitle(inst.options.tmpTitle);
if (Object.isFunction(onComplete)) onComplete(response, json, inst);
}).bind(this);
// onLoading
var onLoading = options.onLoading;
options.onLoading = (function(response, json) {
inst.options.tmpTitle = inst.getTitle();
if (Object.isFunction(onLoading)) onLoading(response, json, inst);
}).bind(this);
// onLoaded
var onLoaded = options.onLoaded;
options.onLoaded = (function(response, json) {
if (Object.isFunction(onLoaded)) onLoaded(response, json, inst);
}).bind(this);
// onException
var onException = options.onException;
options.onException = (function(request, exception) {
if (Object.isFunction(onException)) onException(request, exception, inst);
}).bind(this);
new Ajax.Updater(this.getPanel(), this.getRequest().url, Object.extend({
onLoading: function(tabs) {
tabs.setTitle('Loading...');
}
}, options || {}));
} 
// IFRAME
else if(loadType == 'iframe') {
this.iframe = new Element('iframe', {
name   : this.getRequest().id,
id     : this.getRequest().id,
src    : this.getRequest().url
});
this.getPanel().update(this.iframe); 
this.setLoaded(true);
}
}
},
/**
* reload()
*
* @desc Reload the Ajax request or iframe.
*/
reload: function(options) {
this.setLoaded(false);
this.load(options);
}
});
/**
* Tabs Quick Syntax
* $(#element).tabs({options})
*
* Extend DOM to add method.
* This extension allows greatly ease the management 
* and handling of Tabs instances. 
*/
Element.addMethods({
tabs : function(element, options) {
var id = element.id.camelize();
// create namespace if dont exist
if(!Prototype.X.Registry.Tabs[id]) {
Prototype.X.Registry.Tabs[id] = {};
var instance = new Tabs(element, options);
Prototype.X.Registry.Tabs[id].instance = instance;
return instance;
} 
var instance = Prototype.X.Registry.Tabs[id].instance;
instance.setOptions(instance.options, options, true);
return instance;
}
});
// -------------------------------------------------------------------

/**
* @author Ryan Johnson <ryan@livepipe.net>
* @copyright 2007 LivePipe LLC
* @package Control.Modal
* @license MIT
* @url http://livepipe.net/projects/control_modal/
* @version 2.2.3
*/
if(typeof(Control) == "undefined")
Control = {};
Control.Modal = Class.create();
Object.extend(Control.Modal,{
loaded: false,
loading: false,
loadingTimeout: false,
overlay: false,
container: false,
current: false,
ie: false,
effects: {
containerFade: false,
containerAppear: false,
overlayFade: false,
overlayAppear: false
},
targetRegexp: /#(.+)$/,
imgRegexp: /\.(jpe?g|gif|png|tiff?)$/i,
overlayStyles: {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 998
},
overlayIEStyles: {
position: 'absolute',
top: 0,
left: 0,
zIndex: 998
},
disableHoverClose: false,
load: function(){
if(!Control.Modal.loaded){
Control.Modal.loaded = true;
Control.Modal.ie = !(typeof document.body.style.maxHeight != 'undefined');
Control.Modal.overlay = $(document.createElement('div'));
Control.Modal.overlay.id = 'modal_overlay';
Object.extend(Control.Modal.overlay.style,Control.Modal['overlay' + (Control.Modal.ie ? 'IE' : '') + 'Styles']);
Control.Modal.overlay.hide();
Control.Modal.container = $(document.createElement('div'));
Control.Modal.container.id = 'modal_container';
Control.Modal.container.hide();
Control.Modal.loading = $(document.createElement('div'));
Control.Modal.loading.id = 'modal_loading';
Control.Modal.loading.hide();
var body_tag = document.getElementsByTagName('body')[0];
body_tag.appendChild(Control.Modal.overlay);
body_tag.appendChild(Control.Modal.container);
body_tag.appendChild(Control.Modal.loading);
Control.Modal.container.observe('mouseout',function(event){
if(!Control.Modal.disableHoverClose && Control.Modal.current && Control.Modal.current.options.hover && !Position.within(Control.Modal.container,Event.pointerX(event),Event.pointerY(event)))
Control.Modal.close();
});
}
},
open: function(contents,options){
options = options || {};
if(!options.contents)
options.contents = contents;
var modal_instance = new Control.Modal(false,options);
modal_instance.open();
return modal_instance;
},
close: function(force){
if(typeof(force) != 'boolean')
force = false;
if(Control.Modal.current)
Control.Modal.current.close(force);
},
attachEvents: function(){
document.observe('contentloaded', Control.Modal.load);
},
//attachEvents: function(){
//Event.observe(window,'load',Control.Modal.load);
//Event.observe(window,'unload',Event.unloadCache,false);
//},
center: function(element){
if(!element._absolutized){
element.setStyle({
position: 'absolute'
});
element._absolutized = true;
}
var dimensions = element.getDimensions();
Position.prepare();
var offset_left = (Position.deltaX + Math.floor((Control.Modal.getWindowWidth() - dimensions.width) / 2));
var offset_top = (Position.deltaY + ((Control.Modal.getWindowHeight() > dimensions.height) ? Math.floor((Control.Modal.getWindowHeight() - dimensions.height) / 2) : 0));
element.setStyle({
top: ((dimensions.height <= Control.Modal.getDocumentHeight()) ? ((offset_top != null && offset_top > 0) ? offset_top : '0') + 'px' : 0),
left: ((dimensions.width <= Control.Modal.getDocumentWidth()) ? ((offset_left != null && offset_left > 0) ? offset_left : '0') + 'px' : 0)
});
},
getWindowWidth: function(){
return (self.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0);
},
getWindowHeight: function(){
return (self.innerHeight ||  document.documentElement.clientHeight || document.body.clientHeight || 0);
},
getDocumentWidth: function(){
return Math.min(document.body.scrollWidth,Control.Modal.getWindowWidth());
},
getDocumentHeight: function(){
return Math.max(document.body.scrollHeight,Control.Modal.getWindowHeight());
},
onKeyDown: function(event){
if(event.keyCode == Event.KEY_ESC)
Control.Modal.close();
}
});
Object.extend(Control.Modal.prototype,{
mode: '',
html: false,
href: '',
element: false,
src: false,
imageLoaded: false,
ajaxRequest: false,
initialize: function(element,options){
this.element = $(element);
this.options = {
beforeOpen: Prototype.emptyFunction,
afterOpen: Prototype.emptyFunction,
afterUpdate: Prototype.emptyFunction,
afterEffect: Prototype.emptyFunction,
beforeClose: Prototype.emptyFunction,
afterClose: Prototype.emptyFunction,
onSuccess: Prototype.emptyFunction,
onFailure: Prototype.emptyFunction,
onException: Prototype.emptyFunction,
beforeImageLoad: Prototype.emptyFunction,
afterImageLoad: Prototype.emptyFunction,
autoOpenIfLinked: true,
contents: false,
loading: false, //display loading indicator
fade: false,
fadeDuration: 0.75,
image: false,
imageCloseOnClick: true,
hover: false,
iframe: false,
iframeTemplate: new Template('<iframe src="#{href}" width="100%" height="100%" frameborder="0" id="#{id}"></iframe>'),
evalScripts: true, //for Ajax, define here instead of in requestOptions
requestOptions: {}, //for Ajax.Request
overlayDisplay: true,
overlayClassName: '',
overlayCloseOnClick: true,
containerClassName: '',
opacity: 0.3,
zIndex: 998,
width: null,
height: null,
offsetLeft: 0, //for use with 'relative'
offsetTop: 0, //for use with 'relative'
position: 'absolute' //'absolute' or 'relative'
};
Object.extend(this.options,options || {});
var target_match = false;
var image_match = false;
if(this.element){
target_match = Control.Modal.targetRegexp.exec(this.element.href);
image_match = Control.Modal.imgRegexp.exec(this.element.href);
}
if(this.options.position == 'mouse')
this.options.hover = true;
if(this.options.contents){
this.mode = 'contents';
}else if(this.options.image || image_match){
this.mode = 'image';
this.src = this.element.href;
}else if(target_match){
this.mode = 'named';
var x = $(target_match[1]);
this.html = x.innerHTML;
x.remove();
this.href = target_match[1];
}else{
this.mode = (this.options.iframe) ? 'iframe' : 'ajax';
this.href = this.element.href;
}
if(this.element){
if(this.options.hover){
this.element.observe('mouseover',this.open.bind(this));
this.element.observe('mouseout',function(event){
if(!Position.within(Control.Modal.container,Event.pointerX(event),Event.pointerY(event)))
this.close();
}.bindAsEventListener(this));
}else{
this.element.onclick = function(event){
this.open();
Event.stop(event);
return false;
}.bindAsEventListener(this);
}
}
var targets = Control.Modal.targetRegexp.exec(window.location);
this.position = function(event){
if(this.options.position == 'absolute')
Control.Modal.center(Control.Modal.container);
else{
var xy = (event && this.options.position == 'mouse' ? [Event.pointerX(event),Event.pointerY(event)] : Position.cumulativeOffset(this.element));
Control.Modal.container.setStyle({
position: 'absolute',
top: xy[1] + (typeof(this.options.offsetTop) == 'function' ? this.options.offsetTop() : this.options.offsetTop) + 'px',
left: xy[0] + (typeof(this.options.offsetLeft) == 'function' ? this.options.offsetLeft() : this.options.offsetLeft) + 'px'
});
}
if(Control.Modal.ie){
Control.Modal.overlay.setStyle({
height: Control.Modal.getDocumentHeight() + 'px',
width: Control.Modal.getDocumentWidth() + 'px'
});
}
}.bind(this);
if(this.mode == 'named' && this.options.autoOpenIfLinked && targets && targets[1] && targets[1] == this.href)
this.open();
},
showLoadingIndicator: function(){
if(this.options.loading){
Control.Modal.loadingTimeout = window.setTimeout(function(){
var modal_image = $('modal_image');
if(modal_image)
modal_image.hide();
Control.Modal.loading.style.zIndex = this.options.zIndex + 1;
Control.Modal.loading.update('<img id="modal_loading" src="' + this.options.loading + '"/>');
Control.Modal.loading.show();
Control.Modal.center(Control.Modal.loading);
}.bind(this),250);
}
},
hideLoadingIndicator: function(){
if(this.options.loading){
if(Control.Modal.loadingTimeout)
window.clearTimeout(Control.Modal.loadingTimeout);
var modal_image = $('modal_image');
if(modal_image)
modal_image.show();
Control.Modal.loading.hide();
}
},
open: function(force){
if(!force && this.notify('beforeOpen') === false)
return;
if(!Control.Modal.loaded)
Control.Modal.load();
//Control.Modal.close();
if(!this.options.hover)
Event.observe($(document.getElementsByTagName('body')[0]),'keydown',Control.Modal.onKeyDown);
Control.Modal.current = this;
if(!this.options.hover)
Control.Modal.overlay.setStyle({
zIndex: this.options.zIndex,
opacity: this.options.opacity
});
Control.Modal.container.setStyle({
zIndex: this.options.zIndex + 1,
width: (this.options.width ? (typeof(this.options.width) == 'function' ? this.options.width() : this.options.width) + 'px' : null),
height: (this.options.height ? (typeof(this.options.height) == 'function' ? this.options.height() : this.options.height) + 'px' : null)
});
if(Control.Modal.ie && !this.options.hover){
$A(document.getElementsByTagName('select')).each(function(select){
select.style.visibility = 'hidden';
});
}
Control.Modal.overlay.addClassName(this.options.overlayClassName);
Control.Modal.container.className = '';
Control.Modal.container.addClassName(this.options.containerClassName);
switch(this.mode){
case 'image':
this.imageLoaded = false;
this.notify('beforeImageLoad');
this.showLoadingIndicator();
var img = document.createElement('img');
img.onload = function(img){
this.hideLoadingIndicator();
this.update([img]);
if(this.options.imageCloseOnClick)
$(img).observe('click',Control.Modal.close);
this.position();
this.notify('afterImageLoad');
img.onload = null;
}.bind(this,img);
img.src = this.src;
img.id = 'modal_image';
break;
case 'ajax':
this.notify('beforeLoad');
var options = {
method: 'post',
onSuccess: function(request){
this.hideLoadingIndicator();
this.update(request.responseText);
this.notify('onSuccess',request);
this.ajaxRequest = false;
}.bind(this),
onFailure: function(){
this.notify('onFailure');
}.bind(this),
onException: function(){
this.notify('onException');
}.bind(this)
};
Object.extend(options,this.options.requestOptions);
this.showLoadingIndicator();
this.ajaxRequest = new Ajax.Request(this.href,options);
break;
case 'iframe':
this.update(this.options.iframeTemplate.evaluate({href: this.href, id: 'modal_iframe'}));
break;
case 'contents':
this.update((typeof(this.options.contents) == 'function' ? this.options.contents() : this.options.contents));
break;
case 'named':
this.update(this.html);
break;
}
if(!this.options.hover){
if(this.options.overlayCloseOnClick && this.options.overlayDisplay)
Control.Modal.overlay.observe('click',Control.Modal.close);
if(this.options.overlayDisplay){
if(this.options.fade){
if(Control.Modal.effects.overlayFade)
Control.Modal.effects.overlayFade.cancel();
Control.Modal.effects.overlayAppear = new Effect.Appear(Control.Modal.overlay,{
queue: {
position: 'front',
scope: 'Control.Modal'
},
to: this.options.opacity,
duration: this.options.fadeDuration / 2,
afterFinish: function() {
this.notify('afterEffect');
}.bind(this)
});
}else
Control.Modal.overlay.show();
}
}
if(this.options.position == 'mouse'){
this.mouseHoverListener = this.position.bindAsEventListener(this);
this.element.observe('mousemove',this.mouseHoverListener);
}
this.notify('afterOpen');
},
update: function(html){
if(typeof(html) == 'string')
Control.Modal.container.update(html);
else{
Control.Modal.container.update('');
(html.each) ? html.each(function(node){
Control.Modal.container.appendChild(node);
}) : Control.Modal.container.appendChild(node);
}
/*
if(this.options.fade){
if(Control.Modal.effects.containerFade)
Control.Modal.effects.containerFade.cancel();
Control.Modal.effects.containerAppear = new Effect.Appear(Control.Modal.container,{
queue: {
position: 'end',
scope: 'Control.Modal'
},
to: 1,
duration: this.options.fadeDuration / 2
});
}else
*/
Control.Modal.container.show();
this.position();
Event.observe(window,'resize',this.position,false);
Event.observe(window,'scroll',this.position,false);
this.notify('afterUpdate');
},
close: function(force){
if(!force && this.notify('beforeClose') === false)
return;
if(this.ajaxRequest)
this.ajaxRequest.transport.abort();
this.hideLoadingIndicator();
if(this.mode == 'image'){
var modal_image = $('modal_image');
if(this.options.imageCloseOnClick && modal_image)
modal_image.stopObserving('click',Control.Modal.close);
}
if(Control.Modal.ie && !this.options.hover){
$A(document.getElementsByTagName('select')).each(function(select){
select.style.visibility = 'visible';
});
}
if(!this.options.hover)
Event.stopObserving(window,'keyup',Control.Modal.onKeyDown);
Control.Modal.current = false;
Event.stopObserving(window,'resize',this.position,false);
Event.stopObserving(window,'scroll',this.position,false);
if(!this.options.hover){
if(this.options.overlayCloseOnClick && this.options.overlayDisplay)
Control.Modal.overlay.stopObserving('click',Control.Modal.close);
if(this.options.overlayDisplay){
if(this.options.fade){
if(Control.Modal.effects.overlayAppear)
Control.Modal.effects.overlayAppear.cancel();
Control.Modal.effects.overlayFade = new Effect.Fade(Control.Modal.overlay,{
queue: {
position: 'end',
scope: 'Control.Modal'
},
from: this.options.opacity,
to: 0,
duration: this.options.fadeDuration / 2
});
}else
Control.Modal.overlay.hide();
}
}
if(this.options.fade){
if(Control.Modal.effects.containerAppear)
Control.Modal.effects.containerAppear.cancel();
Control.Modal.effects.containerFade = new Effect.Fade(Control.Modal.container,{
queue: {
position: 'front',
scope: 'Control.Modal'
},
from: 1,
to: 0,
duration: this.options.fadeDuration / 2,
afterFinish: function(){
Control.Modal.container.update('');
this.resetClassNameAndStyles();
}.bind(this)
});
}else{
Control.Modal.container.hide();
Control.Modal.container.update('');
this.resetClassNameAndStyles();
}
if(this.options.position == 'mouse')
this.element.stopObserving('mousemove',this.mouseHoverListener);
this.notify('afterClose');
},
resetClassNameAndStyles: function(){
Control.Modal.overlay.removeClassName(this.options.overlayClassName);
Control.Modal.container.removeClassName(this.options.containerClassName);
Control.Modal.container.setStyle({
height: null,
width: null,
top: null,
left: null
});
},
notify: function(event_name){
try{
if(this.options[event_name])
return [this.options[event_name].apply(this.options[event_name],$A(arguments).slice(1))];
}catch(e){
if(e != $break)
throw e;
else
return false;
}
}
});
if(typeof(Object.Event) != 'undefined')
Object.Event.extend(Control.Modal);
Control.Modal.attachEvents();

/* SWFObject v2.1 <http://code.google.com/p/swfobject/>
Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis
This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject=function(){var b="undefined",Q="object",n="Shockwave Flash",p="ShockwaveFlash.ShockwaveFlash",P="application/x-shockwave-flash",m="SWFObjectExprInst",j=window,K=document,T=navigator,o=[],N=[],i=[],d=[],J,Z=null,M=null,l=null,e=false,A=false;var h=function(){var v=typeof K.getElementById!=b&&typeof K.getElementsByTagName!=b&&typeof K.createElement!=b,AC=[0,0,0],x=null;if(typeof T.plugins!=b&&typeof T.plugins[n]==Q){x=T.plugins[n].description;if(x&&!(typeof T.mimeTypes!=b&&T.mimeTypes[P]&&!T.mimeTypes[P].enabledPlugin)){x=x.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AC[0]=parseInt(x.replace(/^(.*)\..*$/,"$1"),10);AC[1]=parseInt(x.replace(/^.*\.(.*)\s.*$/,"$1"),10);AC[2]=/r/.test(x)?parseInt(x.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof j.ActiveXObject!=b){var y=null,AB=false;try{y=new ActiveXObject(p+".7")}catch(t){try{y=new ActiveXObject(p+".6");AC=[6,0,21];y.AllowScriptAccess="always"}catch(t){if(AC[0]==6){AB=true}}if(!AB){try{y=new ActiveXObject(p)}catch(t){}}}if(!AB&&y){try{x=y.GetVariable("$version");if(x){x=x.split(" ")[1].split(",");AC=[parseInt(x[0],10),parseInt(x[1],10),parseInt(x[2],10)]}}catch(t){}}}}var AD=T.userAgent.toLowerCase(),r=T.platform.toLowerCase(),AA=/webkit/.test(AD)?parseFloat(AD.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,q=false,z=r?/win/.test(r):/win/.test(AD),w=r?/mac/.test(r):/mac/.test(AD);/*@cc_on q=true;@if(@_win32)z=true;@elif(@_mac)w=true;@end@*/return{w3cdom:v,pv:AC,webkit:AA,ie:q,win:z,mac:w}}();var L=function(){if(!h.w3cdom){return }f(H);if(h.ie&&h.win){try{K.write("<script id=__ie_ondomload defer=true src=//:><\/script>");J=C("__ie_ondomload");if(J){I(J,"onreadystatechange",S)}}catch(q){}}if(h.webkit&&typeof K.readyState!=b){Z=setInterval(function(){if(/loaded|complete/.test(K.readyState)){E()}},10)}if(typeof K.addEventListener!=b){K.addEventListener("DOMContentLoaded",E,null)}R(E)}();function S(){if(J.readyState=="complete"){J.parentNode.removeChild(J);E()}}function E(){if(e){return }if(h.ie&&h.win){var v=a("span");try{var u=K.getElementsByTagName("body")[0].appendChild(v);u.parentNode.removeChild(u)}catch(w){return }}e=true;if(Z){clearInterval(Z);Z=null}var q=o.length;for(var r=0;r<q;r++){o[r]()}}function f(q){if(e){q()}else{o[o.length]=q}}function R(r){if(typeof j.addEventListener!=b){j.addEventListener("load",r,false)}else{if(typeof K.addEventListener!=b){K.addEventListener("load",r,false)}else{if(typeof j.attachEvent!=b){I(j,"onload",r)}else{if(typeof j.onload=="function"){var q=j.onload;j.onload=function(){q();r()}}else{j.onload=r}}}}}function H(){var t=N.length;for(var q=0;q<t;q++){var u=N[q].id;if(h.pv[0]>0){var r=C(u);if(r){N[q].width=r.getAttribute("width")?r.getAttribute("width"):"0";N[q].height=r.getAttribute("height")?r.getAttribute("height"):"0";if(c(N[q].swfVersion)){if(h.webkit&&h.webkit<312){Y(r)}W(u,true)}else{if(N[q].expressInstall&&!A&&c("6.0.65")&&(h.win||h.mac)){k(N[q])}else{O(r)}}}}else{W(u,true)}}}function Y(t){var q=t.getElementsByTagName(Q)[0];if(q){var w=a("embed"),y=q.attributes;if(y){var v=y.length;for(var u=0;u<v;u++){if(y[u].nodeName=="DATA"){w.setAttribute("src",y[u].nodeValue)}else{w.setAttribute(y[u].nodeName,y[u].nodeValue)}}}var x=q.childNodes;if(x){var z=x.length;for(var r=0;r<z;r++){if(x[r].nodeType==1&&x[r].nodeName=="PARAM"){w.setAttribute(x[r].getAttribute("name"),x[r].getAttribute("value"))}}}t.parentNode.replaceChild(w,t)}}function k(w){A=true;var u=C(w.id);if(u){if(w.altContentId){var y=C(w.altContentId);if(y){M=y;l=w.altContentId}}else{M=G(u)}if(!(/%$/.test(w.width))&&parseInt(w.width,10)<310){w.width="310"}if(!(/%$/.test(w.height))&&parseInt(w.height,10)<137){w.height="137"}K.title=K.title.slice(0,47)+" - Flash Player Installation";var z=h.ie&&h.win?"ActiveX":"PlugIn",q=K.title,r="MMredirectURL="+j.location+"&MMplayerType="+z+"&MMdoctitle="+q,x=w.id;if(h.ie&&h.win&&u.readyState!=4){var t=a("div");x+="SWFObjectNew";t.setAttribute("id",x);u.parentNode.insertBefore(t,u);u.style.display="none";var v=function(){u.parentNode.removeChild(u)};I(j,"onload",v)}U({data:w.expressInstall,id:m,width:w.width,height:w.height},{flashvars:r},x)}}function O(t){if(h.ie&&h.win&&t.readyState!=4){var r=a("div");t.parentNode.insertBefore(r,t);r.parentNode.replaceChild(G(t),r);t.style.display="none";var q=function(){t.parentNode.removeChild(t)};I(j,"onload",q)}else{t.parentNode.replaceChild(G(t),t)}}function G(v){var u=a("div");if(h.win&&h.ie){u.innerHTML=v.innerHTML}else{var r=v.getElementsByTagName(Q)[0];if(r){var w=r.childNodes;if(w){var q=w.length;for(var t=0;t<q;t++){if(!(w[t].nodeType==1&&w[t].nodeName=="PARAM")&&!(w[t].nodeType==8)){u.appendChild(w[t].cloneNode(true))}}}}}return u}function U(AG,AE,t){var q,v=C(t);if(v){if(typeof AG.id==b){AG.id=t}if(h.ie&&h.win){var AF="";for(var AB in AG){if(AG[AB]!=Object.prototype[AB]){if(AB.toLowerCase()=="data"){AE.movie=AG[AB]}else{if(AB.toLowerCase()=="styleclass"){AF+=' class="'+AG[AB]+'"'}else{if(AB.toLowerCase()!="classid"){AF+=" "+AB+'="'+AG[AB]+'"'}}}}}var AD="";for(var AA in AE){if(AE[AA]!=Object.prototype[AA]){AD+='<param name="'+AA+'" value="'+AE[AA]+'" />'}}v.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+AF+">"+AD+"</object>";i[i.length]=AG.id;q=C(AG.id)}else{if(h.webkit&&h.webkit<312){var AC=a("embed");AC.setAttribute("type",P);for(var z in AG){if(AG[z]!=Object.prototype[z]){if(z.toLowerCase()=="data"){AC.setAttribute("src",AG[z])}else{if(z.toLowerCase()=="styleclass"){AC.setAttribute("class",AG[z])}else{if(z.toLowerCase()!="classid"){AC.setAttribute(z,AG[z])}}}}}for(var y in AE){if(AE[y]!=Object.prototype[y]){if(y.toLowerCase()!="movie"){AC.setAttribute(y,AE[y])}}}v.parentNode.replaceChild(AC,v);q=AC}else{var u=a(Q);u.setAttribute("type",P);for(var x in AG){if(AG[x]!=Object.prototype[x]){if(x.toLowerCase()=="styleclass"){u.setAttribute("class",AG[x])}else{if(x.toLowerCase()!="classid"){u.setAttribute(x,AG[x])}}}}for(var w in AE){if(AE[w]!=Object.prototype[w]&&w.toLowerCase()!="movie"){F(u,w,AE[w])}}v.parentNode.replaceChild(u,v);q=u}}}return q}function F(t,q,r){var u=a("param");u.setAttribute("name",q);u.setAttribute("value",r);t.appendChild(u)}function X(r){var q=C(r);if(q&&(q.nodeName=="OBJECT"||q.nodeName=="EMBED")){if(h.ie&&h.win){if(q.readyState==4){B(r)}else{j.attachEvent("onload",function(){B(r)})}}else{q.parentNode.removeChild(q)}}}function B(t){var r=C(t);if(r){for(var q in r){if(typeof r[q]=="function"){r[q]=null}}r.parentNode.removeChild(r)}}function C(t){var q=null;try{q=K.getElementById(t)}catch(r){}return q}function a(q){return K.createElement(q)}function I(t,q,r){t.attachEvent(q,r);d[d.length]=[t,q,r]}function c(t){var r=h.pv,q=t.split(".");q[0]=parseInt(q[0],10);q[1]=parseInt(q[1],10)||0;q[2]=parseInt(q[2],10)||0;return(r[0]>q[0]||(r[0]==q[0]&&r[1]>q[1])||(r[0]==q[0]&&r[1]==q[1]&&r[2]>=q[2]))?true:false}function V(v,r){if(h.ie&&h.mac){return }var u=K.getElementsByTagName("head")[0],t=a("style");t.setAttribute("type","text/css");t.setAttribute("media","screen");if(!(h.ie&&h.win)&&typeof K.createTextNode!=b){t.appendChild(K.createTextNode(v+" {"+r+"}"))}u.appendChild(t);if(h.ie&&h.win&&typeof K.styleSheets!=b&&K.styleSheets.length>0){var q=K.styleSheets[K.styleSheets.length-1];if(typeof q.addRule==Q){q.addRule(v,r)}}}function W(t,q){var r=q?"visible":"hidden";if(e&&C(t)){C(t).style.visibility=r}else{V("#"+t,"visibility:"+r)}}function g(s){var r=/[\\\"<>\.;]/;var q=r.exec(s)!=null;return q?encodeURIComponent(s):s}var D=function(){if(h.ie&&h.win){window.attachEvent("onunload",function(){var w=d.length;for(var v=0;v<w;v++){d[v][0].detachEvent(d[v][1],d[v][2])}var t=i.length;for(var u=0;u<t;u++){X(i[u])}for(var r in h){h[r]=null}h=null;for(var q in swfobject){swfobject[q]=null}swfobject=null})}}();return{registerObject:function(u,q,t){if(!h.w3cdom||!u||!q){return }var r={};r.id=u;r.swfVersion=q;r.expressInstall=t?t:false;N[N.length]=r;W(u,false)},getObjectById:function(v){var q=null;if(h.w3cdom){var t=C(v);if(t){var u=t.getElementsByTagName(Q)[0];if(!u||(u&&typeof t.SetVariable!=b)){q=t}else{if(typeof u.SetVariable!=b){q=u}}}}return q},embedSWF:function(x,AE,AB,AD,q,w,r,z,AC){if(!h.w3cdom||!x||!AE||!AB||!AD||!q){return }AB+="";AD+="";if(c(q)){W(AE,false);var AA={};if(AC&&typeof AC===Q){for(var v in AC){if(AC[v]!=Object.prototype[v]){AA[v]=AC[v]}}}AA.data=x;AA.width=AB;AA.height=AD;var y={};if(z&&typeof z===Q){for(var u in z){if(z[u]!=Object.prototype[u]){y[u]=z[u]}}}if(r&&typeof r===Q){for(var t in r){if(r[t]!=Object.prototype[t]){if(typeof y.flashvars!=b){y.flashvars+="&"+t+"="+r[t]}else{y.flashvars=t+"="+r[t]}}}}f(function(){U(AA,y,AE);if(AA.id==AE){W(AE,true)}})}else{if(w&&!A&&c("6.0.65")&&(h.win||h.mac)){A=true;W(AE,false);f(function(){var AF={};AF.id=AF.altContentId=AE;AF.width=AB;AF.height=AD;AF.expressInstall=w;k(AF)})}}},getFlashPlayerVersion:function(){return{major:h.pv[0],minor:h.pv[1],release:h.pv[2]}},hasFlashPlayerVersion:c,createSWF:function(t,r,q){if(h.w3cdom){return U(t,r,q)}else{return undefined}},removeSWF:function(q){if(h.w3cdom){X(q)}},createCSS:function(r,q){if(h.w3cdom){V(r,q)}},addDomLoadEvent:f,addLoadEvent:R,getQueryParamValue:function(v){var u=K.location.search||K.location.hash;if(v==null){return g(u)}if(u){var t=u.substring(1).split("&");for(var r=0;r<t.length;r++){if(t[r].substring(0,t[r].indexOf("="))==v){return g(t[r].substring((t[r].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(A&&M){var q=C(m);if(q){q.parentNode.replaceChild(M,q);if(l){W(l,true);if(h.ie&&h.win){M.style.display="block"}}M=null;l=null;A=false}}}}}();

//  Prototip 1.1.0
//  by Nick Stakenburg - http://www.nickstakenburg.com
//  08-11-2007
//
//  More information on this project:
//  http://www.nickstakenburg.com/projects/prototip/
//
//  Licensed under the Creative Commons Attribution 3.0 License
//  http://creativecommons.org/licenses/by/3.0/
//
var Prototip = {
Version: '1.1.0',
REQUIRED_Prototype: '1.6.0',
REQUIRED_Scriptaculous: '1.8.0',
start: function() { this.require('Prototype'); },
require: function(library) {
if ((typeof window[library] == 'undefined') ||
(this.convertVersionString(window[library].Version) < this.convertVersionString(this['REQUIRED_' + library])))
throw('Prototip requires ' + library + ' >= ' + this['REQUIRED_' + library]);
},
// based on Scriptaculous' implementation
convertVersionString: function(versionString) {
var r = versionString.split('.');
return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
},
// fixed viewport.getDimensions. Also excludes scrollbars in firefox. Valid doctype required.
viewport : {
getDimensions: function() {
var dimensions = { };
var B = Prototype.Browser;
$w('width height').each(function(d) {
var D = d.capitalize();
if (B.Opera) dimensions[d] = document.body['client' + D];
else if (B.WebKit) dimensions[d] = self['inner' + D];
else dimensions[d] = document.documentElement['client' + D];
});
return dimensions;
}
}
};
Prototip.start();
var Tips = {
// Configuration
closeButtons: false,
zIndex: 1200,
fixIE: (function(agent){
var version = new RegExp('MSIE ([\\d.]+)').exec(agent);
return version ? (parseFloat(version[1]) <= 6) : false;
})(navigator.userAgent),
tips : [],
visible : [],
add: function(tip) {
this.tips.push(tip);
},
remove: function(element) {
var tip = this.tips.find(function(t){ return t.element == $(element); });
if (tip) {
tip.deactivate();
if (tip.tooltip) {
tip.wrapper.remove();
if (Tips.fixIE) tip.iframeShim.remove();
}
this.tips = this.tips.without(tip);
}
},
zIndexRestore : 1200,
raise: function(tip) {
var highestZ = this.zIndexHighest();
if (!highestZ) {
tip.style.zIndex = this.zIndexRestore;
return;
}
var newZ = (tip.style.zIndex != highestZ) ? highestZ + 1 : highestZ;
this.tips.pluck('wrapper').invoke('removeClassName', 'highest');
tip.setStyle({ zIndex : newZ }).addClassName('highest');
},
zIndexHighest: function() {
var highestZ = this.visible.max(function(v) {
return parseInt(v.style.zIndex);
});
return highestZ;
},
addVisibile: function(tip) {
this.removeVisible(tip);
this.visible.push(tip);
},
removeVisible: function(tip) {
this.visible = this.visible.without(tip);
}
};
var Tip = Class.create({
initialize: function(element, content) {
this.element = $(element);
Tips.remove(this.element);
this.content = content;
var isHooking = (arguments[2] && arguments[2].hook);
var isShowOnClick = (arguments[2] && arguments[2].showOn == 'click');
this.options = Object.extend({
className: 'default',                 // see css, this will lead to .prototip .default
closeButton: Tips.closeButtons,       // true, false
delay: !isShowOnClick ? 0.2 : false,  // seconds before tooltip appears
duration: 0.3,                        // duration of the effect
effect: false,                        // false, 'appear' or 'blind'
hideOn: 'mouseout',
hook: false,                          // { element: topLeft|topRight|bottomLeft|bottomRight, tip: see element }
offset: isHooking ? {x:0, y:0} : {x:16, y:16},
fixed: isHooking ? true : false,      // follow the mouse if false
showOn: 'mousemove',
target: this.element,                 // or another element
title: false,
viewport: isHooking ? false : true    // keep within viewport if mouse is followed
}, arguments[2] || {});
this.target = $(this.options.target);
this.setup();
if (this.options.effect) {
Prototip.require('Scriptaculous');
this.queue = { position: 'end', limit: 1, scope: this.wrapper.identify() }
}
Tips.add(this);
this.activate();
},
setup: function() {
// Everything that needs to be build for observing is done here
this.wrapper = new Element('div', { 'class' : 'prototip' }).setStyle({
display: 'none', zIndex: Tips.zIndex++ });
this.wrapper.identify();
if (Tips.fixIE) {
this.iframeShim = new Element('iframe', { 'class' : 'iframeShim', src: 'javascript:false;' }).setStyle({
display: 'none', zIndex: Tips.zIndexRestore - 1 });
}
this.tip = new Element('div', { 'class' : 'tipcontent' }).update(this.content);
this.tip.insert(new Element('div').setStyle({ clear: 'both' }));
if (this.options.closeButton || (this.options.hideOn.element && this.options.hideOn.element == 'closeButton'))
this.closeButton = new Element('a', { href: 'javascript:;', 'class' : 'close' });
},
build: function() {
if (Tips.fixIE) document.body.appendChild(this.iframeShim).setOpacity(0);
// effects go smooth with extra wrapper
var wrapper = 'wrapper';
if (this.options.effect) {
this.effectWrapper = this.wrapper.appendChild(new Element('div', { 'class' : 'effectWrapper' }));
wrapper = 'effectWrapper';
}
this.tooltip = this[wrapper].appendChild(new Element('div', { 'class' : 'tooltip ' + this.options.className }));
if (this.options.title || this.options.closeButton) {
this.toolbar = this.tooltip.appendChild(new Element('div', { 'class' : 'toolbar' }));
this.title = this.toolbar.appendChild(new Element('div', { 'class' : 'title' }).update(this.options.title || ' '));
}
this.tooltip.insert(this.tip);
document.body.appendChild(this.wrapper);
// fixate elements for better positioning and effects
var fixate = (this.options.effect) ? [this.wrapper, this.effectWrapper]: [this.wrapper];
if (Tips.fixIE) fixate.push(this.iframeShim);
// fix width
var fixedWidth = this.wrapper.getWidth();
fixate.invoke('setStyle', { width: fixedWidth + 'px' });
// make toolbar width fixed
if(this.toolbar) {
this.wrapper.setStyle({ visibility : 'hidden' }).show();
this.toolbar.setStyle({ width: this.toolbar.getWidth() + 'px'});
this.wrapper.hide().setStyle({ visibility : 'visible' });
}
// add close button
if (this.closeButton)
this.title.insert({ top: this.closeButton }).insert(new Element('div').setStyle({ clear: 'both' }));
var fixedHeight = this.wrapper.getHeight();
fixate.invoke('setStyle', { width: fixedWidth + 'px', height: fixedHeight + 'px' });
this[this.options.effect ? wrapper : 'tooltip'].hide();
},
activate: function() {
this.eventShow = this.showDelayed.bindAsEventListener(this);
this.eventHide = this.hide.bindAsEventListener(this);
// if fixed use mouseover instead of mousemove for less event calls
if (this.options.fixed && this.options.showOn == 'mousemove') this.options.showOn = 'mouseover';
if(this.options.showOn == this.options.hideOn) {
this.eventToggle = this.toggle.bindAsEventListener(this);
this.element.observe(this.options.showOn, this.eventToggle);
}
this.hideElement = Object.isUndefined(this.options.hideOn.element) ? 'element' : this.options.hideOn.element;
var hideOptions = {
'element': this.eventToggle ? [] : [this.element],
'target': this.eventToggle ? [] : [this.target],
'tip': this.eventToggle ? [] : [this.wrapper],
'closeButton': [],
'.close' : this.tip.select('.close')
}
this.hideTargets = hideOptions[this.hideElement];
// add show and hide observers
if (this.element && !this.eventToggle) this.element.observe(this.options.showOn, this.eventShow);
this.hideAction = (this.options.hideOn.event || this.options.hideOn);
if (this.hideTargets) this.hideTargets.invoke('observe', this.hideAction, this.eventHide);
// add position observer if not fixed
if (!this.options.fixed && this.options.showOn == 'click') {
this.eventPosition = this.position.bindAsEventListener(this);
this.element.observe('mousemove', this.eventPosition);
}
// add hide observers to close button and non click elements when they are not the close (delay needs this)
if (this.closeButton) this.closeButton.observe('click', this.eventHide);
if (this.options.showOn != 'click' && this.hideElement != 'element') {
this.eventCheckDelay = this.checkDelay.bindAsEventListener(this);
this.element.observe('mouseout', this.eventCheckDelay);
}
// observe wrapper to raise zIndex
this.wrapper.observe('mouseover', function(){ Tips.raise(this.wrapper); }.bind(this));
},
deactivate: function() {
if(this.options.showOn == this.options.hideOn)
this.element.stopObserving(this.options.showOn, this.eventToggle);
else {
this.element.stopObserving(this.options.showOn, this.eventShow);
this.hideTargets.invoke('stopObserving', this.hideAction, this.eventHide);
}
if (this.eventPosition) this.element.stopObserving('mousemove', this.eventPosition);
if (this.closeButton) this.closeButton.stopObserving();
if (this.eventCheckDelay) this.element.stopObserving('mouseout', this.eventCheckDelay);
this.wrapper.stopObserving();
},
showDelayed: function(event){
if (!this.tooltip) this.build();
this.position(event); // follow mouse
if (this.wrapper.visible()) return;
this.checkDelay();
this.timer = this.show.bind(this).delay(this.options.delay);
},
checkDelay: function(){
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
},
show: function(){
if (this.wrapper.visible() && this.options.effect != 'appear') return;
if (Tips.fixIE) this.iframeShim.show();
Tips.addVisibile(this.wrapper);
this.wrapper.show();
if (!this.options.effect) this.tooltip.show();
else {
if (this.activeEffect) Effect.Queues.get(this.queue.scope).remove(this.activeEffect);
this.activeEffect = Effect[Effect.PAIRS[this.options.effect][0]](this.effectWrapper,
{ duration: this.options.duration, queue: this.queue});
}
this.options.onShow();
},
hide: function(){
this.checkDelay();
if(!this.wrapper.visible()) return;
if (!this.options.effect) {
if (Tips.fixIE) this.iframeShim.hide();
this.tooltip.hide();
this.wrapper.hide();
Tips.removeVisible(this.wrapper);
}
else {
if (this.activeEffect) Effect.Queues.get(this.queue.scope).remove(this.activeEffect);
this.activeEffect = Effect[Effect.PAIRS[this.options.effect][1]](this.effectWrapper,
{ duration: this.options.duration, queue: this.queue, afterFinish: function(){
if (Tips.fixIE) this.iframeShim.hide();
this.wrapper.hide();
Tips.removeVisible(this.wrapper);
}.bind(this)});
}
this.options.onHide();
},
toggle: function(event){
if (this.wrapper && this.wrapper.visible()) this.hide(event);
else this.showDelayed(event);
},
position: function(event){
if (!this.wrapper.hasClassName('highest')) Tips.raise(this.wrapper);
var offset = {left: this.options.offset.x, top: this.options.offset.y};
var targetPosition = Position.cumulativeOffset(this.target);
var tipd = this.wrapper.getDimensions();
var pos = { left: (this.options.fixed) ? targetPosition[0] : Event.pointerX(event),
top: (this.options.fixed) ? targetPosition[1] : Event.pointerY(event) };
// add offsets
pos.left += offset.left;
pos.top += offset.top;
if (this.options.hook) {
var dims = {target: this.target.getDimensions(), tip: tipd}
var hooks = {target: Position.cumulativeOffset(this.target), tip: Position.cumulativeOffset(this.target)}
for(var z in hooks) {
switch(this.options.hook[z]){
case 'topRight':
hooks[z][0] += dims[z].width;
break;
case 'topMiddle':
hooks[z][0] += (dims[z].width / 2);
break;
case 'rightMiddle':
hooks[z][0] += dims[z].width;
hooks[z][1] += (dims[z].height / 2);
break;
case 'bottomLeft':
hooks[z][1] += dims[z].height;
break;
case 'bottomRight':
hooks[z][0] += dims[z].width;
hooks[z][1] += dims[z].height;
break;
case 'bottomMiddle':
hooks[z][0] += (dims[z].width / 2);
hooks[z][1] += dims[z].height;
break;
case 'leftMiddle':
hooks[z][1] += (dims[z].height / 2);
break;
}
}
// move based on hooks
pos.left += -1*(hooks.tip[0] - hooks.target[0]);
pos.top += -1*(hooks.tip[1] - hooks.target[1]);
}
// move tooltip when there is a different target
if (!this.options.fixed && this.element !== this.target) {
var elementPosition = Position.cumulativeOffset(this.element);
pos.left += -1*(elementPosition[0] - targetPosition[0]);
pos.top += -1*(elementPosition[1] - targetPosition[1]);
}
if (!this.options.fixed && this.options.viewport) {
var scroll = document.viewport.getScrollOffsets();
var viewport = Prototip.viewport.getDimensions();
var pair = {left: 'width', top: 'height'};
for(var z in pair) {
if ((pos[z] + tipd[pair[z]] - scroll[z]) > viewport[pair[z]])
pos[z] = pos[z] - tipd[pair[z]] - 2*offset[z];
}
}
var finalPosition = { left: pos.left + 'px', top: pos.top + 'px' };
this.wrapper.setStyle(finalPosition);
if (Tips.fixIE) this.iframeShim.setStyle(finalPosition);
}
});

// =========================================================================
//
// xmldom.js - an XML DOM parser in JavaScript.
//
//This is the classic DOM that has shipped with XML for <SCRIPT>
//  since the beginning. For a more standards-compliant DOM, you may
//  wish to use the standards-compliant W3C DOM that is included
//  with XML for <SCRIPT> versions 3.0 and above
//
// version 3.1
//
// =========================================================================
//
// Copyright (C) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//
// Visit the XML for <SCRIPT> home page at http://xmljs.sourceforge.net
//
// =========================================================================
// =========================================================================
// =========================================================================
// CONSTANTS
// =========================================================================
// =========================================================================
// =========================================================================
//define the characters which constitute whitespace, and quotes
var whitespace = "\n\r\t ";
var quotes = "\"'";
// =========================================================================
// =========================================================================
// =========================================================================
// CONVENIENCE FUNCTIONS
// =========================================================================
// =========================================================================
// =========================================================================
function convertEscapes(str) {
/*******************************************************************************************************************
function: convertEscapes
Author: David Joham <djoham@yahoo.com>
Description:
Characters such as less-than signs, greater-than signs and ampersands are
illegal in XML syntax and must be escaped before being inserted into the DOM.
This function is a convience function to take those escaped characters and
return them to their original values for processing outside the parser
This XML Parser automagically converts the content of the XML elements to
their non-escaped values when xmlNode.getText() is called for every element
except CDATA.
EXAMPLES:
&amp; == &
&lt; == <
&gt; == >
*********************************************************************************************************************/
// not all Konqueror installations have regex support for some reason. Here's the original code using regexes
// that is probably a little more efficient if it matters to you
/*
var escAmpRegEx = /&amp;/g;
var escLtRegEx = /&lt;/g;
var escGtRegEx = /&gt;/g;
str = str.replace(escAmpRegEx, "&");
str = str.replace(escLtRegEx, "<");
str = str.replace(escGtRegEx, ">");
*/
var gt;
//&lt;
gt = -1;
while (str.indexOf("&lt;", gt + 1) > -1) {
var gt = str.indexOf("&lt;", gt + 1);
var newStr = str.substr(0, gt);
newStr += "<";
newStr = newStr + str.substr(gt + 4, str.length);
str = newStr;
}
//&gt;
gt = -1;
while (str.indexOf("&gt;", gt + 1) > -1) {
var gt = str.indexOf("&gt;", gt + 1);
var newStr = str.substr(0, gt);
newStr += ">";
newStr = newStr + str.substr(gt + 4, str.length);
str = newStr;
}
//&amp;
gt = -1;
while (str.indexOf("&amp;", gt + 1) > -1) {
var gt = str.indexOf("&amp;", gt + 1);
var newStr = str.substr(0, gt);
newStr += "&";
newStr = newStr + str.substr(gt + 5, str.length);
str = newStr;
}
return str;
} // end function convertEscapes
function convertToEscapes(str) {
/*******************************************************************************************************************
function: convertToEscapes
Author: David Joham djoham@yahoo.com
Description:
Characters such as less-than signs, greater-than signs and ampersands are
illegal in XML syntax. This function is a convience function to escape those
characters out to there legal values.
EXAMPLES:
< == &lt;
> == &gt;
& == &amp;
*********************************************************************************************************************/
// not all Konqueror installations have regex support for some reason. Here's the original code using regexes
// that is probably a little more efficient if it matters to you
/*
var escAmpRegEx = /&/g;
var escLtRegEx = /</g;
var escGtRegEx = />/g;
str = str.replace(escAmpRegEx, "&amp;");
str = str.replace(escLtRegEx, "&lt;");
str = str.replace(escGtRegEx, "&gt;");
*/
// start with &
var gt = -1;
while (str.indexOf("&", gt + 1) > -1) {
gt = str.indexOf("&", gt + 1);
var newStr = str.substr(0, gt);
newStr += "&amp;";
newStr = newStr + str.substr(gt + 1, str.length);
str = newStr;
}
// now <
gt = -1;
while (str.indexOf("<", gt + 1) > -1) {
var gt = str.indexOf("<", gt + 1);
var newStr = str.substr(0, gt);
newStr += "&lt;";
newStr = newStr + str.substr(gt + 1, str.length);
str = newStr;
}
//now >
gt = -1;
while (str.indexOf(">", gt + 1) > -1) {
var gt = str.indexOf(">", gt + 1);
var newStr = str.substr(0, gt);
newStr += "&gt;";
newStr = newStr + str.substr(gt + 1, str.length);
str = newStr;
}
return str;
} // end function convertToEscapes
function _displayElement(domElement, strRet) {
/*******************************************************************************************************************
function:       _displayElement
Author: djoham@yahoo.com
Description:
returns the XML string associated with the DOM element passed in
recursively calls itself if child elements are found
*********************************************************************************************************************/
if(domElement==null) {
return;
}
if(!(domElement.nodeType=='ELEMENT')) {
return;
}
var tagName = domElement.tagName;
var tagInfo = "";
tagInfo = "<" + tagName;
// attributes
var attributeList = domElement.getAttributeNames();
for(var intLoop = 0; intLoop < attributeList.length; intLoop++) {
var attribute = attributeList[intLoop];
tagInfo = tagInfo + " " + attribute + "=";
tagInfo = tagInfo + "\"" + domElement.getAttribute(attribute) + "\"";
}
//close the element name
tagInfo = tagInfo + ">";
strRet=strRet+tagInfo;
// children
if(domElement.children!=null) {
var domElements = domElement.children;
for(var intLoop = 0; intLoop < domElements.length; intLoop++) {
var childNode = domElements[intLoop];
if(childNode.nodeType=='COMMENT') {
strRet = strRet + "<!--" + childNode.content + "-->";
}
else if(childNode.nodeType=='TEXT') {
var cont = trim(childNode.content,true,true);
strRet = strRet + childNode.content;
}
else if (childNode.nodeType=='CDATA') {
var cont = trim(childNode.content,true,true);
strRet = strRet + "<![CDATA[" + cont + "]]>";
}
else {
strRet = _displayElement(childNode, strRet);
}
} // end looping through the DOM elements
} // end checking for domElements.children = null
//ending tag
strRet = strRet + "</" + tagName + ">";
return strRet;
} // end function displayElement
function firstWhiteChar(str,pos) {
/*******************************************************************************************************************
function: firstWhiteChar
Author: may106@psu.edu ?
Description:
return the position of the first whitespace character in str after position pos
*********************************************************************************************************************/
if (isEmpty(str)) {
return -1;
}
while(pos < str.length) {
if (whitespace.indexOf(str.charAt(pos))!=-1) {
return pos;
}
else {
pos++;
}
}
return str.length;
} // end function firstWhiteChar
function isEmpty(str) {
/*******************************************************************************************************************
function: isEmpty
Author: mike@idle.org
Description:
convenience function to identify an empty string
*********************************************************************************************************************/
return (str==null) || (str.length==0);
} // end function isEmpty
function trim(trimString, leftTrim, rightTrim) {
/*******************************************************************************************************************
function: trim
Author: may106@psu.edu
Description:
helper function to trip a string (trimString) of leading (leftTrim)
and trailing (rightTrim) whitespace
*********************************************************************************************************************/
if (isEmpty(trimString)) {
return "";
}
// the general focus here is on minimal method calls - hence only one
// substring is done to complete the trim.
if (leftTrim == null) {
leftTrim = true;
}
if (rightTrim == null) {
rightTrim = true;
}
var left=0;
var right=0;
var i=0;
var k=0;
// modified to properly handle strings that are all whitespace
if (leftTrim == true) {
while ((i<trimString.length) && (whitespace.indexOf(trimString.charAt(i++))!=-1)) {
left++;
}
}
if (rightTrim == true) {
k=trimString.length-1;
while((k>=left) && (whitespace.indexOf(trimString.charAt(k--))!=-1)) {
right++;
}
}
return trimString.substring(left, trimString.length - right);
} // end function trim
// =========================================================================
// =========================================================================
// =========================================================================
// XML DOC FUNCTIONS
// =========================================================================
// =========================================================================
// =========================================================================
function XMLDoc(source, errFn) {
/*******************************************************************************************************************
function:       XMLDoc
Author: mike@idle.org
Description:
a constructor for an XML document
source: the string containing the document
errFn: the (optional) function used to log errors
*********************************************************************************************************************/
// stack for document construction
this.topNode=null;
// set up the properties and methods for this object
this.errFn = errFn;          // user defined error functions
this.createXMLNode = _XMLDoc_createXMLNode;
this.error = _XMLDoc_error;
this.getUnderlyingXMLText = _XMLDoc_getUnderlyingXMLText;
this.handleNode = _XMLDoc_handleNode;
this.hasErrors = false;      // were errors found during the parse?
this.insertNodeAfter =  _XMLDoc_insertNodeAfter;
this.insertNodeInto = _XMLDoc_insertNodeInto;
this.loadXML = _XMLDoc_loadXML;
this.parse = _XMLDoc_parse;
this.parseAttribute = _XMLDoc_parseAttribute;
this.parseDTD = _XMLDoc_parseDTD;
this.parsePI = _XMLDoc_parsePI;
this.parseTag = _XMLDoc_parseTag;
this.removeNodeFromTree = _XMLDoc_removeNodeFromTree;
this.replaceNodeContents = _XMLDoc_replaceNodeContents;
this.selectNode = _XMLDoc_selectNode;
this.selectNodeText = _XMLDoc_selectNodeText;
this.source = source;        // the string source of the document
// parse the document
if (this.parse()) {
// we've run out of markup - check the stack is now empty
if (this.topNode!=null) {
return this.error("expected close " + this.topNode.tagName);
}
else {
return true;
}
}
} // end function XMLDoc
function _XMLDoc_createXMLNode(strXML) {
/*******************************************************************************************************************
function:   _XMLDoc_createXMLNode
Author: djoham@yahoo.com
Description:
convienience function to create a new node that inherits
the properties of the document object
*********************************************************************************************************************/
return new XMLDoc(strXML, this.errFn).docNode;
} // end function _XMLDoc_createXMLNode
function _XMLDoc_error(str) {
/*******************************************************************************************************************
function:       _XMLDoc_error
Author: mike@idle.org
Description:
used to log an error in parsing or validating
*********************************************************************************************************************/
this.hasErrors=true;
if(this.errFn){
this.errFn("ERROR: " + str);
}else if(this.onerror){
this.onerror("ERROR: " + str); 
}
return 0;
} // end function _XMLDoc_error
function _XMLDoc_getTagNameParams(tag,obj){
/*******************************************************************************************************************
function:       _XMLDoc_getTagNameParams
Author: xwisdom@yahoo.com
Description:
convienience function for the nodeSearch routines
*********************************************************************************************************************/
var elm=-1,e,s=tag.indexOf('[');
var attr=[];
if(s>=0){
e=tag.indexOf(']');
if(e>=0)elm=tag.substr(s+1,(e-s)-1);
else obj.error('expected ] near '+tag);
tag=tag.substr(0,s);
if(isNaN(elm) && elm!='*'){
attr=elm.substr(1,elm.length-1); // remove @
attr=attr.split('=');
if(attr[1]) { //remove "
s=attr[1].indexOf('"');
attr[1]=attr[1].substr(s+1,attr[1].length-1);
e=attr[1].indexOf('"');
if(e>=0) attr[1]=attr[1].substr(0,e);
else obj.error('expected " near '+tag)
};elm=-1;
}else if(elm=='*') elm=-1;
}
return [tag,elm,attr[0],attr[1]]
} // end function _XMLDoc_getTagNameParams
function _XMLDoc_getUnderlyingXMLText() {
/*******************************************************************************************************************
function:       _XMLDoc_getUnderlyingXMLText
Author: djoham@yahoo.com
Description:
kicks off the process that returns the XML text representation of the XML
document inclusive of any changes made by the manipulation of the DOM
*********************************************************************************************************************/
var strRet = "";
//for now, hardcode the xml version 1 information. When we handle Processing Instructions later, this
//should be looked at again
strRet = strRet + "<?xml version=\"1.0\"?>";
if (this.docNode==null) {
return;
}
strRet = _displayElement(this.docNode, strRet);
return strRet;
} // end function _XMLDoc_getCurrentXMLText
function _XMLDoc_handleNode(current) {
/*******************************************************************************************************************
function:   _XMLDoc_handleNode
Author: mike@idle.org
Description:
adds a markup element to the document
*********************************************************************************************************************/
if ((current.nodeType=='COMMENT') && (this.topNode!=null)) {
return this.topNode.addElement(current);
}
else if ((current.nodeType=='TEXT') ||  (current.nodeType=='CDATA')) {
// if the current node is a text node:
// if the stack is empty, and this text node isn't just whitespace, we have
// a problem (we're not in a document element)
if(this.topNode==null) {
if (trim(current.content,true,false)=="") {
return true;
}
else {
return this.error("expected document node, found: " + current);
}
}
else {
// otherwise, append this as child to the element at the top of the stack
return this.topNode.addElement(current);
}
}
else if ((current.nodeType=='OPEN') || (current.nodeType=='SINGLE')) {
// if we find an element tag (open or empty)
var success = false;
// if the stack is empty, this node becomes the document node
if(this.topNode==null) {
this.docNode = current;
current.parent = null;
success = true;
}
else {
// otherwise, append this as child to the element at the top of the stack
success = this.topNode.addElement(current);
}
if (success && (current.nodeType!='SINGLE')) {
this.topNode = current;
}
// rename it as an element node
current.nodeType = "ELEMENT";
return success;
}
// if it's a close tag, check the nesting
else if (current.nodeType=='CLOSE') {
// if the stack is empty, it's certainly an error
if (this.topNode==null) {
return this.error("close tag without open: " +  current.toString());
}
else {
// otherwise, check that this node matches the one on the top of the stack
if (current.tagName!=this.topNode.tagName) {
return this.error("expected closing " + this.topNode.tagName + ", found closing " + current.tagName);
}
else {
// if it does, pop the element off the top of the stack
this.topNode = this.topNode.getParent();
}
}
}
return true;
} // end function _XMLDoc_handleNode
function _XMLDoc_insertNodeAfter (referenceNode, newNode) {
/*******************************************************************************************************************
function:   _XMLDoc_insertNodeAfter
Author: djoham@yahoo.com
Description:
inserts a new XML node after the reference node;
for example, if we insert the node <tag2>hello</tag2>
after tag1 in the xml <rootnode><tag1></tag1></rootnode>
we will end up with <rootnode><tag1></tag1><tag2>hello</tag2></rootnode>
NOTE: the return value of this function is a new XMLDoc object!!!!
*********************************************************************************************************************/
var parentXMLText = this.getUnderlyingXMLText();
var selectedNodeXMLText = referenceNode.getUnderlyingXMLText();
var originalNodePos = parentXMLText.indexOf(selectedNodeXMLText) + selectedNodeXMLText.length;
var newXML = parentXMLText.substr(0,originalNodePos);
newXML += newNode.getUnderlyingXMLText();
newXML += parentXMLText.substr(originalNodePos);
var newDoc = new XMLDoc(newXML, this.errFn);
return newDoc;
} // end function _XMLDoc_insertNodeAfter
function _XMLDoc_insertNodeInto (referenceNode, insertNode) {
/*******************************************************************************************************************
function:   _XMLDoc_insertNodeInto
Author: mike@idle.org
Description:
inserts a new XML node into the reference node;
for example, if we insert the node <tag2>hello</tag2>
into tag1 in the xml <rootnode><tag1><tag3>foo</tag3></tag1></rootnode>
we will end up with <rootnode><tag1><tag2>hello</tag2><tag3>foo</tag3></tag1></rootnode>
NOTE: the return value of this function is a new XMLDoc object!!!!
*********************************************************************************************************************/
var parentXMLText = this.getUnderlyingXMLText();
var selectedNodeXMLText = referenceNode.getUnderlyingXMLText();
var endFirstTag = selectedNodeXMLText.indexOf(">") + 1;
var originalNodePos = parentXMLText.indexOf(selectedNodeXMLText) + endFirstTag;
var newXML = parentXMLText.substr(0,originalNodePos);
newXML += insertNode.getUnderlyingXMLText();
newXML += parentXMLText.substr(originalNodePos);
var newDoc = new XMLDoc(newXML, this.errFn);
return newDoc;
} // end function _XMLDoc_insertNodeInto
function _XMLDoc_loadXML(source){
/*******************************************************************************************************************
function:   _XMLDoc_insertNodeInto
Author: xwisdom@yahoo.com
Description:
allows an already existing XMLDoc object to load XML
*********************************************************************************************************************/
this.topNode=null;
this.hasErrors = false;
this.source=source;
// parse the document
return this.parse();
} // end function _XMLDoc_loadXML
function _XMLDoc_parse() {
/*******************************************************************************************************************
function:   _XMLDoc_parse
Author: mike@idle.org
Description:
scans through the source for opening and closing tags
checks that the tags open and close in a sensible order
*********************************************************************************************************************/
var pos = 0;
// set up the arrays used to store positions of < and > characters
err = false;
while(!err) {
var closing_tag_prefix = '';
var chpos = this.source.indexOf('<',pos);
var open_length = 1;
var open;
var close;
if (chpos ==-1) {
break;
}
open = chpos;
// create a text node
var str = this.source.substring(pos, open);
if (str.length!=0) {
err = !this.handleNode(new XMLNode('TEXT',this, str));
}
// handle Programming Instructions - they can't reliably be handled as tags
if (chpos == this.source.indexOf("<?",pos)) {
pos = this.parsePI(this.source, pos + 2);
if (pos==0) {
err=true;
}
continue;
}
// nobble the document type definition
if (chpos == this.source.indexOf("<!DOCTYPE",pos)) {
pos = this.parseDTD(this.source, chpos+ 9);
if (pos==0) {
err=true;
}
continue;
}
// if we found an open comment, we need to ignore angle brackets
// until we find a close comment
if(chpos == this.source.indexOf('<!--',pos)) {
open_length = 4;
closing_tag_prefix = '--';
}
// similarly, if we find an open CDATA, we need to ignore all angle
// brackets until a close CDATA sequence is found
if (chpos == this.source.indexOf('<![CDATA[',pos)) {
open_length = 9;
closing_tag_prefix = ']]';
}
// look for the closing sequence
chpos = this.source.indexOf(closing_tag_prefix + '>',chpos);
if (chpos ==-1) {
return this.error("expected closing tag sequence: " + closing_tag_prefix + '>');
}
close = chpos + closing_tag_prefix.length;
// create a tag node
str = this.source.substring(open+1, close);
var n = this.parseTag(str);
if (n) {
err = !this.handleNode(n);
}
pos = close +1;
// and loop
}
return !err;
} // end function _XMLDoc_parse
function _XMLDoc_parseAttribute(src,pos,node) {
/*******************************************************************************************************************
function:   _XMLDoc_parseAttribute
Author: mike@idle.org
Description:
parse an attribute out of a tag string
*********************************************************************************************************************/
// chew up the whitespace, if any
while ((pos<src.length) && (whitespace.indexOf(src.charAt(pos))!=-1)) {
pos++;
}
// if there's nothing else, we have no (more) attributes - just break out
if (pos >= src.length) {
return pos;
}
var p1 = pos;
while ((pos < src.length) && (src.charAt(pos)!='=')) {
pos++;
}
var msg = "attributes must have values";
// parameters without values aren't allowed.
if(pos >= src.length) {
return this.error(msg);
}
// extract the parameter name
var paramname = trim(src.substring(p1,pos++),false,true);
// chew up whitespace
while ((pos < src.length) && (whitespace.indexOf(src.charAt(pos))!=-1)) {
pos++;
}
// throw an error if we've run out of string
if (pos >= src.length) {
return this.error(msg);
}
msg = "attribute values must be in quotes";
// check for a quote mark to identify the beginning of the attribute value
var quote = src.charAt(pos++);
// throw an error if we didn't find one
if (quotes.indexOf(quote)==-1) {
return this.error(msg);
}
p1 = pos;
while ((pos < src.length) && (src.charAt(pos)!=quote)) {
pos++;
}
// throw an error if we found no closing quote
if (pos >= src.length) {
return this.error(msg);
}
// store the parameter
if (!node.addAttribute(paramname,trim(src.substring(p1,pos++),false,true))) {
return 0;
}
return pos;
}  //end function _XMLDoc_parseAttribute
function _XMLDoc_parseDTD(str,pos) {
/*******************************************************************************************************************
function:   _XMLDoc_parseDTD
Author: mike@idle.org
Description:
parse a document type declaration
NOTE: we're just going to discard the DTD
*********************************************************************************************************************/
// we're just going to discard the DTD
var firstClose = str.indexOf('>',pos);
if (firstClose==-1) {
return this.error("error in DTD: expected '>'");
}
var closing_tag_prefix = '';
var firstOpenSquare = str.indexOf('[',pos);
if ((firstOpenSquare!=-1) && (firstOpenSquare < firstClose)) {
closing_tag_prefix = ']';
}
while(true) {
var closepos = str.indexOf(closing_tag_prefix + '>',pos);
if (closepos ==-1) {
return this.error("expected closing tag sequence: " + closing_tag_prefix + '>');
}
pos = closepos + closing_tag_prefix.length +1;
if (str.substring(closepos-1,closepos+2) != ']]>') {
break;
}
}
return pos;
} // end function _XMLDoc_ParseDTD
function _XMLDoc_parsePI(str,pos) {
/*******************************************************************************************************************
function:   _XMLDoc_parsePI
Author: mike@idle.org
Description:
parse a processing instruction
NOTE: we just swallow them up at the moment
*********************************************************************************************************************/
// we just swallow them up
var closepos = str.indexOf('?>',pos);
return closepos + 2;
} // end function _XMLDoc_parsePI
function _XMLDoc_parseTag(src) {
/*******************************************************************************************************************
function:   _XMLDoc_parseTag
Author: mike@idle.org
Description:
parse out a non-text element (incl. CDATA, comments)
handles the parsing of attributes
*********************************************************************************************************************/
// if it's a comment, strip off the packaging, mark it a comment node
// and return it
if (src.indexOf('!--')==0) {
return new XMLNode('COMMENT', this, src.substring(3,src.length-2));
}
// if it's CDATA, do similar
if (src.indexOf('![CDATA[')==0) {
return new XMLNode('CDATA', this, src.substring(8,src.length-2));
}
var n = new XMLNode();
n.doc = this;
if (src.charAt(0)=='/') {
n.nodeType = 'CLOSE';
src = src.substring(1);
}
else {
// otherwise it's an open tag (possibly an empty element)
n.nodeType = 'OPEN';
}
// if the last character is a /, check it's not a CLOSE tag
if (src.charAt(src.length-1)=='/') {
if (n.nodeType=='CLOSE') {
return this.error("singleton close tag");
}
else {
n.nodeType = 'SINGLE';
}
// strip off the last character
src = src.substring(0,src.length-1);
}
// set up the properties as appropriate
if (n.nodeType!='CLOSE') {
n.attributes = new Array();
}
if (n.nodeType=='OPEN') {
n.children = new Array();
}
// trim the whitespace off the remaining content
src = trim(src,true,true);
// chuck out an error if there's nothing left
if (src.length==0) {
return this.error("empty tag");
}
// scan forward until a space...
var endOfName = firstWhiteChar(src,0);
// if there is no space, this is just a name (e.g. (<tag>, <tag/> or </tag>
if (endOfName==-1) {
n.tagName = src;
return n;
}
// otherwise, we should expect attributes - but store the tag name first
n.tagName = src.substring(0,endOfName);
// start from after the tag name
var pos = endOfName;
// now we loop:
while(pos< src.length) {
pos = this.parseAttribute(src, pos, n);
if (this.pos==0) {
return null;
}
// and loop
}
return n;
} // end function _XMLDoc_parseTag
function _XMLDoc_removeNodeFromTree(node) {
/*******************************************************************************************************************
function:   _XMLDoc_removeNodeFromTree
Author: djoham@yahoo.com
Description:
removes the specified node from the tree
NOTE: the return value of this function is a new XMLDoc object
*********************************************************************************************************************/
var parentXMLText = this.getUnderlyingXMLText();
var selectedNodeXMLText = node.getUnderlyingXMLText();
var originalNodePos = parentXMLText.indexOf(selectedNodeXMLText);
var newXML = parentXMLText.substr(0,originalNodePos);
newXML += parentXMLText.substr(originalNodePos + selectedNodeXMLText.length);
var newDoc = new XMLDoc(newXML, this.errFn);
return newDoc;
} // end function _XMLDoc_removeNodeFromTree
function _XMLDoc_replaceNodeContents(referenceNode, newContents) {
/*******************************************************************************************************************
function:   _XMLDoc_replaceNodeContents
Author: djoham@yahoo.com
Description:
make a node object out of the newContents text
coming in ----
The "X" node will be thrown away and only the children
used to replace the contents of the reference node
NOTE: the return value of this function is a new XMLDoc object
*********************************************************************************************************************/
var newNode = this.createXMLNode("<X>" + newContents + "</X>");
referenceNode.children = newNode.children;
return this;
} // end function _XMLDoc_replaceNodeContents
function _XMLDoc_selectNode(tagpath){
/*******************************************************************************************************************
function:_XMLDoc_selectNode
Author:xwisdom@yahoo.com
Description:
selects a single node using the nodes tag path.
examples: /node1/node2  or  /taga/tag1[0]/tag2
*********************************************************************************************************************/
tagpath = trim(tagpath, true, true);
var srcnode,node,tag,params,elm,rg;
var tags,attrName,attrValue,ok;
srcnode=node=((this.source)?this.docNode:this);
if (!tagpath) return node;
if(tagpath.indexOf('/')==0)tagpath=tagpath.substr(1);
tagpath=tagpath.replace(tag,'');
tags=tagpath.split('/');
tag=tags[0];
if(tag){
if(tagpath.indexOf('/')==0)tagpath=tagpath.substr(1);
tagpath=tagpath.replace(tag,'');
params=_XMLDoc_getTagNameParams(tag,this);
tag=params[0];elm=params[1];
attrName=params[2];attrValue=params[3];
node=(tag=='*')? node.getElements():node.getElements(tag);
if (node.length) {
if(elm<0){
srcnode=node;var i=0;
while(i<srcnode.length){
if(attrName){
if (srcnode[i].getAttribute(attrName)!=attrValue) ok=false;
else ok=true;
}else ok=true;
if(ok){
node=srcnode[i].selectNode(tagpath);
if(node) return node;
}
i++;
}
}else if (elm<node.length){
node=node[elm].selectNode(tagpath);
if(node) return node;
}
}
}
} // end function _XMLDoc_selectNode
function _XMLDoc_selectNodeText(tagpath){
/*******************************************************************************************************************
function:_XMLDoc_selectNodeText
Author:xwisdom@yahoo.com
Description:
selects a single node using the nodes tag path and then returns the node text.
*********************************************************************************************************************/
var node=this.selectNode(tagpath);
if (node != null) {
return node.getText();
}
else {
return null;
}
} // end function _XMLDoc_selectNodeText
// =========================================================================
// =========================================================================
// =========================================================================
// XML NODE FUNCTIONS
// =========================================================================
// =========================================================================
// =========================================================================
function XMLNode(nodeType,doc, str) {
/*******************************************************************************************************************
function: xmlNode
Author: mike@idle.org
Description:
XMLNode() is a constructor for a node of XML (text, comment, cdata, tag, etc)
nodeType = indicates the node type of the node
doc == contains a reference to the XMLDoc object describing the document
str == contains the text for the tag or text entity
*********************************************************************************************************************/
// the content of text (also CDATA and COMMENT) nodes
if (nodeType=='TEXT' || nodeType=='CDATA' || nodeType=='COMMENT' ) {
this.content = str;
}
else {
this.content = null;
}
this.attributes = null; // an array of attributes (used as a hash table)
this.children = null;   // an array (list) of the children of this node
this.doc = doc;         // a reference to the document
this.nodeType = nodeType;          // the type of the node
this.parent = "";
this.tagName = "";           // the name of the tag (if a tag node)
// configure the methods
this.addAttribute = _XMLNode_addAttribute;
this.addElement = _XMLNode_addElement;
this.getAttribute = _XMLNode_getAttribute;
this.getAttributeNames = _XMLNode_getAttributeNames;
this.getElementById = _XMLNode_getElementById;
this.getElements = _XMLNode_getElements;
this.getText = _XMLNode_getText;
this.getParent = _XMLNode_getParent;
this.getUnderlyingXMLText = _XMLNode_getUnderlyingXMLText;
this.removeAttribute = _XMLNode_removeAttribute;
this.selectNode = _XMLDoc_selectNode;
this.selectNodeText = _XMLDoc_selectNodeText;
this.toString = _XMLNode_toString;
}  // end function XMLNode
function _XMLNode_addAttribute(attributeName,attributeValue) {
/*******************************************************************************************************************
function: _XMLNode_addAttribute
Author: mike@idle.org
Description:
add an attribute to a node
*********************************************************************************************************************/
//if the name is found, the old value is overwritten by the new value
this.attributes['_' + attributeName] = attributeValue;
return true;
} // end function _XMLNode_addAttribute
function _XMLNode_addElement(node) {
/*******************************************************************************************************************
function: _XMLNode_addElement
Author: mike@idle.org
Description:
add an element child to a node
*********************************************************************************************************************/
node.parent = this;
this.children[this.children.length] = node;
return true;
} // end function _XMLNode_addElement
function _XMLNode_getAttribute(name) {
/*******************************************************************************************************************
function: _XMLNode_getAttribute
Author: mike@idle.org
Description:
get the value of a named attribute from an element node
NOTE: we prefix with "_" because of the weird 'length' meta-property
*********************************************************************************************************************/
if (this.attributes == null) {
return null;
}
return this.attributes['_' + name];
} // end function _XMLNode_getAttribute
function _XMLNode_getAttributeNames() {
/*******************************************************************************************************************
function: _XMLNode_getAttributeNames
Author: mike@idle.org
Description:
get a list of attribute names for the node
NOTE: we prefix with "_" because of the weird 'length' meta-property
NOTE: Version 1.0 of getAttributeNames breaks backwards compatibility. Previous to 1.0
getAttributeNames would return null if there were no attributes. 1.0 now returns an
array of length 0.
*********************************************************************************************************************/
if (this.attributes == null) {
var ret = new Array();
return ret;
}
var attlist = new Array();
for (var a in this.attributes) {
attlist[attlist.length] = a.substring(1);
}
return attlist;
} // end function _XMLNode_getAttributeNames
function _XMLNode_getElementById(id) {
/***********************************************************************************
Function: getElementById
Author: djoham@yahoo.com
Description:
Brute force searches through the XML DOM tree
to find the node with the unique ID passed in
************************************************************************************/
var node = this;
var ret;
//alert("tag name=" + node.tagName);
//alert("id=" + node.getAttribute("id"));
if (node.getAttribute("id") == id) {
return node;
}
else{
var elements = node.getElements();
//alert("length=" + rugrats.length);
var intLoop = 0;
//do NOT use a for loop here. For some reason
//it kills some browsers!!!
while (intLoop < elements.length) {
//alert("intLoop=" + intLoop);
var element = elements[intLoop];
//alert("recursion");
ret = element.getElementById(id);
if (ret != null) {
//alert("breaking");
break;
}
intLoop++;
}
}
return ret;
} // end function _XMLNode_getElementById
function _XMLNode_getElements(byName) {
/*******************************************************************************************************************
function:   _XMLNode_getElements
Author: mike@idle.org
Description:
get an array of element children of a node
with an optional filter by name
NOTE: Version 1.0 of getElements breaks backwards compatibility. Previous to 1.0
getElements would return null if there were no attributes. 1.0 now returns an
array of length 0.
*********************************************************************************************************************/
if (this.children==null) {
var ret = new Array();
return ret;
}
var elements = new Array();
for (var i=0; i<this.children.length; i++) {
if ((this.children[i].nodeType=='ELEMENT') && ((byName==null) || (this.children[i].tagName == byName))) {
elements[elements.length] = this.children[i];
}
}
return elements;
} // end function _XMLNode_getElements
function _XMLNode_getText() {
/*******************************************************************************************************************
function:       _XMLNode_getText
Author: mike@idle.org
Description:
a method to get the text of a given node (recursively, if it's an element)
*********************************************************************************************************************/
if (this.nodeType=='ELEMENT') {
if (this.children==null) {
return null;
}
var str = "";
for (var i=0; i < this.children.length; i++) {
var t = this.children[i].getText();
str +=  (t == null ? "" : t);
}
return str;
}
else if (this.nodeType=='TEXT') {
return convertEscapes(this.content);
}
else {
return this.content;
}
} // end function _XMLNode_getText
function _XMLNode_getParent() {
/*******************************************************************************************************************
function:       _XMLNode_getParent
Author: mike@idle.org
Description:
get the parent of this node
*********************************************************************************************************************/
return this.parent;
} // end function _XMLNode_getParent
function _XMLNode_getUnderlyingXMLText() {
/*******************************************************************************************************************
function:       David Joham
Author: djoham@yahoo.com
Description:
returns the underlying XML text for the node
by calling the _displayElement function
*********************************************************************************************************************/
var strRet = "";
strRet = _displayElement(this, strRet);
return strRet;
} // end function _XMLNode_getUnderlyingXMLText
function _XMLNode_removeAttribute(attributeName) {
/*******************************************************************************************************************
function:       _XMLNode_removeAttribute
Author: djoham@yahoo.com
Description:
remove an attribute from a node
*********************************************************************************************************************/
if(attributeName == null) {
return this.doc.error("You must pass an attribute name into the removeAttribute function");
}
//now remove the attribute from the list.
// I want to keep the logic for adding attribtues in one place. I'm
// going to get a temp array of attributes and values here and then
// use the addAttribute function to re-add the attributes
var attributes = this.getAttributeNames();
var intCount = attributes.length;
var tmpAttributeValues = new Array();
for ( intLoop = 0; intLoop < intCount; intLoop++) {
tmpAttributeValues[intLoop] = this.getAttribute(attributes[intLoop]);
}
// now blow away the old attribute list
this.attributes = new Array();
//now add the attributes back to the array - leaving out the one we're removing
for (intLoop = 0; intLoop < intCount; intLoop++) {
if ( attributes[intLoop] != attributeName) {
this.addAttribute(attributes[intLoop], tmpAttributeValues[intLoop]);
}
}
return true;
} // end function _XMLNode_removeAttribute
function _XMLNode_toString() {
/*******************************************************************************************************************
function:       _XMLNode_toString
Author: mike@idle.org
Description:
produces a diagnostic string description of a node
*********************************************************************************************************************/
return "" + this.nodeType + ":" + (this.nodeType=='TEXT' || this.nodeType=='CDATA' || this.nodeType=='COMMENT' ? this.content : this.tagName);
} // end function _XMLNode_toString



























if (!window.MvcSkel) { var MvcSkel = new Object(); }
Object.extend(MvcSkel, {
getVar: function(name) {
return ( $(name)) ?  $(name).value : false;
}
});
ErrorManager = Class.create({
initialize: function(obj) {
if (!window.__errors) { __errors = new Object(); };
if (!window.__errors_count) { __errors_count = 0 };
this.errWin = null;
if (typeof(obj) != 'undefined') {
obj.errors.each(function(el) {
el.msg.each(function(msg) {
this.raise(el.label, msg);
}.bind(this));
}.bind(this));
}
},
raise: function(label, description) {
if (typeof __errors[label] != 'object') {
__errors[label] = new Array();
}
__errors[label].push(description);
__errors_count++;
},
clear: function() {
__errors = new Object();
__errors_count = 0;
},
haveErrors: function() {
return __errors_count ? true : false;  
},
getErrorsAlert: function() {
if (!__errors_count) { return false; };
var content = i18n('errorsTitle') + ':' + "\n";
for(err in __errors) {
f = true;
__errors[err].each(function(e) {
if (f) {
content += "\n" + err + ':' + "\n";
f = false;
}
content += "\t" + '- ' + e + "\n";
});
}
alert(content);
this.clear();
return true;
},
getHtmlContent: function() {
var content = '<div class="header">'
content += '<h2>' + i18n('errorsTitle') + '</h2>';
content += '<div class="close"><a id="errorsClose" href="javascript:void(0);">' + i18n('errorsClose') + '</a></div>'
content += '</div>';
content += '<table class="errors" cellpadding="0" cellspacing="0" border="0">';
content += '<tbody>';
for(err in __errors) {
f = true;
__errors[err].each(function(e) {
content += '<tr>';
if (f) {
content += '<td class="label" rowspan="' + __errors[err].size() + '">' + err + ':</td>';
f = false;
}
content += '<td class="description">- ' + e + '</td>';
content += '</tr>';
});
content += '<tr><td class="space" colspan="2"></td></tr>';
}
content += '</tbody>';
content += '</table>';
return content;
},
getErrors: function() {
if (!__errors_count) { return false; };
this.errWin = new Control.Modal(false, {
contents: this.getHtmlContent(),
fade: true, fadeDuration: 0.5, opacity: 0.8,
width: 450,
containerClassName: 'mfmodal',
afterOpen: function() {
Event.observe('errorsClose', 'click', function() {
this.errWin.close();
}.bind(this));
}.bind(this)
});
this.errWin.open();
this.clear();
return true;
},
checkEmail: function(email) {
var regexp = new RegExp('^[\\w\\-\\.]+@[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z0-9\\-]+$');
if (!regexp.test(email)) {
this.raise(i18n('errorEmail'), i18n('errorWrongEmail'));
}
}
});
GameAutocomplete = Class.create({
initialize: function() {
if (!$('autocompleteGames')) return false;
this.limit = 15;
var url = new UrlConstructor('search/autocomplete');
url.addVar('limit', this.limit);
var options = {
script:url.construct() + '?',
varname:'input',
json:true, noresults: i18n('autocompleteNoResults'),
shownoresults:false, maxresults:this.limit, minchars: 3,
setWidth:true, minWidth:270, maxWidth:350,
afterShow: this.pngFix.bind(this),
callback: this.redirect.bind(this)
};
var json = new AutoComplete('autocompleteGames', options);
},
pngFix: function(div) {
if (typeof(DD_belatedPNG) != 'object') return;
['.ac_corner', '.ac_bar', '.ac_footer', '.ac_header'].each(function(val) { DD_belatedPNG.fix(val); });
},
redirect: function(obj) {
var s = new UrlSeoConstructor('GAME_PRESENTATION');
s.addSeoVar('game_name', obj.value.replace(/\s+/ig, '-'));
s.addSeoVar('game_id', obj.id);
s.addSeoVar('game_category_id', 0);
window.location = s.construct();
}
});
GameAutocompleteEffect = Class.create({
initialize: function() {
var form = $('searchForm');
if (!form) return;
var autoInput = form.down('input');
autoInput.observe('click', function() {
if (!autoInput.hasClassName('firstEnterText')) return;
autoInput.removeClassName('firstEnterText');
autoInput.value = '';
}.bind(this));
autoInput.observe('blur', function() {
if (autoInput.value == '') {
autoInput.addClassName('firstEnterText');
autoInput.value = i18n('search_box_default_text');
}
}.bind(this));
}
});
UrlConstructor = Class.create();
UrlConstructor.prototype = {
initialize: function(url) {
this.vars = {};
this.url = url;
this.action = false;
},
setAction: function(isAction) {
this.action = isAction;
},
addVar: function(name, value) {
var cmd = "var v = {" + name + ": '" + value + "'};";
eval(cmd);
Object.extend(this.vars, v);
},
construct: function() {
var url = MvcSkel.getVar('rootUrl');
if (this.action) {
url += "actionController/";
}
url += (this.url) ? this.url : '';
for(i in this.vars) {
url += '/' + i + '/' + encodeURIComponent(this.vars[i]);
}
return url;
}
}
UrlSeoConstructor = Class.create({
initialize: function(view) {
this.vars = new Hash();
this.seo = new Hash();
this.view = view;
},
addVar: function(name, value) {
this.vars.set(name, value);
},
addSeoVar: function(name, value) {
this.seo.set(name, value);
},
construct: function() {
var url = seourl.get(this.view);
this.seo.each(function(pair) {
var re = new RegExp("%" + pair.key + "%", "ig");
url = url.replace(re, pair.value);
});
if (query = this.vars.toQueryString()) { url += '?' + query; }
return url;
}
});
ImageUploader = Class.create();
ImageUploader.prototype = {
initialize: function() {
this.options = {
baseClass: 'uploader',
baseProgress: 'progress',
action: 'change'
};
this.form = null;
this.timer = null;
this.initAction();
},
initAction: function() {
$$('input.' + this.options.baseClass).each(function(uploaderInput) {
Event.observe(uploaderInput, this.options.action, function(event) {
clearTimeout(this.timer);
this.timer = this.activateUpload.bind(this).delay(5, Event.element(event));
}.bind(this));
}.bind(this));
},
activateUpload: function(activeInput) {
var suffix = activeInput.up('form').id.match(/-(.*)$/)[1];
this.form = $(this.options.baseClass + '-' + suffix);
this.hideForm(suffix);
this.form.submit();
},
hideForm: function(suffix) {
this.form.hide();
var prg = $(this.options.baseProgress + '-' + suffix);
if (!prg) {
this.form.up().appendChild(new Element('span', {
'class': 'progressBar',
id: this.options.baseProgress + '-' + suffix
}).update('progress'));
} else {
prg.show();
}
},
showForm: function(suffix) {
$(this.options.baseProgress + '-' + suffix).hide();
this.form.show();
this.form.down('.uploader').value = '';
},
uploadComplete: function(suffix, file, url) {
//        alert(suffix + '-' + file + '-' + url);
$(suffix + '-uploadedimg').src = url;
this.showForm(suffix);
this.form.down('.save').writeAttribute('value', file);
},
uploadError: function() {
alert("Error");
}
}
WaitWindow = Class.create({
initialize: function() {
if (typeof(ww) != 'undefined' && ww) return ;
this.def = {
overlayCloseOnClick: false,
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
containerClassName: 'ajaxLoader'
}
this.modal = null;
this.opened = false;
},
show: function(afterOpen) {
var waitContent = new Element('div', {id: 'boxProcess'});
var opt = { 
contents: [waitContent],
afterEffect: function() { if (Object.isFunction(afterOpen)) afterOpen(); },
afterOpen: function() { this.opened = true; }.bind(this)
};
this.modal = new Control.Modal(false, Object.extend(this.def, opt));
this.modal.open();
},
hide: function() {
if (this.opened) this.modal.close();
}
});
InitSWFobjects = Class.create({
initialize: function() {
this.swf = new Array();
this.swfobjects = new Array();
$$('input.swf').each(function(swfInput, idx) {
this.initObject(idx, swfInput.name, swfInput.value);
}.bind(this));
this.createObjects();
},
initObject: function(idx, prefix, target) {
this.swf[idx] = {
target: target,
object: null,
id: null,
x: 0,
y: 0,
version: 0,
variables: {},
param: {}
};
$$('input.' + prefix).each(function(el) {
if (/param/.test(el.className)) {
eval('var obj = {' + el.name + ' : \'' + el.value + '\'}');
Object.extend(this.swf[idx].param, obj);
} else if (/variable/.test(el.className)) {
eval('var obj = {' + el.name + ' : \'' + el.value + '\'}');
Object.extend(this.swf[idx].variables, obj);
} else {
switch (el.name) {
case 'swfobject': this.swf[idx].object = el.value; break;
case 'swfid': this.swf[idx].id = el.value; break;
case 'swfx': this.swf[idx].x = el.value; break;
case 'swfy': this.swf[idx].y = el.value; break;
case 'swfversion': this.swf[idx].version = el.value; break;
}
}
}.bind(this));
},
createObjects: function() {
this.swf.each(function(swfobj, idx) {
var params = {};
for (i in swfobj.param) {
eval('var tmp = {' + i + ': \'' + swfobj.param[i] + '\'};');
Object.extend(params, tmp);
}
var flashvars = {};
for (k in swfobj.variables) {
eval('var tmp = {' + k + ': \'' + swfobj.variables[k] + '\'};');
Object.extend(flashvars, tmp);
}
var attributes = {
id: swfobj.id
};
swfobject.embedSWF(
swfobj.object,
swfobj.target,
swfobj.x,
swfobj.y,
swfobj.version,
"expressInstall.swf",
flashvars,
params,
attributes
);
}.bind(this));
}
});
CategoryDropDownList = Class.create({
initialize: function(id) {
this.button = $(id);
if (!this.button) return false;
this.list = $(this.button.className.match(/list-(\w+)\s?/)[1]);
if (!this.list) return false;
this.myEffects = {
updown: 0.4
};
this.delay = 2000;
this.timer = null;
this.open = false;
this.initAction();
},
initAction: function() {
this.button.observe('click', this.openList.bind(this));
this.button.observe('mouseover', this.clearTimer.bind(this));
this.button.observe('mouseout', this.setTimer.bind(this));
this.list.observe('mouseover', this.clearTimer.bind(this));
this.list.observe('mouseout', this.setTimer.bind(this));
},
openList: function() {
if (this.open) {
this.closeList();
} else {
this.open = true;
new Effect.BlindDown(this.list, {duration: this.myEffects.updown, transition: Effect.Transitions.sinoidal});
}
},
closeList: function() {
new Effect.BlindUp(this.list, {duration: this.myEffects.updown, transition: Effect.Transitions.sinoidal});
this.open = false;
},
clearTimer: function() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
},
setTimer: function() {
if (!this.timer && this.open) {
this.timer = setTimeout(function() {
this.closeList();
this.timer = null;
}.bind(this), this.delay);
}
}
});
MFTooltip = Class.create({
initialize: function() {
$$('.tooltip').each(this.initTip.bind(this));
},
getContent: function(content) {
var el = new Element('div');
var elSpan = new Element('span', {'class': 'tip'}).update(content);
var elDiv = new Element('div', {'class': 'pointer'}).update('pointer');
el.appendChild(elSpan);
el.appendChild(elDiv);
return el;
},
initTip: function(imgTip) {
if (!imgTip.title.blank()) {
var cName = 'righttip';
var offsetX = 0;
if (/lefttip/.test(imgTip.className)) {
cName = 'lefttip';
offsetX = -220;
}
var txt = imgTip.title.strip();
imgTip.title = '';
new Tip(
imgTip,
this.getContent(txt),
{
className: cName,
effect: 'appear',
duration: 0.2,
delay: 0.2,
offset: {x: 14 + offsetX, y: -10},
onShow: function () {},
onHide: function () {}
}
);
}
}
});
SimpleDebug = Class.create({
initialize: function() {
this.level = 10;
this.history = new Hash();
this.saveInHistory = true;
},
onLoadInit: function(level) {
this.saveInHistory = false;
this.level = level ? level : 0;
if (this.level == 0) {
_Debug = false;
}
this.win = this.create();
if (this.level != 0) {
this.history.each(function(pair){
this.add(pair.value.m, pair.value.c);
}.bind(this));
}
this.docScroll();
},
create: function() {
var main = new Element('div', {id: 'debugWrapper'}).setStyle({
position: 'absolute', bottom: '0', right: '0', height: '100px', width: '800px', 
textAlign: 'left', backgroundColor: '#FFFFFF', zIndex: 1000
}).hide();
var content = new Element('div', {id: 'debugOut'}).setStyle({
overflow: 'auto', height: '80px', marginTop: '5px'
});
var h = new Element('div').update('Debug window');
var close = new Element('a').observe('click', this.closeDebug.bind(this)).update('[X]');
close.setStyle({'float': 'right', cursor: 'pointer'});
var clear = new Element('a').observe('click', this.clearDebug.bind(this)).update('[CLEAR]');
clear.setStyle({'float': 'right', cursor: 'pointer'});
var copy  = new Element('a').observe('click', this.copyDebug.bind(this)).update('[COPY (FF only)]');
copy.setStyle({'float': 'right', cursor: 'pointer'});
main.insert(close);
main.insert(clear);
main.insert(copy);
main.insert(h);
main.insert(content);
$(document.body).insert(main);
Event.observe(window, 'scroll', this.docScroll.bind(this), false);
return main;
},
docScroll: function() {
this.win.setStyle({bottom : -this.win.cumulativeScrollOffset().top + 'px'});
},
closeDebug: function() {
this.win.hide();
},
clearDebug: function() {
$('debugOut').update();
},
copyDebug: function() {
var text = '';
$('debugOut').select('div').each(function(logLine) {
text += logLine.innerHTML + "\n";
});
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].getService();
if (clipboard) clipboard = clipboard.QueryInterface(Components.interfaces.nsIClipboard);
var transferable = Components.classes["@mozilla.org/widget/transferable;1"].createInstance();
if (transferable) transferable = transferable.QueryInterface(Components.interfaces.nsITransferable);
transferable.addDataFlavor("text/unicode");
var textObj = new Object();
var textObj = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
if (textObj) {
textObj.data = text;
transferable.setTransferData("text/unicode", textObj, text.length*2);
var clipid=Components.interfaces.nsIClipboard;
clipboard.setData(transferable,null,clipid.kGlobalClipboard);
}       
},
decode: function(msg) {
if (msg.inspect) {
//msg = msg.inspect();
} else if (msg.message && msg.name) {
msg = msg.name.toString() + ': ' + msg.message.toString();
} else if (msg.toString) {
msg = msg.toString();
}
return msg.escapeHTML();
},
stop: function(msg) {
if (this.level >= 4) {
this.add(msg, 'green');
alert('Log: ' + msg);
} else if (this.level == -1) this.console(msg);
},
debug: function(msg) {
if (this.level >= 3) this.add(msg, 'black');
else if (this.level == -1) this.console(msg);
},
info: function(msg) {
if (this.level >= 2) this.add(msg, 'gray');
else if (this.level == -1) this.console(msg);
},
err: function(msg) {
if (this.level >= 1) this.add(msg, 'red');
else if (this.level == -1) this.console(msg);
},
add: function(msg, color) {
if (this.saveInHistory) {
this.history.set(this.history.size() + 1, {m: msg, c: color});
} else {
this.win.show();
var cd = new Date();
var line = new Element('div').setStyle({color: color});
line.insert(new Element('b').update(
formatNumber(cd.getDate()) + '/' + formatNumber(cd.getMonth() + 1) + '/' + cd.getFullYear() + ' ' +
formatNumber(cd.getHours()) + ':' + formatNumber(cd.getMinutes()) + ':' + formatNumber(cd.getSeconds()) +
':&nbsp;'));
line.insert(this.decode(msg))
this.win.down('div', 1).insert(line);
}
},
console: function(msg) {
try {
if (typeof(console.log) == 'function') console.log(msg);
} catch(e) { }
}
});
ModalErrorWindow = Class.create({
initialize: function(errorCode) {
this.modal = new Control.Modal(false, {
contents: function() {
var showError = new UrlConstructor('error/by_code');
showError.addVar('code', errorCode);
new Ajax.Request(showError.construct(), {
onComplete: function(request) {
this.modal.update(request.responseText);
$('modalClose').observe('click', this.close.bind(this));
}.bind(this)
});
return i18n('loadingDialog');
}.bind(this),
afterClose: this.afterClose.bind(this),
beforeOpen: this.beforeOpen.bind(this),
fade: true, fadeDuration: 0.4, opacity: 0.8, width: 500, containerClassName: 'mfmodal'
});
},
open: function() {
this.modal.open();        
},
close: function() {
this.modal.close();
},
afterClose: function() {
if ($('browserPlayer')) $('browserPlayer').show();
},
beforeOpen: function() {
if ($('browserPlayer')) $('browserPlayer').hide();
}
});
SubmitFormByLink = Class.create({
initialize: function() {
this.defaultClass = 'submitFormByLink';
$$('.submitFormByLink').each(function(el) {
el.observe('click', function() {
var form = el.up('form');
if (!form.down('.search_text').hasClassName('firstEnterText')) form.submit();
else {
var url = new UrlSeoConstructor('CATEGORY_INDEX_ALL');
window.location = url.construct();
}  
});    
});
}
});
function i18n(key) {
return lang.get(key);
}
function getLocale() {
var lang = null;
try {
if (Prototype.Browser.IE) {
lang = $$('meta[httpEquiv="Content-Language"]').first().content.toLowerCase();
} else {
lang = $$('meta[http-equiv="Content-Language"]').first().content.toLowerCase();
}
} catch (e) {
lang = 'fr';
}
var path = '';
if (lang == 'en') {
path = '/en';
} else if (lang == 'de') {
path = '/de';
} else if (lang == 'it') {
path = '/it';
} else if (lang == 'sv') {
path = '/sv';
}
return {lang: lang, path: path};
}
function getPortalId() {
if (portal = $('portalId')) {
return portal.getValue();
}
return false;
}
function formatNumber(num) {
return (num < 10) ? '0' + num : num;
}

/** 
* Author and Version Information {{{
* author: Antonio Ramirez http://webeaters.blogspot.com
*
* class: AutoComplete for Prototype 1.6.0
*
* version: 1.2.1 - 2007-11-11 
* (based on AutoSuggest 2.1.3 - 2007-07-19)
* version: 1.3.0 - 2008-01-03 by Andrew Nicols <andrew@nicols.co.uk>
*  - Fixed incorrect title-casing - CSS is Case Sensitive!!!
*  - Adjusted the way in which the Notifier images are loaded.
*  - Changed json code to pass all json variables back instead of just id, value and name
*  - Fixed 'GMAIL' code such that if valueSep is undefined, it is ignored
*  - Changed the default for valueSep to null
*  - Fixed the resetTimeout function
*
* REFERENCES AND THANKS 
* this class is based on the work in AutoSuggest.js of
* Timothy Groves - http://www.brandspankingnew.net
* and adapted for use with prototype 1.6.0
*
* UPDATED by R��da HADJOUTI
* GMAIL like AutoComplete (semicolon separator) Update
*
}}}*/
var AutoComplete = Class.create();
AutoComplete.prototype = { // {{{
Version: '1.3.0',
REQUIRED_PROTOTYPE: '1.6.0',
initialize: function (id, param) { // {{{
// check whether we have the appropiate javascript libraries
this.PROTOTYPE_CHECK();
// Get the field we're watching.
// It needs to be a valid field so throw an error if it's not valid or can't be found.
this.fld = $(id);
if (!this.fld)
{
throw("AutoComplete requires a field id to initialize");
}
// Init variables
this.sInp = ""; // input value 
this.nInpC = 0;// input value length
this.aSug = []; // suggestions array 
this.iHigh = 0;// level of list selection 
// Parameter Handling {{{
// Set the use specified options
this.options = param ? param : {};
// These are the default settings {{{
var k, def = {
valueSep:null,
minchars:1,
meth:"get",
varname:"input",
className:"autocomplete",
timeout:8000,
delay:500,
offsety:-5,
shownoresults: true,
noresults: "No results were found.",
maxheight: 250,
cache: true,
maxentries: 25,
onAjaxError:null,
setWidth: false,
minWidth: 100,
maxWidth: 200,
useNotifier: true
};
//}}}
// Overlay any values which weren't user specified.
for (k in def) 
{
if (typeof(this.options[k]) != typeof(def[k]))
this.options[k] = def[k];
}
// End of Parameter Handling }}}
// Not everyone wants to use the Notifier. Give them the option
if (this.options.useNotifier)
{
this.fld.addClassName('ac_field');
}
// set keyup handler for field
// and prevent AutoComplete from client
var p = this;
// NOTE: not using addEventListener because UpArrow fired twice in Safari
this.fld.onkeypress = function(ev){ return p.onKeyPress(ev); };
this.fld.onkeyup      = function(ev){ return p.onKeyUp(ev); };
// ARN-DEBUG Chances are we want to reset the timeout when they lose focus, at least that's what I prefer
this.fld.onblur  = function(ev){ p.resetTimeout(); return true; };
// ARN-DEBUG Not sure what this is about!
this.fld.setAttribute("AutoComplete","off");
}, //}}}
convertVersionString: function (versionString){ // {{{
var r = versionString.split('.');
return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
}, // }}}
PROTOTYPE_CHECK: function() { // {{{
if((typeof Prototype=='undefined') || 
(typeof Element == 'undefined') || 
(typeof Element.Methods=='undefined') ||
(this.convertVersionString(Prototype.Version) < 
this.convertVersionString(this.REQUIRED_PROTOTYPE)))
throw("AutoComplete requires the Prototype JavaScript framework >= " +
this.REQUIRED_PROTOTYPE);
}, // }}}
// set responses to keypress events in the field
// this allows the user to use the arrow keys to scroll through the results
// ESCAPE clears the list
// RETURN sets the current highlighted value
// UP/DOWN move around the list
onKeyPress: function (e) { // {{{
if (!e) e = window.event;
var key= e.keyCode || e.wich;
switch(key)
{
case Event.KEY_RETURN:
if (this.iHigh > 0) {
this.setHighlightedValue();
Event.stop(e);
}
break;
case Event.KEY_TAB:
this.setHighlightedValue();
//Event.stop(e);
break;
case Event.KEY_ESC:
this.clearSuggestions();
break;
}
return true;
}, //}}}
onKeyUp: function (e) { // {{{
if (!e) e = window.event;
var key = e.keyCode || e.wich;
if (key == Event.KEY_UP || key == Event.KEY_DOWN) 
{
this.changeHighlight(key);
Event.stop(e);
}
else this.getSuggestions(this.fld.value);
return true;
}, //}}}
getSuggestions: function(val) { // {{{
// input the same? do nothing
if(val==this.sInp) return false;
// kill the old list
if($(this.acID)) $(this.acID).remove();
this.sInp = val;
// input length is less than the min required to trigger a request
// do nothing
if (val.length < this.options.minchars)
{
this.aSug = [];
this.nInpC= val.length; 
return false;
}
// Here we will detect if there is a comma and the splitted value has a value to check
// comma stars a new search and val is converted to the new value after the comma
var ol= this.nInpC; // old length
this.nInpC= val.length ? val.length : 0;
// if caching enabled, and we didn't receive the maxentries value
// and user is typing (ie. length of input is increasing)
// filter results out of suggestions from last request
var l = this.aSug.length;
if( this.options.cache && ( this.nInpC > ol ) && l && ( l < this.options.maxentries ) )
{
var arr = new Array();
for (var i=0;i<l;i++) {
if (this.aSug[i].value.toLowerCase().indexOf(val.toLowerCase()) != -1)
{
arr.push(this.aSug[i]);
}
}
this.aSug = arr;
// recreate the list
this.createList(this.aSug);
} else {
// do new request
var p = this;
//var input= this.sInp; // send the converted new value (comma)
clearTimeout(this.ajID); // ajax id timer
this.ajID = setTimeout( function () {p.doAjaxRequest(p.sInp)}, this.options.delay);
}
document.helper = this;
return false;
}, // }}}
getLastInput : function(str) { // {{{
var ret = str;
if (undefined != this.options.valueSep) {
var idx = ret.lastIndexOf(this.options.valueSep);
ret = idx == -1 ? ret : ret.substring(idx + 1, ret.length);
}
return ret;
}, // }}}
doAjaxRequest: function (input) { // {{{
// we have to check here if there is a new splitted value (, or ;)
// always check against the last part of the comma and then check
// saved input is still the value of the field
if (input != this.fld.value) 
return false;
// Gmail like : get only the last user's input
this.sInp = this.getLastInput(this.sInp);
// create ajax request
// do we need to call a function to recreate the url?
if (typeof this.options.script == 'function')
var url = this.options.script(encodeURIComponent(this.sInp));
else
var url = this.options.script+this.options.varname+'='+encodeURIComponent(this.sInp);
if(!url) return false;
var p = this;
var m = this.options.meth;  // get or post?
var options = {
method: m,
onSuccess: function (req) { // {{{
if( p.options.useNotifier )
{
p.fld.removeClassName('ac_field_busy');
p.fld.addClassName('ac_field');
};
p.setSuggestions(req,input);
}, // }}}
onFailure: (typeof p.options.onAjaxError == 'function')? function (status) { // {{{
if (p.options.useNotifier)
{
p.fld.removeClassName('ac_field_busy');
p.fld.addClassName('ac_field');
}
p.options.onAjaxError(status)
} : // }}}
function (status) { // {{{
if (p.options.useNotifier)
{
p.fld.removeClassName('ac_field_busy');
p.fld.addClassName('ac_field');
}
alert("AJAX error: "+status); 
} // }}}
}
// make new ajax request
new Ajax.Request(url, options);
}, // }}}
setSuggestions: function (req, input) { // {{{
// if field input no longer matches what was passed to the request
// don't show the suggestions
// here we need to check against the splitted values if any (, or ;)
if (input != this.fld.value)
return false;
this.aSug = [];
if(this.options.json) 
{ // response in json format?
var jsondata = eval('(' + req.responseText + ')');
this.aSug = jsondata.results;
} else {
// response in xml format?
var results = req.responseXML.getElementsByTagName('results')[0].childNodes;
for(var i=0;i<results.length;i++)
{
if(results[i].hasChildNodes())
this.aSug.push(  { 'id':results[i].getAttribute('id'), 'value':results[i].childNodes[0].nodeValue, 'info':results[i].getAttribute('info') }  );
}
}
this.acID = 'ac_'+this.fld.id;
this.createList(this.aSug);
}, // }}}
createDOMElement: function ( type, attr, cont, html ) { // {{{
var ne = document.createElement( type );
if (!ne)
return 0;
for (var a in attr)
ne[a] = attr[a];
var t = typeof(cont);
if (t == "string" && !html)
ne.appendChild( document.createTextNode(cont) );
else if (t == "string" && html)
ne.innerHTML = cont;
else if (t == "object")
ne.appendChild( cont );
return ne;
}, // }}}
createList:function(arr) { // {{{
// get rid of the old list if any  
if($(this.acID)) $(this.acID).remove();
// clear list removal timeout
this.killTimeout();
// if no results, and showNoResults is false, do nothing
if (arr.length == 0 && !this.options.shownoresults) return false;
// create holding div
var div= this.createDOMElement('div', {id:this.acID, className:this.options.className});
// create div header
var hcorner = this.createDOMElement('div', {className: 'ac_corner'});
var hbar= this.createDOMElement('div', {className: 'ac_bar'});
var header= this.createDOMElement('div', {className: 'ac_header'});
header.appendChild(hcorner);
header.appendChild(hbar);
div.appendChild(header);
// create and populate ul
var ul= this.createDOMElement('ul', {id:'ac_ul'});
var p = this; // pointer that we will need later on
// no results?
if (arr.length == 0 && this.options.shownoresults)
{
var li = this.createDOMElement('li', {className: 'ac_warning'}, this.options.noresults );
ul.appendChild(li);
} else {
// loop through arr of suggestions creating an LI element for each of them
for (var i=0,l = arr.length; i<l; i++)
{
// format output with the input enclosed in a EM elementFromPoint
// (as HTML not DOM)
var val = arr[i].value;
var st = val.toLowerCase().indexOf(this.sInp.toLowerCase()); // HERE WE CHECK AGAINST THE SPLITTED VALUE IF ANY***
var output = val.substring(0,st) + '<em>' + val.substring(st,st+this.sInp.length) + '</em>' + val.substring(st+this.sInp.length);
var span= this.createDOMElement('span',{},output,true); // type of, properties, output, isHTML?
if(arr[i].info != '') // do we need to add extra info?
{
var br= this.createDOMElement('br',{});
span.appendChild(br);
var small = this.createDOMElement('small',{}, arr[i].info);
span.appendChild(small);
}
var a = this.createDOMElement('a',{href:'#'});
var tl= this.createDOMElement('span',{className:'tl'},'&nbsp;',true);
var tr= this.createDOMElement('span',{className:'tr'},'&nbsp;',true);
a.appendChild(tl);
a.appendChild(tr);
a.appendChild(span); // add the object span into the link
a.name = i+1;
a.onclick = function () { // {{{
p.setHighlightedValue();
return false; 
}; // }}}
a.onmouseover= function () { // {{{
p.setHighlight(this.name); 
}; // }}} 
if (l-i == 1) {
var li = this.createDOMElement('li', {className:'last'}, a);
} else {
var li = this.createDOMElement('li', {}, a); // add the link element to a li element
}
// finally add the newly created li element to the ul element 
ul.appendChild(li);
}
}
div.appendChild(ul); // add the newly created list to the div element
// create div footer
var fcorner = this.createDOMElement('div', {className: 'ac_corner'});
var fbar= this.createDOMElement('div', {className: 'ac_bar'});
var footer= this.createDOMElement('div', {className: 'ac_footer'});
footer.appendChild(fcorner);
footer.appendChild(fbar);
div.appendChild(footer);
// get position of target textfield
// position holding div below it
// set width of holding div to width of field 
// if 
var pos         = this.fld.cumulativeOffset();
div.style.left = pos[0] + "px";
div.style.top = pos[1] + this.fld.offsetHeight + "px";
var w = 
(
this.options.setWidth && this.fld.offsetWidth < this.options.minWidth
)
? this.options.minWidth : 
(
this.options.setWidth && this.fld.offsetWidth > this.options.maxWidth
)
? this.options.maxWidth : 
this.fld.offsetWidth;
div.style.width = w + "px";
// set mouseover functions for div
// when mouse pointer leaves div, set a timeout to remove the list after an interval
// when mouse enters div, kill the timeout so the list won't be removed
//
div.onmouseover = function(){ p.killTimeout() };
div.onmouseout = function(){ p.resetTimeout() };
// add DIV to document
document.getElementsByTagName("body")[0].appendChild(div);
this.options.afterShow(div);
// highlight first item
this.iHigh = 0;
//this.setHighlight(1);
// remove list after interval
this.toID= setTimeout(
function () {
p.clearSuggestions() 
}, this.options.timeout
);
if( this.options.useNotifier )
{
this.fld.removeClassName('ac_field');
this.fld.addClassName('ac_field_busy');
};
}, // }}}
changeHighlight:function(key) { // {{{
var list = $("ac_ul");
if (!list)
return false;
var n;
n = (key == Event.KEY_DOWN || key == Event.KEY_TAB)? this.iHigh + 1 : this.iHigh - 1; // false assumed to be Event.KEY_UP
n = (n > list.childNodes.length)? list.childNodes.length : ((n < 1)? 1 : n);
this.setHighlight(n);
}, // }}}
setHighlight:function(n) { // {{{
var list = $('ac_ul');
if (!list) return false;
if (this.iHigh > 0) this.clearHighlight();
this.iHigh = Number(n);
list.childNodes[this.iHigh-1].className = 'ac_highlight';
this.killTimeout();
}, // }}}
clearHighlight:function() { // {{{
var list = $('ac_ul');
if(!list) return false;
if(this.iHigh > 0)
{
list.childNodes[this.iHigh-1].className = '';
this.iHigh = 0;
}
}, // }}}
setHighlightedValue:function() { // {{{
if (this.iHigh)
{
// HERE WE NEED TO IMPLEMENT THE GMAIL LIKE SPLITTED VALUE
if (!this.aSug[this.iHigh - 1]) return;
// Gmail like
if (undefined != this.options.valueSep) {
var str = this.getLastInput(this.fld.value);
var idx = this.fld.value.lastIndexOf(str);
str = this.aSug[ this.iHigh -1 ].value + this.options.valueSep;
this.sInp = this.fld.value = idx == -1 ? str : this.fld.value.substring(0, idx) + str;
} else {
var str = this.getLastInput(this.fld.value);
var idx = this.fld.value.lastIndexOf(str);
str = this.aSug[ this.iHigh -1 ].value;
this.sInp = this.fld.value = idx == -1 ? str : this.fld.value.substring(0, idx) + str;
}
// move cursor to end of input (safari)
this.fld.focus();
if(this.fld.selectionStart)
this.fld.setSelectionRange(this.sInp.length, this.sInp.length);
this.clearSuggestions();
// pass selected object to callback function, if exists
if (typeof this.options.callback == 'function')
this.options.callback(this.aSug[this.iHigh-1]); // the object has the properties we want, it will depend of
}
}, // }}}
killTimeout:function() { // {{{
clearTimeout(this.toID);
}, // }}}
resetTimeout:function() { // {{{
this.killTimeout();
var p = this;
this.toID = setTimeout(
function () { 
p.clearSuggestions();
}, p.options.timeout
);
// ARN-DEBUG Added p.options.timeout back :|
}, // }}}
clearSuggestions:function () { // {{{
this.killTimeout();
if ($(this.acID))
{
this.fadeOut(300,function () {
$(this.acID).remove();
} );
}
}, // }}}
fadeOut:function (milliseconds, callback) { // {{{
this._fadeFrom = 1;
this._fadeTo= 0;
this._afterUpdateInternal = callback;
this._fadeDuration= milliseconds;
this._fadeInterval = 50;
this._fadeTime = 0;
var p = this;
this._fadeIntervalID = setInterval(
function() {
p._changeOpacity()
}, this._fadeInterval
);
}, // }}}
_changeOpacity: function() { // {{{
if (!$(this.acID))
{
this._fadeIntervalID=clearInterval(this._fadeIntervalID);
return;
} 
this._fadeTime += this._fadeInterval;
var ieop = Math.round( (this._fadeFrom + ((this._fadeTo - this._fadeFrom) * (this._fadeTime/this._fadeDuration))) * 100)
var op = ieop / 100;
var el = $(this.acID);
if (el.filters) // internet explorer
{
try {
el.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieop;
} catch (e) { 
// If it is not set initially, the browser will throw an error.
// This will set it if it is not set yet.
el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+ieop+')';
}
} else{
el.style.opacity = op;
}
if (this._fadeTime >= this._fadeDuration)
{
clearInterval( this._fadeIntervalID );
if (typeof this._afterUpdateInternal == 'function')
this._afterUpdateInternal();
}
} // }}}
} // }}}
// vim: set filetype=javascript foldmethod=marker foldlevel=5:


var __meta = false;
CategoryPage = Class.create({
initialize: function() {
this.fmSwitch = $('fmGameType');
if (this.fmSwitch) this.switchTypeGame();
this.sortingPngFix();
},
sortingPngFix: function() {
if (typeof(DD_belatedPNG) != 'object' || !$('sortByParam')) return;
$('sortByParam').select('img').each(function(el) { 
if (/\.png$/i.test(el.src)) DD_belatedPNG.fixPng(el);
});
},
switchTypeGame: function() {
this.fmSwitch.select('input').each(function(el) {
el.observe('click', this.fmAction.bind(this));
}.bind(this));
},
fmAction: function(event) {
this.fmSwitch.submit();
return false;
}
});
ControlGamePage = Class.create({
initialize: function() {
this.window_control = $('controlsDrawerContainer');
this.window_mygames = $('drawerMyBrowserGameContainer');
this.window_livescores = $('drawerLiveLeaderboardContainer');
this.btn_controlgame = $('buttonControlsGame');
this.btn_mybrowsergames = $('buttonMyBrowserGames');
this.btn_livescores = $('buttonLiveLeaderboard');
this.mainContainer = $('containerProduct');
this.valueLeftBorder = '49px';
this.valueRightBorder = '272px';
this.initControllers();
this.initBrowserGamesList();
if ((!this.btn_controlgame) && (!this.btn_mybrowsergames) && (!this.btn_livescores)) return;
if (!(this.mainContainer).hasClassName('displayNone')) this.hideElementSelectIE6(this.mainContainer);
if (this.btn_controlgame) {
this.btn_controlgame.observe('click', function(event) {
this.addEventOnButton(Event.element(event), this.window_control);
}.bind(this));
}
if (this.btn_mybrowsergames) {
this.btn_mybrowsergames.observe('click', function(event) {
this.addEventOnButton(Event.element(event), this.window_mygames);
}.bind(this));
}
if (this.btn_livescores) {
this.btn_livescores.observe('click', function(event) {
this.addEventOnButton(Event.element(event), this.window_livescores);
}.bind(this));
}
},
addEventOnButton: function(el, window) {
if (window) {
if (this.isAlreadyShowedWindow(window)) {
this.hideWindow(window, true);
el.removeClassName('btnPressed');
}
else {
if ((window != this.window_control) && (this.window_control)) {
if (this.isAlreadyShowedWindow(this.window_control)) {
this.hideWindow(this.window_control, false);
this.btn_controlgame.down('span').removeClassName('btnPressed');
}
}
if ((window) != (this.window_mygames) && (this.window_mygames)) {
if (this.isAlreadyShowedWindow(this.window_mygames)) {
this.hideWindow(this.window_mygames, false);
this.btn_mybrowsergames.down('span').removeClassName('btnPressed');
}
}
if ((window) != (this.window_livescores) && (this.window_livescores)) {
if (this.isAlreadyShowedWindow(this.window_livescores)) {
this.hideWindow(this.window_livescores, false);
this.btn_livescores.down('span').removeClassName('btnPressed');
}
} 
this.showWindow(el, window);
}
}
},
showWindow: function(el, window) {
if (this.isBrowserIE6()) {
this.hideElementSelectIE6(this.mainContainer);
}
window.removeClassName('displayNone');
el.addClassName('btnPressed');
new Effect.Morph(window, {
style: { left: this.valueLeftBorder },
duration: 0.8,
afterFinish: function(){
if (this.isBrowserIE6()) {
this.hideElementSelectIE6(this.mainContainer);
}
if (window.down('div.scrollerDrawerWindow')) {
window.select('.scrollerDrawerWindow').each(function(el) {el.addClassName('utilScrollerDrawer')});
window.select('.scrollerDrawerWindow').each(function(el) {el.removeClassName('displayNone')});
}
}.bind(this)
});    
},
hideWindow: function(window, needShow) {
if (window.down('div.scrollerDrawerWindow')) {
window.select('.scrollerDrawerWindow').each(function(el) {el.removeClassName('utilScrollerDrawer')});
window.select('.scrollerDrawerWindow').each(function(el) {el.addClassName('displayNone')});
}
new Effect.Morph(window, {
style: { left: this.valueRightBorder },
duration: 0.6,
afterFinish: function(){
if (this.isBrowserIE6() && needShow) {
this.showElementSelectIE6(this.mainContainer);
}
window.addClassName('displayNone');
}.bind(this)
});
},
isAlreadyShowedWindow: function(window) {
if (!window.hasClassName('displayNone')) {
return true;
}
else {
return false;
}
},
isBrowserIE6: function() {
Prototype.Browser.IEVersion = parseFloat(navigator.appVersion.split(';')[1].strip().split(' ')[1]);
Prototype.Browser.IE6 = Prototype.Browser.IEVersion == 6;
return Prototype.Browser.IE6;
},
hideElementSelectIE6: function(event) {
if (!this.isBrowserIE6()) return;
event.select('select').each(function(el) {
if (el.hasClassName('needHideThisSelect')) {
el.hide();
el.addClassName('needShowThisSelect');
}
}.bind(this));
},
showElementSelectIE6: function(event) {
event.select('select').each(function(el) {
if (el.hasClassName('needShowThisSelect')) {
el.show();
el.removeClassName('needShowThisSelect');
}
}.bind(this));
},
initControllers: function() {
this.keyboardTab = $('keyboardTab');
this.joypadTab = $('joypadTab');
if (!this.keyboardTab || !this.joypadTab) return ;
this.keyboardTab.observe('click', function() {
this.keyboardActive();
}.bind(this));
this.joypadTab.observe('click', function() {
this.joypadActive();
}.bind(this));
this.drawerJoypad = $('drawerJoypad');
this.drawerKeyboard = $('drawerKeyboard');
},
initBrowserGamesList: function() {
if (this.btn_mybrowsergames) this.btn_mybrowsergames.observe('click', this.loadBrowserGamesList.bind(this));
},
loadBrowserGamesList: function() {
if (!this.window_mygames.down('img').hasClassName('spinner')) return ;
var browser = new Hash();
try {
var xml = writeFullyDownloadedListGamesEmulatorAllTechno();
var xmlDoc = getXmlDoc(xml);
result = xmlDoc.getElementsByTagName('ContentDescriptor');
for (var i = 0; i < result.length; i++) {
el = result.item(i);
browser.set(el.getAttribute('Id'), new MetaGame(el.getAttribute('Id'), el.getAttribute('codeTechno')));
}
var url = new UrlConstructor('profile/get_games_info');
new Ajax.Request(url.construct(), {
method: 'post',
parameters: 'games=' + browser.keys().toJSON(),
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
if (typeof(obj.games) != 'undefined') {
browser.keys().each(function(technoId) { 
if (typeof(obj.games[technoId]) == 'object') 
Object.extend(browser.get(technoId), obj.games[technoId]);
else
browser.unset(technoId);
}.bind(this));
this.renderBrowserGamesList(browser);
} 
}.bind(this)
});
} catch(e) {
log.err(e);
this.hideWindow(this.window_mygames, false);
}
},
renderBrowserGamesList: function(browserGamesList) {
var container = this.window_mygames.down('.scrollerDrawerWindow');
container.removeClassName('center');
container.update('');
var browserSorted = new Array();
browserGamesList.each(function(item) {
browserSorted[item.value.gameId] = item.value; 
});   
browserSorted.sort(sortByTitle);
browserSorted.each(function(metaGame) {
var link = new Element('a', {href: metaGame.gameUrl + '?autostart=1'});
link.insert(new Element('p').update(metaGame.title));
container.insert(link);
});
},
keyboardActive: function() {
if (this.joypadTab.hasClassName('inactiveTab')) return ;
this.joypadTab.addClassName('inactiveTab');
this.keyboardTab.removeClassName('inactiveTab');
this.drawerKeyboard.removeClassName('displayNone');
this.drawerJoypad.addClassName('displayNone');
},
joypadActive: function() {
if (this.keyboardTab.hasClassName('inactiveTab')) return ;
this.keyboardTab.addClassName('inactiveTab');
this.joypadTab.removeClassName('inactiveTab');
this.drawerKeyboard.addClassName('displayNone');
this.drawerJoypad.removeClassName('displayNone');
},
initVolume: function() {
if (!$('volumeComponent')) return;
var zoom_slider = $('zoom_slider');
var volume = new Control.Slider(zoom_slider.down('.handle'), zoom_slider, {
range: $R(0, 100),
sliderValue: 100,
increment: 10,
values: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
onSlide: function(value) {
//log.debug('change volume on slide. value: ' + value);
//setVolumeAllTechno(value);
},
onChange: function(value) {
log.debug('change volume on change. value: ' + value);
setVolumeAllTechno(parseInt(value));
}
});
$('volume_min').observe('click', function(event) {
var value = volume.value - volume.increment;
log.debug('change volume on button min. value: ' + value);
volume.setValue(value);
}.bind(this));
$('volume_max').observe('click', function(event) {
var value = volume.value + volume.increment;
log.debug('change volume on button max. value: ' + value);
volume.setValue(value);
}.bind(this));
}
});
HomePage = Class.create({
initialize: function() {
if (!$('mostPlayed')) return;
if ($('mostPlayedContent').down('tr')) {
$('mostPlayedContent').down('tr').addClassName('rolloverElementGame');
}
$('mostPlayed').select('tr').each(function(el) {
el.observe('mouseover', this.overLineMostPlayed.bind(this));
}.bind(this));
$('mostPlayed').select('tr').each(function(el) {
el.observe('mouseout', this.outLineMostPlayed.bind(this));
}.bind(this));
},
overLineMostPlayed: function(event) {
var elA = Event.element(event);
if (!elA.hasClassName('rowLinkGame')) {
elA = elA.up('tr');
}
$(elA).addClassName('rolloverElementGame');
if ($('mostPlayedContent').down('tr') != $(elA)) {
$('mostPlayedContent').down('tr').removeClassName('rolloverElementGame');
}
},
outLineMostPlayed: function(event) {
var elA = Event.element(event);
if (!elA.hasClassName('rowLinkGame')) {
elA = elA.up('tr');
}
$(elA).removeClassName('rolloverElementGame');
$('mostPlayedContent').down('tr').addClassName('rolloverElementGame');
}
});
GameRating = Class.create({
initialize: function() {
this.ratingSelector = $('ratingSelector');
if (this.ratingSelector) this.ratingSelector.observe('change', this.saveRate.bind(this));
},
saveRate: function(event) {
var rate = Event.element(event);
var rateUrl = new UrlConstructor('game/add_rate');
rateUrl.setAction(true);
rateUrl.addVar('rating', rate.getValue());
rateUrl.addVar('gameId', $('gameId').getValue());
new Ajax.Request(rateUrl.construct(), {
method: 'get',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (er.getErrors()) return;
this.disableRate();
this.updateRate(obj.rating);
}.bind(this)
});
},
disableRate: function() {
this.ratingSelector.disabled = true;
},
updateRate: function(rating) {
$('gameRating').update(rating);
}
});
LastPlayedEffect = Class.create({
initialize: function() {
if (typeof(isOk) != 'undefined') {
if ((isOk == false) && $('hideMaskLastPlayed')) {
$('hideMaskLastPlayed').removeClassName('displayNone');
}
} 
}
});
/* NEED REFACTORING IN FUTURE */
Welcome = Class.create({
initialize: function() {
this.initConfirm();
this.initUac();
this.initFirewall();
this.initDisplayProperties();
if ($('vista') && isVista()) {
$('vista').value = 1;
}
},
/*
initInstallStatus: function() {
if (!$('playerStatusContent')) { return false; }
var sContext = null;
if (playerReady()) {
sContext = 'player_installed';
} else {
sContext = 'player_not_installed';
}
var browser = null;
if (Prototype.Browser.IE) {
browser = 'ie';
} else if (Prototype.Browser.Gecko) {
browser = 'gecko';
}
var url = new UrlConstructor('welcome/' + sContext);
url.addVar('browser', browser);
url.addVar('vista', isVista() ? 1 : 0);
new Ajax.Request(url.construct(), {
onSuccess: function(transport) {
$('playerStatusContent').update(transport.responseText);
if (isVista()) {
$('dxVista').value = 1;
}
}.bind(this)
});
},
*/
initUac: function() {
if (!$('uacShow')) { return false; }
Event.observe('uacShow', 'click', function() {
var url = new UrlConstructor('welcome/vista_uac');
this.modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
this.modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
this.modal.close();
}.bind(this));
}.bind(this)
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 950,
containerClassName: 'mfmodal'
});
this.modal.open();
});
},
initFirewall: function() {
if (!$('firewallShow')) { return false; }
Event.observe('firewallShow', 'click', function() {
var url = new UrlConstructor('welcome/firewall_info');
url.addVar('type', $('selectedFirewall').getValue());
this.modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
this.modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
this.modal.close();
}.bind(this));
}.bind(this)
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 850,
containerClassName: 'mfmodal'
});
this.modal.open();
});
},
initDisplayProperties: function() {
if (!$('showDisplayProperties')) { return false; }
Event.observe('showDisplayProperties', 'click', function() {
var url = new UrlConstructor('welcome/display_properties');
url.addVar('isVista', (isVista()) ? 1 : 0);
this.modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
this.modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
this.modal.close();
}.bind(this));
}.bind(this)
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 850,
containerClassName: 'mfmodal'
});
this.modal.open();
});
},
/*
initPlay: function() {
if (!$('addToDownloadUrl') || !$('technoGameId')) { return false; }
$$('a.add_to_download_list').each(function(el) {
el.observe('click', function() {
if (playerReady()) {
if (playerVersionCheck(function() {
downloadLauncher($('technoGameId').getValue(), 1);
})) {
downloadLauncher($('technoGameId').getValue(), 1);
} else {
return false;
}
} else {
var url = new UrlSeoConstructor('PROFILE_GAMES');
window.location = url.construct();
}
});
});
},
*/
initConfirm: function() {
if (!$('confirm')) { return false; }
Event.observe('confirm', 'click', function(event) {
var el = Event.element(event);
if (el.checked) {
this.active($('continue'));
} else {
this.unactive($('continue'));
}
}.bind(this));
},
active: function(el) {
el.src = el.src.replace(/_un/, '_');
el.disabled = false;
},
unactive: function(el) {
el.src = el.src.replace(/_a/, '_una');
el.disabled = true;
}
});
function isVista() {
return navigator.userAgent.match(/windows\s+nt\s+6\.\d+;/i) ? true : false;
}
Registration = Class.create({
initialize: function() {
new CryptoInfo();
this.initStep2();
this.initPromo();
this.initTypeCard();
this.cancel();
},
initStep2: function() {
var buttonSubmit = $('buttonSubmit');
if (buttonSubmit) {
buttonSubmit.down('a').observe('click', function() {
ww.show(function() {
document.forms.paymentForm.submit();
});
});
}
var boxMonth = $('boxMonthPrice');
var boxYear = $('boxYearPrice');
if ((boxMonth) && (boxYear)) {
boxMonth.observe('click', function() {
if (!boxMonth.down('input').disabled) {
boxMonth.down('input').checked = 'true';
this.setOfferFrom(boxMonth);
}
}.bind(this));
boxYear.observe('click', function() {
if (!boxYear.down('input').disabled) {
boxYear.down('input').checked = 'true';
this.setOfferFrom(boxYear);
}
}.bind(this));
}
},
setOfferFrom: function(box) {
$$('input.formOffer').each(function(el) { el.value = box.down('input.offerId').value; });
$('calculatedPrice').update(box.down('input.price').value);
},
initPromo: function() {
var btPromo = $('verifyPromo');
if (!btPromo) return;
var fmPromo = $(document.forms.promoForm);
btPromo.observe('click', function() {
fmPromo.request({
method: 'get',
parameters: { offer: $('formOffer').value },
onComplete: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (er.getErrors()) return;
if (obj.promoOffer == 1) {
document.boxPrice.costPlan[0].disabled = true;
document.boxPrice.costPlan[1].disabled = true;
$('promoInput').disabled = true;
$$('input.formOffer').each(function(el) { el.value = obj.promoOfferId; });
$$('input.formPromo').each(function(el) { el.value = obj.promoCode; });
$('calculatedPrice').update(obj.price);
} else if (obj.promoOfferWOPayment == 1) {
window.location = obj.url;
}
}.bind(this)
});
}.bind(this));
},
cancel: function() {
if (!$('cancelSubscription')) { return false; }
Event.observe('cancelSubscription', 'click', function() {
var url = new UrlConstructor('registration/confirm_cancel');
var modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
modal.close();
}.bind(this));
Event.observe('validate_cancel', 'click', function() {
var url = new UrlConstructor('registration/validate_cancel');
url.addVar('code', $('number').getValue());
new Ajax.Request(url.construct(), {
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
if (obj.validation == 0) {
$('number_text').addClassName('errorText');
$('number_alert').writeAttribute('title', obj.msg);
$('number_alert').setStyle({
display: 'block'
});
new MFTooltip();
} else {
$('transactionId').value = obj.tId;
modal.close();
}
}.bind(this)
});
});
}
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.3,
opacity: 0.8,
width: 510,
containerClassName: 'mfmodal',
afterClose: function() {
if ($('transactionId').value != '') {
setTimeout(function() {
$('transactionId').up('form').submit();
}, 500);
}
}
});
modal.open();
});
},
initTypeCard: function() {
var select = 'paymentType';
if (!$(select)) { return false; }
$(select).observe('change', function() {
this.changeTypeCard(select);
}.bind(this));
this.changeTypeCard(select);
},
changeTypeCard: function(id) {
if ((['PAYPAL'].indexOf($(id).value) !== -1)) {
this.showPaypal();
} else {
this.resetForm();
}
},
showPaypal: function() {
this.hideElements(['completeYourOrderContainer', 'headerPaymentArea', 'billingAddressBox']);
this.hideElements($$('.paypalHide'));
if ($('containerPaypal')) $('containerPaypal').removeClassName('displayNone');
},
resetForm: function() {
this.showElements(['completeYourOrderContainer', 'headerPaymentArea', 'billingAddressBox']);
this.showElements($$('.paypalHide'));
if ($('containerPaypal')) {
if (!$('containerPaypal').hasClassName('displayNone')) $('containerPaypal').addClassName('displayNone');    
}
},
hideElements: function(arr) {
arr.each(function(el) { if (el = $(el)) el.hide()});
},
showElements: function(arr) {
arr.each(function(el) { if (el = $(el)) el.show()});
}
});
function cancelReason() {
if (!$('cancelSubscription')) { return false; }
Event.observe('cancelSubscription', 'click', function() {
var url = new UrlConstructor('registration/confirm_cancel');
var modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
modal.close();
}.bind(this));
Event.observe('validate_cancel', 'click', function() {
var url = new UrlConstructor('registration/validate_cancel');
url.addVar('code', $('number').getValue());
new Ajax.Request(url.construct(), {
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
if (obj.validation == 0) {
$('number_text').addClassName('errorText');
$('number_alert').writeAttribute('title', obj.msg);
$('number_alert').setStyle({
display: 'block'
});
new MFTooltip();
} else {
$('transactionId').value = obj.tId;
modal.close();
}
}.bind(this)
});
});
}
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.3,
opacity: 0.8,
width: 510,
containerClassName: 'mfmodal',
afterClose: function() {
if ($('transactionId').value != '') {
setTimeout(function() {
$('transactionId').up('form').submit();
}, 500);
}
}
});
modal.open();
});
}
VideoScreeenShot = Class.create({
initialize: function() {
if (!$('thumbnailContent')) {return false;}
if ($('thumbnailContent').offsetWidth < $('thumbnailContent').offsetHeight) {
this.scrollLength = 340; // vertical scroll
} else {
this.scrollLength = 85;  // horizontal scroll
}
if ($('video-0').height <= 30) {
$('video-0').src = 'http://img.metaboli.fr/common/V4/images/videoNo_sm.jpg';
}
$('video-0').addClassName('goodSizeVideo');
this.countElements = $('thumbnailContent').childElements().size();
this.countPages = Math.ceil(this.countElements/4);
this.currentPage = 1;
this.buttonNavUp = $('buttonNavUp');
this.buttonNavDown = $('buttonNavDown');
var sDiv = $('thumbnailContent').down();
if ($('selectedItem').getValue() != 0) {
var screen = parseInt($('selectedItem').getValue());
sDiv = sDiv.next(screen - 1);
if (screen > 3) {
this.currentPage = Math.ceil(parseInt($('selectedItem').getValue()) / this.countPages);
}
}
this.render();
this.buttonNavDown.observe('click', function(event) {
if (/downInactive/.test(Event.element(event).className)) {
return false;
}
this.currentPage++;
this.render();
}.bind(this));
this.buttonNavUp.observe('click', function(event) {
if (/upInactive/.test(Event.element(event).className)) {
return false;
}
this.currentPage--;
this.render();
}.bind(this));
$('viewScreenshot').observe('click', this.modalView);
$('thumbnailContent').select('img').each(function(elImg) {
Event.observe(elImg, 'click', function(event) {
var elImg = Event.element(event);
this.selectItem(elImg);
}.bind(this));
}.bind(this));
sDiv.addClassName('thumbSelected');
this.selectItem(sDiv.down('img'));
},
render: function() {
$('thumbnailContent').setStyle({
marginTop: -(this.currentPage * this.scrollLength) + 'px'
});
$('thumbnailContent').setStyle({
marginTop: -((this.currentPage - 1) * this.scrollLength) + 'px'
});
if (this.currentPage == 1) {
this.buttonNavUp.addClassName('upInactive');
} else {
this.buttonNavUp.removeClassName('upInactive');
}
if (this.currentPage == this.countPages) {
this.buttonNavDown.addClassName('downInactive');
} else {
this.buttonNavDown.removeClassName('downInactive');
}
},
selectItem: function(elImg) {
$('thumbnailContent').childElements().each(function(el) {
el.removeClassName('thumbSelected');
}.bind(this));
if (/screen/.test(elImg.id)) {
$('videoScript').update();
$('productVideo').setStyle({display: 'none'});
var container = $('productScreenshot');
var r = elImg.src.match(/^(.*)_sm(.*)$/);
container.down('img').src = r[1] + r[2];
container.setStyle({display: 'block'});
} else if (/video/.test(elImg.id)) {
$('productScreenshot').setStyle({display: 'none'});
if ($('videoScript')) {
var video = elImg.src.match(/^(.*)_sm.*$/)[1] + '.flv';
$('videoScript').innerHTML = '<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ' +
'codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" ' +
'WIDTH="450" HEIGHT="450">' +
'<PARAM NAME=movie VALUE="' + $('videoPlayerUrl').getValue() + '">' +
'<PARAM NAME=quality VALUE=high>' +
'<PARAM NAME=bgcolor VALUE=#000000>' +
'<PARAM NAME=wmode VALUE=transparent>' +
'<PARAM NAME=flashvars VALUE="videoPath=' + video + '&lang=' + getLocale().lang + '">' +
'<EMBED FLASHVARS="videoPath=' + video + '&lang=' + getLocale().lang + '" src="' + $('videoPlayerUrl').getValue() + '"' +
'quality=high bgcolor="#000000" ' +
'WIDTH="450" HEIGHT="450" NAME="" ALIGN="" wmode="transparent" ' +
'TYPE="application/x-shockwave-flash" ' +
'PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">' +
'</EMBED>' +
'</OBJECT>';
}
$('productVideo').setStyle({display: 'block'});
}
this.render();
elImg.up('div').addClassName('thumbSelected');
},
modalView: function(event) {
var elImg = Event.element(event);
//         var a = Builder.node('a', {href: elImg.src});
var a = new Element('a', {href: elImg.src});
this.modal = new Control.Modal(a, {
fade: true,
image: true,
containerClassName: 'viewFullScrennShot'
});
this.modal.open();
}
});
function showManual(gameId) {
var url = new UrlConstructor('game/show_manual');
url.addVar('gameId', gameId);
new Ajax.Request(url.construct(), {
method: 'get',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (!er.getErrors()) {
window.open(obj.manualHref);
}
}
});
}
/* END OF NEED REFACTORING IN FUTURE */
CryptoInfo = Class.create({
initialize: function() {
this.crypto = $('cryptoInfo');
if (this.crypto) Event.observe(this.crypto, 'click', this.showCryptoInfo.bind(this));
},
showCryptoInfo: function() {
var url = new UrlConstructor('registration/crypto');
this.modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
this.modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
this.modal.close();
}.bind(this));
}.bind(this)
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 550,
height: 300,
containerClassName: 'mfmodal'
});
this.modal.open();
}
});
Profile = Class.create({
initialize: function() {
this.btnPassword = $('headerChangePassword');
this.btnEmail = $('headerChangeEmail');
this.btnUpdate = $('headerUpdateBilling');
this.btnNewsletter = $('headerChangeNewsletter');
this.payPalForm = $('payPalFormUpdate');
this.initCreditCardUpdate();        
this.initAccountPage();
},
initCreditCardUpdate: function() {
if (!this.btnUpdate) return ;
this.btnUpdate.observe('click', this.toggleCardUpdatBlock.bind(this));
if (this.payPalForm) return ;
this.btnUpdate.next().down('a.cancelButtonInfo').observe('click', this.toggleCardUpdatBlock.bind(this));
this.btnUpdate.next().down('a.saveButtonInfo').observe('click', this.creditCardUpdateProcess.bind(this));
},
initAccountPage: function() {
if (!this.btnPassword && !this.btnEmail) return ;
if (this.btnPassword) {
this.btnPassword.observe('click', this.togglePasswordBlock.bind(this));
this.btnPassword.next().down('a.cancelButtonInfo').observe('click', this.togglePasswordBlock.bind(this)); 
this.btnPassword.next().down('a.saveButtonInfo').observe('click', this.savePassword.bind(this)); 
} 
if (this.btnEmail) {
this.btnEmail.observe('click', this.toggleEmailBlock.bind(this));
this.btnEmail.next().down('a.cancelButtonInfo').observe('click', this.toggleEmailBlock.bind(this));
this.btnEmail.next().down('a.saveButtonInfo').observe('click', this.savePersonal.bind(this));
}
if (this.btnNewsletter) {
this.btnNewsletter.observe('click', this.toggleNewsletterBlock.bind(this));
this.btnNewsletter.next().down('a.cancelButtonInfo').observe('click', this.toggleNewsletterBlock.bind(this));
this.btnNewsletter.next().down('a.saveButtonInfo').observe('click', this.saveNewsletter.bind(this));
}
},
togglePasswordBlock: function() {
if ($('containerChangePassword').hasClassName('displayNone')) {
$('containerChangePassword').removeClassName('displayNone');
$('headerChangePassword').down('.pic_box_toggle').removeClassName('state_collapse');
} else {
$('containerChangePassword').addClassName('displayNone');
$('headerChangePassword').down('.pic_box_toggle').addClassName('state_collapse');
}
},
toggleEmailBlock: function() {
if ($('containerChangeEmail').hasClassName('displayNone')) {
$('containerChangeEmail').removeClassName('displayNone');
$('headerChangeEmail').down('.pic_box_toggle').removeClassName('state_collapse');
} else {
$('containerChangeEmail').addClassName('displayNone');
$('headerChangeEmail').down('.pic_box_toggle').addClassName('state_collapse');
}
},
toggleNewsletterBlock: function() {
if ($('containerChangeNewsletter').hasClassName('displayNone')) {
$('containerChangeNewsletter').removeClassName('displayNone');
$('headerChangeNewsletter').down('.pic_box_toggle').removeClassName('state_collapse');
} else {
$('containerChangeNewsletter').addClassName('displayNone');
$('headerChangeNewsletter').down('.pic_box_toggle').addClassName('state_collapse');
}
},
toggleCardUpdatBlock: function() {
if ($('containerUpdateBilling').hasClassName('displayNone')) {
$('containerUpdateBilling').removeClassName('displayNone');
$('headerUpdateBilling').down('.pic_box_toggle').removeClassName('state_collapse');
if (!this.payPalForm) {
this.clearCardUpdateInfo();
}
} else {
$('containerUpdateBilling').addClassName('displayNone');
$('headerUpdateBilling').down('.pic_box_toggle').addClassName('state_collapse');
}
},
clearCardUpdateInfo: function() {
var form = $('fmCardUpdate');
form.getInputs('text').each(function(el) { el.value = '' });
form.select('select').each(function(el) { el.selectedIndex = 0 });
},
savePassword: function(event) {
var form = $('passwordEdit');
var url = new UrlConstructor('profile/account_save_password');
url.setAction(true);
new Ajax.Request(url.construct(), {
parameters: form.serialize(),
method: 'post',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
form.getInputs('password').each(function(el) { el.value = ''; });
var er = new ErrorManager(obj);
if (er.getErrors()) return false;
this.togglePasswordBlock();
var modal = new Control.Modal(false, {
contents: obj.info,
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 500,
containerClassName: 'mfmodal'
});
modal.open();
Event.observe('modalClose', 'click', function() { modal.close() });
}.bind(this)
});
},
savePersonal: function(event) {
var form = $('infoEdit');
var url = new UrlConstructor('profile/account_save_personal');
url.setAction(true);
new Ajax.Request(url.construct(), {
parameters: form.serialize(),
method: 'post',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (er.getErrors()) return ;
this.toggleEmailBlock();
var modal = new Control.Modal(false, {
contents: obj.info,
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 500,
containerClassName: 'mfmodal'
});
modal.open();
Event.observe('modalClose', 'click', function() { modal.close() });
}.bind(this)
});
},
saveNewsletter: function(event) {
var form = $('newsletterEdit');
var url = new UrlConstructor('profile/account_save_newsletter');
url.setAction(true);
new Ajax.Request(url.construct(), {
parameters: form.serialize(),
method: 'post',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (er.getErrors()) return ;
this.toggleNewsletterBlock();
var modal = new Control.Modal(false, {
contents: obj.info,
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 500,
containerClassName: 'mfmodal'
});
modal.open();
Event.observe('modalClose', 'click', function() { modal.close() });
}.bind(this)
});
},
creditCardUpdateProcess: function(event) {
ww.show();
var form = $('fmCardUpdate');
var url = new UrlConstructor('profile/account_save_card_update');
url.setAction(true);
url.addVar('process', 'upgrade');
new Ajax.Request(url.construct(), {
parameters: form.serialize(),
method: 'post',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (er.getErrors()) return ;
this.toggleCardUpdatBlock();
this.clearCardUpdateInfo();
var modal = new Control.Modal(false, {
contents: obj.info,
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 500,
containerClassName: 'mfmodal'
});
modal.open();
Event.observe('modalClose', 'click', function() { modal.close() });
}.bind(this)
});
}
});
MultiPlayerKey  = Class.create({
initialize: function() {
this.modal = null;
this.initMultiPlayerKey();
},
initMultiPlayerKey: function() {
$$('a.getMultiplayerKey').each(function(el) {
Event.observe(el, 'click', function(event) {
var link = Event.element(event);
if (link.up('a')) {
link = link.up('a');
}
var gameId = link.href.match(/.*?_(\d+)/)[1];
this.show(gameId);
}.bind(this));
}.bind(this));
},
show: function(gameId) {
this.modal = new Control.Modal(false, {
contents: function() {
var url = new UrlConstructor('game/multiplayer_key');
url.addVar('gameId', gameId);
new Ajax.Request(url.construct(), {
onComplete: function(request){
this.modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
this.modal.close();
}.bind(this));
Event.observe('fmMultiPlayer', 'submit', function(event) {
this.getMultiplayerKey(gameId);
Event.stop(event);
}.bind(this));
Event.observe('sendKey', 'click', function() {
this.getMultiplayerKey(gameId);
}.bind(this));
}.bind(this)
});
return i18n('loadingDialog');
}.bind(this),
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 500,
containerClassName: 'mfmodal'
});
this.modal.open();
},
getMultiplayerKey: function(gameId) {
var url = new UrlConstructor('game/get_multiplayer_key');
url.setAction(true);
url.addVar('email', $('keyEmail').getValue());
url.addVar('gameId', gameId);
new Ajax.Request(url.construct(), {
onComplete: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
if (!er.getErrorsAlert()) {
alert(i18n('getMultiplayerKey'));
this.modal.close();
}
}.bind(this)
});
}
});
GameTapPlayer = Class.create({
initialize: function() {
if (this.isSupportedSystem()) return false;
this.cookieName = this.updaterId = 'webUpdater';
this.updater = $('gameTapUpdater');
this.enumUpdaterStatus = {
NO_UPDATE: 0,
UPDATE_NEW_VERSION: 1,
UPDATE_OLD_VERSION: 2
};
if (playerLogin && ssoToken) {
if (this.getUpdateByCookie()) {
this.checkVersion(function() {
this.initPlayer();
}.bind(this)); 
} else {
this.initPlayer();
}
}
},
isSupportedSystem: function() {
if (!Prototype.System.isWin || (!Prototype.Browser.IE && !Prototype.Browser.Gecko)) {
var cntx = $('context').getValue();
if (/profile_games/.test(cntx)) {
myGames = new MyGames();
} 
return true;
}
return false;
},
getUpdateByCookie: function() {
return (!Cookie.get(this.cookieName)) ? true : false; 
},
setUpdateByCookie: function() {
/*
Cookie.set(this.cookieName, 1, {
expires: (1).day(),
path: '/'
});
*/
},
isNewVersionAvailable: function(callback) {
if (Prototype.Browser.IE) {
log.debug('engine InternetExplorer detected');
var versionUpdaterPluginsServerIE = DEFINE.get('UPDATER_PLUGINS_SERVER_IE');
this.updater.update('<object id="' + this.updaterId + '" classid="clsid:C8AEB218-8B7A-4E15-AC17-0EE8D99B80EB" codebase="http://archives.gametap.com/static/cab_headless/GameTapWebUpdater.cab#version=' + versionUpdaterPluginsServerIE + '" width="0" height="0"></object>');
} else {
log.debug('engine Mozilla detected');
var versionUpdaterPluginsServerFF = DEFINE.get('UPDATER_PLUGINS_SERVER_FF');
if (this.checkInstallXpiMozilla(versionUpdaterPluginsServerFF)) {
this.updater.update('<object id="' + this.updaterId + '" type="application/x-vnd-gametap-webupdater" width="0px" height="0px"></object>');  
} else {
callback(true); 
return ;               
}
}
this.checkUpdater(callback);
},
checkUpdater: function(callback) {
var updaterInstance = $(this.updaterId);
if (!updaterInstance) callback(false);
try {
log.debug('init updater');
updaterInstance.InitUpdater(DEFINE.get('INIT_UPDATER_URL') + '/', DEFINE.get('INIT_UPDATER_URL'));
var hasUpdate = updaterInstance.CheckUpdater();
if (hasUpdate == this.enumUpdaterStatus.UPDATE_NEW_VERSION || hasUpdate == this.enumUpdaterStatus.UPDATE_OLD_VERSION) {
log.debug('new version available. status: ' + hasUpdate);
callback(true);
} else {
log.debug('no update to do. ' + hasUpdate);
callback(false);
}
} catch(e) {
log.err('problem with check updater. msg: ' + e.message);
if (Prototype.Browser.IE) {
callback(true);
} else {
setTimeout(function(){
this.checkUpdater(callback);
}.bind(this), 1000);
}
}
},
checkVersion: function(callback) {
var cntx = $('context').getValue();
if (/installplayer_.*/.test(cntx) || /subscription_step2/.test(cntx)) return ;
var status = _gameTapPlayerType();
var installUrl = new UrlSeoConstructor('INSTALLPLAYER_INSTALL');
installUrl.addVar('browser', (Prototype.Browser.IE) ? 'IE' : 'FF');
log.debug('gameTapPlayerType result: ' + status);
switch (status) {
case 1:
case 2:
case 3:
case 5:
this.isNewVersionAvailable(function(newVersion) {
if (newVersion) {
log.stop('new version silent player available');
installUrl.addVar('type', 'update');
window.location = installUrl.construct();
} else {
this.setUpdateByCookie();
if (!Cookie.get('migration')) {
log.stop('go to migration step');
var migrationUrl = new UrlSeoConstructor('INSTALLPLAYER_MIGRATION');
migrationUrl.addVar('browser', (Prototype.Browser.IE) ? 'IE' : 'FF');
window.location = migrationUrl.construct();
} else {
log.debug('migration cookie presented. migration not allowed');
}
}
}.bind(this));
break;
case 0:
log.stop('Player not found');
window.location = installUrl.construct();
break;
case 4:
this.isNewVersionAvailable(function(newVersion) {
if (newVersion) {
log.stop('new version available');
installUrl.addVar('type', 'update');
window.location = installUrl.construct();
} else {
log.debug('player already updated. set cookie');
this.setUpdateByCookie();
callback();
}
}.bind(this));
break;
}
},
checkInstallXpiMozilla: function(versionUpdaterPluginsServerFF, allowInstall) {
var numPlugins    = navigator.plugins.length;
var installed     = false;
var clientVersion = '0';
var urlXpi = DEFINE.get('GAMETAP_XPI'); 
log.debug('installation of the xpi server: ' + versionUpdaterPluginsServerFF);
for (var i=0; i < numPlugins; i++ ) {
var plugin = navigator.plugins[i];
log.debug('plugin detected: ' + plugin.filename);
if (plugin.filename.toLowerCase() == "npgametapwebupdater.dll") {
clientVersion = plugin.description.substring(plugin.name.length + 1, plugin.description.length);
log.debug('version of the xpi: ' + clientVersion);
installed = true;
break;
}
}
var regEx = /\./g;
var clientVersionInt = parseInt(clientVersion.replace(regEx, ''));
var serverVersionInt = parseInt(versionUpdaterPluginsServerFF.replace(regEx, ''));
if ((!installed) && (typeof InstallTrigger != "undefined") && InstallTrigger.enabled()) {
log.debug('installation of the xpi client');
if (allowInstall) InstallTrigger.startSoftwareUpdate(urlXpi, 0);
return false;
} else if (clientVersionInt < serverVersionInt) {
log.debug('udate the previously xpi ' + clientVersion +' with this one ' + versionUpdaterPluginsServerFF);
if (allowInstall) InstallTrigger.startSoftwareUpdate(urlXpi, 0);
return false;
} 
return installed;   
},
initPlayer: function() {
if (!browserGame) {
try {
log.debug('Player initialization');
isDrmReadyOnClientComputer(playerLogin, ssoToken, 'gameTapPlayer');
} catch (e) {
log.err(e);
}
}
}
});
GameTapInstaller = Class.create(GameTapPlayer, {
runInstaller: function() {
if (Prototype.Browser.IE) {
log.debug('engine InternetExplorer detected');
var versionUpdaterPluginsServerIE = DEFINE.get('UPDATER_PLUGINS_SERVER_IE');
this.updater.update('<object id="' + this.updaterId + '" classid="clsid:C8AEB218-8B7A-4E15-AC17-0EE8D99B80EB" codebase="http://archives.gametap.com/static/cab_headless/GameTapWebUpdater.cab#version=' + versionUpdaterPluginsServerIE + '" width="0" height="0"></object>');
} else {
log.debug('engine Mozilla detected');
var versionUpdaterPluginsServerFF = DEFINE.get('UPDATER_PLUGINS_SERVER_FF');
if (this.checkInstallXpiMozilla(versionUpdaterPluginsServerFF, true)) {
this.updater.update('<object id="' + this.updaterId + '" type="application/x-vnd-gametap-webupdater" width="0px" height="0px"></object>');
} else {
return null;
}
}
this.launchUpdater();
},
launchUpdater: function() {
updaterInstance = $(this.updaterId);
if (!updaterInstance) return false;
try {
log.debug('init updater');
updaterInstance.InitUpdater(DEFINE.get('INIT_UPDATER_URL') + '/', DEFINE.get('INIT_UPDATER_URL'));
var hasUpdate = updaterInstance.CheckUpdater();
if (hasUpdate == this.enumUpdaterStatus.UPDATE_NEW_VERSION || hasUpdate == this.enumUpdaterStatus.UPDATE_OLD_VERSION) {
log.debug('new version available. status: ' + hasUpdate);
updaterInstance.LaunchUpdater();
} else {
var profile = new UrlSeoConstructor('PROFILE_DASHBOARD');
window.location = profile.construct();
log.debug('no update to do. ' + hasUpdate);
}
} catch (e) {
log.err('problem with check updater. msg: ' + e.message);
setTimeout(function() {this.launchUpdater(); }.bind(this), 1000);
}
}
});
GameTapMigration = Class.create({
initialize: function() {
this.silentPlayerId = 'silentPlayer';
this.silentPlayer = $('gameTapSilentPlayer');
this.playerInstance = null;
this.initSilentPlayer();
},
initSilentPlayer: function() {
if (Prototype.Browser.IE) {
this.silentPlayer.insert('<object id="' + this.silentPlayerId + '" classid="clsid:1FDD3AEC-D3F6-4278-B4FE-2963C3764EBF" width="0" height="0"></object>');
} else {
this.silentPlayer.insert('<object id="' + this.silentPlayerId + '" type="application/x-vnd-gametap-webplayer" width="0px" height="0px"></object>');
}
this.playerInstance = $(this.silentPlayerId);
if (this.playerInstance == null) {
log.info('silent player not loaded');
} else {
log.debug('init migration');
try {
this.checkMigration();
} catch(e) {
log.err('plugins migration cancelled : ' + e.message);
setTimeout(function() { this.checkMigration(); }.bind(this), 2000);
}
}
},
checkMigration: function() {
var typePlayer = _gameTapPlayerType();
switch (typePlayer) {
case 1:
case 2:
case 3:
case 5:
$('btnContinueUnactive').addClassName('displayNone');
var btContinue = $('btnContinueActive').removeClassName('displayNone').down('a');
btContinue.observe('click', this.startMigration.bind(this));
}
},
startMigration: function() {
$('btnContinueUnactive').removeClassName('displayNone');
$('btnContinueActive').addClassName('displayNone');
$('migrationInfo').addClassName('displayNone');
$('migrationInprocess').removeClassName('displayNone');
setTimeout(function() {
try {
var typePlayer = _gameTapPlayerType();
var parameterMigration = null;
var seedLaunch = new UrlSeoConstructor('SPEED_LAUNCH');
var base = new UrlSeoConstructor('BASE');
var mShortcutBase = seedLaunch.construct();
var mBase = base.construct();
var mDeskIcons = new String($('createShortcuts').checked);
switch (typePlayer) {
case 0: //No GameTap Player found.
log.debug('no migration to do');
break;
case 1: //GameTap Rich Player detected
log.debug('gametap rich player detected');
parameterMigration = "migrateKickoff=true;migrateDeskIcons=" + mDeskIcons + ";migrateGameShortcutBase=" + mShortcutBase + ";migrateHomeShortcut=" + mBase;
log.debug('migration will be launch');
break;
case 2: //GameTap Web Player detected              
log.debug('gametap web player detected');
parameterMigration = "migrateKickoff=true;migrateDeskIcons=" + mDeskIcons + ";migrateGameShortcutBase=" + mShortcutBase + ";migrateHomeShortcut=" + mBase;
break;
case 3: //GameTap Web Player and GameTap Rich Player detected.
log.debug('gametap web player and gametap rich player detected');
parameterMigration = "migrateKickoff=true;migrateDeskIcons=" + mDeskIcons + ";migrateGameShortcutBase=" + mShortcutBase + ";migrateHomeShortcut=" + mBase;
log.debug('migration will be launch');
break;
case 4: //GameTap Silent Player detected.
log.debug('no migration to do');
break;
case 5: //GameTap Silent Player and GameTap Rich Player dectected.
log.debug('gametap silent player and gametap rich player dectected');
parameterMigration = "migrateKickoff=true;migrateDeskIcons=" + mDeskIcons + ";migrateGameShortcutBase=" + mShortcutBase + ";migrateHomeShortcut=" + mBase;
log.debug('migration will be launch');
break;
default:
log.debug('no migration to do. player status: ' + typePlayer);
}
if (parameterMigration) {
log.debug('migration parameter: ' + parameterMigration);
this.playerInstance.InitializePlugin(false, parameterMigration);
}
} catch(e) {
log.err(e);
}
}.bind(this), 2000);
}
});
CancelInfo = Class.create({
initialize: function() {
var cancelLang = new String($('cancel').getValue());
if (!cancelLang.empty()) this.showCancelInfo(cancelLang);
},
showCancelInfo: function(cancelLang) {
var modal = new Control.Modal(false, {
contents: function() {
var showInfo = new UrlConstructor('registration/cancel_info');
showInfo.addVar('lang', cancelLang);
new Ajax.Request(showInfo.construct(), {
onComplete: function(request){
modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
modal.close();
});
}
});
return i18n('loadingDialog');
},
fade: true,
fadeDuration: 0.4,
opacity: 0.8,
width: 500,
containerClassName: 'mfmodal'
});
modal.open();
}
});
SubmitFormByClick = Class.create({
initialize: function(fmId) {
this.fmSubmit = $(fmId);
if (!this.fmSubmit) return ;
this.fmSubmit.select('input').each(function(el) {
el.observe('click', this.fmAction.bind(this));
}.bind(this));
this.fmSubmit.select('img').each(function(el) {
el.observe('click', function() {
var x64 = this.fmSubmit.select('input').first();
x64.checked = !x64.checked;
this.fmAction();
}.bind(this));
}.bind(this));
},
fmAction: function() {
this.fmSubmit.submit();
return false;
}
});
var ww = null;
var gameControls = null;
var myGames = null;
var webPlayer = null;
var log = new SimpleDebug();
Event.observe(window, 'load', function() {
var logLevel = null;
try { 
logLevel = DEFINE.get('LOG_LEVEL');
} catch(e) { 
logLevel = 0; 
}
log.onLoadInit(logLevel);
if (typeof(redirectStepWait) == 'function') {
new InitSWFobjects();
redirectStepWait();
return true;
}
transparentbg('main_bg');
ww = new WaitWindow();
var cntx = $('context').getValue();
if (typeof(_gameTapPlayerType) == 'function' && !/installplayer_install/.test(cntx)) {
new GameTapPlayer();
}
if (/installplayer_install/.test(cntx)) {
var installer = new GameTapInstaller();
installer.runInstaller();
return false;
}
if (/installplayer_migration/.test(cntx)) {
var migration = new GameTapMigration();
return false;
}
if (/profile_account/.test(cntx)) {
new CryptoInfo();
new Profile();
} 
if (/profile_.*/.test(cntx)) new LastPlayedEffect();
if (/subscription_.*/.test(cntx)) new Registration();
if (/welcome_.*/.test(cntx)) new Welcome();
if (/game_.*/.test(cntx)) {
gameControls = new ControlGamePage();
new GameRating();
new GamePage();
}
if (/registration_cancel/.test(cntx)) cancelReason();
if (/game_videoscreenshots/.test(cntx)) new VideoScreeenShot();
['dropdownGenre', 'dropdownSubscription'].each(function(el) { new CategoryDropDownList(el) });
['fmX64'].each(function(el) { new SubmitFormByClick(el); });
new CancelInfo();
new HomePage();
new InitSWFobjects();
new GameAutocomplete();
new GameAutocompleteEffect();
new SubmitFormByLink();
new CategoryPage();
new MFTooltip();
});
Event.observe(document, 'click', function() {
transparentbg('main_bg');
});
Prototype.System = {
isMac: navigator.appVersion.indexOf("Mac") != -1 ? true : false,
isNix: navigator.appVersion.indexOf("Linux") != -1 ? true : false,
isWin: navigator.appVersion.indexOf("Windows") != -1 ? true : false
}
function showInfoModalWindow(url) {
var modal = new Control.Modal(false, {
contents: function() {
new Ajax.Request(url.construct(), {
onComplete: function(request){
modal.update(request.responseText);
Event.observe('modalClose', 'click', function() {
modal.close();
});
}
});
return i18n('loadingDialog');
},
fade: true, fadeDuration: 0.4, opacity: 0.8, width: 500,
containerClassName: 'mfmodal'
});
modal.open();
}
function showErrorModalWindow(errorCode) {
if ([4063, 4064, 4108, 'UD10', 'UD19', 'UD20', 'UD21', 'UD23'].indexOf(errorCode) !== -1) return ;
if (errorCode > 5000) return ;
var err = new ModalErrorWindow(errorCode);
err.open();
}
WebPlayerButton = Class.create({
initialize: function(classArr, label) {
this.classArr = classArr;
this.classArr.push('btn');
this.el = new Element('div', {'class': this.classArr.join(' ')}).update(new Element('span').update(label));
this.initEvents();
},
initEvents: function() {
this.el.observe('mouseover', function() {
if (this.el.hasClassName('btnNormal')) {
this.el.observe('mouseout', function() {
if (this.el.hasClassName('btnHover')) {
this.el.removeClassName('btnHover');
this.el.addClassName('btnNormal');
}
}.bind(this));        
this.el.removeClassName('btnNormal');
this.el.removeClassName('btnInactive');
this.el.addClassName('btnHover');
}
}.bind(this));        
},
toNormal: function() {
this.el.removeClassName('btnHover');
this.el.removeClassName('btnInactive');
this.el.addClassName('btnNormal');
this.activateClick();
},
toInactive: function() {
this.el.removeClassName('btnHover');
this.el.removeClassName('btnNormal');
this.el.addClassName('btnInactive');
this.deactivateClick();
},
getButton: function() {
return this.el;
},
setClickObserve: function(fn) {
this.click = fn;
},
activateClick: function() {
this.el.observe('click', this.click);
},
deactivateClick: function() {
this.el.stopObserving('click', this.click);
},
observe: function(ev, fn) {
this.el.observe(ev, fn);
}  
});
WebPlayerSaveSlot = Class.create({
initialize: function(slotId) {
this.saved = false;
this.slot = slotId;   
this.desc = 'Slot ' + this.slot + ' - enter name';
},
setSaved: function(tstamp, desc, screen, aspect) {
this.saved = true;
this.tstamp = tstamp;
this.desc = desc;
this.screen = screen;
this.aspect = aspect;
this.setDateTime();
},
setDateTime: function() {
var dateSaved = new Date();
dateSaved.setTime(this.tstamp + (dateSaved.getTimezoneOffset() * 60000));
var theHour = dateSaved.getHours();
this.date = (dateSaved.getMonth() + 1) + "/" + dateSaved.getDate() + "/" + dateSaved.getFullYear();
this.time = ((theHour <= 12) ? ((theHour == 0) ? 12 : theHour) : theHour - 12) + ":" + dateSaved.getMinutes() + ((theHour < 12) ? "am" : "pm");
},
getScreen: function(){
var d = new Date();
var src = this.screen + "?creationTime=" + ((new Date()).valueOf().toString());
log.debug('slot: ' + this.slot + ' src: ' + src + ' aspect: ' + this.aspect);
var img = new Element('img', {'src': src});
img.hide();
img.observe('load', function() {
if (this.aspect > 1) {
img.setStyle({
'width': '172px',
'height': parseInt(172/this.aspect) + 'px'
});
img.setStyle({
'marginTop': parseInt((137 - parseInt(img.getHeight())) / 2) + 'px'
});
}
else {
img.setStyle({
'height': '137px',
'width': parseInt(137 * this.aspect) + 'px'
});
}
img.show();
}.bind(this));
return img;
}
});
WebPlayer = Class.create({
initialize: function(login, password, technoId, containerId) {
this.player = null;
this.define = {
playerId:       'player',
playerContainer:'browserPlayer',
playerWidth:    666,
playerHeight:   500
};
this.login = login;
this.password = password;
this.technoId = technoId;
this.container = $(containerId);
navigator.plugins.refresh(false);
this.player  = new Element('object', {
'id':       this.define.playerId,
'width':    this.define.playerWidth + 'px', 
'height':   this.define.playerHeight + 'px'
});
if (Prototype.Browser.IE) {
// ActiveX Plugin.
this.player.setAttribute("classid", "CLSID:1FDD3AEC-D3F6-4278-B4FE-2963C3764EBF");
} else {
// NPAPI Plugin.
this.player.setAttribute("type", "application/x-vnd-gametap-webplayer");
}
this.container.insert(this.player);
this.player.InitializePlugin(true, '');
log.debug('insert webPlayer component done');
},
loginUser: function() {
var loginString = "gametap://user=" + this.login + "," + this.password + ":999999999@player";
this.player.LoginUser(loginString);
},
loadGame: function() {
if ($('noticeAreaContent')) {
log.debug('move HTML content and show launch process');
this.player.insert({
before: $('noticeAreaContent').innerHTML
});
$('noticeAreaContent').remove();
$('browserGameLaunch').removeClassName('displayNone');
}
this.player.HidePlayer();
log.debug('try get current game before load: ' + this.player.GetCurrentGame());
urlServlet = new UrlSeoConstructor('ADD_TO_DOWNLOAD_LIST');
StartDownloadGameTap(urlServlet.construct(), this.technoId, 1);
},
playGame: function() {
log.debug('remove notice area and show player');
$('noticeArea').remove();
this.player.ShowPlayer();
this.player.focus();
player_play();
log.debug('init fullscreen and reset buttons');
this.initFullscreen();
this.initReset();
},
setProgress: function(technoId, progress) {
if (technoId = this.technoId && progress >= 0 && progress <=100) {
$('browserCurrentProgress').setStyle({'width': progress + '%'});
$('textValueProgress').update(progress + '%');
}
},
setInDownloadState: function() {
log.debug('show download progress bar');
$('browserGameLaunch').addClassName('displayNone');
$('gameDownloading').removeClassName('displayNone');
},
initFullscreen: function() {
Event.observe('buttonFullscreen', 'click', function() {
log.debug('fullscreen mode');
this.player.Fullscreen();
}.bind(this));  
},
initReset: function() {
Event.observe('buttonResetGame', 'click', function() {
log.debug('reset game');
this.player.ResetGame();
}.bind(this));  
},
shutdownPlayer: function() {
this.player.ShutdownPlayer();
},
getSaveState: function() {
try {
var saveState = this.player.GetSaveStateInfo().evalJSON(); 
} catch(e) {
log.err(e);
}
var slots = new Hash();
$R(1, 12).each(function(slotId) {
var state = saveState.find(function(s) { 
return s.slotNumber == slotId;
}); 
var slot = new WebPlayerSaveSlot(slotId);
if (typeof(state) != 'undefined') {
slot.setSaved(state.timeStamp, state.description, state.screenShotPath, state.aspectRatio); 
}
slots.set(slotId, slot); 
});
return slots;
},
getCurrentScreen: function(screen) {
var src = this.player.GetCurrentGameScreenImageSource() + "?creationTime=" + ((new Date()).valueOf().toString()) + "&intensity=1&alpha=1";
var img = new Element('img', {'src': src});
img.hide();
img.observe('load', function() {
if (img.width / img.height > 1) {
img.setStyle({
'width': '150px'
});
} else {
img.setStyle({
'height': '110px'
});
}
img.show();
});
return img;
},
addNumList: function(arr) {
var ol = new Element('ol', {'class': 'numList'});
arr.each(function(title) {
ol.insert(new Element('li').update(title));
});
return ol;
},
addTopContainer: function(cntr, saveable) {
var topClass = ['topContainer'];
if (saveable) topClass.push('topContainerLine');
var topContainer = new Element('div', {'class': topClass.join(' ')});
topContainer.insert(new Element('div', {'class': 'currentScreen'}).update(this.getCurrentScreen()));
this.btnResume = new WebPlayerButton(['btnResume'], 'Resume Game');
this.btnResume.observe('click', this.onCancel.bind(this));
topContainer.insert(this.btnResume.getButton());
topContainer.insert(new Element('div', {'class': 'gamePaused'}).update('Game Paused'));
if (saveable) {
var ul = new Element('ul', {'class': 'gamePaused'});
ul.insert(new Element('li', {'class': 'first'}).update('Saving or Loading a game'));
ul.insert(new Element('li', {'class': 'second'}).update(
new Element('div').update('Save Game')).insert(
this.addNumList(['Select a game slot.', 'Enter a name for the save.', 'Click the Save Game button.'])
).insert(
new Element('span', {'class': 'note'}).update('(Note: Select an occupied save slot to overwritean existing save.)'
)
));
ul.insert(new Element('li', {'class': 'third'}).update(new Element('div').update('Load Game')).insert(
this.addNumList(['Select a game slot.', 'Click the Load Game button.'])
));
topContainer.insert(ul);
}
cntr.insert(topContainer);
return cntr;
},
addBottomContainer: function(cntr) {
var bottomContainer = new Element('div', {'class': 'bottomContainer'});
this.left = new Element('div', {'class': 'leftContainer'});
this.getSaveState().each(function(pair) {
var slot = pair.value;
var cellClassArr = ['cellContainer'];
var top = new Element('div', {'class': 'top'});
var bottom = new Element('div', {'class': 'bottom'});
if (slot.saved) {
cellClassArr.push('saved');
top.insert(slot.getScreen());
bottom.insert(new Element('div').update(slot.desc));
bottom.insert(new Element('div', {'class': 'date'}).update(slot.date));
bottom.insert(new Element('div', {'class': 'time'}).update(slot.time));
} else {
bottom.update(slot.desc);
}
var slotSquere = new Element('div', {'class': cellClassArr.join(' '), 'id': 'cell-' + slot.slot});
slotSquere.observe('click', this.cellClick.bind(this));
slotSquere.insert(top);
slotSquere.insert(bottom);
this.left.insert(slotSquere);
}.bind(this));
this.right = new Element('div', {'class': 'rightContainer'});
this.btnSaveGame = new WebPlayerButton(['btnCommon', 'btnInactive'], 'Save Game'); 
this.btnSaveGame.setClickObserve(this.onSaveState.bind(this)); 
this.btnLoadGame = new WebPlayerButton(['btnCommon', 'btnInactive'], 'Load Game'); 
this.btnLoadGame.setClickObserve(this.onLoadState.bind(this)); 
this.btnDeleteGame = new WebPlayerButton(['btnCommon', 'btnInactive'], 'Delete Game');
this.btnDeleteGame.setClickObserve(this.onDeleteState.bind(this)); 
this.btnCancelGame = new WebPlayerButton(['btnCommon', 'btnNormal'], 'Cancel');
this.btnCancelGame.observe('click', this.onCancel.bind(this));
[this.btnSaveGame, this.btnLoadGame, this.btnDeleteGame, this.btnCancelGame].each(function(btn) {
this.right.insert(btn.getButton());
}.bind(this));
this.setButtonsPosition();
bottomContainer.insert(this.left);
bottomContainer.insert(this.right);
cntr.insert(bottomContainer);
return cntr;       
},
setButtonsPosition: function() {
var s = this.right.childElements().size();
this.right.setStyle({paddingTop: parseInt((350 - 27 * s)/2) + 'px'});
},
clearCells: function() {
$$('.cellContainer').each(function(clearCell) {
if (clearCell.hasClassName('selected')) {
clearCell.removeClassName('selected');
if (!clearCell.hasClassName('saved')) {
var bottom = clearCell.down('.bottom');
var prev = bottom.down('input.prev');
if (prev) bottom.update(prev.value);
}
}
});
},
cellClick: function(event) {
var cell = Event.element(event).up('.cellContainer');
if (cell.hasClassName('selected')) return ;
this.clearCells();
cell.addClassName('selected');
var bottom = cell.down('.bottom');
if (cell.hasClassName('saved')) {
this.btnSaveGame.toNormal();
this.btnLoadGame.toNormal();
this.btnDeleteGame.toNormal();
} else {
var input = new Element('input', {'type': 'text'});
var inputPrev = new Element('input', {'class': 'prev', 'type': 'hidden', 'value': bottom.innerHTML});
bottom.update(input);
bottom.insert(inputPrev);
input.focus();
this.btnSaveGame.toNormal();
this.btnLoadGame.toInactive();
this.btnDeleteGame.toInactive();
}
this.cell = {
cell: cell,
saved: cell.hasClassName('saved') ? true : false,
slot: cell.id.match(/cell-(\d+)/)[1],
bottom: bottom,
input: input,
prev: inputPrev
};
},
onSaveState: function() {
if (this.cell.saved && !confirm('Replace save?')) {
this.clearCells();
} else {
try {
var save;
if (this.cell.saved) {
save = this.getSaveState().get(this.cell.slot).desc;
} else {
save = this.cell.input.value.blank() ? this.cell.prev.value : this.cell.input.value;    
}
this.player.SaveState(this.cell.slot, save);
var slot = this.getSaveState().get(this.cell.slot);
this.cell.bottom.update();
this.cell.cell.addClassName('saved');
this.cell.cell.down('.top').update('').insert(slot.getScreen());
this.cell.bottom.insert(new Element('div').update(slot.desc));
this.cell.bottom.insert(new Element('div', {'class': 'date'}).update(slot.date));
this.cell.bottom.insert(new Element('div', {'class': 'time'}).update(slot.time));
this.btnLoadGame.toNormal();
this.btnDeleteGame.toNormal();
} catch(e) {
log.err(e);
}
}
},
onLoadState: function() {
try {
this.player.LoadState(this.cell.slot);
this.onCancel();
} catch(e) {
log.err(e);
}
},
onDeleteState: function() {
if (confirm('Delete save?')) {
try {
this.player.DeleteSaveState(this.cell.slot);
var slot = this.getSaveState().get(this.cell.slot);
this.cell.bottom.update(slot.desc);
this.cell.cell.removeClassName('saved');
this.cell.cell.down('.top').update();
this.btnLoadGame.toInactive();
this.btnDeleteGame.toInactive();
} catch(e) {
log.err(e);
}
} else {
this.clearCells();
}
},
onCancel: function() {
if (this.slContainer) {
new Effect.Opacity(this.slContainer, {
from: 1,
to: 0,
duration: 0.5,
afterFinish: function(){
this.slContainer.remove();
this.slContainer = null;
this.player.ShowPlayer();
}.bind(this)
});
}
},
onGamePause: function() {
try {
var currentGame = this.player.GetCurrentGame();
var saveable = this.player.GetGameInfo(currentGame).evalJSON().strGameSaveable;
} catch(e) {
log.err(e);
}
this.player.HidePlayer();
this.currentCell = false;
this.slContainer = new Element('div', {'class': 'slContainer'});
this.player.insert({
before: this.addTopContainer(this.slContainer, saveable)
});
this.container.insert();
if (saveable) 
this.player.insert({
before: this.addBottomContainer(this.slContainer)
});
this.slContainer.setOpacity(0);
new Effect.Opacity(this.slContainer, { from: 0, to: 1, duration: 0.5 });
},
onGameResume: function() {
this.onCancel();
}
});
function ProgressHandler(technoId, progress) {
log.debug('ProgressHandler. technoId: ' + technoId + ' progress: ' + progress);
var progress = parseInt(progress);
var installMigration = $('progressElement');
if (installMigration) {
installMigration.down('.currentProgress').setStyle({width: ((progress == 100) ? 101 : progress) + '%'});
installMigration.down('.textValueProgress').update(progress + '%');
} 
if (typeof(gameManager) == 'object') {
if (autoLaunch && !gameManager.inprocess.browser && gameManager.inprocess.technoId == technoId) {
log.debug('launch precached game');
try {
if (myGames.inDownload.keys().first() != technoId) gameManager.onGameAddToListCB(technoId);
autoLaunch = false;
player_play();
} catch (e) {
log.err(e);
}
}
if (gameManager.downloadClick && gameManager.downloadClick == technoId && !gameManager.redirectionInProcess) {
ww.hide();
}
}
if (webPlayer) webPlayer.setProgress(technoId, progress);
}
function StatusHandler(step, status){
log.debug("StatusHandler Step: " + step + " Status: " + status);
var cntx = $('context').getValue();
var stepCodes = new Object();
stepCodes.UNLOAD_GAME_COMPLETE                      = 0;     // unload game complete  
stepCodes.USER_LOGIN_COMPLETE                       = 1;     // user login complete
stepCodes.LOAD_GAME_COMPLETE                        = 2;     // load game complete
stepCodes.PLAY_GAME_COMPLETE                        = 3;     // play game complete
stepCodes.INIT_COMPLETE                             = 4;     // initialization complete (optional migration happens here)
stepCodes.INITIAL_PRECACHE_DOWNLOAD_COMPLETE        = 10000; // game has reached precache minimum
stepCodes.DOWNLOAD_STARTS                           = 10001; // download queue started
stepCodes.DOWNLOAD_STOPS                            = 10002; // download queue stopped
stepCodes.FULLY_DOWNLOADED                          = 10003; // game is fully downloaded
stepCodes.PRIORITY_CHANGED                          = 10004; // game has changed priority 
stepCodes.GAME_ENQUEUED                             = 10005; // game is added to the queue 
stepCodes.GAME_DEQUEUED                             = 10006; // game is removed from the queue
stepCodes.GAME_REMOVED                              = 10007; // game is removed from hard drive
try{
switch (step){
case stepCodes.UNLOAD_GAME_COMPLETE:
log.debug("unload game completed successfully! "+status);
// not sure why this is logged out on unload complete
break;
case stepCodes.USER_LOGIN_COMPLETE:
if (isLoginPlayerDone) return ;
log.debug("login completed successfully!");
isLoginPlayerDone = true;
if (webPlayer) {
webPlayer.loadGame();
}
if (typeof(gameManager) != 'undefined' && gameManager) {
gameManager.initGamePageButtonsV2();
}
if (/profile_games/.test(cntx)) {
log.debug('show my games list');
myGames = new MyGames();
gameManager.initAutorun();
}
if (/profile_.*/.test(cntx)) {
gameManager.initFavorites();
}
break;
case stepCodes.INIT_COMPLETE:
log.debug("init completed successfully!");
if (!/installplayer_migration/.test(cntx)) {
log.debug('handle initialization event');
isInitPlayerDone = true;
if (webPlayer) {
webPlayer.loginUser();
} else {
player_login(p_login_gametap, p_sso_token_gametap);
} 
} else {
log.debug('handle migration event');
ProgressHandler(100);
$('btnContinueUnactive').addClassName('displayNone');
var next = $('btnContinueActive').removeClassName('displayNone').down('a');
var profile = new UrlSeoConstructor('PROFILE_DASHBOARD');
next.href = profile.construct();
}
break;
case stepCodes.LOAD_GAME_COMPLETE:
log.debug("load game completed successfully! "+status);
if (webPlayer) {
webPlayer.playGame();
} else {
if (autoLaunch == true) {
player_play();
}
}
break;
case stepCodes.PLAY_GAME_COMPLETE:
log.debug("play game completed successfully! "+status);
if (typeof(gameManager) == 'object') {
if (gameManager.inprocess.browser) {
log.debug('try get controllers for browser game');
gameManager.updateBrowserGameController();
}
}
ww.hide();
var rpUrl = new UrlConstructor('profile/recently_played');
rpUrl.addVar('technoId', status);
new Ajax.Request(rpUrl.construct(), {
onComplete: function(request){
}
});
break;
case stepCodes.INITIAL_PRECACHE_DOWNLOAD_COMPLETE:
log.debug("game is precached. techno id: "+status);
if (typeof(myGames) == 'object') myGames.updateReadyToPlayCB(status);
break;
case stepCodes.DOWNLOAD_STARTS:
log.debug('download starts');
if (typeof(myGames) == 'object') {
myGames.gameDownloadingStartCB(myGames.inDownload.keys().first());
} 
break;
case stepCodes.DOWNLOAD_STOPS:
log.debug('download stops');
if (typeof(myGames) == 'object') {
myGames.gameDownloadingStopCB(myGames.inDownload.keys().first());
}
break;
case stepCodes.FULLY_DOWNLOADED:
log.debug("fully downloaded game. techno id: " + status);
if (/profile_games/.test(cntx) && typeof(myGames) == 'object') {
myGames.moveGameToCompletedCB(status);
}  
try {
var seedLaunch = new UrlSeoConstructor('SPEED_LAUNCH');
createShortCutGameTap(status, seedLaunch.construct());
log.err('speed launch created. Techno id: ' + status);
} catch (e) {
log.err('speed launch create failed. msg: ' + e.message);
}
break;
case stepCodes.PRIORITY_CHANGED:
break;
case stepCodes.GAME_ENQUEUED:
log.debug("add game to the queue. techno id: "+status);
if (webPlayer) {
webPlayer.setInDownloadState();
} else {
if (typeof(gameManager) == 'object') {
gameManager.onGameAddToListCB(status);
gameManager.downloadClick = status; 
}     
}
break;
case stepCodes.GAME_DEQUEUED:
log.debug("removed game from the queue. techno id: "+status);
if (typeof(myGames) == 'object') myGames.removeFromListCB(status);
break;
case stepCodes.GAME_REMOVED:
log.debug("game removed. techno id: "+status);
if (typeof(myGames) == 'object') myGames.removeGameCB(status, false);
break;
default:
log.debug('unknown step. code: ' + step + ' status: ' + status);
}
}catch(e){
log.err(e);
}
}
/*
* StatusHandler Step: 10006 Status: 144981960
*/
function ErrorCodeHandler(errorCode){
log.err('errorCodeHandler error code: ' + errorCode);
if (typeof(gameManager) == 'object' && (4100 <= errorCode) && (errorCode <= 4199)) {
gameManager.inprocess.technoId = 0;
}
switch(errorCode) {
case 5010:
if (webPlayer) webPlayer.onGamePause();
break;
case 5011:
if (webPlayer) webPlayer.onGameResume();
break;
case 5008:  
log.debug('HL_EVENT_MIGRATION_START');
break;
case 5009:  
log.debug('HL_EVENT_MIGRATION_END');
ProgressHandler(100);
navigator.plugins.refresh(false);
Cookie.set('migration', 1, {
expires: (1).year(),
path: '/'
});
var sendEmailUrl = new UrlConstructor('install_player/send_email');
new Ajax.Request(sendEmailUrl.construct(), {
method: 'get',
onSuccess: function(transport) {
var obj = transport.responseText.evalJSON();
var er = new ErrorManager(obj);
er.getErrors();
$('btnContinueUnactive').addClassName('displayNone');
var next = $('btnContinueActive').removeClassName('displayNone').down('a');
var s = new UrlSeoConstructor('PROFILE_DASHBOARD');
next.href = s.construct();
}.bind(this)
});
break;
case 5001:
log.debug('HL_EVENT_CONTROLLER_PLUGIN');
if (typeof(gameManager) == 'object') {
log.debug('try get controllers for browser game');
gameManager.updateBrowserGameController();
}
case 5002:
log.debug('HL_EVENT_CONTROLLER_REMOVE');
if (typeof(gameControls) == 'object') {
log.debug('switch to keyboard controller tab');
gameControls.keyboardActive();
}
default:    
log.debug('Unknown error. Code: ' + errorCode);
showErrorModalWindow(errorCode);
} 
}
function UpdaterProgressHandler(progress, action, target){
log.debug('UpdaterProgressHandler. progress: ' + progress);
var progress = parseInt(progress);
if (progress == 0) return null;
$('progressElement').down('.currentProgress').setStyle({width: ((progress == 100) ? 101 : progress) + '%'});
$('progressElement').down('.textValueProgress').update(progress + '%');
}
function UpdaterErrorCodeHandler(errorCode) {
log.err('UpdaterErrorCodeHandler: ' + errorCode);
showErrorModalWindow(errorCode);
}
function UpdaterStatusHandler(step, status){
var stepCodes = new Object();
stepCodes.INSTALL_NEW_PLAYER                = 1;     // the install is currenly in progress
stepCodes.UPDATE_END                        = 2;     // update done
log.debug('UpdaterStatusHandler: ' + step);
switch(step) {
case 1: 
log.debug('New player installing ...');
break;
case 2: 
$('btnContinueUnactive').addClassName('displayNone');
var next = $('btnContinueActive').removeClassName('displayNone').down('a');
if (Prototype.Browser.Gecko) {
log.debug('apply navigator.plugins.refresh(false)');
navigator.plugins.refresh(false);
}
var step = gameTapPlayerType();
log.debug('Update player done. Next step: ' + step);
switch(step) {
case 1:
case 2:
case 3:
case 5:
var s = new UrlSeoConstructor('INSTALLPLAYER_MIGRATION');
s.addVar('browser', (Prototype.Browser.IE) ? 'IE' : 'FF');
next.href = s.construct();
break;
case 0:
case 4:
var s = new UrlSeoConstructor('PROFILE_DASHBOARD');
next.href = s.construct();
break;
}
break;
}
}
function parseUri(str) {
var o   = parseUri.options,
m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
uri = {},
i   = 14;
while (i--) uri[o.key[i]] = m[i] || "";
uri[o.q.name] = {};
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
if ($1) uri[o.q.name][$1] = $2;
});
return uri;
};
parseUri.options = {
strictMode: false,
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
q:   {
name:   "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
}
};
function getElementsByName_iefix(tag, name) {
var elem = document.getElementsByTagName(tag);
var arr = new Array();
for(i = 0, iarr = 0; i < elem.length; i++) {
att = elem[i].getAttribute("name");
if(att == name) {
arr[iarr] = elem[i];
iarr++;
}
}
return arr;
}
function transparentbg(tbg_id) {
var bak = getElementsByName_iefix("div", tbg_id);
for(i = 0; i < bak.length; i++) {
bak[i].style.height = bak[i].parentNode.clientHeight + "px";
}
}


// LiveValidation 1.3 (prototype.js version)
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
// LiveValidation is licensed under the terms of the MIT License
var LiveValidation=Class.create();Object.extend(LiveValidation,{VERSION:"1.3 prototype",TEXTAREA:1,TEXT:2,PASSWORD:3,CHECKBOX:4,SELECT:5,FILE:6,massValidate:function(C){var D=true;for(var B=0,A=C.length;B<A;++B){var E=C[B].validate();if(D){D=E;}}return D;}});LiveValidation.prototype={validClass:"LV_valid",invalidClass:"LV_invalid",messageClass:"LV_validation_message",validFieldClass:"LV_valid_field",invalidFieldClass:"LV_invalid_field",initialize:function(B,A){if(!B){throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");}this.element=$(B);if(!this.element){throw new Error("LiveValidation::initialize - No element with reference or id of '"+B+"' exists!");}this.elementType=this.getElementType();this.validations=[];this.form=this.element.form;this.options=Object.extend({validMessage:"Thankyou!",onValid:function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();},onInvalid:function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();},insertAfterWhatNode:this.element,onlyOnBlur:false,wait:0,onlyOnSubmit:false},A||{});var C=this.options.insertAfterWhatNode||this.element;this.options.insertAfterWhatNode=$(C);Object.extend(this,this.options);if(this.form){this.formObj=LiveValidationForm.getInstance(this.form);this.formObj.addField(this);}this.boundFocus=this.doOnFocus.bindAsEventListener(this);Event.observe(this.element,"focus",this.boundFocus);if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.boundClick=this.validate.bindAsEventListener(this);Event.observe(this.element,"click",this.boundClick);case LiveValidation.SELECT:case LiveValidation.FILE:this.boundChange=this.validate.bindAsEventListener(this);Event.observe(this.element,"change",this.boundChange);break;default:if(!this.onlyOnBlur){this.boundKeyup=this.deferValidation.bindAsEventListener(this);Event.observe(this.element,"keyup",this.boundKeyup);}this.boundBlur=this.validate.bindAsEventListener(this);Event.observe(this.element,"blur",this.boundBlur);}}},destroy:function(){if(this.formObj){this.formObj.removeField(this);this.formObj.destroy();}Event.stopObserving(this.element,"focus",this.boundFocus);if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:Event.stopObserving(this.element,"click",this.boundClick);case LiveValidation.SELECT:case LiveValidation.FILE:Event.stopObserving(this.element,"change",this.boundChange);break;default:if(!this.onlyOnBlur){Event.stopObserving(this.element,"keyup",this.boundKeyup);}Event.stopObserving(this.element,"blur",this.boundBlur);}}this.validations=[];this.removeMessageAndFieldClass();},add:function(A,B){this.validations.push({type:A,params:B||{}});return this;},remove:function(A,B){this.validations=this.validations.reject(function(C){return(C.type==A&&C.params==B);});return this;},deferValidation:function(A){if(this.wait>=300){this.removeMessageAndFieldClass();}if(this.timeout){clearTimeout(this.timeout);}this.timeout=setTimeout(this.validate.bind(this),this.wait);},doOnBlur:function(){this.focused=false;this.validate();},doOnFocus:function(){this.focused=true;this.removeMessageAndFieldClass();},getElementType:function(){switch(true){case (this.element.nodeName.toUpperCase()=="TEXTAREA"):return LiveValidation.TEXTAREA;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="TEXT"):return LiveValidation.TEXT;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="PASSWORD"):return LiveValidation.PASSWORD;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="CHECKBOX"):return LiveValidation.CHECKBOX;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="FILE"):return LiveValidation.FILE;case (this.element.nodeName.toUpperCase()=="SELECT"):return LiveValidation.SELECT;case (this.element.nodeName.toUpperCase()=="INPUT"):throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an "+this.element.type+" input!");default:throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");}},doValidations:function(){this.validationFailed=false;for(var C=0,A=this.validations.length;C<A;++C){var B=this.validations[C];switch(B.type){case Validate.Presence:case Validate.Confirmation:case Validate.Acceptance:this.displayMessageWhenEmpty=true;this.validationFailed=!this.validateElement(B.type,B.params);break;default:this.validationFailed=!this.validateElement(B.type,B.params);break;}if(this.validationFailed){return false;}}this.message=this.validMessage;return true;},validateElement:function(A,C){var D=(this.elementType==LiveValidation.SELECT)?this.element.options[this.element.selectedIndex].value:this.element.value;if(A==Validate.Acceptance){if(this.elementType!=LiveValidation.CHECKBOX){throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");}D=this.element.checked;}var E=true;try{A(D,C);}catch(B){if(B instanceof Validate.Error){if(D!==""||(D===""&&this.displayMessageWhenEmpty)){this.validationFailed=true;this.message=B.message;E=false;}}else{throw B;}}finally{return E;}},validate:function(){if(!this.element.disabled){var A=this.doValidations();if(A){this.onValid();return true;}else{this.onInvalid();return false;}}else{return true;}},enable:function(){this.element.disabled=false;return this;},disable:function(){this.element.disabled=true;this.removeMessageAndFieldClass();return this;},createMessageSpan:function(){var A=document.createElement("span");var B=document.createTextNode(this.message);A.appendChild(B);return A;},insertMessage:function(B){this.removeMessage();var A=this.validationFailed?this.invalidClass:this.validClass;if((this.displayMessageWhenEmpty&&(this.elementType==LiveValidation.CHECKBOX||this.element.value==""))||this.element.value!=""){$(B).addClassName(this.messageClass+(" "+A));if(nxtSibling=this.insertAfterWhatNode.nextSibling){this.insertAfterWhatNode.parentNode.insertBefore(B,nxtSibling);}else{this.insertAfterWhatNode.parentNode.appendChild(B);}}},addFieldClass:function(){this.removeFieldClass();if(!this.validationFailed){if(this.displayMessageWhenEmpty||this.element.value!=""){if(!this.element.hasClassName(this.validFieldClass)){this.element.addClassName(this.validFieldClass);}}}else{if(!this.element.hasClassName(this.invalidFieldClass)){this.element.addClassName(this.invalidFieldClass);}}},removeMessage:function(){if(nxtEl=this.insertAfterWhatNode.next("."+this.messageClass)){nxtEl.remove();}},removeFieldClass:function(){this.element.removeClassName(this.invalidFieldClass);this.element.removeClassName(this.validFieldClass);},removeMessageAndFieldClass:function(){this.removeMessage();this.removeFieldClass();}};var LiveValidationForm=Class.create();Object.extend(LiveValidationForm,{instances:{},getInstance:function(A){var B=Math.random()*Math.random();if(!A.id){A.id="formId_"+B.toString().replace(/\./,"")+new Date().valueOf();}if(!LiveValidationForm.instances[A.id]){LiveValidationForm.instances[A.id]=new LiveValidationForm(A);}return LiveValidationForm.instances[A.id];}});LiveValidationForm.prototype={initialize:function(A){this.element=$(A);this.fields=[];this.oldOnSubmit=this.element.onsubmit||function(){};this.element.onsubmit=function(C){var B=(LiveValidation.massValidate(this.fields))?this.oldOnSubmit.call(this.element,C)!==false:false;if(!B){Event.stop(C);}}.bindAsEventListener(this);},addField:function(A){this.fields.push(A);},removeField:function(A){this.fields=this.fields.without(A);},destroy:function(A){if(this.fields.length!=0&&!A){return false;}this.element.onsubmit=this.oldOnSubmit;LiveValidationForm.instances[this.element.id]=null;return true;}};var Validate={Presence:function(A,B){var C=Object.extend({failureMessage:"Can't be empty!"},B||{});if(A===""||A===null||A===undefined){Validate.fail(C.failureMessage);}return true;},Numericality:function(B,C){var A=B;var B=Number(B);var C=C||{};var D={notANumberMessage:C.notANumberMessage||"Must be a number!",notAnIntegerMessage:C.notAnIntegerMessage||"Must be an integer!",wrongNumberMessage:C.wrongNumberMessage||"Must be "+C.is+"!",tooLowMessage:C.tooLowMessage||"Must not be less than "+C.minimum+"!",tooHighMessage:C.tooHighMessage||"Must not be more than "+C.maximum+"!",is:((C.is)||(C.is==0))?C.is:null,minimum:((C.minimum)||(C.minimum==0))?C.minimum:null,maximum:((C.maximum)||(C.maximum==0))?C.maximum:null,onlyInteger:C.onlyInteger||false};if(!isFinite(B)){Validate.fail(D.notANumberMessage);}if(D.onlyInteger&&((/\.0+$|\.$/.test(String(A)))||(B!=parseInt(B)))){Validate.fail(D.notAnIntegerMessage);}switch(true){case (D.is!==null):if(B!=Number(D.is)){Validate.fail(D.wrongNumberMessage);}break;case (D.minimum!==null&&D.maximum!==null):Validate.Numericality(B,{tooLowMessage:D.tooLowMessage,minimum:D.minimum});Validate.Numericality(B,{tooHighMessage:D.tooHighMessage,maximum:D.maximum});break;case (D.minimum!==null):if(B<Number(D.minimum)){Validate.fail(D.tooLowMessage);}break;case (D.maximum!==null):if(B>Number(D.maximum)){Validate.fail(D.tooHighMessage);}break;}return true;},Format:function(A,B){var A=String(A);var C=Object.extend({failureMessage:"Not valid!",pattern:/./,negate:false},B||{});if(!C.negate&&!C.pattern.test(A)){Validate.fail(C.failureMessage);}if(C.negate&&C.pattern.test(A)){Validate.fail(C.failureMessage);}return true;},Email:function(A,B){var C=Object.extend({failureMessage:"Must be a valid email address!"},B||{});Validate.Format(A,{failureMessage:C.failureMessage,pattern:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});return true;},Length:function(A,B){var A=String(A);var B=B||{};var C={wrongLengthMessage:B.wrongLengthMessage||"Must be "+B.is+" characters long!",tooShortMessage:B.tooShortMessage||"Must not be less than "+B.minimum+" characters long!",tooLongMessage:B.tooLongMessage||"Must not be more than "+B.maximum+" characters long!",is:((B.is)||(B.is==0))?B.is:null,minimum:((B.minimum)||(B.minimum==0))?B.minimum:null,maximum:((B.maximum)||(B.maximum==0))?B.maximum:null};switch(true){case (C.is!==null):if(A.length!=Number(C.is)){Validate.fail(C.wrongLengthMessage);}break;case (C.minimum!==null&&C.maximum!==null):Validate.Length(A,{tooShortMessage:C.tooShortMessage,minimum:C.minimum});Validate.Length(A,{tooLongMessage:C.tooLongMessage,maximum:C.maximum});break;case (C.minimum!==null):if(A.length<Number(C.minimum)){Validate.fail(C.tooShortMessage);}break;case (C.maximum!==null):if(A.length>Number(C.maximum)){Validate.fail(C.tooLongMessage);}break;default:throw new Error("Validate::Length - Length(s) to validate against must be provided!");}return true;},Inclusion:function(C,D){var E=Object.extend({failureMessage:"Must be included in the list!",within:[],allowNull:false,partialMatch:false,caseSensitive:true,negate:false},D||{});if(E.allowNull&&C==null){return true;}if(!E.allowNull&&C==null){Validate.fail(E.failureMessage);}if(!E.caseSensitive){var A=[];E.within.each(function(F){if(typeof F=="string"){F=F.toLowerCase();}A.push(F);});E.within=A;if(typeof C=="string"){C=C.toLowerCase();}}var B=(E.within.indexOf(C)==-1)?false:true;if(E.partialMatch){B=false;E.within.each(function(F){if(C.indexOf(F)!=-1){B=true;}});}if((!E.negate&&!B)||(E.negate&&B)){Validate.fail(E.failureMessage);}return true;},Exclusion:function(A,B){var C=Object.extend({failureMessage:"Must not be included in the list!",within:[],allowNull:false,partialMatch:false,caseSensitive:true},B||{});C.negate=true;Validate.Inclusion(A,C);return true;},Confirmation:function(A,B){if(!B.match){throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");}var C=Object.extend({failureMessage:"Does not match!",match:null},B||{});C.match=$(B.match);if(!C.match){throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '"+C.match+"'!");}if(A!=C.match.value){Validate.fail(C.failureMessage);}return true;},Acceptance:function(A,B){var C=Object.extend({failureMessage:"Must be accepted!"},B||{});if(!A){Validate.fail(C.failureMessage);}return true;},Custom:function(A,B){var C=Object.extend({against:function(){return true;},args:{},failureMessage:"Not valid!"},B||{});if(!C.against(A,C.args)){Validate.fail(C.failureMessage);}return true;},now:function(A,D,C){if(!A){throw new Error("Validate::now - Validation function must be provided!");}var E=true;try{A(D,C||{});}catch(B){if(B instanceof Validate.Error){E=false;}else{throw B;}}finally{return E;}},Error:function(A){this.message=A;this.name="ValidationError";},fail:function(A){throw new Validate.Error(A);}};
