/*  Prototype JavaScript framework, version 1.5.0
*  (c) 2005-2007 Sam Stephenson
*
*  Prototype is freely distributable under the terms of an MIT-style license.
*  For details, see the Prototype web site: http://prototype.conio.net/
*
/*--------------------------------------------------------------------------*/
var Prototype = {
Version: '1.5.0',
BrowserFeatures: {
XPath: !!document.evaluate
},
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
emptyFunction: function() {},
K: function(x) { return x }
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var Abstract = new Object();
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 === undefined) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : object.toString();
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
},
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);
}
});
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
Function.prototype.bindAsEventListener = function(object) {
var __method = this, args = $A(arguments), object = args.shift();
return function(event) {
return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
}
}
Object.extend(Number.prototype, {
toColorPart: function() {
var digits = this.toString(16);
if (this < 16) return '0' + digits;
return digits;
},
succ: function() {
return this + 1;
},
times: function(iterator) {
$R(0, this, true).each(iterator);
return this;
}
});
var Try = {
these: function() {
var returnValue;
for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) {}
}
return returnValue;
}
}
/*--------------------------------------------------------------------------*/
var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
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);
},
stop: function() {
if (!this.timer) return;
clearInterval(this.timer);
this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = true;
this.callback(this);
} finally {
this.currentlyExecuting = false;
}
}
}
}
String.interpret = function(value){
return value == null ? '' : String(value);
}
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 = count === undefined ? 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 this;
},
truncate: function(length, truncation) {
length = length || 30;
truncation = truncation === undefined ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : 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 div = document.createElement('div');
var text = document.createTextNode(this);
div.appendChild(text);
return div.innerHTML;
},
unescapeHTML: function() {
var div = document.createElement('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 name = decodeURIComponent(pair[0]);
var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
if (hash[name] !== undefined) {
if (hash[name].constructor != Array)
hash[name] = [hash[name]];
if (value) hash[name].push(value);
}
else hash[name] = value;
}
return hash;
});
},
toArray: function() {
return this.split('');
},
succ: function() {
return this.slice(0, this.length - 1) +
String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
},
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.replace(/\\/g, '\\\\');
if (useDoubleQuotes)
return '"' + escapedString.replace(/"/g, '\\"') + '"';
else
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
}
});
String.prototype.gsub.prepareReplacement = function(replacement) {
if (typeof replacement == 'function') return replacement;
var template = new Template(replacement);
return function(match) { return template.evaluate(match) };
}
String.prototype.parseQuery = String.prototype.toQueryParams;
var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
initialize: function(template, pattern) {
this.template = template.toString();
this.pattern  = pattern || Template.Pattern;
},
evaluate: function(object) {
return this.template.gsub(this.pattern, function(match) {
var before = match[1];
if (before == '\\') return match[2];
return before + String.interpret(object[match[3]]);
});
}
}
var $break    = new Object();
var $continue = new Object();
var Enumerable = {
each: function(iterator) {
var index = 0;
try {
this._each(function(value) {
try {
iterator(value, index++);
} catch (e) {
if (e != $continue) throw e;
}
});
} catch (e) {
if (e != $break) throw e;
}
return this;
},
eachSlice: function(number, iterator) {
var index = -number, slices = [], array = this.toArray();
while ((index += number) < array.length)
slices.push(array.slice(index, index+number));
return slices.map(iterator);
},
all: function(iterator) {
var result = true;
this.each(function(value, index) {
result = result && !!(iterator || Prototype.K)(value, index);
if (!result) throw $break;
});
return result;
},
any: function(iterator) {
var result = false;
this.each(function(value, index) {
if (result = !!(iterator || Prototype.K)(value, index))
throw $break;
});
return result;
},
collect: function(iterator) {
var results = [];
this.each(function(value, index) {
results.push((iterator || Prototype.K)(value, index));
});
return results;
},
detect: function(iterator) {
var result;
this.each(function(value, index) {
if (iterator(value, index)) {
result = value;
throw $break;
}
});
return result;
},
findAll: function(iterator) {
var results = [];
this.each(function(value, index) {
if (iterator(value, index))
results.push(value);
});
return results;
},
grep: function(pattern, iterator) {
var results = [];
this.each(function(value, index) {
var stringValue = value.toString();
if (stringValue.match(pattern))
results.push((iterator || Prototype.K)(value, index));
})
return results;
},
include: function(object) {
var found = false;
this.each(function(value) {
if (value == object) {
found = true;
throw $break;
}
});
return found;
},
inGroupsOf: function(number, fillWith) {
fillWith = fillWith === undefined ? null : fillWith;
return this.eachSlice(number, function(slice) {
while(slice.length < number) slice.push(fillWith);
return slice;
});
},
inject: function(memo, iterator) {
this.each(function(value, index) {
memo = iterator(memo, value, index);
});
return memo;
},
invoke: function(method) {
var args = $A(arguments).slice(1);
return this.map(function(value) {
return value[method].apply(value, args);
});
},
max: function(iterator) {
var result;
this.each(function(value, index) {
value = (iterator || Prototype.K)(value, index);
if (result == undefined || value >= result)
result = value;
});
return result;
},
min: function(iterator) {
var result;
this.each(function(value, index) {
value = (iterator || Prototype.K)(value, index);
if (result == undefined || value < result)
result = value;
});
return result;
},
partition: function(iterator) {
var trues = [], falses = [];
this.each(function(value, index) {
((iterator || Prototype.K)(value, index) ?
trues : falses).push(value);
});
return [trues, falses];
},
pluck: function(property) {
var results = [];
this.each(function(value, index) {
results.push(value[property]);
});
return results;
},
reject: function(iterator) {
var results = [];
this.each(function(value, index) {
if (!iterator(value, index))
results.push(value);
});
return results;
},
sortBy: function(iterator) {
return this.map(function(value, index) {
return {value: value, criteria: iterator(value, index)};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
},
toArray: function() {
return this.map();
},
zip: function() {
var iterator = Prototype.K, args = $A(arguments);
if (typeof args.last() == 'function')
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,
member:  Enumerable.include,
entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0, length = iterable.length; i < length; i++)
results.push(iterable[i]);
return results;
}
}
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(value && value.constructor == Array ?
value.flatten() : [value]);
});
},
without: function() {
var values = $A(arguments);
return this.select(function(value) {
return !values.include(value);
});
},
indexOf: function(object) {
for (var i = 0, length = this.length; i < length; i++)
if (this[i] == object) return i;
return -1;
},
reverse: function(inline) {
return (inline !== false ? this : this.toArray())._reverse();
},
reduce: function() {
return this.length > 1 ? this : this[0];
},
uniq: function() {
return this.inject([], function(array, value) {
return array.include(value) ? array : array.concat([value]);
});
},
clone: function() {
return [].concat(this);
},
size: function() {
return this.length;
},
inspect: function() {
return '[' + this.map(Object.inspect).join(', ') + ']';
}
});
Array.prototype.toArray = Array.prototype.clone;
function $w(string){
string = string.strip();
return string ? string.split(/\s+/) : [];
}
if(window.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(arguments[i].constructor == Array) {
for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
array.push(arguments[i][j]);
} else {
array.push(arguments[i]);
}
}
return array;
}
}
var Hash = function(obj) {
Object.extend(this, obj || {});
};
Object.extend(Hash, {
toQueryString: function(obj) {
var parts = [];
this.prototype._each.call(obj, function(pair) {
if (!pair.key) return;
if (pair.value && pair.value.constructor == Array) {
var values = pair.value.compact();
if (values.length < 2) pair.value = values.reduce();
else {
key = encodeURIComponent(pair.key);
values.each(function(value) {
value = value != undefined ? encodeURIComponent(value) : '';
parts.push(key + '=' + encodeURIComponent(value));
});
return;
}
}
if (pair.value == undefined) pair[1] = '';
parts.push(pair.map(encodeURIComponent).join('='));
});
return parts.join('&');
}
});
Object.extend(Hash.prototype, Enumerable);
Object.extend(Hash.prototype, {
_each: function(iterator) {
for (var key in this) {
var value = this[key];
if (value && value == Hash.prototype[key]) continue;
var pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
},
keys: function() {
return this.pluck('key');
},
values: function() {
return this.pluck('value');
},
merge: function(hash) {
return $H(hash).inject(this, function(mergedHash, pair) {
mergedHash[pair.key] = pair.value;
return mergedHash;
});
},
remove: function() {
var result;
for(var i = 0, length = arguments.length; i < length; i++) {
var value = this[arguments[i]];
if (value !== undefined){
if (result === undefined) result = value;
else {
if (result.constructor != Array) result = [result];
result.push(value)
}
}
delete this[arguments[i]];
}
return result;
},
toQueryString: function() {
return Hash.toQueryString(this);
},
inspect: function() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
}
});
function $H(object) {
if (object && object.constructor == Hash) return object;
return new Hash(object);
};
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
initialize: function(start, end, exclusive) {
this.start = start;
this.end = end;
this.exclusive = exclusive;
},
_each: function(iterator) {
var value = this.start;
while (this.include(value)) {
iterator(value);
value = value.succ();
}
},
include: function(value) {
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
});
var $R = function(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
}
var Ajax = {
getTransport: function() {
return Try.these(
function() {return new XMLHttpRequest()},
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
) || false;
},
activeRequestCount: 0
}
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
register: function(responder) {
if (!this.include(responder))
this.responders.push(responder);
},
unregister: function(responder) {
this.responders = this.responders.without(responder);
},
dispatch: function(callback, request, transport, json) {
this.each(function(responder) {
if (typeof responder[callback] == 'function') {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) {}
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate: function() {
Ajax.activeRequestCount++;
},
onComplete: function() {
Ajax.activeRequestCount--;
}
});
Ajax.Base = function() {};
Ajax.Base.prototype = {
setOptions: function(options) {
this.options = {
method:       'post',
asynchronous: true,
contentType:  'application/x-www-form-urlencoded',
encoding:     'UTF-8',
parameters:   ''
}
Object.extend(this.options, options || {});
this.options.method = this.options.method.toLowerCase();
if (typeof this.options.parameters == 'string')
this.options.parameters = this.options.parameters.toQueryParams();
}
}
Ajax.Request = Class.create();
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
_complete: false,
initialize: function(url, options) {
this.transport = Ajax.getTransport();
this.setOptions(options);
this.request(url);
},
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = this.options.parameters;
if (!['get', 'post'].include(this.method)) {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
params = Hash.toQueryString(params);
if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
// when GET, append parameters to URL
if (this.method == 'get' && params)
this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
try {
Ajax.Responders.dispatch('onCreate', this, this.transport);
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);
if (this.options.asynchronous)
setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
var body = this.method == 'post' ? (this.options.postBody || params) : null;
this.transport.send(body);
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
},
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete))
this.respondToReadyState(this.transport.readyState);
},
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
};
if (this.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 (typeof extras.push == 'function')
for (var i = 0, length = extras.length; i < length; i += 2)
headers[extras[i]] = extras[i+1];
else
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
}
for (var name in headers)
this.transport.setRequestHeader(name, headers[name]);
},
success: function() {
return !this.transport.status
|| (this.transport.status >= 200 && this.transport.status < 300);
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState];
var transport = this.transport, json = this.evalJSON();
if (state == 'Complete') {
try {
this._complete = true;
(this.options['on' + this.transport.status]
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(transport, json);
} catch (e) {
this.dispatchException(e);
}
if ((this.getHeader('Content-type') || 'text/javascript').strip().
match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
this.evalResponse();
}
try {
(this.options['on' + state] || Prototype.emptyFunction)(transport, json);
Ajax.Responders.dispatch('on' + state, this, transport, json);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name);
} catch (e) { return null }
},
evalJSON: function() {
try {
var json = this.getHeader('X-JSON');
return json ? eval('(' + json + ')') : null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval(this.transport.responseText);
} catch (e) {
this.dispatchException(e);
}
},
dispatchException: function(exception) {
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Updater = Class.create();
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
initialize: function(container, url, options) {
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
}
this.transport = Ajax.getTransport();
this.setOptions(options);
var onComplete = this.options.onComplete || Prototype.emptyFunction;
this.options.onComplete = (function(transport, param) {
this.updateContent();
onComplete(transport, param);
}).bind(this);
this.request(url);
},
updateContent: function() {
var receiver = this.container[this.success() ? 'success' : 'failure'];
var response = this.transport.responseText;
if (!this.options.evalScripts) response = response.stripScripts();
if (receiver = $(receiver)) {
if (this.options.insertion)
new this.options.insertion(receiver, response);
else
receiver.update(response);
}
if (this.success()) {
if (this.onComplete)
setTimeout(this.onComplete.bind(this), 10);
}
}
});
Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
initialize: function(container, url, options) {
this.setOptions(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = {};
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
updateComplete: function(request) {
if (this.options.decay) {
this.decay = (request.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = request.responseText;
}
this.timer = setTimeout(this.onTimerEvent.bind(this),
this.decay * this.frequency * 1000);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
elements.push($(arguments[i]));
return elements;
}
if (typeof element == 'string')
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(query.snapshotItem(i));
return results;
};
}
document.getElementsByClassName = function(className, parentElement) {
if (Prototype.BrowserFeatures.XPath) {
var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
return document._getElementsByXPath(q, parentElement);
} else {
var children = ($(parentElement) || document.body).getElementsByTagName('*');
var elements = [], child;
for (var i = 0, length = children.length; i < length; i++) {
child = children[i];
if (Element.hasClassName(child, className))
elements.push(Element.extend(child));
}
return elements;
}
};
/*--------------------------------------------------------------------------*/
if (!window.Element)
var Element = new Object();
Element.extend = function(element) {
if (!element || _nativeExtensions || element.nodeType == 3) return element;
try{
	if (!element._extended && element.tagName && element != window) {
		var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
		if (element.tagName == 'FORM')
		Object.extend(methods, Form.Methods);
		if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
		Object.extend(methods, Form.Element.Methods);
		Object.extend(methods, Element.Methods.Simulated);
		for (var property in methods) {
		var value = methods[property];
		if (typeof value == 'function' && !(property in element))
		element[property] = cache.findOrStore(value);
		}
	}
	element._extended = true;
}catch(e){

}
return element;
};
Element.extend.cache = {
findOrStore: function(value) {
return this[value] = this[value] || function() {
return value.apply(null, [this].concat($A(arguments)));
}
}
};
Element.Methods = {
visible: function(element) {
return $(element).style.display != 'none';
},
toggle: function(element) {
element = $(element);
Element[Element.visible(element) ? 'hide' : 'show'](element);
return element;
},
hide: function(element) {
$(element).style.display = 'none';
return element;
},
show: function(element) {
$(element).style.display = '';
return element;
},
remove: function(element) {
element = $(element);
element.parentNode.removeChild(element);
return element;
},
update: function(element, html) {
html = typeof html == 'undefined' ? '' : html.toString();
$(element).innerHTML = html.stripScripts();
setTimeout(function() {html.evalScripts()}, 10);
return element;
},
replace: function(element, html) {
element = $(element);
html = typeof html == 'undefined' ? '' : html.toString();
if (element.outerHTML) {
element.outerHTML = html.stripScripts();
} else {
var range = element.ownerDocument.createRange();
range.selectNodeContents(element);
element.parentNode.replaceChild(
range.createContextualFragment(html.stripScripts()), element);
}
setTimeout(function() {html.evalScripts()}, 10);
return element;
},
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 $A($(element).getElementsByTagName('*'));
},
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 (typeof selector == 'string')
selector = new Selector(selector);
return selector.match($(element));
},
up: function(element, expression, index) {
return Selector.findElement($(element).ancestors(), expression, index);
},
down: function(element, expression, index) {
return Selector.findElement($(element).descendants(), expression, index);
},
previous: function(element, expression, index) {
return Selector.findElement($(element).previousSiblings(), expression, index);
},
next: function(element, expression, index) {
return Selector.findElement($(element).nextSiblings(), expression, index);
},
getElementsBySelector: function() {
var args = $A(arguments), element = $(args.shift());
return Selector.findChildElements(element, args);
},
getElementsByClassName: function(element, className) {
return document.getElementsByClassName(className, element);
},
readAttribute: function(element, name) {
element = $(element);
if (document.all && !window.opera) {
var t = Element._attributeTranslations;
if (t.values[name]) return t.values[name](element, name);
if (t.names[name])  name = t.names[name];
var attribute = element.attributes[name];
if(attribute) return attribute.nodeValue;
}
return element.getAttribute(name);
},
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;
if (elementClassName.length == 0) return false;
if (elementClassName == className ||
elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
return true;
return false;
},
addClassName: function(element, className) {
if (!(element = $(element))) return;
Element.classNames(element).add(className);
return element;
},
removeClassName: function(element, className) {
if (!(element = $(element))) return;
Element.classNames(element).remove(className);
return element;
},
toggleClassName: function(element, className) {
if (!(element = $(element))) return;
Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
return element;
},
observe: function() {
Event.observe.apply(Event, arguments);
return $A(arguments).first();
},
stopObserving: function() {
Event.stopObserving.apply(Event, arguments);
return $A(arguments).first();
},
// 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.match(/^\s*$/);
},
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
while (element = element.parentNode)
if (element == ancestor) return true;
return false;
},
scrollTo: function(element) {
element = $(element);
var pos = Position.cumulativeOffset(element);
window.scrollTo(pos[0], pos[1]);
return element;
},
getStyle: function(element, style) {
element = $(element);
if (['float','cssFloat'].include(style))
style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
style = style.camelize();
var value = element.style[style];
if (!value) {
if (document.defaultView && document.defaultView.getComputedStyle) {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css[style] : null;
} else if (element.currentStyle) {
value = element.currentStyle[style];
}
}
if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
value = element['offset'+style.capitalize()] + 'px';
if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
if (Element.getStyle(element, 'position') == 'static') value = 'auto';
if(style == 'opacity') {
if(value) return parseFloat(value);
if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
if(value[1]) return parseFloat(value[1]) / 100;
return 1.0;
}
return value == 'auto' ? null : value;
},
setStyle: function(element, style) {
element = $(element);
for (var name in style) {
var value = style[name];
if(name == 'opacity') {
if (value == 1) {
value = (/Gecko/.test(navigator.userAgent) &&
!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
} else if(value == '') {
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
} else {
if(value < 0.00001) value = 0;
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')';
}
} else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
element.style[name.camelize()] = value;
}
return element;
},
getDimensions: function(element) {
element = $(element);
var display = $(element).getStyle('display');
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
makePositioned: function(element) {
element = $(element);
var pos = Element.getStyle(element, 'position');
if (pos == 'static' || !pos) {
element._madePositioned = true;
element.style.position = 'relative';
// Opera returns the offset relative to the positioning context, when an
// element is position relative but top and left have not been defined
if (window.opera) {
element.style.top = 0;
element.style.left = 0;
}
}
return element;
},
undoPositioned: function(element) {
element = $(element);
if (element._madePositioned) {
element._madePositioned = undefined;
element.style.position =
element.style.top =
element.style.left =
element.style.bottom =
element.style.right = '';
}
return element;
},
makeClipping: function(element) {
element = $(element);
if (element._overflow) return element;
element._overflow = element.style.overflow || 'auto';
if ((Element.getStyle(element, 'overflow') || 'visible') != '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;
}
};
Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
Element._attributeTranslations = {};
Element._attributeTranslations.names = {
colspan:   "colSpan",
rowspan:   "rowSpan",
valign:    "vAlign",
datetime:  "dateTime",
accesskey: "accessKey",
tabindex:  "tabIndex",
enctype:   "encType",
maxlength: "maxLength",
readonly:  "readOnly",
longdesc:  "longDesc"
};
Element._attributeTranslations.values = {
_getAttr: function(element, attribute) {
return element.getAttribute(attribute, 2);
},
_flag: function(element, attribute) {
return $(element).hasAttribute(attribute) ? attribute : null;
},
style: function(element) {
return element.style.cssText.toLowerCase();
},
title: function(element) {
var node = element.getAttributeNode('title');
return node.specified ? node.nodeValue : null;
}
};
Object.extend(Element._attributeTranslations.values, {
href: Element._attributeTranslations.values._getAttr,
src:  Element._attributeTranslations.values._getAttr,
disabled: Element._attributeTranslations.values._flag,
checked:  Element._attributeTranslations.values._flag,
readonly: Element._attributeTranslations.values._flag,
multiple: Element._attributeTranslations.values._flag
});
Element.Methods.Simulated = {
hasAttribute: function(element, attribute) {
var t = Element._attributeTranslations;
attribute = t.names[attribute] || attribute;
return $(element).getAttributeNode(attribute).specified;
}
};
// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
Element.Methods.update = function(element, html) {
element = $(element);
html = typeof html == 'undefined' ? '' : html.toString();
var tagName = element.tagName.toUpperCase();
if (['THEAD','TBODY','TR','TD'].include(tagName)) {
var div = document.createElement('div');
switch (tagName) {
case 'THEAD':
case 'TBODY':
div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
depth = 2;
break;
case 'TR':
div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
depth = 3;
break;
case 'TD':
div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
depth = 4;
}
$A(element.childNodes).each(function(node){
element.removeChild(node)
});
depth.times(function(){ div = div.firstChild });
$A(div.childNodes).each(
function(node){ element.appendChild(node) });
} else {
element.innerHTML = html.stripScripts();
}
setTimeout(function() {html.evalScripts()}, 10);
return element;
}
};
Object.extend(Element, Element.Methods);
var _nativeExtensions = false;
if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
var className = 'HTML' + tag + 'Element';
if(window[className]) return;
var klass = window[className] = {};
klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
});
Element.addMethods = function(methods) {
Object.extend(Element.Methods, methods || {});
function copy(methods, destination, onlyIfAbsent) {
onlyIfAbsent = onlyIfAbsent || false;
var cache = Element.extend.cache;
for (var property in methods) {
var value = methods[property];
if (!onlyIfAbsent || !(property in destination))
destination[property] = cache.findOrStore(value);
}
}
if (typeof HTMLElement != 'undefined') {
copy(Element.Methods, HTMLElement.prototype);
copy(Element.Methods.Simulated, HTMLElement.prototype, true);
copy(Form.Methods, HTMLFormElement.prototype);
[HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
copy(Form.Element.Methods, klass.prototype);
});
_nativeExtensions = true;
}
}
var Toggle = new Object();
Toggle.display = Element.toggle;
/*--------------------------------------------------------------------------*/
Abstract.Insertion = function(adjacency) {
this.adjacency = adjacency;
}
Abstract.Insertion.prototype = {
initialize: function(element, content) {
this.element = $(element);
this.content = content.stripScripts();
if (this.adjacency && this.element.insertAdjacentHTML) {
try {
this.element.insertAdjacentHTML(this.adjacency, this.content);
} catch (e) {
var tagName = this.element.tagName.toUpperCase();
if (['TBODY', 'TR'].include(tagName)) {
this.insertContent(this.contentFromAnonymousTable());
} else {
throw e;
}
}
} else {
this.range = this.element.ownerDocument.createRange();
if (this.initializeRange) this.initializeRange();
this.insertContent([this.range.createContextualFragment(this.content)]);
}
setTimeout(function() {content.evalScripts()}, 10);
},
contentFromAnonymousTable: function() {
var div = document.createElement('div');
div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
return $A(div.childNodes[0].childNodes[0].childNodes);
}
}
var Insertion = new Object();
Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
initializeRange: function() {
this.range.setStartBefore(this.element);
},
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.parentNode.insertBefore(fragment, this.element);
}).bind(this));
}
});
Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
initializeRange: function() {
this.range.selectNodeContents(this.element);
this.range.collapse(true);
},
insertContent: function(fragments) {
fragments.reverse(false).each((function(fragment) {
this.element.insertBefore(fragment, this.element.firstChild);
}).bind(this));
}
});
Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
initializeRange: function() {
this.range.selectNodeContents(this.element);
this.range.collapse(this.element);
},
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.appendChild(fragment);
}).bind(this));
}
});
Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
initializeRange: function() {
this.range.setStartAfter(this.element);
},
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.parentNode.insertBefore(fragment,
this.element.nextSibling);
}).bind(this));
}
});
/*--------------------------------------------------------------------------*/
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);
var Selector = Class.create();
Selector.prototype = {
initialize: function(expression) {
this.params = {classNames: []};
this.expression = expression.toString().strip();
this.parseExpression();
this.compileMatcher();
},
parseExpression: function() {
function abort(message) { throw 'Parse error in selector: ' + message; }
if (this.expression == '')  abort('empty expression');
var params = this.params, expr = this.expression, match, modifier, clause, rest;
while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
params.attributes = params.attributes || [];
params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
expr = match[1];
}
if (expr == '*') return this.params.wildcard = true;
while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
modifier = match[1], clause = match[2], rest = match[3];
switch (modifier) {
case '#':       params.id = clause; break;
case '.':       params.classNames.push(clause); break;
case '':
case undefined: params.tagName = clause.toUpperCase(); break;
default:        abort(expr.inspect());
}
expr = rest;
}
if (expr.length > 0) abort(expr.inspect());
},
buildMatchExpression: function() {
var params = this.params, conditions = [], clause;
if (params.wildcard)
conditions.push('true');
if (clause = params.id)
conditions.push('element.readAttribute("id") == ' + clause.inspect());
if (clause = params.tagName)
conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
if ((clause = params.classNames).length > 0)
for (var i = 0, length = clause.length; i < length; i++)
conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
if (clause = params.attributes) {
clause.each(function(attribute) {
var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
var splitValueBy = function(delimiter) {
return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
}
switch (attribute.operator) {
case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
case '|=':      conditions.push(
splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
); break;
case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
case '':
case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
}
});
}
return conditions.join(' && ');
},
compileMatcher: function() {
this.match = new Function('element', 'if (!element.tagName) return false; \
element = $(element); \
return ' + this.buildMatchExpression());
},
findElements: function(scope) {
var element;
if (element = $(this.params.id))
if (this.match(element))
if (!scope || Element.childOf(element, scope))
return [element];
scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
var results = [];
for (var i = 0, length = scope.length; i < length; i++)
if (this.match(element = scope[i]))
results.push(Element.extend(element));
return results;
},
toString: function() {
return this.expression;
}
}
Object.extend(Selector, {
matchElements: function(elements, expression) {
var selector = new Selector(expression);
return elements.select(selector.match.bind(selector)).map(Element.extend);
},
findElement: function(elements, expression, index) {
if (typeof expression == 'number') index = expression, expression = false;
return Selector.matchElements(elements, expression || '*')[index || 0];
},
findChildElements: function(element, expressions) {
return expressions.map(function(expression) {
return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
var selector = new Selector(expr);
return results.inject([], function(elements, result) {
return elements.concat(selector.findElements(result || element));
});
});
}).flatten();
}
});
function $$() {
return Selector.findChildElements(document, $A(arguments));
}
var Form = {
reset: function(form) {
$(form).reset();
return form;
},
serializeElements: function(elements, getHash) {
var data = elements.inject({}, function(result, element) {
if (!element.disabled && element.name) {
var key = element.name, value = $(element).getValue();
if (value != undefined) {
if (result[key]) {
if (result[key].constructor != Array) result[key] = [result[key]];
result[key].push(value);
}
else result[key] = value;
}
}
return result;
});
return getHash ? data : Hash.toQueryString(data);
}
};
Form.Methods = {
serialize: function(form, getHash) {
return Form.serializeElements(Form.getElements(form), getHash);
},
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().each(function(element) {
element.blur();
element.disabled = 'true';
});
return form;
},
enable: function(form) {
form = $(form);
form.getElements().each(function(element) {
element.disabled = '';
});
return form;
},
findFirstElement: function(form) {
return $(form).getElements().find(function(element) {
return element.type != 'hidden' && !element.disabled &&
['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
});
},
focusFirstElement: function(form) {
form = $(form);
form.findFirstElement().activate();
return form;
}
}
Object.extend(Form, Form.Methods);
/*--------------------------------------------------------------------------*/
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 Hash.toQueryString(pair);
}
}
return '';
},
getValue: function(element) {
element = $(element);
var method = element.tagName.toLowerCase();
return Form.Element.Serializers[method](element);
},
clear: function(element) {
$(element).value = '';
return element;
},
present: function(element) {
return $(element).value != '';
},
activate: function(element) {
element = $(element);
element.focus();
if (element.select && ( element.tagName.toLowerCase() != 'input' ||
!['button', 'reset', 'submit'].include(element.type) ) )
element.select();
return element;
},
disable: function(element) {
element = $(element);
element.disabled = true;
return element;
},
enable: function(element) {
element = $(element);
element.blur();
element.disabled = false;
return element;
}
}
Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;
var $F = Form.Element.getValue;
/*--------------------------------------------------------------------------*/
Form.Element.Serializers = {
input: function(element) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':
return Form.Element.Serializers.inputSelector(element);
default:
return Form.Element.Serializers.textarea(element);
}
},
inputSelector: function(element) {
return element.checked ? element.value : null;
},
textarea: function(element) {
return element.value;
},
select: function(element) {
return this[element.type == 'select-one' ?
'selectOne' : 'selectMany'](element);
},
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 = function() {}
Abstract.TimedObserver.prototype = {
initialize: function(element, frequency, callback) {
this.frequency = frequency;
this.element   = $(element);
this.callback  = callback;
this.lastValue = this.getValue();
this.registerCallback();
},
registerCallback: function() {
setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
onTimerEvent: function() {
var value = this.getValue();
var changed = ('string' == typeof this.lastValue && 'string' == typeof value
? this.lastValue != value : String(this.lastValue) != String(value));
if (changed) {
this.callback(this.element, value);
this.lastValue = value;
}
}
}
Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
getValue: function() {
return Form.serialize(this.element);
}
});
/*--------------------------------------------------------------------------*/
Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
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.bind(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();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
getValue: function() {
return Form.serialize(this.element);
}
});
if (!window.Event) {
var Event = new Object();
}
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,
element: function(event) {
return event.target || event.srcElement;
},
isLeftClick: function(event) {
return (((event.which) && (event.which == 1)) ||
((event.button) && (event.button == 1)));
},
pointerX: function(event) {
return event.pageX || (event.clientX +
(document.documentElement.scrollLeft || document.body.scrollLeft));
},
pointerY: function(event) {
return event.pageY || (event.clientY +
(document.documentElement.scrollTop || document.body.scrollTop));
},
stop: function(event) {
if (event.preventDefault) {
event.preventDefault();
event.stopPropagation();
} else {
event.returnValue = false;
event.cancelBubble = true;
}
},
// find the first node with the given tagName, starting from the
// node the event was triggered on; traverses the DOM upwards
findElement: function(event, tagName) {
var element = Event.element(event);
while (element.parentNode && (!element.tagName ||
(element.tagName.toUpperCase() != tagName.toUpperCase())))
element = element.parentNode;
return element;
},
observers: false,
_observeAndCache: function(element, name, observer, useCapture) {
if (!this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element, name, observer, useCapture]);
element.addEventListener(name, observer, useCapture);
} else if (element.attachEvent) {
this.observers.push([element, name, observer, useCapture]);
element.attachEvent('on' + name, observer);
}
},
unloadCache: function() {
if (!Event.observers) return;
for (var i = 0, length = Event.observers.length; i < length; i++) {
Event.stopObserving.apply(this, Event.observers[i]);
Event.observers[i][0] = null;
}
Event.observers = false;
},
observe: function(element, name, observer, useCapture) {
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|| element.attachEvent))
name = 'keydown';
Event._observeAndCache(element, name, observer, useCapture);
},
stopObserving: function(element, name, observer, useCapture) {
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|| element.detachEvent))
name = 'keydown';
if (element.removeEventListener) {
element.removeEventListener(name, observer, useCapture);
} else if (element.detachEvent) {
try {
element.detachEvent('on' + name, observer);
} catch (e) {}
}
}
});
/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
Event.observe(window, 'unload', Event.unloadCache, false);
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;
},
realOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.scrollTop  || 0;
valueL += element.scrollLeft || 0;
element = element.parentNode;
} while (element);
return [valueL, valueT];
},
cumulativeOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop  || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
},
positionedOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop  || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
if (element) {
if(element.tagName=='BODY') break;
var p = Element.getStyle(element, 'position');
if (p == 'relative' || p == 'absolute') break;
}
} while (element);
return [valueL, valueT];
},
offsetParent: 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;
},
// 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 = this.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 = this.realOffset(element);
this.xcomp = x + offsetcache[0] - this.deltaX;
this.ycomp = y + offsetcache[1] - this.deltaY;
this.offset = this.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;
},
page: 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)
if (Element.getStyle(element,'position')=='absolute') break;
} while (element = element.offsetParent);
element = forElement;
do {
if (!window.opera || element.tagName=='BODY') {
valueT -= element.scrollTop  || 0;
valueL -= element.scrollLeft || 0;
}
} while (element = element.parentNode);
return [valueL, valueT];
},
clone: function(source, target) {
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 = Position.page(source);
// find coordinate system to use
target = $(target);
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(target,'position') == 'absolute') {
parent = Position.offsetParent(target);
delta = Position.page(parent);
}
// 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)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
if(options.setHeight) target.style.height = source.offsetHeight + 'px';
},
absolutize: function(element) {
element = $(element);
if (element.style.position == 'absolute') return;
Position.prepare();
var offsets = Position.positionedOffset(element);
var top     = offsets[1];
var left    = offsets[0];
var width   = element.clientWidth;
var height  = element.clientHeight;
element._originalLeft   = left - parseFloat(element.style.left  || 0);
element._originalTop    = top  - parseFloat(element.style.top || 0);
element._originalWidth  = element.style.width;
element._originalHeight = element.style.height;
element.style.position = 'absolute';
element.style.top    = top + 'px';
element.style.left   = left + 'px';
element.style.width  = width + 'px';
element.style.height = height + 'px';
},
relativize: function(element) {
element = $(element);
if (element.style.position == 'relative') return;
Position.prepare();
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;
}
}
// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
Position.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 [valueL, valueT];
}
}
Element.addMethods();
/**
Copyright (c) 2005, Brad Neuberg, bkn3@columbia.edu
http://codinginparadise.org
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.
The JSON class near the end of this file is
Copyright 2005, JSON.org
*/
/** An object that provides DHTML history, history data, and bookmarking
for AJAX applications. */
window.dhtmlHistory = {
/** Initializes our DHTML history. You should
call this after the page is finished loading. */
/** public */ initialize: function() {
// only Internet Explorer needs to be explicitly initialized;
// other browsers don't have its particular behaviors.
// Basicly, IE doesn't autofill form data until the page
// is finished loading, which means historyStorage won't
// work until onload has been fired.
if (this.isInternetExplorer() == false) {
return;
}
// if this is the first time this page has loaded...
if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
this.fireOnNewListener = false;
this.firstLoad = true;
historyStorage.put("DhtmlHistory_pageLoaded", true);
}
// else if this is a fake onload event
else {
this.fireOnNewListener = true;
this.firstLoad = false;
}
},
/** Adds a history change listener. Note that
only one listener is supported at this
time. */
/** public */ addListener: function(callback) {
this.listener = callback;
// if the page was just loaded and we
// should not ignore it, fire an event
// to our new listener now
if (this.fireOnNewListener == true) {
this.fireHistoryEvent(this.currentLocation);
this.fireOnNewListener = false;
}
},
/** public */ add: function(newLocation, historyData) {
// most browsers require that we wait a certain amount of time before changing the
// location, such as 200 milliseconds; rather than forcing external callers to use
// window.setTimeout to account for this to prevent bugs, we internally handle this
// detail by using a 'currentWaitTime' variable and have requests wait in line
var self = this;
var addImpl = function() {
// indicate that the current wait time is now less
if (self.currentWaitTime > 0)
self.currentWaitTime = self.currentWaitTime - self.WAIT_TIME;
// remove any leading hash symbols on newLocation
newLocation = self.removeHash(newLocation);
// IE has a strange bug; if the newLocation
// is the same as _any_ preexisting id in the
// document, then the history action gets recorded
// twice; throw a programmer exception if there is
// an element with this ID
var idCheck = document.getElementById(newLocation);
if (idCheck != undefined || idCheck != null) {
var message =
"Exception: History locations can not have "
+ "the same value as _any_ id's "
+ "that might be in the document, "
+ "due to a bug in Internet "
+ "Explorer; please ask the "
+ "developer to choose a history "
+ "location that does not match "
+ "any HTML id's in this "
+ "document. The following ID "
+ "is already taken and can not "
+ "be a location: "
+ newLocation;
throw message;
}
// store the history data into history storage
historyStorage.put(newLocation, historyData);
// indicate to the browser to ignore this upcomming
// location change
self.ignoreLocationChange = true;
// indicate to IE that this is an atomic location change
// block
this.ieAtomicLocationChange = true;
// save this as our current location
self.currentLocation = newLocation;
// change the browser location
window.location.hash = newLocation;
// change the hidden iframe's location if on IE
if (self.isInternetExplorer())
self.iframe.src = "blank.html?" + newLocation;
// end of atomic location change block
// for IE
this.ieAtomicLocationChange = false;
};
// now execute this add request after waiting a certain amount of time, so as to
// queue up requests
window.setTimeout(addImpl, this.currentWaitTime);
// indicate that the next request will have to wait for awhile
this.currentWaitTime = this.currentWaitTime + this.WAIT_TIME;
},
/** public */ isFirstLoad: function() {
if (this.firstLoad == true) {
return true;
}
else {
return false;
}
},
/** public */ isInternational: function() {
return false;
},
/** public */ getVersion: function() {
return "0.05";
},
/** Gets the current hash value that is in the browser's
location bar, removing leading # symbols if they are present. */
/** public */ getCurrentLocation: function() {
var currentLocation = this.removeHash(window.location.hash);
return currentLocation;
},
/** Our current hash location, without the "#" symbol. */
/** private */ currentLocation: null,
/** Our history change listener. */
/** private */ listener: null,
/** A hidden IFrame we use in Internet Explorer to detect history
changes. */
/** private */ iframe: null,
/** Indicates to the browser whether to ignore location changes. */
/** private */ ignoreLocationChange: null,
/** The amount of time in milliseconds that we should wait between add requests.
Firefox is okay with 200 ms, but Internet Explorer needs 400. */
/** private */ WAIT_TIME: 200,
/** The amount of time in milliseconds an add request has to wait in line before being
run on a window.setTimeout. */
/** private */ currentWaitTime: 0,
/** A flag that indicates that we should fire a history change event
when we are ready, i.e. after we are initialized and
we have a history change listener. This is needed due to
an edge case in browsers other than Internet Explorer; if
you leave a page entirely then return, we must fire this
as a history change event. Unfortunately, we have lost
all references to listeners from earlier, because JavaScript
clears out. */
/** private */ fireOnNewListener: null,
/** A variable that indicates whether this is the first time
this page has been loaded. If you go to a web page, leave
it for another one, and then return, the page's onload
listener fires again. We need a way to differentiate
between the first page load and subsequent ones.
This variable works hand in hand with the pageLoaded
variable we store into historyStorage.*/
/** private */ firstLoad: null,
/** A variable to handle an important edge case in Internet
Explorer. In IE, if a user manually types an address into
their browser's location bar, we must intercept this by
continiously checking the location bar with an timer
interval. However, if we manually change the location
bar ourselves programmatically, when using our hidden
iframe, we need to ignore these changes. Unfortunately,
these changes are not atomic, so we surround them with
the variable 'ieAtomicLocationChange', that if true,
means we are programmatically setting the location and
should ignore this atomic chunked change. */
/** private */ ieAtomicLocationChange: null,
/** Creates the DHTML history infrastructure. */
/** private */ create: function() {
// get our initial location
var initialHash = this.getCurrentLocation();
// save this as our current location
this.currentLocation = initialHash;
// write out a hidden iframe for IE and
// set the amount of time to wait between add() requests
if (this.isInternetExplorer()) {
var html = "<iframe style='border: 0px; width: 1px; "
+ "height: 1px; position: absolute; bottom: 0px; "
+ "right: 0px; visibility: visible;' "
+ "name='DhtmlHistoryFrame' id='DhtmlHistoryFrame' "
+ "src='blank.html?" + initialHash + "'>"
+ "</iframe>"
//         document.write(html);
historyDiv.innerHTML = html;
// wait 400 milliseconds between history
// updates on IE, versus 200 on Firefox
this.WAIT_TIME = 400;
}
// add an unload listener for the page; this is
// needed for Firefox 1.5+ because this browser caches all
// dynamic updates to the page, which can break some of our
// logic related to testing whether this is the first instance
// a page has loaded or whether it is being pulled from the cache
var self = this;
window.onunload = function() {
self.firstLoad = null;
};
// determine if this is our first page load;
// for Internet Explorer, we do this in
// this.iframeLoaded(), which is fired on
// page load. We do it there because
// we have no historyStorage at this point
// in IE, which only exists after the page
// is finished loading for that browser
if (this.isInternetExplorer() == false) {
if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
this.ignoreLocationChange = true;
this.firstLoad = true;
historyStorage.put("DhtmlHistory_pageLoaded", true);
}
else {
// indicate that we want to pay attention
// to this location change
this.ignoreLocationChange = false;
// For browser's other than IE, fire
// a history change event; on IE,
// the event will be thrown automatically
// when it's hidden iframe reloads
// on page load.
// Unfortunately, we don't have any
// listeners yet; indicate that we want
// to fire an event when a listener
// is added.
this.fireOnNewListener = true;
}
}
else { // Internet Explorer
// the iframe will get loaded on page
// load, and we want to ignore this fact
this.ignoreLocationChange = true;
}
if (this.isInternetExplorer()) {
this.iframe = document.getElementById("DhtmlHistoryFrame");
}
// other browsers can use a location handler that checks
// at regular intervals as their primary mechanism;
// we use it for Internet Explorer as well to handle
// an important edge case; see checkLocation() for
// details
var self = this;
var locationHandler = function() {
self.checkLocation();
};
setInterval(locationHandler, 100);
},
/** Notify the listener of new history changes. */
/** private */ fireHistoryEvent: function(newHash) {
// extract the value from our history storage for
// this hash
var historyData = historyStorage.get(newHash);
// call our listener
this.listener.call(null, newHash, historyData);
},
/** Sees if the browsers has changed location.  This is the primary history mechanism
for Firefox. For Internet Explorer, we use this to handle an important edge case:
if a user manually types in a new hash value into their Internet Explorer location
bar and press enter, we want to intercept this and notify any history listener. */
/** private */ checkLocation: function() {
// ignore any location changes that we made ourselves
// for browsers other than Internet Explorer
if (this.isInternetExplorer() == false
&& this.ignoreLocationChange == true) {
this.ignoreLocationChange = false;
return;
}
// if we are dealing with Internet Explorer
// and we are in the middle of making a location
// change from an iframe, ignore it
if (this.isInternetExplorer() == false
&& this.ieAtomicLocationChange == true) {
return;
}
// get hash location
var hash = this.getCurrentLocation();
// see if there has been a change
if (hash == this.currentLocation)
return;
// on Internet Explorer, we need to intercept users manually
// entering locations into the browser; we do this by comparing
// the browsers location against the iframes location; if they
// differ, we are dealing with a manual event and need to
// place it inside our history, otherwise we can return
this.ieAtomicLocationChange = true;
if (this.isInternetExplorer()
&& this.getIFrameHash() != hash) {
this.iframe.src = "blank.html?" + hash;
}
else if (this.isInternetExplorer()) {
// the iframe is unchanged
return;
}
// save this new location
this.currentLocation = hash;
this.ieAtomicLocationChange = false;
// notify listeners of the change
this.fireHistoryEvent(hash);
},
/** Gets the current location of the hidden IFrames
that is stored as history. For Internet Explorer. */
/** private */ getIFrameHash: function() {
// get the new location
var historyFrame = document.getElementById("DhtmlHistoryFrame");
var doc = historyFrame.contentWindow.document;
var hash = new String(doc.location.search);
if (hash.length == 1 && hash.charAt(0) == "?")
hash = "";
else if (hash.length >= 2 && hash.charAt(0) == "?")
hash = hash.substring(1);
return hash;
},
/** Removes any leading hash that might be on a location. */
/** private */ removeHash: function(hashValue) {
if (hashValue == null || hashValue == undefined)
return null;
else if (hashValue == "")
return "";
else if (hashValue.length == 1 && hashValue.charAt(0) == "#")
return "";
else if (hashValue.length > 1 && hashValue.charAt(0) == "#")
return hashValue.substring(1);
else
return hashValue;
},
/** For IE, says when the hidden iframe has finished loading. */
/** private */ iframeLoaded: function(newLocation) {
// ignore any location changes that we made ourselves
if (this.ignoreLocationChange == true) {
this.ignoreLocationChange = false;
return;
}
// get the new location
var hash = new String(newLocation.search);
if (hash.length == 1 && hash.charAt(0) == "?")
hash = "";
else if (hash.length >= 2 && hash.charAt(0) == "?")
hash = hash.substring(1);
// move to this location in the browser location bar
// if we are not dealing with a page load event
if (this.pageLoadEvent != true) {
window.location.hash = hash;
}
// notify listeners of the change
this.fireHistoryEvent(hash);
},
/** Determines if this is Internet Explorer. */
/** private */ isInternetExplorer: function() {
var userAgent = navigator.userAgent.toLowerCase();
if (document.all && userAgent.indexOf('msie')!=-1) {
return true;
}
else {
return false;
}
}
};
/** An object that uses a hidden form to store history state
across page loads. The chief mechanism for doing so is using
the fact that browser's save the text in form data for the
life of the browser and cache, which means the text is still
there when the user navigates back to the page. See
http://codinginparadise.org/weblog/2005/08/ajax-tutorial-saving-session-across.html
for full details. */
window.historyStorage = {
/** If true, we are debugging and show the storage textfield. */
/** public */ debugging: false,
/** Our hash of key name/values. */
/** private */ storageHash: new Object(),
/** If true, we have loaded our hash table out of the storage form. */
/** private */ hashLoaded: false,
/** public */ put: function(key, value) {
this.assertValidKey(key);
// if we already have a value for this,
// remove the value before adding the
// new one
if (this.hasKey(key)) {
this.remove(key);
}
// store this new key
this.storageHash[key] = value;
// save and serialize the hashtable into the form
this.saveHashTable();
},
/** public */ get: function(key) {
this.assertValidKey(key);
// make sure the hash table has been loaded
// from the form
this.loadHashTable();
var value = this.storageHash[key];
if (value == undefined)
return null;
else
return value;
},
/** public */ remove: function(key) {
this.assertValidKey(key);
// make sure the hash table has been loaded
// from the form
this.loadHashTable();
// delete the value
delete this.storageHash[key];
// serialize and save the hash table into the
// form
this.saveHashTable();
},
/** Clears out all saved data. */
/** public */ reset: function() {
this.storageField.value = "";
this.storageHash = new Object();
},
/** public */ hasKey: function(key) {
this.assertValidKey(key);
// make sure the hash table has been loaded
// from the form
this.loadHashTable();
if (typeof this.storageHash[key] == "undefined")
return false;
else
return true;
},
/** Determines whether the key given is valid;
keys can only have letters, numbers, the dash,
underscore, spaces, or one of the
following characters:
!@#$%^&*()+=:;,./?|\~{}[] */
/** public */ isValidKey: function(key) {
// allow all strings, since we don't use XML serialization
// format anymore
return (typeof key == "string");
/*
if (typeof key != "string")
key = key.toString();
var matcher =
/^[a-zA-Z0-9_ \!\@\#\$\%\^\&\*\(\)\+\=\:\;\,\.\/\?\|\\\~\{\}\[\]]*$/;
return matcher.test(key);*/
},
/** A reference to our textarea field. */
/** private */ storageField: null,
/** private */ init: function() {
// write a hidden form into the page
var styleValue = "position: absolute; top: -1000px; left: -1000px;";
if (this.debugging == true) {
styleValue = "width: 30em; height: 30em;";
}
var newContent =
"<form id='historyStorageForm' "
+ "method='GET' "
+ "style='" + styleValue + "'>"
+ "<textarea id='historyStorageField' "
+ "style='" + styleValue + "'"
+ "name='historyStorageField'></textarea>"
+ "</form>";
//      document.write(newContent);
historyDiv.innerHTML = newContent;
this.storageField = document.getElementById("historyStorageField");
},
/** Asserts that a key is valid, throwing
an exception if it is not. */
/** private */ assertValidKey: function(key) {
if (this.isValidKey(key) == false) {
throw "Please provide a valid key for "
+ "window.historyStorage, key= "
+ key;
}
},
/** Loads the hash table up from the form. */
/** private */ loadHashTable: function() {
if (this.hashLoaded == false) {
// get the hash table as a serialized
// string
var serializedHashTable = this.storageField.value;
if (serializedHashTable != "" &&
serializedHashTable != null) {
// destringify the content back into a
// real JavaScript object
this.storageHash = eval('(' + serializedHashTable + ')');
}
this.hashLoaded = true;
}
},
/** Saves the hash table into the form. */
/** private */ saveHashTable: function() {
this.loadHashTable();
// serialized the hash table
var serializedHashTable = JSON.stringify(this.storageHash);
// save this value
this.storageField.value = serializedHashTable;
}
};
/** The JSON class is copyright 2005 JSON.org. */
Array.prototype.______array = '______array';
var JSON = {
org: 'http://www.JSON.org',
copyright: '(c)2005 JSON.org',
license: 'http://www.crockford.com/JSON/license.html',
stringify: function (arg) {
var c, i, l, s = '', v;
switch (typeof arg) {
case 'object':
if (arg) {
if (arg.______array == '______array') {
for (i = 0; i < arg.length; ++i) {
v = this.stringify(arg[i]);
if (s) {
s += ',';
}
s += v;
}
return '[' + s + ']';
} else if (typeof arg.toString != 'undefined') {
for (i in arg) {
v = arg[i];
if (typeof v != 'undefined' && typeof v != 'function') {
v = this.stringify(v);
if (s) {
s += ',';
}
s += this.stringify(i) + ':' + v;
}
}
return '{' + s + '}';
}
}
return 'null';
case 'number':
return isFinite(arg) ? String(arg) : 'null';
case 'string':
l = arg.length;
s = '"';
for (i = 0; i < l; i += 1) {
c = arg.charAt(i);
if (c >= ' ') {
if (c == '\\' || c == '"') {
s += '\\';
}
s += c;
} else {
switch (c) {
case '\b':
s += '\\b';
break;
case '\f':
s += '\\f';
break;
case '\n':
s += '\\n';
break;
case '\r':
s += '\\r';
break;
case '\t':
s += '\\t';
break;
default:
c = c.charCodeAt();
s += '\\u00' + Math.floor(c / 16).toString(16) +
(c % 16).toString(16);
}
}
}
return s + '"';
case 'boolean':
return String(arg);
default:
return 'null';
}
},
parse: function (text) {
var at = 0;
var ch = ' ';
function error(m) {
throw {
name: 'JSONError',
message: m,
at: at - 1,
text: text
};
}
function next() {
ch = text.charAt(at);
at += 1;
return ch;
}
function white() {
while (ch != '' && ch <= ' ') {
next();
}
}
function str() {
var i, s = '', t, u;
if (ch == '"') {
outer:          while (next()) {
if (ch == '"') {
next();
return s;
} else if (ch == '\\') {
switch (next()) {
case 'b':
s += '\b';
break;
case 'f':
s += '\f';
break;
case 'n':
s += '\n';
break;
case 'r':
s += '\r';
break;
case 't':
s += '\t';
break;
case 'u':
u = 0;
for (i = 0; i < 4; i += 1) {
t = parseInt(next(), 16);
if (!isFinite(t)) {
break outer;
}
u = u * 16 + t;
}
s += String.fromCharCode(u);
break;
default:
s += ch;
}
} else {
s += ch;
}
}
}
error("Bad string");
}
function arr() {
var a = [];
if (ch == '[') {
next();
white();
if (ch == ']') {
next();
return a;
}
while (ch) {
a.push(val());
white();
if (ch == ']') {
next();
return a;
} else if (ch != ',') {
break;
}
next();
white();
}
}
error("Bad array");
}
function obj() {
var k, o = {};
if (ch == '{') {
next();
white();
if (ch == '}') {
next();
return o;
}
while (ch) {
k = str();
white();
if (ch != ':') {
break;
}
next();
o[k] = val();
white();
if (ch == '}') {
next();
return o;
} else if (ch != ',') {
break;
}
next();
white();
}
}
error("Bad object");
}
function num() {
var n = '', v;
if (ch == '-') {
n = '-';
next();
}
while (ch >= '0' && ch <= '9') {
n += ch;
next();
}
if (ch == '.') {
n += '.';
while (next() && ch >= '0' && ch <= '9') {
n += ch;
}
}
if (ch == 'e' || ch == 'E') {
n += 'e';
next();
if (ch == '-' || ch == '+') {
n += ch;
next();
}
while (ch >= '0' && ch <= '9') {
n += ch;
next();
}
}
v = +n;
if (!isFinite(v)) {
error("Bad number");
} else {
return v;
}
}
function word() {
switch (ch) {
case 't':
if (next() == 'r' && next() == 'u' && next() == 'e') {
next();
return true;
}
break;
case 'f':
if (next() == 'a' && next() == 'l' && next() == 's' &&
next() == 'e') {
next();
return false;
}
break;
case 'n':
if (next() == 'u' && next() == 'l' && next() == 'l') {
next();
return null;
}
break;
}
error("Syntax error");
}
function val() {
white();
switch (ch) {
case '{':
return obj();
case '[':
return arr();
case '"':
return str();
case '-':
return num();
default:
return ch >= '0' && ch <= '9' ? num() : word();
}
}
return val();
}
};
/**
* TrimPath Template. Release 1.0.38.
* Copyright (C) 2004, 2005 Metaha.
*
* TrimPath Template is licensed under the GNU General Public License
* and the Apache License, Version 2.0, as follows:
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var TrimPath;
// TODO: Debugging mode vs stop-on-error mode - runtime flag.
// TODO: Handle || (or) characters and backslashes.
// TODO: Add more modifiers.
(function() {               // Using a closure to keep global namespace clean.
if (TrimPath == null)
TrimPath = new Object();
if (TrimPath.evalEx == null)
TrimPath.evalEx = function(src) { return eval(src); };
var UNDEFINED;
if (Array.prototype.pop == null)  // IE 5.x fix from Igor Poteryaev.
Array.prototype.pop = function() {
if (this.length === 0) {return UNDEFINED;}
return this[--this.length];
};
if (Array.prototype.push == null) // IE 5.x fix from Igor Poteryaev.
Array.prototype.push = function() {
for (var i = 0; i < arguments.length; ++i) {this[this.length] = arguments[i];}
return this.length;
};
TrimPath.parseTemplate = function(tmplContent, optTmplName, optEtc) {
if (optEtc == null)
optEtc = TrimPath.parseTemplate_etc;
var funcSrc = parse(tmplContent, optTmplName, optEtc);
var func = TrimPath.evalEx(funcSrc, optTmplName, 1);
if (func != null)
return new optEtc.Template(optTmplName, tmplContent, funcSrc, func, optEtc);
return null;
}
try {
String.prototype.process = function(context, optFlags) {
var template = TrimPath.parseTemplate(this, null);
if (template != null)
return template.process(context, optFlags);
return this;
}
} catch (e) { // Swallow exception, such as when String.prototype is sealed.
}
TrimPath.parseTemplate_etc = {};            // Exposed for extensibility.
TrimPath.loopIndex = 0;
TrimPath.parseTemplate_etc.statementTag = "forelse|for|if|elseif|else|var|macro";
TrimPath.parseTemplate_etc.statementDef = { // Lookup table for statement tags.
"if"     : { delta:  1, prefix: "if (", suffix: ") {", paramMin: 1 },
"else"   : { delta:  0, prefix: "} else {" },
"elseif" : { delta:  0, prefix: "} else if (", suffix: ") {", paramDefault: "true" },
"/if"    : { delta: -1, prefix: "}" },
"for"    : { delta:  1, paramMin: 3,
prefixFunc : function(stmtParts, state, tmplName, etc) {
//daum.debug(stmtParts);
if (stmtParts[2] != "in")
throw new etc.ParseError(tmplName, state.line, "bad for loop statement: " + stmtParts.join(' '));
var iterVar = stmtParts[1];
var listVar = "__LIST__" + iterVar;
TrimPath.loopIndex++;
if(stmtParts.length == 6){
var startVar = stmtParts[4];
var endVar = stmtParts[5];
return [ "var ", listVar, " = ", stmtParts[3], ";",
// Fix from Ross Shaull for hash looping, make sure that we have an array of loop lengths to treat like a stack.
"var __LENGTH_STACK__;",
"if (typeof(__LENGTH_STACK__) == 'undefined' || !__LENGTH_STACK__.length) __LENGTH_STACK__ = new Array();",
"__LENGTH_STACK__[__LENGTH_STACK__.length] = 0;", // Push a new for-loop onto the stack of loop lengths.
"if ((", listVar, ") != null) { ",
"var ", iterVar, "_ct = 0;",       // iterVar_ct variable, added by B. Bittman
//"for (var ", iterVar, "_index in ", listVar, ") { ",
"for (var ","i"+TrimPath.loopIndex,"=", startVar ,"; ","i"+TrimPath.loopIndex,"<", endVar, ";","i"+TrimPath.loopIndex,"++) { ",
iterVar, "_ct++;",
//"if (typeof(", listVar, "[", iterVar, "_index]) == 'function') {continue;}", // IE 5.x fix from Igor Poteryaev.
"__LENGTH_STACK__[__LENGTH_STACK__.length - 1]++;",
//"var ", iterVar, " = ", listVar, "[", iterVar, "_index];"
"var ", iterVar, " = ", listVar, "[","i"+TrimPath.loopIndex,"];"
].join("");
}else{
return [ "var ", listVar, " = ", stmtParts[3], ";",
// Fix from Ross Shaull for hash looping, make sure that we have an array of loop lengths to treat like a stack.
"var __LENGTH_STACK__;",
"if (typeof(__LENGTH_STACK__) == 'undefined' || !__LENGTH_STACK__.length) __LENGTH_STACK__ = new Array();",
"__LENGTH_STACK__[__LENGTH_STACK__.length] = 0;", // Push a new for-loop onto the stack of loop lengths.
"if ((", listVar, ") != null) { ",
"var ", iterVar, "_ct = 0;",       // iterVar_ct variable, added by B. Bittman
//"for (var ", iterVar, "_index in ", listVar, ") { ",
"for (var ","i"+TrimPath.loopIndex,"=0; ","i"+TrimPath.loopIndex,"<", listVar, ".length;","i"+TrimPath.loopIndex,"++) { ",
iterVar, "_ct++;",
//"if (typeof(", listVar, "[", iterVar, "_index]) == 'function') {continue;}", // IE 5.x fix from Igor Poteryaev.
"__LENGTH_STACK__[__LENGTH_STACK__.length - 1]++;",
//"var ", iterVar, " = ", listVar, "[", iterVar, "_index];"
"var ", iterVar, " = ", listVar, "[","i"+TrimPath.loopIndex,"];"
].join("");
}
} },
"forelse" : { delta:  0, prefix: "} } if (__LENGTH_STACK__[__LENGTH_STACK__.length - 1] == 0) { if (", suffix: ") {", paramDefault: "true" },
"/for"    : { delta: -1, prefix: "} }; delete __LENGTH_STACK__[__LENGTH_STACK__.length - 1];" }, // Remove the just-finished for-loop from the stack of loop lengths.
"var"     : { delta:  0, prefix: "var ", suffix: ";" },
"macro"   : { delta:  1,
prefixFunc : function(stmtParts, state, tmplName, etc) {
var macroName = stmtParts[1].split('(')[0];
return [ "var ", macroName, " = function",
stmtParts.slice(1).join(' ').substring(macroName.length),
"{ var _OUT_arr = []; var _OUT = { write: function(m) { if (m) _OUT_arr.push(m); } }; " ].join('');
} },
"/macro"  : { delta: -1, prefix: " return _OUT_arr.join(''); };" }
}
TrimPath.parseTemplate_etc.modifierDef = {
"eat"        : function(v)    { return ""; },
"escape"     : function(s)    { return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); },
"capitalize" : function(s)    { return String(s).toUpperCase(); },
"default"    : function(s, d) { return s != null ? s : d; }
}
TrimPath.parseTemplate_etc.modifierDef.h = TrimPath.parseTemplate_etc.modifierDef.escape;
TrimPath.parseTemplate_etc.Template = function(tmplName, tmplContent, funcSrc, func, etc) {
this.process = function(context, flags) {
if (context == null)
context = {};
if (context._MODIFIERS == null)
context._MODIFIERS = {};
if (context.defined == null)
context.defined = function(str) { return (context[str] != undefined); };
for (var k in etc.modifierDef) {
if (context._MODIFIERS[k] == null)
context._MODIFIERS[k] = etc.modifierDef[k];
}
if (flags == null)
flags = {};
var resultArr = [];
var resultOut = { write: function(m) { resultArr.push(m); } };
try {
func(resultOut, context, flags);
} catch (e) {
if (flags.throwExceptions == true)
throw e;
var result = new String(resultArr.join("") + "[ERROR: " + e.toString() + (e.message ? '; ' + e.message : '') + "]");
result["exception"] = e;
return result;
}
return resultArr.join("");
}
this.name       = tmplName;
this.source     = tmplContent;
this.sourceFunc = funcSrc;
this.toString   = function() { return "TrimPath.Template [" + tmplName + "]"; }
}
TrimPath.parseTemplate_etc.ParseError = function(name, line, message) {
this.name    = name;
this.line    = line;
this.message = message;
}
TrimPath.parseTemplate_etc.ParseError.prototype.toString = function() {
return ("TrimPath template ParseError in " + this.name + ": line " + this.line + ", " + this.message);
}
var parse = function(body, tmplName, etc) {
body = cleanWhiteSpace(body);
var funcText = [ "var TrimPath_Template_TEMP = function(_OUT, _CONTEXT, _FLAGS) { with (_CONTEXT) {" ];
var state    = { stack: [], line: 1 };                              // TODO: Fix line number counting.
var endStmtPrev = -1;
while (endStmtPrev + 1 < body.length) {
var begStmt = endStmtPrev;
// Scan until we find some statement markup.
begStmt = body.indexOf("{", begStmt + 1);
while (begStmt >= 0) {
var endStmt = body.indexOf('}', begStmt + 1);
var stmt = body.substring(begStmt, endStmt);
var blockrx = stmt.match(/^\{(cdata|minify|eval)/); // From B. Bittman, minify/eval/cdata implementation.
if (blockrx) {
var blockType = blockrx[1];
var blockMarkerBeg = begStmt + blockType.length + 1;
var blockMarkerEnd = body.indexOf('}', blockMarkerBeg);
if (blockMarkerEnd >= 0) {
var blockMarker;
if( blockMarkerEnd - blockMarkerBeg <= 0 ) {
blockMarker = "{/" + blockType + "}";
} else {
blockMarker = body.substring(blockMarkerBeg + 1, blockMarkerEnd);
}
var blockEnd = body.indexOf(blockMarker, blockMarkerEnd + 1);
if (blockEnd >= 0) {
emitSectionText(body.substring(endStmtPrev + 1, begStmt), funcText);
var blockText = body.substring(blockMarkerEnd + 1, blockEnd);
if (blockType == 'cdata') {
emitText(blockText, funcText);
} else if (blockType == 'minify') {
emitText(scrubWhiteSpace(blockText), funcText);
} else if (blockType == 'eval') {
if (blockText != null && blockText.length > 0) // From B. Bittman, eval should not execute until process().
funcText.push('_OUT.write( (function() { ' + blockText + ' })() );');
}
begStmt = endStmtPrev = blockEnd + blockMarker.length - 1;
}
}
} else if (body.charAt(begStmt - 1) != '$' &&               // Not an expression or backslashed,
body.charAt(begStmt - 1) != '\\') {              // so check if it is a statement tag.
var offset = (body.charAt(begStmt + 1) == '/' ? 2 : 1); // Close tags offset of 2 skips '/'.
// 10 is larger than maximum statement tag length.
if (body.substring(begStmt + offset, begStmt + 10 + offset).search(TrimPath.parseTemplate_etc.statementTag) == 0)
break;                                              // Found a match.
}
begStmt = body.indexOf("{", begStmt + 1);
}
if (begStmt < 0)                              // In "a{for}c", begStmt will be 1.
break;
var endStmt = body.indexOf("}", begStmt + 1); // In "a{for}c", endStmt will be 5.
if (endStmt < 0)
break;
emitSectionText(body.substring(endStmtPrev + 1, begStmt), funcText);
emitStatement(body.substring(begStmt, endStmt + 1), state, funcText, tmplName, etc);
endStmtPrev = endStmt;
}
emitSectionText(body.substring(endStmtPrev + 1), funcText);
if (state.stack.length != 0)
throw new etc.ParseError(tmplName, state.line, "unclosed, unmatched statement(s): " + state.stack.join(","));
funcText.push("}}; TrimPath_Template_TEMP");
return funcText.join("");
}
var emitStatement = function(stmtStr, state, funcText, tmplName, etc) {
var parts = stmtStr.slice(1, -1).split(' ');
var stmt = etc.statementDef[parts[0]]; // Here, parts[0] == for/if/else/...
if (stmt == null) {                    // Not a real statement.
emitSectionText(stmtStr, funcText);
return;
}
if (stmt.delta < 0) {
if (state.stack.length <= 0)
throw new etc.ParseError(tmplName, state.line, "close tag does not match any previous statement: " + stmtStr);
state.stack.pop();
}
if (stmt.delta > 0)
state.stack.push(stmtStr);
if (stmt.paramMin != null &&
stmt.paramMin >= parts.length)
throw new etc.ParseError(tmplName, state.line, "statement needs more parameters: " + stmtStr);
if (stmt.prefixFunc != null)
funcText.push(stmt.prefixFunc(parts, state, tmplName, etc));
else
funcText.push(stmt.prefix);
if (stmt.suffix != null) {
if (parts.length <= 1) {
if (stmt.paramDefault != null)
funcText.push(stmt.paramDefault);
} else {
for (var i = 1; i < parts.length; i++) {
if (i > 1)
funcText.push(' ');
funcText.push(parts[i]);
}
}
funcText.push(stmt.suffix);
}
}
var emitSectionText = function(text, funcText) {
if (text.length <= 0)
return;
var nlPrefix = 0;               // Index to first non-newline in prefix.
var nlSuffix = text.length - 1; // Index to first non-space/tab in suffix.
while (nlPrefix < text.length && (text.charAt(nlPrefix) == '\n'))
nlPrefix++;
while (nlSuffix >= 0 && (text.charAt(nlSuffix) == ' ' || text.charAt(nlSuffix) == '\t'))
nlSuffix--;
if (nlSuffix < nlPrefix)
nlSuffix = nlPrefix;
if (nlPrefix > 0) {
funcText.push('if (_FLAGS.keepWhitespace == true) _OUT.write("');
var s = text.substring(0, nlPrefix).replace('\n', '\\n'); // A macro IE fix from BJessen.
if (s.charAt(s.length - 1) == '\n')
s = s.substring(0, s.length - 1);
funcText.push(s);
funcText.push('");');
}
var lines = text.substring(nlPrefix, nlSuffix + 1).split('\n');
for (var i = 0; i < lines.length; i++) {
emitSectionTextLine(lines[i], funcText);
if (i < lines.length - 1)
funcText.push('_OUT.write("\\n");\n');
}
if (nlSuffix + 1 < text.length) {
funcText.push('if (_FLAGS.keepWhitespace == true) _OUT.write("');
var s = text.substring(nlSuffix + 1).replace('\n', '\\n');
if (s.charAt(s.length - 1) == '\n')
s = s.substring(0, s.length - 1);
funcText.push(s);
funcText.push('");');
}
}
var emitSectionTextLine = function(line, funcText) {
var endMarkPrev = '}';
var endExprPrev = -1;
while (endExprPrev + endMarkPrev.length < line.length) {
var begMark = "${", endMark = "}";
var begExpr = line.indexOf(begMark, endExprPrev + endMarkPrev.length); // In "a${b}c", begExpr == 1
if (begExpr < 0)
break;
if (line.charAt(begExpr + 2) == '%') {
begMark = "${%";
endMark = "%}";
}
var endExpr = line.indexOf(endMark, begExpr + begMark.length);         // In "a${b}c", endExpr == 4;
if (endExpr < 0)
break;
emitText(line.substring(endExprPrev + endMarkPrev.length, begExpr), funcText);
// Example: exprs == 'firstName|default:"John Doe"|capitalize'.split('|')
var exprArr = line.substring(begExpr + begMark.length, endExpr).replace(/\|\|/g, "#@@#").split('|');
for (var k in exprArr) {
if (exprArr[k].replace) // IE 5.x fix from Igor Poteryaev.
exprArr[k] = exprArr[k].replace(/#@@#/g, '||');
}
funcText.push('_OUT.write(');
emitExpression(exprArr, exprArr.length - 1, funcText);
funcText.push(');');
endExprPrev = endExpr;
endMarkPrev = endMark;
}
emitText(line.substring(endExprPrev + endMarkPrev.length), funcText);
}
var emitText = function(text, funcText) {
if (text == null ||
text.length <= 0)
return;
text = text.replace(/\\/g, '\\\\');
text = text.replace(/\n/g, '\\n');
text = text.replace(/"/g,  '\\"');
funcText.push('_OUT.write("');
funcText.push(text);
funcText.push('");');
}
var emitExpression = function(exprArr, index, funcText) {
// Ex: foo|a:x|b:y1,y2|c:z1,z2 is emitted as c(b(a(foo,x),y1,y2),z1,z2)
var expr = exprArr[index]; // Ex: exprArr == [firstName,capitalize,default:"John Doe"]
if (index <= 0) {          // Ex: expr    == 'default:"John Doe"'
funcText.push(expr);
return;
}
var parts = expr.split(':');
funcText.push('_MODIFIERS["');
funcText.push(parts[0]); // The parts[0] is a modifier function name, like capitalize.
funcText.push('"](');
emitExpression(exprArr, index - 1, funcText);
if (parts.length > 1) {
funcText.push(',');
funcText.push(parts[1]);
}
funcText.push(')');
}
var cleanWhiteSpace = function(result) {
result = result.replace(/\t/g,   "    ");
result = result.replace(/\r\n/g, "\n");
result = result.replace(/\r/g,   "\n");
result = result.replace(/^(\s*\S*(\s+\S+)*)\s*$/, '$1'); // Right trim by Igor Poteryaev.
return result;
}
var scrubWhiteSpace = function(result) {
result = result.replace(/^\s+/g,   "");
result = result.replace(/\s+$/g,   "");
result = result.replace(/\s+/g,   " ");
result = result.replace(/^(\s*\S*(\s+\S+)*)\s*$/, '$1'); // Right trim by Igor Poteryaev.
return result;
}
// The DOM helper functions depend on DOM/DHTML, so they only work in a browser.
// However, these are not considered core to the engine.
//
TrimPath.parseDOMTemplate = function(elementId, optDocument, optEtc) {
if (optDocument == null)
optDocument = document;
var element = optDocument.getElementById(elementId);
var content = element.value;     // Like textarea.value.
if (content == null)
content = element.innerHTML; // Like textarea.innerHTML.
content = content.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
return TrimPath.parseTemplate(content, elementId, optEtc);
}
TrimPath.processDOMTemplate = function(elementId, context, optFlags, optDocument, optEtc) {
return TrimPath.parseDOMTemplate(elementId, optDocument, optEtc).process(context, optFlags);
}
}) ();
// Copyright (c) 2005, 2006 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));
}
/*--------------------------------------------------------------------------*/
/**
* effects.js에서 버전업되면서, 없어졌단다... document.getElementsByClassName로 prototype의 함수로 통합...
*/
Element.childrenWithClassName = function(element, className, findFirst) {
var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
return (c.className && c.className.match(classNameRegExp));
});
if(!results) results = [];
return results;
}
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(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
return element;
}
Element.getOpacity = function(element){
element = $(element);
var opacity;
if (opacity = element.getStyle('opacity'))
return parseFloat(opacity);
if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
if(opacity[1]) return parseFloat(opacity[1]) / 100;
return 1.0;
}
Element.setOpacity = function(element, value){
element= $(element);
if (value == 1){
element.setStyle({ opacity:
(/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : 1.0 });
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
} else {
if(value < 0.00001) value = 0;
element.setStyle({opacity: value});
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.setStyle(
{ filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')' });
}
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) { }
};
/*--------------------------------------------------------------------------*/
Array.prototype.call = function() {
var args = arguments;
this.each(function(f){ f.apply(this, args) });
}
/*--------------------------------------------------------------------------*/
var Effect = {
_elementDoesNotExistError: {
name: 'ElementDoesNotExistError',
message: 'The specified DOM element does not exist, but is required for this effect to operate'
},
tagifyText: function(element) {
if(typeof Builder == 'undefined')
throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
var tagifyStyle = 'position:relative';
if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
element = $(element);
$A(element.childNodes).each( function(child) {
if(child.nodeType==3) {
child.nodeValue.toArray().each( function(character) {
element.insertBefore(
Builder.node('span',{style: tagifyStyle},
character == ' ' ? String.fromCharCode(160) : character),
child);
});
Element.remove(child);
}
});
},
multiple: function(element, effect) {
var elements;
if(((typeof element == 'object') ||
(typeof element == 'function')) &&
(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);
}
};
var Effect2 = Effect; // deprecated
/* ------------- transitions ------------- */
Effect.Transitions = {
linear: Prototype.K,
sinoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
},
reverse: function(pos) {
return 1-pos;
},
flicker: function(pos) {
return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
},
wobble: function(pos) {
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
},
pulse: function(pos, pulses) {
pulses = pulses || 5;
return (
Math.round((pos % (1/pulses)) * pulses) == 0 ?
((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
);
},
none: function(pos) {
return 0;
},
full: function(pos) {
return 1;
}
};
/* ------------- core effects ------------- */
Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, 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 = (typeof effect.options.queue == 'string') ?
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), 40);
},
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();
this.effects.invoke('loop', timePos);
}
});
Effect.Queues = {
instances: $H(),
get: function(queueName) {
if(typeof queueName != 'string') return queueName;
if(!this.instances[queueName])
this.instances[queueName] = new Effect.ScopedQueue();
return this.instances[queueName];
}
}
Effect.Queue = Effect.Queues.get('global');
Effect.DefaultOptions = {
transition: Effect.Transitions.sinoidal,
duration:   1.0,   // seconds
fps:        25.0,  // max. 25fps due to Effect.Queue implementation
sync:       false, // true for combining
from:       0.0,
to:         1.0,
delay:      0.0,
queue:      'parallel'
}
Effect.Base = function() {};
Effect.Base.prototype = {
position: null,
start: function(options) {
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.event('beforeStart');
if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue == 'string' ?
'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.finishOn - this.startOn);
var frame = Math.round(pos * this.options.fps * this.options.duration);
if(frame > this.currentFrame) {
this.render(pos);
this.currentFrame = frame;
}
}
},
render: function(pos) {
if(this.state == 'idle') {
this.state = 'running';
this.event('beforeSetup');
if(this.setup) this.setup();
this.event('afterSetup');
}
if(this.state == 'running') {
if(this.options.transition) pos = this.options.transition(pos);
pos *= (this.options.to-this.options.from);
pos += this.options.from;
this.position = pos;
this.event('beforeUpdate');
if(this.update) this.update(pos);
this.event('afterUpdate');
}
},
cancel: function() {
if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue == 'string' ?
'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() {
return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
}
}
Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
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.Event = Class.create();
Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
initialize: function() {
var options = Object.extend({
duration: 0
}, arguments[0] || {});
this.start(options);
},
update: Prototype.emptyFunction
});
Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
if(!this.element) throw(Effect._elementDoesNotExistError);
// make this work on IE on elements without 'layout'
if(/MSIE/.test(navigator.userAgent) && !window.opera && (!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();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
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() {
// Bug in Opera: Opera returns the "real" position of a static element or
// relative element that does not have top/left explicitly set.
// ==> Always set top and left for position relative elements in your stylesheets
// (to 0 if you do not need them)
this.element.makePositioned();
this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
if(this.options.mode == 'absolute') {
// absolute movement, so we need to calc deltaX and deltaY
this.options.x = this.options.x - this.originalLeft;
this.options.y = this.options.y - this.originalTop;
}
},
update: function(position) {
if(this.options.x == null){
this.element.setStyle({
top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
});
}else if(this.options.y == null){
this.element.setStyle({
left: Math.round(this.options.x  * position + this.originalLeft) + 'px'
});
}else{
this.element.setStyle({
left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
top:  Math.round(this.options.y  * position + this.originalTop)  + '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();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
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 = Math.round(width) + 'px';
if(this.options.scaleY) d.height = Math.round(height) + '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();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
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 = {
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+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
},
finish: function() {
this.element.setStyle(Object.extend(this.oldStyle, {
backgroundColor: this.options.restorecolor
}));
}
});
Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
this.start(arguments[1] || {});
},
setup: function() {
Position.prepare();
var offsets = Position.cumulativeOffset(this.element);
if(this.options.offset) offsets[1] += this.options.offset;
var max = window.innerHeight ?
window.height - window.innerHeight :
document.body.scrollHeight -
(document.documentElement.clientHeight ?
document.documentElement.clientHeight : document.body.clientHeight);
this.scrollStart = Position.deltaY;
this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
},
update: function(position) {
Position.prepare();
window.scrollTo(Position.deltaX,
this.scrollStart + (position*this.delta));
}
});
/* ------------- 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 oldStyle = {
top: element.getStyle('top'),
left: element.getStyle('left') };
return new Effect.Move(element,
{ x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -20, y: 0, duration: 0.05, 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');
return new Effect.Scale(element, window.opera ? 0 : 1,
Object.extend({ scaleContent: false,
scaleX: false,
scaleMode: 'box',
scaleFrom: 100,
restoreAfterFinish: true,
beforeStartInternal: 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().setStyle({bottom: oldInnerBottom});
effect.element.down().undoPositioned();
}
}, arguments[1] || {})
);
}
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
return new Effect.Scale(element, window.opera ? 1 : 0, {
restoreAfterFinish: true,
beforeSetup: function(effect) {
effect.element.makeClipping();
},
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping();
}
});
}
Effect.Grow = function(element) {
element = $(element);
var options = Object.extend({
direction: 'center',
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.full
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: element.getInlineOpacity() };
var dims = element.getDimensions();
var initialMoveX, initialMoveY;
var moveX, moveY;
switch (options.direction) {
case 'top-left':
initialMoveX = initialMoveY = moveX = moveY = 0;
break;
case 'top-right':
initialMoveX = dims.width;
initialMoveY = moveY = 0;
moveX = -dims.width;
break;
case 'bottom-left':
initialMoveX = moveX = 0;
initialMoveY = dims.height;
moveY = -dims.height;
break;
case 'bottom-right':
initialMoveX = dims.width;
initialMoveY = dims.height;
moveX = -dims.width;
moveY = -dims.height;
break;
case 'center':
initialMoveX = dims.width / 2;
initialMoveY = dims.height / 2;
moveX = -dims.width / 2;
moveY = -dims.height / 2;
break;
}
return new Effect.Move(element, {
x: initialMoveX,
y: initialMoveY,
duration: 0.01,
beforeSetup: function(effect) {
effect.element.hide().makeClipping().makePositioned();
},
afterFinishInternal: function(effect) {
new Effect.Parallel(
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
new Effect.Scale(effect.element, 100, {
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
effect.effects[0].element.setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
}
}, options)
)
}
});
}
Effect.Shrink = function(element) {
element = $(element);
var options = Object.extend({
direction: 'center',
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.none
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: element.getInlineOpacity() };
var dims = element.getDimensions();
var moveX, moveY;
switch (options.direction) {
case 'top-left':
moveX = moveY = 0;
break;
case 'top-right':
moveX = dims.width;
moveY = 0;
break;
case 'bottom-left':
moveX = 0;
moveY = dims.height;
break;
case 'bottom-right':
moveX = dims.width;
moveY = dims.height;
break;
case 'center':
moveX = dims.width / 2;
moveY = dims.height / 2;
break;
}
return new Effect.Parallel(
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
], Object.extend({
beforeStartInternal: function(effect) {
effect.effects[0].element.makePositioned().makeClipping();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
}, options)
);
}
Effect.Pulsate = function(element) {
element = $(element);
var options    = arguments[1] || {};
var oldOpacity = element.getInlineOpacity();
var transition = options.transition || Effect.Transitions.sinoidal;
var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
reverser.bind(transition);
return new Effect.Opacity(element,
Object.extend(Object.extend({  duration: 2.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
}, options), {transition: reverser}));
}
Effect.Fold = function(element) {
element = $(element);
var oldStyle = {
top: element.style.top,
left: element.style.left,
width: element.style.width,
height: element.style.height };
element.makeClipping();
return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
scaleX: false,
afterFinishInternal: function(effect) {
new Effect.Scale(element, 1, {
scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping().setStyle(oldStyle);
} });
}}, arguments[1] || {}));
};
Effect.Morph = Class.create();
Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
style: ''
}, arguments[1] || {});
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.options.style.parseStyle().map(function(property){
var originalValue = this.element.getStyle(property[0]);
return $H({
style: property[0],
originalValue: property[1].unit=='color' ?
parseColor(originalValue) : parseFloat(originalValue || 0),
targetValue: property[1].unit=='color' ?
parseColor(property[1].value) : property[1].value,
unit: property[1].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 = $H(), value = null;
this.transforms.each(function(transform){
value = transform.unit=='color' ?
$R(0,2).inject('#',function(m,v,i){
return m+(Math.round(transform.originalValue[i]+
(transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
transform.originalValue + Math.round(
((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
style[transform.style] = value;
});
this.element.setStyle(style);
}
});
Effect.Transform = Class.create();
Object.extend(Effect.Transform.prototype, {
initialize: function(tracks){
this.tracks  = [];
this.options = arguments[1] || {};
this.addTracks(tracks);
},
addTracks: function(tracks){
tracks.each(function(track){
var data = $H(track).values().first();
this.tracks.push($H({
ids:     $H(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 elements = [$(track.ids) || $$(track.ids)].flatten();
return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
}).flatten(),
this.options
);
}
});
Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
'width', 'wordSpacing', 'zIndex'];
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
String.prototype.parseStyle = function(){
var element = Element.extend(document.createElement('div'));
element.innerHTML = '<div style="' + this + '"></div>';
var style = element.down().style, styleRules = $H();
Element.CSS_PROPERTIES.each(function(property){
if(style[property]) styleRules[property] = style[property];
});
var result = $H();
styleRules.each(function(pair){
var property = pair[0], value = pair[1], unit = null;
if(value.parseColor('#zzzzzz') != '#zzzzzz') {
value = value.parseColor();
unit  = 'color';
} else if(Element.CSS_LENGTH.test(value))
var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
}.bind(this));
return result;
};
Element.morph = function(element, style) {
new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
return element;
};
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
function(f) { Element.Methods[f] = Element[f]; }
);
Element.Methods.visualEffect = function(element, effect, options) {
s = effect.gsub(/_/, '-').camelize();
effect_class = s.charAt(0).toUpperCase() + s.substring(1);
new Effect[effect_class](element, options);
return $(element);
};
Element.addMethods();
/**
* metheny Javascript
*/
Number.prototype.nfloor = function(n) {
if(!n) n =0;
var ss = this * Math.pow(10, n);
return Math.floor(ss) / Math.pow(10, n);
}
Number.prototype.floor = function() {
return this.nfloor(0);
}
String.prototype.trim = function() {
if (this) {
return this.replace(/(^\s*)|(\s*$)/g, "");
}
return null;
}
String.prototype.cutString = function(index, tailString) {
if (this) {
if(!tailString || tailString == null) tailString = ""
if(this.length <= index) return this;
else return this.substring(0, index) + tailString;
} else {
return this;
}
}
// mozXPath [http://km0ti0n.blunted.co.uk/mozxpath/] km0ti0n@gmail.com
// Code licensed under Creative Commons Attribution-ShareAlike License
// http://creativecommons.org/licenses/by-sa/2.5/

if( document.implementation.hasFeature("XPath", "3.0") && navigator.userAgent.indexOf("Opera") == -1)
{
XMLDocument.prototype.selectNodes = function(cXPathString, xNode)
{
if( !xNode ) { xNode = this; }
var oNSResolver = this.createNSResolver(this.documentElement)
var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
var aResult = [];
for( var i = 0; i < aItems.snapshotLength; i++)
{
aResult[i] =  aItems.snapshotItem(i);
}
return aResult;
}
XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode)
{
if( !xNode ) { xNode = this; }
var xItems = this.selectNodes(cXPathString, xNode);
if( xItems.length > 0 )
{
return xItems[0];
}
else
{
return null;
}
}
Element.prototype.selectNodes = function(cXPathString)
{
if(this.ownerDocument.selectNodes)
{
return this.ownerDocument.selectNodes(cXPathString, this);
}
else{throw "For XML Elements Only";}
}
Element.prototype.selectSingleNode = function(cXPathString)
{
if(this.ownerDocument.selectSingleNode)
{
return this.ownerDocument.selectSingleNode(cXPathString, this);
}
else{throw "For XML Elements Only";}
}
}
if (DWREngine == null) var DWREngine = {};
DWREngine.setErrorHandler = function(handler) {
DWREngine._errorHandler = handler;
};
DWREngine.getErrorHandler = function() {
return DWREngine._errorHandler;
};
DWREngine.setWarningHandler = function(handler) {
DWREngine._warningHandler = handler;
};
DWREngine.getWarningHandler  = function() {
return _warningHandler._errorHandler;
};
DWREngine.setTimeout = function(timeout) {
DWREngine._timeout = timeout;
};
DWREngine.setPreHook = function(handler) {
DWREngine._preHook = handler;
};
DWREngine.setPostHook = function(handler) {
DWREngine._postHook = handler;
};
DWREngine.XMLHttpRequest = 1;
DWREngine.IFrame = 2;
DWREngine.setMethod = function(newMethod) {
if (newMethod != DWREngine.XMLHttpRequest && newMethod != DWREngine.IFrame) {
DWREngine._handleError("Remoting method must be one of DWREngine.XMLHttpRequest or DWREngine.IFrame");
return;
}
DWREngine._method = newMethod;
};
DWREngine.setVerb = function(verb) {
if (verb != "GET" && verb != "POST") {
DWREngine._handleError("Remoting verb must be one of GET or POST");
return;
}
DWREngine._verb = verb;
};
DWREngine.setOrdered = function(ordered) {
DWREngine._ordered = ordered;
};
DWREngine.setAsync = function(async) {
DWREngine._async = async;
};
DWREngine.setTextHtmlHandler = function(handler) {
DWREngine._textHtmlHandler = handler;
}
DWREngine.defaultMessageHandler = function(message) {
if (typeof message == "object" && message.name == "Error" && message.description) {
alert("Error: " + message.description);
}
else {
if (message.toString().indexOf("0x80040111") == -1) {
alert(message);
}
}
};
DWREngine.beginBatch = function() {
if (DWREngine._batch) {
DWREngine._handleError("Batch already started.");
return;
}
DWREngine._batch = {
map:{ callCount:0 },
paramCount:0,
ids:[],
preHooks:[],
postHooks:[]
};
};
DWREngine.endBatch = function(options) {
var batch = DWREngine._batch;
if (batch == null) {
DWREngine._handleError("No batch in progress.");
return;
}
if (options && options.preHook) batch.preHooks.unshift(options.preHook);
if (options && options.postHook) batch.postHooks.push(options.postHook);
if (DWREngine._preHook) batch.preHooks.unshift(DWREngine._preHook);
if (DWREngine._postHook) batch.postHooks.push(DWREngine._postHook);
if (batch.method == null) batch.method = DWREngine._method;
if (batch.verb == null) batch.verb = DWREngine._verb;
if (batch.async == null) batch.async = DWREngine._async;
if (batch.timeout == null) batch.timeout = DWREngine._timeout;
batch.completed = false;
DWREngine._lastBatch = batch;
DWREngine._batch = null;
if (!DWREngine._ordered) {
DWREngine._sendData(batch);
DWREngine._batches[DWREngine._batches.length] = batch;
}
else {
if (DWREngine._batches.length == 0) {
DWREngine._sendData(batch);
DWREngine._batches[DWREngine._batches.length] = batch;
}
else {
DWREngine._batchQueue[DWREngine._batchQueue.length] = batch;
}
}
};
DWREngine._errorHandler = DWREngine.defaultMessageHandler;
DWREngine._warningHandler = null;
DWREngine._preHook = null;
DWREngine._postHook = null;
DWREngine._batches = [];
DWREngine._batchQueue = [];
DWREngine._handlersMap = {};
DWREngine._method = DWREngine.XMLHttpRequest;
DWREngine._verb = "POST";
DWREngine._ordered = false;
DWREngine._async = true;
DWREngine._batch = null;
DWREngine._timeout = 0;
DWREngine._DOMDocument = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];
DWREngine._XMLHTTP = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
DWREngine._execute = function(path, scriptName, methodName, vararg_params) {
var singleShot = false;
if (DWREngine._batch == null) {
DWREngine.beginBatch();
singleShot = true;
}
var args = [];
for (var i = 0; i < arguments.length - 3; i++) {
args[i] = arguments[i + 3];
}
if (DWREngine._batch.path == null) {
DWREngine._batch.path = path;
}
else {
if (DWREngine._batch.path != path) {
DWREngine._handleError("Can't batch requests to multiple DWR Servlets.");
return;
}
}
var params;
var callData;
var firstArg = args[0];
var lastArg = args[args.length - 1];
if (typeof firstArg == "function") {
callData = { callback:args.shift() };
params = args;
}
else if (typeof lastArg == "function") {
callData = { callback:args.pop() };
params = args;
}
else if (lastArg != null && typeof lastArg == "object" && lastArg.callback != null && typeof lastArg.callback == "function") {
callData = args.pop();
params = args;
}
else if (firstArg == null) {
if (lastArg == null && args.length > 2) {
DWREngine._handleError("Ambiguous nulls at start and end of parameter list. Which is the callback function?");
}
callData = { callback:args.shift() };
params = args;
}
else if (lastArg == null) {
callData = { callback:args.pop() };
params = args;
}
else {
DWREngine._handleError("Missing callback function or metadata object.");
return;
}
var random = Math.floor(Math.random() * 10001);
var id = (random + "_" + new Date().getTime()).toString();
var prefix = "c" + DWREngine._batch.map.callCount + "-";
DWREngine._batch.ids.push(id);
if (callData.method != null) {
DWREngine._batch.method = callData.method;
delete callData.method;
}
if (callData.verb != null) {
DWREngine._batch.verb = callData.verb;
delete callData.verb;
}
if (callData.async != null) {
DWREngine._batch.async = callData.async;
delete callData.async;
}
if (callData.timeout != null) {
DWREngine._batch.timeout = callData.timeout;
delete callData.timeout;
}
if (callData.preHook != null) {
DWREngine._batch.preHooks.unshift(callData.preHook);
delete callData.preHook;
}
if (callData.postHook != null) {
DWREngine._batch.postHooks.push(callData.postHook);
delete callData.postHook;
}
if (callData.errorHandler == null) callData.errorHandler = DWREngine._errorHandler;
if (callData.warningHandler == null) callData.warningHandler = DWREngine._warningHandler;
DWREngine._handlersMap[id] = callData;
DWREngine._batch.map[prefix + "scriptName"] = scriptName;
DWREngine._batch.map[prefix + "methodName"] = methodName;
DWREngine._batch.map[prefix + "id"] = id;
for (i = 0; i < params.length; i++) {
DWREngine._serializeAll(DWREngine._batch, [], params[i], prefix + "param" + i);
}
DWREngine._batch.map.callCount++;
if (singleShot) {
DWREngine.endBatch();
}
};
DWREngine._sendData = function(batch) {
if (batch.map.callCount == 0) return;
for (var i = 0; i < batch.preHooks.length; i++) {
batch.preHooks[i]();
}
batch.preHooks = null;
if (batch.timeout && batch.timeout != 0) {
batch.interval = setInterval(function() { DWREngine._abortRequest(batch); }, batch.timeout);
}
var urlPostfix;
if (batch.map.callCount == 1) {
urlPostfix = batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr";
}
else {
urlPostfix = "Multiple." + batch.map.callCount + ".dwr";
}
if (batch.method == DWREngine.XMLHttpRequest) {
if (window.XMLHttpRequest) {
batch.req = new XMLHttpRequest();
}
else if (window.ActiveXObject && !(navigator.userAgent.indexOf("Mac") >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) {
batch.req = DWREngine._newActiveXObject(DWREngine._XMLHTTP);
}
}
var query = "";
var prop;
if (batch.req) {
batch.map.xml = "true";
if (batch.async) {
batch.req.onreadystatechange = function() { DWREngine._stateChange(batch); };
}
var indexSafari = navigator.userAgent.indexOf("Safari/");
if (indexSafari >= 0) {
var version = navigator.userAgent.substring(indexSafari + 7);
if (parseInt(version, 10) < 400) batch.verb == "GET";
}
if (batch.verb == "GET") {
batch.map.callCount = "" + batch.map.callCount;
for (prop in batch.map) {
var qkey = encodeURIComponent(prop);
var qval = encodeURIComponent(batch.map[prop]);
if (qval == "") DWREngine._handleError("Found empty qval for qkey=" + qkey);
query += qkey + "=" + qval + "&";
}
try {
batch.req.open("GET", batch.path + "/exec/" + urlPostfix + "?" + query, batch.async);
batch.req.send(null);
if (!batch.async) DWREngine._stateChange(batch);
}
catch (ex) {
DWREngine._handleMetaDataError(null, ex);
}
}
else {
for (prop in batch.map) {
if (typeof batch.map[prop] != "function") {
query += prop + "=" + batch.map[prop] + "\n";
}
}
try {
batch.req.open("POST", batch.path + "/exec/" + urlPostfix, batch.async);
batch.req.setRequestHeader('Content-Type', 'text/plain');
batch.req.send(query);
//daum.debug("DWRENGINE send REQUEST.... url:"+batch.path + "/exec/" + urlPostfix + "?" + query);
//daum.debug("DWRENGINE batch.async:"+batch.async);
if (!batch.async) DWREngine._stateChange(batch);
}
catch (ex) {
//daum.debug("DWRENGINE EXCEPTION, ERROR:"+ex);
DWREngine._handleMetaDataError(null, ex);
}
}
}
else {
batch.map.xml = "false";
var idname = "dwr-if-" + batch.map["c0-id"];
batch.div = document.createElement("div");
batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' width='0' height='0' id='" + idname + "' name='" + idname + "'></iframe>";
document.body.appendChild(batch.div);
batch.iframe = document.getElementById(idname);
batch.iframe.setAttribute("style", "width:0px; height:0px; border:0px;");
if (batch.verb == "GET") {
for (prop in batch.map) {
if (typeof batch.map[prop] != "function") {
query += encodeURIComponent(prop) + "=" + encodeURIComponent(batch.map[prop]) + "&";
}
}
query = query.substring(0, query.length - 1);
batch.iframe.setAttribute("src", batch.path + "/exec/" + urlPostfix + "?" + query);
document.body.appendChild(batch.iframe);
}
else {
batch.form = document.createElement("form");
batch.form.setAttribute("id", "dwr-form");
batch.form.setAttribute("action", batch.path + "/exec" + urlPostfix);
batch.form.setAttribute("target", idname);
batch.form.target = idname;
batch.form.setAttribute("method", "POST");
for (prop in batch.map) {
var formInput = document.createElement("input");
formInput.setAttribute("type", "hidden");
formInput.setAttribute("name", prop);
formInput.setAttribute("value", batch.map[prop]);
batch.form.appendChild(formInput);
}
document.body.appendChild(batch.form);
batch.form.submit();
}
}
};
DWREngine._stateChange = function(batch) {
//daum.debug("DWRENGINE onreadystatus batch.req.readyState:"+batch.req.readyState+", batch.path:"+batch.path);
if (!batch.completed && batch.req.readyState == 4) {
try {
var reply = batch.req.responseText;
if (reply == null || reply == "") {
DWREngine._handleMetaDataWarning(null, "No data received from server");
}
else {
var contentType = batch.req.getResponseHeader("Content-Type");
if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
if (DWREngine._textHtmlHandler && contentType.match(/^text\/html/)) {
DWREngine._textHtmlHandler();
}
else {
DWREngine._handleMetaDataWarning(null, "Invalid content type from server: '" + contentType + "'");
}
}
else {
if (reply.search("DWREngine._handle") == -1) {
DWREngine._handleMetaDataWarning(null, "Invalid reply from server");
}
else {
eval(reply);
}
}
}
DWREngine._clearUp(batch);
}
catch (ex) {
if (ex == null) ex = "Unknown error occured";
DWREngine._handleMetaDataWarning(null, ex);
}
finally {
if (DWREngine._batchQueue.length != 0) {
var sendbatch = DWREngine._batchQueue.shift();
DWREngine._sendData(sendbatch);
DWREngine._batches[DWREngine._batches.length] = sendbatch;
}
}
}
};
DWREngine._handleResponse = function(id, reply) {
var handlers = DWREngine._handlersMap[id];
DWREngine._handlersMap[id] = null;
if (handlers) {
try {
if (handlers.callback) handlers.callback(reply);
}
catch (ex) {
DWREngine._handleMetaDataError(handlers, ex);
}
}
if (DWREngine._method == DWREngine.IFrame) {
var responseBatch = DWREngine._batches[DWREngine._batches.length-1];
if (responseBatch.map["c"+(responseBatch.map.callCount-1)+"-id"] == id) {
DWREngine._clearUp(responseBatch);
}
}
};
DWREngine._handleServerError = function(id, error) {
var handlers = DWREngine._handlersMap[id];
DWREngine._handlersMap[id] = null;
if (error.message) DWREngine._handleMetaDataError(handlers, error.message, error);
else DWREngine._handleMetaDataError(handlers, error);
};
DWREngine._eval = function(script) {
return eval(script);
}
DWREngine.stopLastRequest = function(){
var batch = DWREngine._lastBatch;
daum.debug(batch);
if(batch){
clearInterval(batch.interval);
DWREngine._clearUp(batch);
if (batch.req) batch.req.abort();
}
}
DWREngine._abortRequest = function(batch) {
if (batch && !batch.completed) {
clearInterval(batch.interval);
DWREngine._clearUp(batch);
if (batch.req) batch.req.abort();
var handlers;
for (var i = 0; i < batch.ids.length; i++) {
handlers = DWREngine._handlersMap[batch.ids[i]];
//daum.debug("DWRENGINE TIMEOUT ==> "+batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr");
//daum.debug(handlers);
DWREngine._handleMetaDataError(handlers, "Timeout");
}
}
};
DWREngine._clearUp = function(batch) {
if (batch.completed) {
DWREngine._handleError("Double complete");
return;
}
if (batch.div) batch.div.parentNode.removeChild(batch.div);
if (batch.iframe) batch.iframe.parentNode.removeChild(batch.iframe);
if (batch.form) batch.form.parentNode.removeChild(batch.form);
if (batch.req) delete batch.req;
for (var i = 0; i < batch.postHooks.length; i++) {
batch.postHooks[i]();
}
batch.postHooks = null;
for (var i = 0; i < DWREngine._batches.length; i++) {
if (DWREngine._batches[i] == batch) {
DWREngine._batches.splice(i, 1);
break;
}
}
batch.completed = true;
};
DWREngine._handleError = function(reason, ex) {
if (DWREngine._errorHandler) DWREngine._errorHandler(reason, ex);
};
DWREngine._handleWarning = function(reason, ex) {
if (DWREngine._warningHandler) DWREngine._warningHandler(reason, ex);
};
DWREngine._handleMetaDataError = function(handlers, reason, ex) {
if (handlers && typeof handlers.errorHandler == "function") handlers.errorHandler(reason, ex);
else DWREngine._handleError(reason, ex);
};
DWREngine._handleMetaDataWarning = function(handlers, reason, ex) {
if (handlers && typeof handlers.warningHandler == "function") handlers.warningHandler(reason, ex);
else DWREngine._handleWarning(reason, ex);
};
DWREngine._serializeAll = function(batch, referto, data, name) {
if (data == null) {
batch.map[name] = "null:null";
return;
}
switch (typeof data) {
case "boolean":
batch.map[name] = "boolean:" + data;
break;
case "number":
batch.map[name] = "number:" + data;
break;
case "string":
batch.map[name] = "string:" + encodeURIComponent(data);
break;
case "object":
if (data instanceof String) batch.map[name] = "String:" + encodeURIComponent(data);
else if (data instanceof Boolean) batch.map[name] = "Boolean:" + data;
else if (data instanceof Number) batch.map[name] = "Number:" + data;
else if (data instanceof Date) batch.map[name] = "Date:" + data.getTime();
else if (data instanceof Array) batch.map[name] = DWREngine._serializeArray(batch, referto, data, name);
else batch.map[name] = DWREngine._serializeObject(batch, referto, data, name);
break;
case "function":
break;
default:
DWREngine._handleWarning("Unexpected type: " + typeof data + ", attempting default converter.");
batch.map[name] = "default:" + data;
break;
}
};
DWREngine._lookup = function(referto, data, name) {
var lookup;
for (var i = 0; i < referto.length; i++) {
if (referto[i].data == data) {
lookup = referto[i];
break;
}
}
if (lookup) return "reference:" + lookup.name;
referto.push({ data:data, name:name });
return null;
};
DWREngine._serializeObject = function(batch, referto, data, name) {
var ref = DWREngine._lookup(referto, data, name);
if (ref) return ref;
if (data.nodeName && data.nodeType) {
return DWREngine._serializeXml(batch, referto, data, name);
}
var reply = "Object:{";
var element;
for (element in data) {
batch.paramCount++;
var childName = "c" + DWREngine._batch.map.callCount + "-e" + batch.paramCount;
DWREngine._serializeAll(batch, referto, data[element], childName);
reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
}
if (reply.substring(reply.length - 2) == ", ") {
reply = reply.substring(0, reply.length - 2);
}
reply += "}";
return reply;
};
DWREngine._serializeXml = function(batch, referto, data, name) {
var ref = DWREngine._lookup(referto, data, name);
if (ref) return ref;
var output;
if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
else output = data.toXml;
return "XML:" + encodeURIComponent(output);
};
DWREngine._serializeArray = function(batch, referto, data, name) {
var ref = DWREngine._lookup(referto, data, name);
if (ref) return ref;
var reply = "Array:[";
for (var i = 0; i < data.length; i++) {
if (i != 0) reply += ",";
batch.paramCount++;
var childName = "c" + DWREngine._batch.map.callCount + "-e" + batch.paramCount;
DWREngine._serializeAll(batch, referto, data[i], childName);
reply += "reference:";
reply += childName;
}
reply += "]";
return reply;
};
DWREngine._unserializeDocument = function(xml) {
var dom;
if (window.DOMParser) {
var parser = new DOMParser();
dom = parser.parseFromString(xml, "text/xml");
if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
var message = dom.documentElement.firstChild.data;
message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
throw message;
}
return dom;
}
else if (window.ActiveXObject) {
dom = DWREngine._newActiveXObject(DWREngine._DOMDocument);
dom.loadXML(xml);
return dom;
}
else {
var div = document.createElement("div");
div.innerHTML = xml;
return div;
}
};
DWREngine._newActiveXObject = function(axarray) {
var returnValue;
for (var i = 0; i < axarray.length; i++) {
try {
returnValue = new ActiveXObject(axarray[i]);
break;
}
catch (ex) {   }
}
return returnValue;
};
if (typeof window.encodeURIComponent === 'undefined') {
DWREngine._utf8 = function(wide) {
wide = "" + wide;
var c;
var s;
var enc = "";
var i = 0;
while (i < wide.length) {
c = wide.charCodeAt(i++);
if (c >= 0xDC00 && c < 0xE000) continue;
if (c >= 0xD800 && c < 0xDC00) {
if (i >= wide.length) continue;
s = wide.charCodeAt(i++);
if (s < 0xDC00 || c >= 0xDE00) continue;
c = ((c - 0xD800) << 10) + (s - 0xDC00) + 0x10000;
}
if (c < 0x80) {
enc += String.fromCharCode(c);
}
else if (c < 0x800) {
enc += String.fromCharCode(0xC0 + (c >> 6), 0x80 + (c & 0x3F));
}
else if (c < 0x10000) {
enc += String.fromCharCode(0xE0 + (c >> 12), 0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
}
else {
enc += String.fromCharCode(0xF0 + (c >> 18), 0x80 + (c >> 12 & 0x3F), 0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
}
}
return enc;
}
DWREngine._hexchars = "0123456789ABCDEF";
DWREngine._toHex = function(n) {
return DWREngine._hexchars.charAt(n >> 4) + DWREngine._hexchars.charAt(n & 0xF);
}
DWREngine._okURIchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
window.encodeURIComponent = function(s)  {
s = DWREngine._utf8(s);
var c;
var enc = "";
for (var i= 0; i<s.length; i++) {
if (DWREngine._okURIchars.indexOf(s.charAt(i)) == -1) {
enc += "%" + DWREngine._toHex(s.charCodeAt(i));
}
else {
enc += s.charAt(i);
}
}
return enc;
}
}
if (typeof Array.prototype.splice === 'undefined') {
Array.prototype.splice = function(ind, cnt)
{
if (arguments.length == 0) return ind;
if (typeof ind != "number") ind = 0;
if (ind < 0) ind = Math.max(0,this.length + ind);
if (ind > this.length) {
if (arguments.length > 2) ind = this.length;
else return [];
}
if (arguments.length < 2) cnt = this.length-ind;
cnt = (typeof cnt == "number") ? Math.max(0, cnt) : 0;
removeArray = this.slice(ind, ind + cnt);
endArray = this.slice(ind + cnt);
this.length = ind;
for (var i = 2; i < arguments.length; i++) this[this.length] = arguments[i];
for (i = 0; i < endArray.length; i++) this[this.length] = endArray[i];
return removeArray;
}
}
if (typeof Array.prototype.shift === 'undefined') {
Array.prototype.shift = function(str) {
var val = this[0];
for (var i = 1; i < this.length; ++i) this[i - 1] = this[i];
this.length--;
return val;
}
}
if (typeof Array.prototype.unshift === 'undefined') {
Array.prototype.unshift = function() {
var i = unshift.arguments.length;
for (var j = this.length - 1; j >= 0; --j) this[j + i] = this[j];
for (j = 0; j < i; ++j) this[j] = unshift.arguments[j];
}
}
if (typeof Array.prototype.push === 'undefined') {
Array.prototype.push = function() {
var sub = this.length;
for (var i = 0; i < push.arguments.length; ++i) {
this[sub] = push.arguments[i];
sub++;
}
}
}
if (typeof Array.prototype.pop === 'undefined') {
Array.prototype.pop = function() {
var lastElement = this[this.length - 1];
this.length--;
return lastElement;
}
}
/*
* Copyright 2005 Joe Walker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Declare an object to which we can add real functions.
*/
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;
/**
* Set an alternative error handler from the default alert box.
* @see getahead.org/dwr/browser/engine/errors
*/
dwr.engine.setErrorHandler = function(handler) {
dwr.engine._errorHandler = handler;
};
/**
* Set an alternative warning handler from the default alert box.
* @see getahead.org/dwr/browser/engine/errors
*/
dwr.engine.setWarningHandler = function(handler) {
dwr.engine._warningHandler = handler;
};
/**
* Setter for the text/html handler - what happens if a DWR request gets an HTML
* reply rather than the expected Javascript. Often due to login timeout
*/
dwr.engine.setTextHtmlHandler = function(handler) {
dwr.engine._textHtmlHandler = handler;
}
/**
* Set a default timeout value for all calls. 0 (the default) turns timeouts off.
* @see getahead.org/dwr/browser/engine/errors
*/
dwr.engine.setTimeout = function(timeout) {
dwr.engine._timeout = timeout;
};
/**
* The Pre-Hook is called before any DWR remoting is done.
* @see getahead.org/dwr/browser/engine/hooks
*/
dwr.engine.setPreHook = function(handler) {
dwr.engine._preHook = handler;
};
/**
* The Post-Hook is called after any DWR remoting is done.
* @see getahead.org/dwr/browser/engine/hooks
*/
dwr.engine.setPostHook = function(handler) {
dwr.engine._postHook = handler;
};
/**
* Custom headers for all DWR calls
* @see getahead.org/dwr/????
*/
dwr.engine.setHeaders = function(headers) {
dwr.engine._headers = headers;
};
/**
* Custom parameters for all DWR calls
* @see getahead.org/dwr/????
*/
dwr.engine.setParameters = function(parameters) {
dwr.engine._parameters = parameters;
};
/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.XMLHttpRequest = 1;
/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.IFrame = 2;
/** XHR remoting type constant. See dwr.engine.setRpcType() */
dwr.engine.ScriptTag = 3;
/**
* Set the preferred remoting type.
* @param newType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag
* @see getahead.org/dwr/browser/engine/options
*/
dwr.engine.setRpcType = function(newType) {
if (newType != dwr.engine.XMLHttpRequest && newType != dwr.engine.IFrame && newType != dwr.engine.ScriptTag) {
dwr.engine._handleError(null, { name:"dwr.engine.invalidRpcType", message:"RpcType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag" });
return;
}
dwr.engine._rpcType = newType;
};
/**
* Which HTTP method do we use to send results? Must be one of "GET" or "POST".
* @see getahead.org/dwr/browser/engine/options
*/
dwr.engine.setHttpMethod = function(httpMethod) {
if (httpMethod != "GET" && httpMethod != "POST") {
dwr.engine._handleError(null, { name:"dwr.engine.invalidHttpMethod", message:"Remoting method must be one of GET or POST" });
return;
}
dwr.engine._httpMethod = httpMethod;
};
/**
* Ensure that remote calls happen in the order in which they were sent? (Default: false)
* @see getahead.org/dwr/browser/engine/ordering
*/
dwr.engine.setOrdered = function(ordered) {
dwr.engine._ordered = ordered;
};
/**
* Do we ask the XHR object to be asynchronous? (Default: true)
* @see getahead.org/dwr/browser/engine/options
*/
dwr.engine.setAsync = function(async) {
dwr.engine._async = async;
};
/**
* Does DWR poll the server for updates? (Default: false)
* @see getahead.org/dwr/browser/engine/options
*/
dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
if (activeReverseAjax) {
// Bail if we are already started
if (dwr.engine._activeReverseAjax) return;
dwr.engine._activeReverseAjax = true;
dwr.engine._poll();
}
else {
// Can we cancel an existing request?
if (dwr.engine._activeReverseAjax && dwr.engine._pollReq) dwr.engine._pollReq.abort();
dwr.engine._activeReverseAjax = false;
}
// TODO: in iframe mode, if we start, stop, start then the second start may
// well kick off a second iframe while the first is still about to return
// we should cope with this but we don't
};
/**
* Set the preferred polling type.
* @param newPollType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame
* @see getahead.org/dwr/browser/engine/options
*/
dwr.engine.setPollType = function(newPollType) {
if (newPollType != dwr.engine.XMLHttpRequest && newPollType != dwr.engine.IFrame) {
dwr.engine._handleError(null, { name:"dwr.engine.invalidPollType", message:"PollType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame"  });
return;
}
dwr.engine._pollType = newPollType;
};
/**
* The default message handler.
* @see getahead.org/dwr/browser/engine/errors
*/
dwr.engine.defaultErrorHandler = function(message, ex) {
dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);
if (message == null || message == "") alert("A server error has occured. More information may be available in the console.");
// Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
else alert(message);
};
/**
* The default warning handler.
* @see getahead.org/dwr/browser/engine/errors
*/
dwr.engine.defaultWarningHandler = function(message, ex) {
dwr.engine._debug(message);
};
/**
* For reduced latency you can group several remote calls together using a batch.
* @see getahead.org/dwr/browser/engine/batch
*/
dwr.engine.beginBatch = function() {
if (dwr.engine._batch) {
dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
return;
}
dwr.engine._batch = dwr.engine._createBatch();
};
/**
* Finished grouping a set of remote calls together. Go and execute them all.
* @see getahead.org/dwr/browser/engine/batch
*/
dwr.engine.endBatch = function(options) {
var batch = dwr.engine._batch;
if (batch == null) {
dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
return;
}
dwr.engine._batch = null;
if (batch.map.callCount == 0) return;
// The hooks need to be merged carefully to preserve ordering
if (options) dwr.engine._mergeBatch(batch, options);
// In ordered mode, we don't send unless the list of sent items is empty
if (dwr.engine._ordered && dwr.engine._batchesLength != 0) {
dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
}
else {
dwr.engine._sendData(batch);
}
};
/** @deprecated */
dwr.engine.setPollMethod = function(type) { dwr.engine.setPollType(type); };
dwr.engine.setMethod = function(type) { dwr.engine.setRpcType(type); };
dwr.engine.setVerb = function(verb) { dwr.engine.setHttpMethod(verb); };
//==============================================================================
// Only private stuff below here
//==============================================================================
/** The original page id sent from the server */
dwr.engine._origScriptSessionId = "33F66396327C4FB96813911D37C09FC9";
/** The session cookie name */
dwr.engine._sessionCookieName = "JSESSIONID"; // JSESSIONID
/** Is GET enabled for the benefit of Safari? */
dwr.engine._allowGetForSafariButMakeForgeryEasier = "false";
/** The script prefix to strip in the case of scriptTagProtection. */
dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';";
/** The default path to the DWR servlet */
dwr.engine._defaultPath = "/swift/js/ajax";
/** The read page id that we calculate */
dwr.engine._scriptSessionId = null;
/** The function that we use to fetch/calculate a session id */
dwr.engine._getScriptSessionId = function() {
if (dwr.engine._scriptSessionId == null) {
dwr.engine._scriptSessionId = dwr.engine._origScriptSessionId + Math.floor(Math.random() * 1000);
}
return dwr.engine._scriptSessionId;
};
/** A function to call if something fails. */
dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;
/** For debugging when something unexplained happens. */
dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;
/** A function to be called before requests are marshalled. Can be null. */
dwr.engine._preHook = null;
/** A function to be called after replies are received. Can be null. */
dwr.engine._postHook = null;
/** An map of the batches that we have sent and are awaiting a reply on. */
dwr.engine._batches = {};
/** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
dwr.engine._batchesLength = 0;
/** In ordered mode, the array of batches waiting to be sent */
dwr.engine._batchQueue = [];
/** What is the default rpc type */
dwr.engine._rpcType = dwr.engine.XMLHttpRequest;
/** What is the default remoting method (ie GET or POST) */
dwr.engine._httpMethod = "POST";
/** Do we attempt to ensure that calls happen in the order in which they were sent? */
dwr.engine._ordered = false;
/** Do we make the calls async? */
dwr.engine._async = true;
/** The current batch (if we are in batch mode) */
dwr.engine._batch = null;
/** The global timeout */
dwr.engine._timeout = 0;
/** ActiveX objects to use when we want to convert an xml string into a DOM object. */
dwr.engine._DOMDocument = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];
/** The ActiveX objects to use when we want to do an XMLHttpRequest call. */
dwr.engine._XMLHTTP = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
/** Are we doing comet or polling? */
dwr.engine._activeReverseAjax = false;
/** What is the default polling type */
dwr.engine._pollType = dwr.engine.XMLHttpRequest;
//dwr.engine._pollType = dwr.engine.IFrame;
/** The iframe that we are using to poll */
dwr.engine._outstandingIFrames = [];
/** The xhr object that we are using to poll */
dwr.engine._pollReq = null;
/** How many milliseconds between internal comet polls */
dwr.engine._pollCometInterval = 200;
/** How many times have we re-tried to poll? */
dwr.engine._pollRetries = 0;
dwr.engine._maxPollRetries = 0;
/** Do we do a document.reload if we get a text/html reply? */
dwr.engine._textHtmlHandler = null;
/** If you wish to send custom headers with every request */
dwr.engine._headers = null;
/** If you wish to send extra custom request parameters with each request */
dwr.engine._parameters = null;
/** Undocumented interceptors - do not use */
dwr.engine._postSeperator = "\n";
dwr.engine._defaultInterceptor = function(data) {return data;}
dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;
/** Batch ids allow us to know which batch the server is answering */
dwr.engine._nextBatchId = 0;
/** A list of the properties that need merging from calls to a batch */
dwr.engine._propnames = [ "rpcType", "httpMethod", "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];
/** Do we stream, or can be hacked to do so? */
dwr.engine._partialResponseNo = 0;
dwr.engine._partialResponseYes = 1;
dwr.engine._partialResponseFlush = 2;
/**
* @private Send a request. Called by the Javascript interface stub
* @param path part of URL after the host and before the exec bit without leading or trailing /s
* @param scriptName The class to execute
* @param methodName The method on said class to execute
* @param func The callback function to which any returned data should be passed
*       if this is null, any returned data will be ignored
* @param vararg_params The parameters to pass to the above class
*/
dwr.engine._execute = function(path, scriptName, methodName, vararg_params) {
var singleShot = false;
if (dwr.engine._batch == null) {
dwr.engine.beginBatch();
singleShot = true;
}
var batch = dwr.engine._batch;
// To make them easy to manipulate we copy the arguments into an args array
var args = [];
for (var i = 0; i < arguments.length - 3; i++) {
args[i] = arguments[i + 3];
}
// All the paths MUST be to the same servlet
if (batch.path == null) {
batch.path = path;
}
else {
if (batch.path != path) {
dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
return;
}
}
// From the other params, work out which is the function (or object with
// call meta-data) and which is the call parameters
var callData;
var lastArg = args[args.length - 1];
if (typeof lastArg == "function" || lastArg == null) callData = { callback:args.pop() };
else callData = args.pop();
// Merge from the callData into the batch
dwr.engine._mergeBatch(batch, callData);
batch.handlers[batch.map.callCount] = {
exceptionHandler:callData.exceptionHandler,
callback:callData.callback
};
// Copy to the map the things that need serializing
var prefix = "c" + batch.map.callCount + "-";
batch.map[prefix + "scriptName"] = scriptName;
batch.map[prefix + "methodName"] = methodName;
batch.map[prefix + "id"] = batch.map.callCount;
for (i = 0; i < args.length; i++) {
dwr.engine._serializeAll(batch, [], args[i], prefix + "param" + i);
}
// Now we have finished remembering the call, we incr the call count
batch.map.callCount++;
if (singleShot) dwr.engine.endBatch();
};
/** @private Poll the server to see if there is any data waiting */
dwr.engine._poll = function(overridePath) {
if (!dwr.engine._activeReverseAjax) return;
var batch = dwr.engine._createBatch();
batch.map.id = 0; // TODO: Do we need this??
batch.map.callCount = 1;
batch.isPoll = true;
if (navigator.userAgent.indexOf("Gecko/") != -1) {
batch.rpcType = dwr.engine._pollType;
batch.map.partialResponse = dwr.engine._partialResponseYes;
}
else if (document.all) {
batch.rpcType = dwr.engine.IFrame;
batch.map.partialResponse = dwr.engine._partialResponseFlush;
}
else {
batch.rpcType = dwr.engine._pollType;
batch.map.partialResponse = dwr.engine._partialResponseNo;
}
batch.httpMethod = "POST";
batch.async = true;
batch.timeout = 0;
batch.path = (overridePath) ? overridePath : dwr.engine._defaultPath;
batch.preHooks = [];
batch.postHooks = [];
batch.errorHandler = dwr.engine._pollErrorHandler;
batch.warningHandler = dwr.engine._pollErrorHandler;
batch.handlers[0] = {
callback:function(pause) {
dwr.engine._pollRetries = 0;
setTimeout("dwr.engine._poll()", pause);
}
};
// Send the data
dwr.engine._sendData(batch);
if (batch.rpcType == dwr.engine.XMLHttpRequest) {
// if (batch.map.partialResponse != dwr.engine._partialResponseNo) {
dwr.engine._checkCometPoll();
}
};
/** Try to recover from polling errors */
dwr.engine._pollErrorHandler = function(msg, ex) {
// if anything goes wrong then just silently try again (up to 3x) after 10s
dwr.engine._pollRetries++;
dwr.engine._debug("Reverse Ajax poll failed (pollRetries=" + dwr.engine._pollRetries + "): " + ex.name + " : " + ex.message);
if (dwr.engine._pollRetries < dwr.engine._maxPollRetries) {
setTimeout("dwr.engine._poll()", 10000);
}
else {
dwr.engine._debug("Giving up.");
}
};
/** @private Generate a new standard batch */
dwr.engine._createBatch = function() {
var batch = {
map:{
callCount:0,
page:window.location.pathname + window.location.search,
httpSessionId:dwr.engine._getJSessionId(),
scriptSessionId:dwr.engine._getScriptSessionId()
},
charsProcessed:0, paramCount:0,
headers:[], parameters:[],
isPoll:false, headers:{}, handlers:{}, preHooks:[], postHooks:[],
rpcType:dwr.engine._rpcType,
httpMethod:dwr.engine._httpMethod,
async:dwr.engine._async,
timeout:dwr.engine._timeout,
errorHandler:dwr.engine._errorHandler,
warningHandler:dwr.engine._warningHandler,
textHtmlHandler:dwr.engine._textHtmlHandler
};
if (dwr.engine._preHook) batch.preHooks.push(dwr.engine._preHook);
if (dwr.engine._postHook) batch.postHooks.push(dwr.engine._postHook);
var propname, data;
if (dwr.engine._headers) {
for (propname in dwr.engine._headers) {
data = dwr.engine._headers[propname];
if (typeof data != "function") batch.headers[propname] = data;
}
}
if (dwr.engine._parameters) {
for (propname in dwr.engine._parameters) {
data = dwr.engine._parameters[propname];
if (typeof data != "function") batch.parameters[propname] = data;
}
}
return batch;
}
/** @private Take further options and merge them into */
dwr.engine._mergeBatch = function(batch, overrides) {
var propname, data;
for (var i = 0; i < dwr.engine._propnames.length; i++) {
propname = dwr.engine._propnames[i];
if (overrides[propname] != null) batch[propname] = overrides[propname];
}
if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
if (overrides.headers) {
for (propname in overrides.headers) {
data = overrides.headers[propname];
if (typeof data != "function") batch.headers[propname] = data;
}
}
if (overrides.parameters) {
for (propname in overrides.parameters) {
data = overrides.parameters[propname];
if (typeof data != "function") batch.map["p-" + propname] = "" + data;
}
}
};
/** @private What is our session id? */
dwr.engine._getJSessionId =  function() {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) {
return cookie.substring(11, cookie.length);
}
}
return "";
}
/** @private Check for reverse Ajax activity */
dwr.engine._checkCometPoll = function() {
for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
var text = "";
var iframe = dwr.engine._outstandingIFrames[i];
try {
text = dwr.engine._getTextFromCometIFrame(iframe);
}
catch (ex) {
dwr.engine._handleWarning(iframe.batch, ex);
}
if (text != "") dwr.engine._processCometResponse(text, iframe.batch);
}
if (dwr.engine._pollReq) {
var req = dwr.engine._pollReq;
var text = req.responseText;
dwr.engine._processCometResponse(text, req.batch);
}
// If the poll resources are still there, come back again
if (dwr.engine._outstandingIFrames.length > 0 || dwr.engine._pollReq) {
setTimeout("dwr.engine._checkCometPoll()", dwr.engine._pollCometInterval);
}
};
/** @private Extract the whole (executed an all) text from the current iframe */
dwr.engine._getTextFromCometIFrame = function(frameEle) {
var body = frameEle.contentWindow.document.body;
if (body == null) return "";
var text = body.innerHTML;
// We need to prevent IE from stripping line feeds
if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) {
text = text.substring(5, text.length - 7);
}
return text;
};
/** @private Some more text might have come in, test and execute the new stuff */
dwr.engine._processCometResponse = function(response, batch) {
if (batch.charsProcessed == response.length) return;
if (response.length == 0) {
batch.charsProcessed = 0;
return;
}
var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
if (firstStartTag == -1) {
// dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
batch.charsProcessed = response.length;
return;
}
// if (firstStartTag > 0) {
//   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
// }
var lastEndTag = response.lastIndexOf("//#DWR-END#");
if (lastEndTag == -1) {
// dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
return;
}
// Skip the end tag too for next time, remembering CR and LF
if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
batch.charsProcessed = lastEndTag + 13;
}
else {
batch.charsProcessed = lastEndTag + 11;
}
var exec = response.substring(firstStartTag + 13, lastEndTag);
dwr.engine._receivedBatch = batch;
dwr.engine._eval(exec);
dwr.engine._receivedBatch = null;
};
/** @private Actually send the block of data in the batch object. */
dwr.engine._sendData = function(batch) {
batch.map.batchId = dwr.engine._nextBatchId++;
dwr.engine._batches[batch.map.batchId] = batch;
dwr.engine._batchesLength++;
batch.completed = false;
for (var i = 0; i < batch.preHooks.length; i++) {
batch.preHooks[i]();
}
batch.preHooks = null;
// Set a timeout
if (batch.timeout && batch.timeout != 0) {
batch.interval = setInterval(function() { dwr.engine._abortRequest(batch); }, batch.timeout);
}
// Get setup for XMLHttpRequest if possible
if (batch.rpcType == dwr.engine.XMLHttpRequest) {
if (window.XMLHttpRequest) {
batch.req = new XMLHttpRequest();
}
// IE5 for the mac claims to support window.ActiveXObject, but throws an error when it's used
else if (window.ActiveXObject && !(navigator.userAgent.indexOf("Mac") >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) {
batch.req = dwr.engine._newActiveXObject(dwr.engine._XMLHTTP);
}
}
var prop, request;
if (batch.req) {
// Proceed using XMLHttpRequest
if (batch.async) {
batch.req.onreadystatechange = function() { dwr.engine._stateChange(batch); };
}
// If we're polling, record this for monitoring
if (batch.isPoll) {
dwr.engine._pollReq = batch.req;
// In IE XHR is an ActiveX control so you can't augment it like this
// however batch.isPoll uses IFrame on IE so were safe here
batch.req.batch = batch;
}
// Workaround for Safari 1.x POST bug
var indexSafari = navigator.userAgent.indexOf("Safari/");
if (indexSafari >= 0) {
var version = navigator.userAgent.substring(indexSafari + 7);
if (parseInt(version, 10) < 400) {
if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") batch.httpMethod = "GET";
else dwr.engine._handleWarning(batch, { name:"dwr.engine.oldSafari", message:"Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier." });
}
}
batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
request = dwr.engine._constructRequest(batch);
try {
batch.req.open(batch.httpMethod, request.url, batch.async);
try {
for (prop in batch.headers) {
var value = batch.headers[prop];
if (typeof value == "string") batch.req.setRequestHeader(prop, value);
}
if (!batch.headers["Content-Type"]) batch.req.setRequestHeader("Content-Type", "text/plain");
}
catch (ex) {
dwr.engine._handleWarning(batch, ex);
}
batch.req.send(request.body);
if (!batch.async) dwr.engine._stateChange(batch);
}
catch (ex) {
dwr.engine._handleError(batch, ex);
}
}
else if (batch.rpcType != dwr.engine.ScriptTag) {
// Proceed using iframe
var idname = batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map["c0-id"];
batch.div = document.createElement("div");
batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' style='width:0px;height:0px;border:0;' id='" + idname + "' name='" + idname + "'></iframe>";
document.body.appendChild(batch.div);
batch.iframe = document.getElementById(idname);
batch.iframe.batch = batch;
batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
if (batch.isPoll) dwr.engine._outstandingIFrames.push(batch.iframe);
request = dwr.engine._constructRequest(batch);
if (batch.httpMethod == "GET") {
batch.iframe.setAttribute("src", request.url);
// document.body.appendChild(batch.iframe);
}
else {
batch.form = document.createElement("form");
batch.form.setAttribute("id", "dwr-form");
batch.form.setAttribute("action", request.url);
batch.form.setAttribute("target", idname);
batch.form.target = idname;
batch.form.setAttribute("method", batch.httpMethod);
for (prop in batch.map) {
var value = batch.map[prop];
if (typeof value != "function") {
var formInput = document.createElement("input");
formInput.setAttribute("type", "hidden");
formInput.setAttribute("name", prop);
formInput.setAttribute("value", value);
batch.form.appendChild(formInput);
}
}
document.body.appendChild(batch.form);
batch.form.submit();
}
}
else {
batch.httpMethod = "GET"; // There's no such thing as ScriptTag using POST
batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
request = dwr.engine._constructRequest(batch);
batch.script = document.createElement("script");
batch.script.id = "dwr-st-" + batch.map["c0-id"];
batch.script.src = request.url;
document.body.appendChild(batch.script);
}
};
dwr.engine._ModePlainCall = "/call/plaincall/";
dwr.engine._ModeHtmlCall = "/call/htmlcall/";
dwr.engine._ModePlainPoll = "/call/plainpoll/";
dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";
/** @private Work out what the URL should look like */
dwr.engine._constructRequest = function(batch) {
// A quick string to help people that use web log analysers
var request = { url:batch.path + batch.mode, body:null };
if (batch.isPoll == true) {
request.url += "ReverseAjax.dwr";
}
else if (batch.map.callCount == 1) {
request.url += batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr";
}
else {
request.url += "Multiple." + batch.map.callCount + ".dwr";
}
// Play nice with url re-writing
var sessionMatch = location.href.match(/jsessionid=([^?]+)/);
if (sessionMatch != null) {
request.url += ";jsessionid=" + sessionMatch[1];
}
var prop;
if (batch.httpMethod == "GET") {
// Some browsers (Opera/Safari2) seem to fail to convert the callCount value
// to a string in the loop below so we do it manually here.
batch.map.callCount = "" + batch.map.callCount;
request.url += "?";
for (prop in batch.map) {
if (typeof batch.map[prop] != "function") {
request.url += encodeURIComponent(prop) + "=" + encodeURIComponent(batch.map[prop]) + "&";
}
}
request.url = request.url.substring(0, request.url.length - 1);
}
else {
// PERFORMANCE: for iframe mode this is thrown away.
request.body = "";
for (prop in batch.map) {
if (typeof batch.map[prop] != "function") {
request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
}
}
request.body = dwr.engine._contentRewriteHandler(request.body);
}
request.url = dwr.engine._urlRewriteHandler(request.url);
return request;
};
/** @private Called by XMLHttpRequest to indicate that something has happened */
dwr.engine._stateChange = function(batch) {
var toEval;
if (batch.completed) {
dwr.engine._debug("Error: _stateChange() with batch.completed");
return;
}
var req = batch.req;
try {
if (req.readyState != 4) return;
}
catch (ex) {
dwr.engine._handleWarning(batch, ex);
// It's broken - clear up and forget this call
dwr.engine._clearUp(batch);
return;
}
try {
var reply = req.responseText;
reply = dwr.engine._replyRewriteHandler(reply);
var status = req.status; // causes Mozilla to except on page moves
if (reply == null || reply == "") {
dwr.engine._handleWarning(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
}
else if (status != 200) {
dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText });
}
else {
var contentType = req.getResponseHeader("Content-Type");
if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") {
batch.textHtmlHandler();
}
else {
dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
}
}
else {
// Comet replies might have already partially executed
if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
dwr.engine._processCometResponse(reply, batch);
}
else {
if (reply.search("//#DWR") == -1) {
dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
}
else {
toEval = reply;
}
}
}
}
}
catch (ex) {
dwr.engine._handleWarning(batch, ex);
}
dwr.engine._callPostHooks(batch);
// Outside of the try/catch so errors propogate normally:
dwr.engine._receivedBatch = batch;
if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
dwr.engine._eval(toEval);
dwr.engine._receivedBatch = null;
dwr.engine._clearUp(batch);
};
/** @private Called by the server: Execute a callback */
dwr.engine._remoteHandleCallback = function(batchId, callId, reply) {
var batch = dwr.engine._batches[batchId];
if (batch == null) {
dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true);
return;
}
// Error handlers inside here indicate an error that is nothing to do
// with DWR so we handle them differently.
try {
var handlers = batch.handlers[callId];
if (!handlers) {
dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
}
else if (typeof handlers.callback == "function") handlers.callback(reply);
}
catch (ex) {
dwr.engine._handleError(batch, ex);
}
};
/** @private Called by the server: Handle an exception for a call */
dwr.engine._remoteHandleException = function(batchId, callId, ex) {
var batch = dwr.engine._batches[batchId];
if (batch == null) { dwr.engine._debug("Warning: null batch in remoteHandleException", true); return; }
var handlers = batch.handlers[callId];
if (handlers == null) { dwr.engine._debug("Warning: null handlers in remoteHandleException", true); return; }
if (ex.message == undefined) ex.message = "";
if (typeof handlers.exceptionHandler == "function") handlers.exceptionHandler(ex.message, ex);
else if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
};
/** @private Called by the server: The whole batch is broken */
dwr.engine._remoteHandleBatchException = function(ex, batchId) {
var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
if (searchBatch) {
dwr.engine._receivedBatch = dwr.engine._batches[batchId];
}
if (ex.message == undefined) ex.message = "";
dwr.engine._handleError(dwr.engine._receivedBatch, ex);
if (searchBatch) {
dwr.engine._receivedBatch = null;
dwr.engine._clearUp(dwr.engine._batches[batchId]);
}
};
/** @private Called by the server: Reverse ajax should not be used */
dwr.engine._remotePollCometDisabled = function(ex, batchId) {
dwr.engine.setActiveReverseAjax(false);
var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
if (searchBatch) {
dwr.engine._receivedBatch = dwr.engine._batches[batchId];
}
if (ex.message == undefined) ex.message = "";
dwr.engine._handleError(dwr.engine._receivedBatch, ex);
if (searchBatch) {
dwr.engine._receivedBatch = null;
dwr.engine._clearUp(dwr.engine._batches[batchId]);
}
};
/** @private Called by the server: An IFrame reply is about to start */
dwr.engine._remoteBeginIFrameResponse = function(iframe, batchId) {
if (iframe != null) dwr.engine._receivedBatch = iframe.batch;
dwr.engine._callPostHooks(dwr.engine._receivedBatch);
};
/** @private Called by the server: An IFrame reply is just completing */
dwr.engine._remoteEndIFrameResponse = function(batchId) {
dwr.engine._clearUp(dwr.engine._receivedBatch);
dwr.engine._receivedBatch = null;
};
/** @private This is a hack to make the context be this window */
dwr.engine._eval = function(script) {
if (script == null) return null;
if (script == "") { dwr.engine._debug("Warning: blank script", true); return null; }
// dwr.engine._debug("Exec: [" + script + "]", true);
return eval(script);
};
/** @private Called as a result of a request timeout */
dwr.engine._abortRequest = function(batch) {
if (batch && !batch.completed) {
clearInterval(batch.interval);
dwr.engine._clearUp(batch);
if (batch.req) batch.req.abort();
dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
}
};
/** @private call all the post hooks for a batch */
dwr.engine._callPostHooks = function(batch) {
if (batch.postHooks) {
for (var i = 0; i < batch.postHooks.length; i++) {
batch.postHooks[i]();
}
batch.postHooks = null;
}
}
/** @private A call has finished by whatever means and we need to shut it all down. */
dwr.engine._clearUp = function(batch) {
if (!batch) { dwr.engine._debug("Warning: null batch in dwr.engine._clearUp()", true); return; }
if (batch.completed == "true") { dwr.engine._debug("Warning: Double complete", true); return; }
// IFrame tidyup
if (batch.div) batch.div.parentNode.removeChild(batch.div);
if (batch.iframe) {
// If this is a poll frame then stop comet polling
for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
if (dwr.engine._outstandingIFrames[i] == batch.iframe) {
dwr.engine._outstandingIFrames.splice(i, 1);
}
}
batch.iframe.parentNode.removeChild(batch.iframe);
}
if (batch.form) batch.form.parentNode.removeChild(batch.form);
// XHR tidyup: avoid IE handles increase
if (batch.req) {
// If this is a poll frame then stop comet polling
if (batch.req == dwr.engine._pollReq) dwr.engine._pollReq = null;
delete batch.req;
}
if (batch.map && batch.map.batchId) {
delete dwr.engine._batches[batch.map.batchId];
dwr.engine._batchesLength--;
}
batch.completed = true;
// If there is anything on the queue waiting to go out, then send it.
// We don't need to check for ordered mode, here because when ordered mode
// gets turned off, we still process *waiting* batches in an ordered way.
if (dwr.engine._batchQueue.length != 0) {
var sendbatch = dwr.engine._batchQueue.shift();
dwr.engine._sendData(sendbatch);
}
};
/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleError = function(batch, ex) {
if (typeof ex == "string") ex = { name:"unknown", message:ex };
if (ex.message == null) ex.message = "";
if (ex.name == null) ex.name = "unknown";
if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
dwr.engine._clearUp(batch);
};
/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleWarning = function(batch, ex) {
if (typeof ex == "string") ex = { name:"unknown", message:ex };
if (ex.message == null) ex.message = "";
if (ex.name == null) ex.name = "unknown";
if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
dwr.engine._clearUp(batch);
};
/**
* @private Marshall a data item
* @param batch A map of variables to how they have been marshalled
* @param referto An array of already marshalled variables to prevent recurrsion
* @param data The data to be marshalled
* @param name The name of the data being marshalled
*/
dwr.engine._serializeAll = function(batch, referto, data, name) {
if (data == null) {
batch.map[name] = "null:null";
return;
}
switch (typeof data) {
case "boolean":
batch.map[name] = "boolean:" + data;
break;
case "number":
batch.map[name] = "number:" + data;
break;
case "string":
batch.map[name] = "string:" + encodeURIComponent(data);
break;
case "object":
if (data instanceof String) batch.map[name] = "String:" + encodeURIComponent(data);
else if (data instanceof Boolean) batch.map[name] = "Boolean:" + data;
else if (data instanceof Number) batch.map[name] = "Number:" + data;
else if (data instanceof Date) batch.map[name] = "Date:" + data.getTime();
else if (data && data.join) batch.map[name] = dwr.engine._serializeArray(batch, referto, data, name);
else batch.map[name] = dwr.engine._serializeObject(batch, referto, data, name);
break;
case "function":
// We just ignore functions.
break;
default:
dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
batch.map[name] = "default:" + data;
break;
}
};
/** @private Have we already converted this object? */
dwr.engine._lookup = function(referto, data, name) {
var lookup;
// Can't use a map: getahead.org/ajax/javascript-gotchas
for (var i = 0; i < referto.length; i++) {
if (referto[i].data == data) {
lookup = referto[i];
break;
}
}
if (lookup) return "reference:" + lookup.name;
referto.push({ data:data, name:name });
return null;
};
/** @private Marshall an object */
dwr.engine._serializeObject = function(batch, referto, data, name) {
var ref = dwr.engine._lookup(referto, data, name);
if (ref) return ref;
// This check for an HTML is not complete, but is there a better way?
// Maybe we should add: data.hasChildNodes typeof "function" == true
if (data.nodeName && data.nodeType) {
return dwr.engine._serializeXml(batch, referto, data, name);
}
// treat objects as an associative arrays
var reply = "Object_" + dwr.engine._getObjectClassName(data) + ":{";
var element;
for (element in data) {
if (typeof data[element] != "function") {
batch.paramCount++;
var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
dwr.engine._serializeAll(batch, referto, data[element], childName);
reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
}
}
if (reply.substring(reply.length - 2) == ", ") {
reply = reply.substring(0, reply.length - 2);
}
reply += "}";
return reply;
};
/** @private Returns the classname of supplied argument obj */
dwr.engine._errorClasses = { "Error":Error, "EvalError":EvalError, "RangeError":RangeError, "ReferenceError":ReferenceError, "SyntaxError":SyntaxError, "TypeError":TypeError, "URIError":URIError };
dwr.engine._getObjectClassName = function(obj) {
// Try to find the classname by stringifying the object's constructor
// and extract <class> from "function <class>".
if (obj && obj.constructor && obj.constructor.toString)
{
var str = obj.constructor.toString();
var regexpmatch = str.match(/function\s+(\w+)/);
if (regexpmatch && regexpmatch.length == 2) {
return regexpmatch[1];
}
}
// Now manually test against the core Error classes, as these in some
// browsers successfully match to the wrong class in the
// Object.toString() test we will do later
if (obj && obj.constructor) {
for (var errorname in dwr.engine._errorClasses) {
if (obj.constructor == dwr.engine._errorClasses[errorname]) return errorname;
}
}
// Try to find the classname by calling Object.toString() on the object
// and extracting <class> from "[object <class>]"
if (obj) {
var str = Object.prototype.toString.call(obj);
var regexpmatch = str.match(/\[object\s+(\w+)/);
if (regexpmatch && regexpmatch.length==2) {
return regexpmatch[1];
}
}
// Supplied argument was probably not an object, but what is better?
return "Object";
};
/** @private Marshall an object */
dwr.engine._serializeXml = function(batch, referto, data, name) {
var ref = dwr.engine._lookup(referto, data, name);
if (ref) return ref;
var output;
if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
else if (data.toXml) output = data.toXml;
else output = data.innerHTML;
return "XML:" + encodeURIComponent(output);
};
/** @private Marshall an array */
dwr.engine._serializeArray = function(batch, referto, data, name) {
var ref = dwr.engine._lookup(referto, data, name);
if (ref) return ref;
var reply = "Array:[";
for (var i = 0; i < data.length; i++) {
if (i != 0) reply += ",";
batch.paramCount++;
var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
dwr.engine._serializeAll(batch, referto, data[i], childName);
reply += "reference:";
reply += childName;
}
reply += "]";
return reply;
};
/** @private Convert an XML string into a DOM object. */
dwr.engine._unserializeDocument = function(xml) {
var dom;
if (window.DOMParser) {
var parser = new DOMParser();
dom = parser.parseFromString(xml, "text/xml");
if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
var message = dom.documentElement.firstChild.data;
message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
throw message;
}
return dom;
}
else if (window.ActiveXObject) {
dom = dwr.engine._newActiveXObject(dwr.engine._DOMDocument);
dom.loadXML(xml); // What happens on parse fail with IE?
return dom;
}
else {
var div = document.createElement("div");
div.innerHTML = xml;
return div;
}
};
/** @param axarray An array of strings to attempt to create ActiveX objects from */
dwr.engine._newActiveXObject = function(axarray) {
var returnValue;
for (var i = 0; i < axarray.length; i++) {
try {
returnValue = new ActiveXObject(axarray[i]);
break;
}
catch (ex) { /* ignore */ }
}
return returnValue;
};
/** @private Used internally when some message needs to get to the programmer */
dwr.engine._debug = function(message, stacktrace) {
var written = false;
try {
if (window.console) {
if (stacktrace && window.console.trace) window.console.trace();
window.console.log(message);
written = true;
}
else if (window.opera && window.opera.postError) {
window.opera.postError(message);
written = true;
}
}
catch (ex) { /* ignore */ }
if (!written) {
var debug = document.getElementById("dwr-debug");
if (debug) {
var contents = message + "<br/>" + debug.innerHTML;
if (contents.length > 2048) contents = contents.substring(0, 2048);
debug.innerHTML = contents;
}
}
};
