var Prototype = {
    Version: '1.6.0.1',

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

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

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

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

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

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

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

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

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

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

        klass.prototype.constructor = klass;

        return klass;
    }
};

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

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

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

        return this;
    }
};

var Abstract = { };

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

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

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

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

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

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

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

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

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

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

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

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

    isArray: function(object) {
        return object && object.constructor === Array;
    },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return returnValue;
    }
};

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

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

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

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

        this.registerCallback();
    },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return camelized;
    },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },
  
  eachSlice: function(number, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

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

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

  collect: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },
  
  detect: function(iterator, context) {
    iterator = iterator.bind(context);
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },
  
  findAll: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },
  
  grep: function(filter, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);
      
    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator(value, index));
    });
    return results;
  },
  
  include: function(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },
  
  inGroupsOf: function(number, fillWith) {
    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },
  
  inject: function(memo, iterator, context) {
    iterator = iterator.bind(context);
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },
  
  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },
  
  max: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  },
  
  min: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  },
  
  partition: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator(value, index) ? 
        trues : falses).push(value);
    });
    return [trues, falses];
  },
  
  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },
  
  reject: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },
  
  sortBy: function(iterator, context) {
    iterator = iterator.bind(context);
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },
  
  toArray: function() {
    return this.map();
  },
  
  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },
  
  size: function() {
    return this.toArray().length;
  },
  
  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

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

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

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  succ: function() {
    return this + 1;
  },
  
  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  },
  
  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },
  
  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

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

//ICE-3015 -- rename class to avoid conflict with the function having the same name in Tomahawk component JS code
var Hashtable = Class.create(Enumerable, (function() {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Hashtable.prototype.toTemplateReplacements = Hashtable.prototype.toObject;
Hashtable.from = $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },
  
  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },
  
  include: function(value) {
    if (value < this.start) 
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
};
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

var Ice = new Object;
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

Object.methods = function(prototype) {
  for (property in prototype) {
    this.prototype[property] = prototype[property];
  }
};

Object.subclass = function(prototype) {
	var subclazz = function() {
		this.initialize.apply(this, arguments);
	};
	subclazz.methods = this.methods;
	subclazz.subclass = this.subclass;
    subclazz.prototype.initialize = Function.NOOP;
	subclazz.methods(this.prototype);
    subclazz.prototype.initializeSuperclass = this.prototype.initialize ? this.prototype.initialize : Function.NOOP;
    subclazz.methods(prototype || {});
	return subclazz;
};
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

Boolean.prototype.ifTrue = function(e) {
	if (this == true) e();
	return this;
};

Boolean.prototype.ifFalse = function(e) {
	if (this == false) e();
	return this;
};
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

//todo: implement date formatter. this is just an improvisation!
Number.prototype.asZeroPrefixedString = function() {
    return this < 9 ? ('0' + this) : this.toString();
};

Date.prototype.toTimestamp = function() {
    return this.toLocaleTimeString().substr(0, 8);
};
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

Object.extend(String.prototype, {
    asBoolean: function() {
        return 'true' == this || 'yes' == this;
    },

    asNumber:function() {
        return this * 1;
    },

    asElement:function() {
        return document.getElementById(this);
    },

    asExtendedElement:function() {
        var element = this.asElement();
        if (!element) throw 'cannot find element with id: \'' + this + '\'';
        return Ice.ElementModel.Element.adaptToElement(element);
    },

    asRegexp:function() {
        return new RegExp(this);
    },

    contains: function(substring) {
        return this.indexOf(substring) >= 0;
    },

    containsWords: function() {
        return /(\w+)/.test(this);
    }
});

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

Object.extend(Array.prototype, {
    eachWithGuard: function(iterator) {
        this.each(function(element) {
            try {
                iterator(element);
            } catch (e) {
                //ignore
            }
        });
    },

    intersect: function(other) {
        return this.select(function(item) {
            return other.include(item);
        });
    },

    complement: function(other) {
        return this.reject(function(item) {
            return other.include(item); 
        });
    },

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

    isNotEmpty: function() {
        return this.length > 0;
    },

    as: function(context) {
        context.apply(context, this);
    },

    copy: function() {
        return this.collect(function(element) {
            return element;
        });
    },

    copyFrom: function(start, length) {
        var copy = [];
        var end = start + length;
        for (var i = start; i < end; i++) copy.push(this[i]);
        return copy;
    },

    broadcast: function(event) {
        this.each(function(element) {
            element(event);
        });
    },

    broadcaster: function() {
        return function(event) {
            this.broadcast(event);
        }.bind(this);
    },

    asSet: function() {
        var set = [];
        this.each(function(element) {
            if (!set.include(element)) set.push(element);
        });

        return set;
    }
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

Function.prototype.delayFor = function(milliseconds) {
	var thisFunction = this;
	return function() {
		var thisContext = this;
		var thisArguments = arguments;
		var call = function() {
            try {
                thisFunction.apply(thisContext, thisArguments);
            } finally {
                clearInterval(thisArguments.id);
                thisArguments.id = null;
            }
        };
		var id = thisArguments.id = setInterval(call, milliseconds);
        arguments.callee.cancel = function() {
            clearInterval(id);
            thisArguments.id = null;
        };
	};
};

Function.prototype.delayExecutionFor = function(milliseconds) {
    var call = this.delayFor(milliseconds);
    call.apply();
    return call;
};

Function.prototype.repeatEvery = function(milliseconds) {
	var thisFunction = this;
	return function() {
		var thisContext = this;
		var thisArguments = arguments;
		var call = function() {
			thisFunction.apply(thisContext, thisArguments);
		};
		var id = setInterval(call, milliseconds);
		arguments.callee.cancel = function() {
			clearInterval(id);
		};
	};
};

Function.prototype.repeatExecutionEvery = function(milliseconds) {
    var call = this.repeatEvery(milliseconds);
    call.apply(this);
    return call;
};

Function.NOOP = function() {
};
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

window.width = function() {
    return window.innerWidth ? window.innerWidth : (document.documentElement && document.documentElement.clientWidth) ? document.documentElement.clientWidth : document.body.clientWidth;
};

window.height = function() {
    return window.innerHeight ? window.innerHeight : (document.documentElement && document.documentElement.clientHeight) ? document.documentElement.clientHeight : document.body.clientHeight;
};

['onLoad', 'onUnload', 'onBeforeUnload', 'onResize', 'onScroll' ].each(function(name) {
    //avoid to redeclare the callback 
    if (!window[name]) {
        window[name] = function(listener) {
            var eventName = name.toLowerCase();
            var previousListener = window[eventName];
            var listeners = previousListener ? [ previousListener, listener ] : [ listener ];
            window[eventName] = listeners.broadcaster();
            //redefine for next calls
            window[name] = function(listener) {
                //add listener only when not previously added
                if (!listeners.detect(function(existingListener) {
                    return existingListener.toString() == listener.toString();
                })) listeners.push(listener);
            };
        };
    }
});

if(typeof OpenAjax!='undefined'){
    if(typeof OpenAjax.addOnLoad !='undefined'){
        var current = window.onLoad;
        window.onLoad = OpenAjax.addOnLoad;
        OpenAjax.addOnLoad(current);
    }
    if(typeof OpenAjax.addOnUnLoad !='undefined'){
        var current = window.onUnload;
        window.onUnload = OpenAjax.addOnUnLoad;
        OpenAjax.addOnLoad(current);
    }

}


window.onKeyPress = function(listener) {
    var previousListener = document.onkeypress;
    document.onkeypress = previousListener ? function(e) {
        listener(Ice.EventModel.Event.adaptToEvent(e));
        previousListener(e);
    } : function(e) {
        listener(Ice.EventModel.Event.adaptToKeyEvent(e));
    };
};
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice ].as(function(This) {
    This.Enumerator = Object.subclass({
        initialize: function(indexedObject) {
            this.indexedObject = indexedObject;
        },

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

        reverse: function() {
            return new This.ReverseEnumerator(this.indexedObject);
        }
    });

    This.Enumerator.methods(Enumerable);

    This.ReverseEnumerator = This.Enumerator.subclass({
        _each: function(iterator) {
            var start = this.indexedObject.length - 1;
            for (var i = start; i >= 0; i--) iterator(this.indexedObject[i], i);
        },

        reverse: function() {
            return new This.Enumerator(this.indexedObject);
        }
    });

    //public call
    window.$enumerate = function(object) {
        return new This.Enumerator(object);
    };
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Log = new Object ].as(function(This) {
    This.Priority = Object.subclass({
        debug: function(handler, category, message, exception) {
            handler.debug(category, message, exception);
        },

        info: function(handler, category, message, exception) {
            handler.info(category, message, exception);
        },

        warn: function(handler, category, message, exception) {
            handler.warn(category, message, exception);
        },

        error: function(handler, category, message, exception) {
            handler.error(category, message, exception);
        }
    });

    This.Debug = This.Priority.subclass({
        asString: function() {
            return 'Debug';
        }
    });

    This.Info = This.Debug.subclass({
        debug: Function.NOOP,

        asString: function() {
            return 'Info';
        }
    });

    This.Warn = This.Info.subclass({
        info: Function.NOOP,

        asString: function() {
            return 'Warn';
        }
    });

    This.Error = This.Warn.subclass({
        warn: Function.NOOP,

        asString: function() {
            return 'Error';
        }
    });

    This.Priority.DEBUG = new This.Debug;
    This.Priority.INFO = new This.Info;
    This.Priority.WARN = new This.Warn;
    This.Priority.ERROR = new This.Error;
    This.Priority.Levels = [ This.Priority.DEBUG, This.Priority.INFO, This.Priority.WARN, This.Priority.ERROR ];

    This.Logger = Object.subclass({
        initialize: function(category, handler, priority) {
            this.handler = handler || { debug: Function.NOOP, info: Function.NOOP, warn: Function.NOOP, error: Function.NOOP };
            this.category = category;
            this.children = [];
            this.priority = priority || This.Priority.ERROR;
        },

        debug: function(message, exception) {
            this.priority.debug(this.handler, this.category, message, exception);
        },

        info: function(message, exception) {
            this.priority.info(this.handler, this.category, message, exception);
        },

        warn: function(message, exception) {
            this.priority.warn(this.handler, this.category, message, exception);
        },

        error: function(message, exception) {
            this.priority.error(this.handler, this.category, message, exception);
        },

        child: function(category) {
            var childCategory = this.category.copy();
            childCategory.push(category);
            var child = new This.Logger(childCategory, this.handler, this.priority);
            this.children.push(child);
            return child;
        },

        threshold: function(thresholdPriority) {
            this.priority = thresholdPriority;
            this.children.each(function(logger) {
                logger.threshold(thresholdPriority);
            })
        },

        handleWith: function(handler) {
            this.handler = handler;
        }
    });

    This.WindowLogHandler = Object.subclass({
        initialize: function(logger, parentWindow, lines, thresholdPriority) {
            this.lineOptions = [ 25, 50, 100, 200, 400 ];
            this.logger = logger;
            this.logger.handleWith(this);
            this.parentWindow = parentWindow;
            this.lines = lines || this.lineOptions[3];
            this.thresholdPriority = thresholdPriority || This.Priority.DEBUG;
            this.categoryMatcher = /.*/;
            this.closeOnExit = true;
            this.toggle();

            this.parentWindow.onKeyPress(function(e) {
                var key = e.keyCode();
                if ((key == 20 || key == 84) && e.isCtrlPressed() && e.isShiftPressed()) {
                    this.enable();
                }
            }.bind(this));
            this.parentWindow.onUnload(function() {
                window.logger.info('page unloaded!');
                this.disable();
            }.bind(this));
        },

        enable: function() {
            try {
                this.window = this.parentWindow.open('', 'log' + window.identifier, 'scrollbars=1,width=800,height=680');
                var windowDocument = this.window.document;

                this.log = this.window.document.getElementById('log-window');
                this.toggle();
                if (this.log) return;

                windowDocument.body.appendChild(windowDocument.createTextNode(' Close on exit '));
                var closeOnExitCheckbox = windowDocument.createElement('input');
                closeOnExitCheckbox.style.margin = '2px';
                closeOnExitCheckbox.setAttribute('type', 'checkbox');
                closeOnExitCheckbox.defaultChecked = true;
                closeOnExitCheckbox.checked = true;
                closeOnExitCheckbox.onclick = function() {
                    this.closeOnExit = closeOnExitCheckbox.checked;
                }.bind(this);
                windowDocument.body.appendChild(closeOnExitCheckbox);

                windowDocument.body.appendChild(windowDocument.createTextNode(' Lines '));
                var lineCountDropDown = windowDocument.createElement('select');
                lineCountDropDown.style.margin = '2px';
                this.lineOptions.each(function(count, index) {
                    var option = lineCountDropDown.appendChild(windowDocument.createElement('option'));
                    if (this.lines == count) lineCountDropDown.selectedIndex = index;
                    option.appendChild(windowDocument.createTextNode(count.toString()));
                }.bind(this));

                lineCountDropDown.onchange = function(event) {
                    this.lines = this.lineOptions[lineCountDropDown.selectedIndex];
                    this.clearPreviousEvents();
                }.bind(this);
                windowDocument.body.appendChild(lineCountDropDown);

                windowDocument.body.appendChild(windowDocument.createTextNode(' Category '));
                var categoryInputText = windowDocument.createElement('input');
                categoryInputText.style.margin = '2px';
                categoryInputText.setAttribute('type', 'text');
                categoryInputText.setAttribute('value', this.categoryMatcher.source);
                categoryInputText.onchange = function() {
                    this.categoryMatcher = categoryInputText.value.asRegexp();
                }.bind(this);
                windowDocument.body.appendChild(categoryInputText);


                windowDocument.body.appendChild(windowDocument.createTextNode(' Level '));
                var levelDropDown = windowDocument.createElement('select');
                levelDropDown.style.margin = '2px';
                This.Priority.Levels.each(function(priority, index) {
                    var option = levelDropDown.appendChild(windowDocument.createElement('option'));
                    if (this.thresholdPriority == priority) levelDropDown.selectedIndex = index;
                    option.appendChild(windowDocument.createTextNode(priority.asString()));
                }.bind(this));

                this.logger.threshold(this.thresholdPriority);
                levelDropDown.onchange = function(event) {
                    this.thresholdPriority = This.Priority.Levels[levelDropDown.selectedIndex];
                    this.logger.threshold(this.thresholdPriority);
                }.bind(this);
                windowDocument.body.appendChild(levelDropDown);

                var startStopButton = windowDocument.createElement('input');
                startStopButton.style.margin = '2px';
                startStopButton.setAttribute('type', 'button');
                startStopButton.setAttribute('value', 'Stop');
                startStopButton.onclick = function() {
                    startStopButton.setAttribute('value', this.toggle() ? 'Stop' : 'Start');
                }.bind(this);
                windowDocument.body.appendChild(startStopButton);

                var clearButton = windowDocument.createElement('input');
                clearButton.style.margin = '2px';
                clearButton.setAttribute('type', 'button');
                clearButton.setAttribute('value', 'Clear');
                clearButton.onclick = function() {
                    this.clearAllEvents();
                }.bind(this);
                windowDocument.body.appendChild(clearButton);

                this.log = windowDocument.body.appendChild(windowDocument.createElement('pre'));
                this.log.id = 'log-window';
                this.log.style.width = '100%';
                this.log.style.minHeight = '0';
                this.log.style.maxHeight = '550px';
                this.log.style.borderWidth = '1px';
                this.log.style.borderStyle = 'solid';
                this.log.style.borderColor = '#999';
                this.log.style.backgroundColor = '#ddd';
                this.log.style.overflow = 'scroll';

                this.window.onunload = function() {
                    this.disable();
                }.bind(this);
            } catch (e) {
                this.disable();
            }
        },

        disable: function() {
            this.logger.threshold(This.Priority.ERROR);
            this.handle = Function.NOOP;
            if (this.closeOnExit && this.window) this.window.close();
        },

        toggle: function() {
            if (this.handle == Function.NOOP) {
                delete this.handle;
                return true;
            } else {
                this.handle = Function.NOOP;
                return false;
            }
        },

        debug: function(category, message, exception) {
            this.handle('#333', 'debug', category, message, exception)
        },

        info: function(category, message, exception) {
            this.handle('green', 'info', category, message, exception)
        },

        warn: function(category, message, exception) {
            this.handle('orange', 'warn', category, message, exception)
        },

        error: function(category, message, exception) {
            this.handle('red', 'error', category, message, exception)
        },

    //private
        handle: function(colorName, priorityName, category, message, exception) {
            if (this.categoryMatcher.test(category.join('.'))) {
                var elementDocument = this.log.ownerDocument;
                var timestamp = (new Date()).toTimestamp();
                var categoryName = category.join('.');
                ('[' + categoryName + '] : ' + message +
                 (exception ? ('\n' + exception) : '')).split('\n').each(function(line) {
                    if (line.containsWords()) {
                        var eventNode = elementDocument.createElement('div');
                        eventNode.style.padding = '3px';
                        eventNode.style.color = colorName;
                        eventNode.setAttribute("title", timestamp + ' | ' + priorityName)
                        this.log.appendChild(eventNode).appendChild(elementDocument.createTextNode(line));
                    }
                }.bind(this));
                this.log.scrollTop = this.log.scrollHeight;
            }
            this.clearPreviousEvents();
        },

    //private
        clearPreviousEvents: function() {
            var nodes = $A(this.log.childNodes);
            nodes.copyFrom(0, nodes.length - this.lines).each(function(node) {
                this.log.removeChild(node)
            }.bind(this));
        },

    //private
        clearAllEvents: function() {
            $A(this.log.childNodes).each(function(node) {
                this.log.removeChild(node)
            }.bind(this));
        }
    });

    This.NOOPConsole = {
        debug: Function.NOOP, info: Function.NOOP, warn: Function.NOOP, error: Function.NOOP
    };

    This.FirebugLogHandler = Object.subclass({
        initialize: function(logger) {
            logger.handleWith(this);
            this.logger = logger;
            this.console = This.NOOPConsole;
            this.enable();
        },

        enable: function() {
            this.console = window.console;
            this.logger.threshold(This.Priority.DEBUG);
        },

        disable: function() {
            this.console = This.NOOPConsole;
            this.logger.threshold(This.Priority.ERROR);
        },

        toggle: Function.NOOP,

        debug: function(category, message, exception) {
            exception ? this.console.debug(this.format(category, message), exception) : this.console.debug(this.format(category, message));
        },

        info: function(category, message, exception) {
            exception ? this.console.info(this.format(category, message), exception) : this.console.info(this.format(category, message));
        },

        warn: function(category, message, exception) {
            exception ? this.console.warn(this.format(category, message), exception) : this.console.warn(this.format(category, message));
        },

        error: function(category, message, exception) {
            exception ? this.console.error(this.format(category, message), exception) : this.console.error(this.format(category, message));
        },

    //private
        format: function(category, message) {
            return '[' + category.join('.') + '] ' + message;
        }
    });
});



/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Ajax = new Object ].as(function(This) {
    This.Client = Object.subclass({
        initialize: function(logger) {
            this.logger = logger;
            this.cookies = new Object;
            document.cookie.split('; ').each(function(cookieDetails) {
                var cookie = cookieDetails.split('=');
                this.cookies[cookie.first()] = cookie.last();
            }.bind(this));
            //determine which request factory should be used
            try {
                if (window.createRequest) {
                    this.createRequest = function() {
                        var request = new This.RequestProxy(window.createRequest(), this.logger);
                        //override 'post' method since ICEBrowser cannot send 'POST' requests properly.
                        request.post = function(asynchronous, path, query, requestConfigurator) {
                            this.get(asynchronous, path, query, requestConfigurator);
                        };
                        return request;
                    }.bind(this);
                } else if (window.XMLHttpRequest) {
                    this.createRequest = function() {
                        return new This.RequestProxy(new XMLHttpRequest(), this.logger);
                    }.bind(this);
                } else if (window.ActiveXObject) {
                    this.createRequest = function() {
                        return new This.RequestProxy(new ActiveXObject('Microsoft.XMLHTTP'), this.logger);
                    }.bind(this);
                }
            } catch (e) {
                this.logger.error('failed to create factory request', e);
            }
        },

        getAsynchronously: function(path, query, requestConfigurator) {
            return this.createRequest().getAsynchronously(path, query, requestConfigurator);
        },

        getSynchronously: function(path, query, requestConfigurator) {
            return this.createRequest().getSynchronously(path, query, requestConfigurator);
        },

        postAsynchronously: function(path, query, requestConfigurator) {
            return this.createRequest().postAsynchronously(path, query, requestConfigurator);
        },

        postSynchronously: function(path, query, requestConfigurator) {
            return this.createRequest().postSynchronously(path, query, requestConfigurator);
        }
    });

    This.RequestProxy = Object.subclass({
        initialize: function(request, logger) {
            this.identifier = + Math.random().toString().substr(2, 7);
            this.request = request;
            this.logger = logger;
            this.callbacks = [];
            var self = this;
            //avoid using libraries for this callback -- see: http://dev.rubyonrails.org/ticket/5393
            this.responseCallback = function() {
                if (request.readyState == 4) {
                    self.logger.debug('[' + self.identifier + '] : receive [' + self.statusCode() + '] ' + self.statusText());
                    var size = self.callbacks.length;
                    for (var i = 0; i < size; i++) {
                        try {
                            self.callbacks[i](self);
                        } catch (e) {
                            logger.warn('connection closed prematurely');
                            self.close();
                        }
                    }
                }
            };
        },

        statusCode: function() {
            try {
                return this.request.status;
            } catch (e) {
                return 0;
            }
        },

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

        on: function(test, handler) {
            this.callbacks.push(function(request) {
                if (test(request)) handler(request);
            });
        },

        isServerError: function() {
            try {
                return this.request.status == 500;
            } catch (e) {
                return false;
            }
        },

        isEmpty: function() {
            try {
                return this.request.responseText == '';
            } catch (e) {
                return true;
            }
        },

        getAsynchronously: function(path, query, requestConfigurator) {
            //the 'rand' parameter is used to force IE into creating new request object, thus avoiding potential infinite loops.
            this.request.open('GET', path + '?' + query + '&rand=' + Math.random(), true);
            if (requestConfigurator) requestConfigurator(this);
            this.request.onreadystatechange = this.responseCallback;
            this.logger.debug('[' + this.identifier + '] : send asynchronous GET');
            this.request.send('');
            return this;
        },

        postAsynchronously:  function(path, query, requestConfigurator) {
            this.request.open('POST', path, true);
            if (requestConfigurator) requestConfigurator(this);
            this.request.onreadystatechange = this.responseCallback;
            //the 'rand' parameter is used to force Firefox to commit the response to the server.
            this.logger.debug('[' + this.identifier + '] : send asynchronous POST');
            this.request.send(query + '&rand=' + Math.random() + '\n\n');
            return this;
        },

        getSynchronously: function(path, query, requestConfigurator) {
            //the 'rand' parameter is used to force IE into creating new request object, thus avoiding potential infinite loops.
            this.request.open('GET', path + '?' + query + '&rand=' + Math.random(), false);
            if (requestConfigurator) requestConfigurator(this);
            this.logger.debug('[' + this.identifier + '] : send synchronous GET');
            this.request.send('');
            this.responseCallback();
            return this;
        },

        postSynchronously:  function(path, query, requestConfigurator) {
            this.request.open('POST', path, false);
            if (requestConfigurator) requestConfigurator(this);
            //the 'rand' parameter is used to force Firefox to commit the response to the server.
            this.logger.debug('[' + this.identifier + '] : send synchronous POST');
            this.request.send(query + '&rand=' + Math.random() + '\n\n');
            this.responseCallback();
            return this;
        },

        setRequestHeader: function(name, value) {
            this.request.setRequestHeader(name, value);
        },

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

        containsResponseHeader: function(name) {
            try {
                var header = this.request.getResponseHeader(name);
                return header && header != '';
            } catch (e) {
                return false;
            }
        },

        content: function() {
            try {
                return this.request.responseText;
            } catch (e) {
                return '';
            }
        },

        contentAsDOM: function() {
            return this.request.responseXML;
        },

        abort: function() {
            if (this.request) {
                try {
                    //replace the callback to avoid infinit loop since the callback is
                    //executed also when the connection is aborted.
                    //also, setting 'onreadystatechange' to null will cause a memory leak in IE6
                    this.request.onreadystatechange = Function.NOOP;
                    this.request.abort();
                } catch (e) {
                    //ignore, the request was discarded by the browser
                } finally {
                    //avoid potential memory leaks since 'this.request' is a native object
                    this.request = null;
                    this.logger.debug('[' + this.identifier + '] : connection aborted');
                }
            }
        },

        close: function() {
            if (this.request) {
                try {
                    //replace the callback to avoid infinit loop since the callback is
                    //executed also when the connection is aborted.
                    //also, setting 'onreadystatechange' to null will cause a memory leak in IE6
                    this.request.onreadystatechange = Function.NOOP;
                } catch (e) {
                    //ignore, the request was discarded by the browser
                } finally {
                    //avoid potential memory leaks since 'this.request' is a native object
                    this.request = null;
                    this.logger.debug('[' + this.identifier + '] : connection closed');
                }
            }
        }
    });
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Parameter = new Object ].as(function(This) {
    This.Query = Object.subclass({
        initialize: function() {
            this.parameters = [];
        },

        add: function(name, value) {
            this.parameters.push(new This.Association(name, value));
            return this;
        },

        addParameter: function(parameter) {
            this.parameters.push(parameter);
            return this;
        },

        addQuery: function(query) {
            query.serializeOn(this);
            return this;
        },

        asURIEncodedString: function() {
            return this.parameters.inject('', function(result, association, index) {
                return result += (index == 0) ? association.asURIEncodedString() : '&' + association.asURIEncodedString();
            });
        },

        asString: function() {
            return this.parameters.inject('', function(result, parameter, index) {
                return result + '\n| ' + parameter.asString() + ' |';
            });
        },

        sendOn: function(connection) {
            connection.send(this);
        },

        serializeOn: function(query) {
            this.parameters.each(function(parameter) {
                parameter.serializeOn(query);
            });
        }
    });

    This.Query.create = function(execute) {
        var query = new This.Query;
        execute.apply(this, [ query ]);
        return query;
    }

    This.Association = Object.subclass({
        initialize: function(name, value) {
            this.name = name;
            this.value = value;
        },

        asURIEncodedString: function() {
            return encodeURIComponent(this.name) + '=' + encodeURIComponent(this.value);
        },

        asString: function() {
            return this.name + '=' + this.value;
        },

        serializeOn: function(query) {
            query.add(this.name, this.value);
        }
    });
});

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Geometry = new Object ].as(function(This) {
    This.Point = Object.subclass({
        initialize: function(x, y) {
            this.x = x;
            this.y = y;
        },

        asString: function() {
            return 'point [' + this.x + ', ' + this.y + ']';
        },

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

        serializeOn: function(query) {
            query.add('ice.event.x', this.x);
            query.add('ice.event.y', this.y);
        }
    });
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.ElementModel = new Object ].as(function(This) {

    This.TemporaryContainer = function() {
        var container = document.body.appendChild(document.createElement('div'));
        container.style.visibility = 'hidden';
        container.style.display = 'none';
        This.TemporaryContainer = function() {
            return container;
        };

        return container;
    };

    This.Update = Object.subclass({
        initialize: function(element) {
            this.element = element;
            var tag = element.getAttribute('tag');
            this.startTag = function(html) {
                html.push('<');
                html.push(tag);
                this.eachAttribute(function(name, value) {
                    html.push(' ');
                    html.push(name);
                    html.push('="');
                    html.push(value);
                    html.push('"');
                });
                html.push('>');
            };
            this.endTag = function(html) {
                html.push('</');
                html.push(tag);
                html.push('>');
            };
        },

        eachAttribute: function(iterator) {
            $enumerate(this.element.getElementsByTagName('attribute')).each(function(attribute) {
                var value = attribute.firstChild ? attribute.firstChild.data : '';
                iterator(attribute.getAttribute('name'), value);
            });
        },

        content: function() {
            var contentElement = this.element.getElementsByTagName('content')[0];
            return contentElement.firstChild ?
                   contentElement.firstChild.data.replace(/<\!\#cdata\#/g, '<![CDATA[').replace(/\#\#>/g, ']]>') : '';
        },

        asHTML: function() {
            var html = [];
            this.startTag(html);
            html.push(this.content());
            this.endTag(html);
            return html.join('');
        },

        asString: function() {
            var html = [];
            this.startTag(html);
            html.push('...');
            this.endTag(html);
            return html.join('');
        }
    });

    This.Element = Object.subclass({
        MouseListenerNames: [ 'onClick', 'onDblClick', 'onMouseDown', 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp' ],

        KeyListenerNames: [ 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onHelp' ],

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

        id: function() {
            return this.element.id;
        },

        isSubmit: function() {
            return false;
        },

        form: function() {
            var parent = this.element.parentNode;
            while (parent) {
                if (parent.tagName && parent.tagName.toLowerCase() == 'form') return This.Element.adaptToElement(parent);
                parent = parent.parentNode;
            }

            throw 'Cannot find enclosing form.';
        },

        updateDOM: function(update) {
            this.replaceHtml(update.asHTML());
        },

        replaceHtml: function(html) {
            this.withTemporaryContainer(function(container) {
                container.innerHTML = html;
                var newElement = container.firstChild;
                this.disconnectEventListeners();
                this.replaceHostElementWith(newElement);
            });
        },

        disconnectEventListeners: function() {
            //shutdown bridge if this is the container it is attached to!
            if (this.element.bridge) {
                this.element.bridge.dispose();
            }

            var elements = this.element.getElementsByTagName('*');
            var length = elements.length;
            for (var i = 0; i < length; i++) {
                var element = elements[i];
                //todo: avoid wrapping the elements just before disposing them
                //disconnect listeners
                $element(element).eachListenerName(function(listenerName) {
                    element[listenerName.toLowerCase()] = null;
                });
            }
        },

        serializeOn: function(query) {
        },

        sendOn: function(connection) {
            Query.create(function(query) {
                this.serializeOn(query);
            }.bind(this)).sendOn(connection);
        },

        send: function() {
            this.sendOn(connection);
        },

        withTemporaryContainer: function(execute) {
            try {
                execute.apply(this, [ This.TemporaryContainer() ]);
            } finally {
                This.TemporaryContainer().innerHTML = '';
            }
        },

        defaultReplaceHostElementWith: function(newElement) {
            this.displayOff();
            this.element.parentNode.replaceChild(newElement, this.element);
            this.element = newElement;
        },

        replaceHostElementWith: function(newElement) {
            this.defaultReplaceHostElementWith(newElement);
        },

        //hide deleted elements -- Firefox 1.0.x renders tables after they are removed from the document.
        displayOff: /Safari/.test(navigator.userAgent) ? Function.NOOP : function() {
            this.element.style.display = 'none';
        },

        eachListenerName: function(iterator) {
            this.MouseListenerNames.each(iterator);
            this.KeyListenerNames.each(iterator);
        },

        findBridge: function() {
            return this.findContainerFor('bridge').bridge;
        },

        findConnection: function() {
            return this.findBridge().connection;
        },

        findContainerFor: function(property) {
            var parent = this.element;
            while (parent) {
                if (parent[property]) {
                    return parent;
                } else {
                    parent = parent.parentNode;
                }
            }

            throw 'couldn\'t find container for property: ' + property;
        }
    });

    This.Element.adaptToElement = function(e) {
        //no polymophism here...'switch' is the way then.
        if (!e)
            return new This.Element(e);
        switch (e.tagName.toLowerCase()) {
            case 'textarea':
            case 'input': return new This.InputElement(e);
            case 'th':
            case 'td':
            case 'tr': return new This.TableCellElement(e);
            case 'button': return new This.ButtonElement(e);
            case 'select': return new This.SelectElement(e);
            case 'form': return new This.FormElement(e);
            case 'body': return new This.BodyElement(e);
            case 'script': return new This.ScriptElement(e);
            case 'title': return new This.TitleElement(e);
            case 'a': return new This.AnchorElement(e);
            case 'iframe': return new This.IFrameElement(e);
            default : return new This.Element(e);
        }
    };

    This.InputElement = This.Element.subclass({
        InputListenerNames: [ 'onBlur', 'onFocus', 'onChange' ],

        initialize: function(element) {
            this.element = element;
            var type = element.type.toLowerCase();
            this.isSubmitElement = type == 'submit' || type == 'image' || type == 'button';
        },

        isSubmit: function() {
            return this.isSubmitElement;
        },

        form: function() {
            return This.Element.adaptToElement(this.element.form);
        },

        focus: function() {
            var onFocusListener = this.element.onfocus;
            this.element.onfocus = Function.NOOP;
            this.element.focus();
            this.element.onfocus = onFocusListener;
        },

        serializeOn: function(query) {
            switch (this.element.type.toLowerCase()) {
                case 'image':
                case 'textarea':
                case 'submit':
                case 'hidden':
                case 'password':
                case 'text': query.add(this.element.name, this.element.value); break;
                case 'checkbox':
                case 'radio': if (this.element.checked) query.add(this.element.name, this.element.value || 'on'); break;
            }
        },

        eachListenerName: function(iterator) {
            this.MouseListenerNames.each(iterator);
            this.KeyListenerNames.each(iterator);
            this.InputListenerNames.each(iterator);
        }
    });

    This.SelectElement = This.InputElement.subclass({
        isSubmit: function() {
            return false;
        },

        replaceHostElementWith: function(newElement) {
            this.defaultReplaceHostElementWith(newElement);
        },

        serializeOn: function(query) {
            $enumerate(this.element.options).select(function(option) {
                return option.selected;
            }).each(function(selectedOption) {
                var value = selectedOption.value || (selectedOption.value == '' ? '' : selectedOption.text);
                query.add(this.element.name, value);
            }.bind(this));
        }
    });

    This.ButtonElement = This.InputElement.subclass({

        initialize: function(element) {
            this.element = element;
            this.isSubmitElement = element.type.toLowerCase() == 'submit';
        },

        isSubmit: function() {
            return this.isSubmitElement;
        },

        replaceHostElementWith: function(newElement) {
            this.defaultReplaceHostElementWith(newElement);
        },

        serializeOn: function(query) {
            query.add(this.element.name, this.element.value);
        }
    });

    This.FormElement = This.Element.subclass({
        FormListenerNames: [ 'onReset', 'onSubmit', 'submit' ],
        FormAttributeNames: [ 'acceptcharset', 'action', 'enctype', 'method', 'name', 'target' ],

        detectDefaultSubmit: function() {
            var formElements = this.element.elements;
            var length = formElements.length;
            var defaultID = this.element.id + ':default';

            for (var i = 0; i < length; i++) {
                var formElement = formElements[i];
                if (formElement.id == defaultID) {
                    return This.Element.adaptToElement(formElement);
                }
            }

            return null;
        },

        eachFormElement: /Safari/.test(navigator.userAgent) ? function(iterator) {
            //todo: find a more performant way to discard old form elements in Safari
            var newestElements = [];
            $enumerate(this.element.elements).reverse().each(function(element) {
                //Safari keeps old form elements around so they need to be discarded
                if (!newestElements.detect(function(newestElement) {
                    return element.id && newestElement.id && newestElement.id == element.id;
                })) {
                    newestElements.push(element);
                    iterator(This.Element.adaptToElement(element));
                }
            });
        } : function(iterator) {
            $enumerate(this.element.elements).each(function(e) {
                iterator(This.Element.adaptToElement(e));
            });
        },

        //captures normal form submit events and sends them through a XMLHttpRequest
        captureOnSubmit: function() {
            var previousOnSubmit = this.element.onsubmit;
            this.element.onsubmit = function(event) {
                if (previousOnSubmit) previousOnSubmit();
                $event(event).cancelDefaultAction();
                iceSubmit(this, null, event); // Inner 'this' is outer 'this.element'
            };
        },

        //redirect normal form submits through a XMLHttpRequest
        redirectSubmit: function() {
            this.element.submit = function() {
                iceSubmit(this, null, null); // Inner 'this' is outer 'this.element'
            };
        },

        captureAndRedirectSubmit: function() {
            this.captureOnSubmit();
            this.redirectSubmit();
        },

        updateDOM: function(update) {
            this.disconnectEventListeners();
            this.element.innerHTML = update.content();
            var remove = function(name) {
                this.element[name] = null;
            }.bind(this);
            this.FormAttributeNames.each(function(name) {
                this.element.removeAttribute(name);
            }.bind(this));
            this.eachListenerName(remove);
            update.eachAttribute(function(name, value) {
                try {
                    this.element.setAttribute(name, value);
                } catch (e) {
                    logger.error('failed to set attribute ' + name + ':' + value, e);
                }
            }.bind(this));
        },

        serializeOn: function(query) {
            this.eachFormElement(function(formElement) {
                if (!formElement.isSubmit()) formElement.serializeOn(query);
            });
        },

        eachListenerName: function(iterator) {
            this.MouseListenerNames.each(iterator);
            this.KeyListenerNames.each(iterator);
            this.FormListenerNames.each(iterator);
        },

        submit: function() {
            iceSubmit(this.element, null, null);
        }
    });


    This.BodyElement = This.Element.subclass({
        replaceHtml: function(html) {
            this.disconnectEventListeners();
            //strip <noscript> tag to fix Safari bug
            // #3131 If this is a response from an error code, there may not be a <noscript> tag.
            var start = new RegExp('\<noscript\>', 'g').exec(html);
            if (start == null) {
                this.element.innerHTML = html;
            } else {
                var end = new RegExp('\<\/noscript\>', 'g').exec(html);
                this.element.innerHTML = html.substring(0, start.index) + html.substring(end.index + 11, html.length);
            }
        }
    });

    This.ScriptElement = This.Element.subclass({
        updateDOM: function(update) {
            //if script element is updated its code will be evaluate in IE (thus evaluating it twice)
            //evaluate code in the 'window' context
            var scriptCode = update.content();
            if (scriptCode != '' && scriptCode != ';') {
                var evalFunc = function() {
                    eval(scriptCode);
                };
                evalFunc.apply(window);
            }
        }
    });

    This.TitleElement = This.Element.subclass({
        updateDOM: function(update) {
            this.element.ownerDocument.title = update.content();
        }
    });

    This.AnchorElement = This.Element.subclass({
        isSubmit: function() {
            return true;
        },

        focus: function() {
            var onFocusListener = this.element.onfocus;
            this.element.onfocus = Function.NOOP;
            this.element.focus();
            this.element.onfocus = onFocusListener;
        },

        serializeOn: function(query) {
            if (this.element.name) query.add(this.element.name, this.element.name);
        }
    });

    This.TableCellElement = This.Element.subclass({
        replaceHtml: function(html) {
            this.withTemporaryContainer(function(container) {
                container.innerHTML = '<TABLE>' + html + '</TABLE>';
                var newElement = container.firstChild;
                while ((null != newElement) && (this.element.id != newElement.id)) {
                    newElement = newElement.firstChild;
                }
                this.disconnectEventListeners();
                this.replaceHostElementWith(newElement);
            });
        }
    });

    This.IFrameElement = This.Element.subclass({
        replaceHostElementWith: function(newElement) {
            this.eachAttributeName(function(attributeName) {
                var value = newElement.getAttribute(attributeName);
                if (value == null) {
                    this.element.removeAttribute(attributeName);
                } else {
                    this.element.setAttribute(attributeName, value);
                }
            }.bind(this));

            //special case for the 'src' attribute (Safari bug?)
            var oldLocation = this.element.contentWindow.location.href;
            var newLocation = newElement.contentWindow.location.href;
            if (oldLocation != newLocation) {
                this.element.contentWindow.location = newLocation;
            }

            //overwrite listeners and bind them to the existing element
            this.eachListenerName(function(listenerName) {
                var name = listenerName.toLowerCase();
                this.element[name] = newElement[name] ? newElement[name].bind(this.element) : null;
                newElement[name] = null;
            }.bind(this));
        },

        eachAttributeName: function(iterator) {
            ['title', 'lang', 'dir', 'class', 'style', 'align', 'frameborder',
                'width', 'height', 'hspace', 'ismap', 'longdesc', 'marginwidth',
                'marginheight', 'name', 'scrolling'].each(iterator);
        }
    });

    //public call
    window.$element = This.Element.adaptToElement;
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.EventModel = new Object, Ice.ElementModel.Element, Ice.Parameter.Query, Ice.Geometry ].as(function(This, Element, Query, Geometry) {
    This.IE = new Object;
    This.Netscape = new Object;

    This.Event = Object.subclass({
        initialize: function(event, currentElement) {
            this.event = event;
            this.currentElement = currentElement;
        },

        cancel: function() {
            this.cancelBubbling();
            this.cancelDefaultAction();
        },

        isKeyEvent: function() {
            return false;
        },

        isMouseEvent: function() {
            return false;
        },

    //todo: rename to 'capturingElement'
        captured: function() {
            return this.currentElement ? Element.adaptToElement(this.currentElement) : null;
        },

        serializeEventOn: function(query) {
            query.add('ice.event.target', this.target() && this.target().id());
            query.add('ice.event.captured', this.captured() && this.captured().id());
            query.add('ice.event.type', 'on' + this.event.type);
        },

        serializeOn: function(query) {
            this.serializeEventOn(query);
        },

        sendOn: function(connection) {
            Query.create(function(query) {
                query.add('ice.submit.partial', false);
                try {
                    this.captured().serializeOn(query);
                    this.serializeOn(query);
                } catch (e) {
                    this.serializeOn(query);
                }
            }.bind(this)).sendOn(connection);
        },

        sendFullOn: function(connection) {
            Query.create(function(query) {
                query.add('ice.submit.partial', false);
                try {
                    this.captured().serializeOn(query);
                    this.form().serializeOn(query);
                    this.serializeOn(query);
                } catch (e) {
                    this.serializeOn(query);
                }
            }.bind(this)).sendOn(connection);
        },

        sendWithCondition: function(condition) {
            if (condition(this)) this.send();
        },

        send: function() {
            this.cancel();
            this.sendOn(this.captured().findConnection());
        },

        sendFull: function() {
            this.cancel();
            this.sendFullOn(this.captured().findConnection());
        }
    });

    This.IE.Event = This.Event.subclass({
    //todo: rename to 'triggeringElement'
        target: function() {
            return this.event.srcElement ? Element.adaptToElement(this.event.srcElement) : null;
        },

        cancelBubbling: function() {
            this.event.cancelBubble = true;
        },

        cancelDefaultAction: function() {
            this.event.returnValue = false;
        }
    });

    This.Netscape.Event = This.Event.subclass({
    //todo: rename to 'triggeringElement'
        target: function() {
            return this.event.target ? Element.adaptToElement(this.event.target) : null;
        },

        cancelBubbling: function() {
            this.event.stopPropagation();
        },

        cancelDefaultAction: function() {
            this.event.preventDefault();
        }
    });

    var KeyAndMouseEventMethods = {
        isAltPressed: function() {
            return this.event.altKey;
        },

        isCtrlPressed: function() {
            return this.event.ctrlKey;
        },

        isShiftPressed: function() {
            return this.event.shiftKey;
        },

        isMetaPressed: function() {
            return this.event.metaKey;
        },

        serializeKeyAndMouseEventOn: function(query) {
            query.add('ice.event.alt', this.isAltPressed());
            query.add('ice.event.ctrl', this.isCtrlPressed());
            query.add('ice.event.shift', this.isShiftPressed());
            query.add('ice.event.meta', this.isMetaPressed());
        }
    };

    var MouseEventMethods = {
        isMouseEvent: function() {
            return true;
        },

        serializeOn: function(query) {
            this.serializeEventOn(query);
            this.serializeKeyAndMouseEventOn(query);
            this.pointer().serializeOn(query);
            query.add('ice.event.left', this.isLeftButton());
            query.add('ice.event.right', this.isRightButton());
        }
    };

    This.IE.MouseEvent = This.IE.Event.subclass({
        pointer: function() {
            return new Geometry.Point(this.event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft), this.event.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
        },

        isLeftButton: function() {
            return this.event.button == 1;
        },

        isRightButton: function() {
            return this.event.button == 2;
        }
    });
    This.IE.MouseEvent.methods(KeyAndMouseEventMethods);
    This.IE.MouseEvent.methods(MouseEventMethods);

    This.Netscape.MouseEvent = This.Netscape.Event.subclass({
        pointer: function() {
            return new Geometry.Point(this.event.pageX, this.event.pageY);
        },

        isLeftButton: function() {
            return this.event.which == 1;
        },

        isRightButton: function() {
            return this.event.which == 3;
        }
    });
    This.Netscape.MouseEvent.methods(KeyAndMouseEventMethods);
    This.Netscape.MouseEvent.methods(MouseEventMethods);

    var KeyEventMethods = {
        keyCharacter: function() {
            return String.fromCharCode(this.keyCode());
        },

        isEnterKey: function() {
            return this.keyCode() == 13;
        },

        isEscKey: function() {
            return this.keyCode() == 27;
        },

        isBackspaceKey: function() {
            return this.keyCode() == 8;
        },

        isDeleteKey: function() {
            return this.keyCode() == 46 || /*safari*/this.keyCode() == 63272;
        },

        isSpaceKey: function() {
            return this.keyCode() == 32;
        },

        isTabKey: function() {
            return this.keyCode() == 9 || /*safari*/(this.isShiftPressed() && this.keyCode() == 25);
        },

        isHomeKey: function() {
            return this.keyCode() == 36 || /*safari*/this.keyCode() == 63273;
        },

        isEndKey: function() {
            return this.keyCode() == 35 || /*safari*/this.keyCode() == 63275;
        },

        isPageUpKey: function() {
            return this.keyCode() == 33 || /*safari*/this.keyCode() == 63276;
        },

        isPageDownKey: function() {
            return this.keyCode() == 34 || /*safari*/this.keyCode() == 63277;
        },

        isArrowUpKey: function() {
            return this.keyCode() == 38 || /*safari*/this.keyCode() == 63232;
        },

        isArrowDownKey: function() {
            return this.keyCode() == 40 || /*safari*/this.keyCode() == 63233;
        },

        isArrowLeftKey: function() {
            return this.keyCode() == 37 || /*safari*/this.keyCode() == 63234;
        },

        isArrowRightKey: function() {
            return this.keyCode() == 39 || /*safari*/this.keyCode() == 63235;
        },

        isKeyEvent: function() {
            return true;
        },

        serializeOn: function(query) {
            this.serializeEventOn(query);
            this.serializeKeyAndMouseEventOn(query);
            query.add('ice.event.keycode', this.keyCode());
        }
    };

    This.IE.KeyEvent = This.IE.Event.subclass({
        keyCode: function() {
            return this.event.keyCode;
        }
    });
    This.IE.KeyEvent.methods(KeyAndMouseEventMethods);
    This.IE.KeyEvent.methods(KeyEventMethods);

    This.Netscape.KeyEvent = This.Netscape.Event.subclass({
        keyCode: function() {
            return this.event.which == 0 ? this.event.keyCode : this.event.which;
        }
    });
    This.Netscape.KeyEvent.methods(KeyAndMouseEventMethods);
    This.Netscape.KeyEvent.methods(KeyEventMethods);

    This.UnknownEvent = This.Event.subclass({
        initialize: function(currentElement) {
            this.currentElement = currentElement;
        },

        target: function() {
            return this.currentElement == null ? null : Element.adaptToElement(this.currentElement);
        },

        serializeEventOn: function(query) {
            query.add('ice.event.target', this.target() && this.target().id());
            query.add('ice.event.captured', this.captured() && this.captured().id());
            query.add('ice.event.type', 'unknown');
        },

        cancelBubbling: Function.NOOP,

        cancelDefaultAction: Function.NOOP
    });

    This.Event.adaptToPlainEvent = function(e, currentElement) {
        return window.event ? new This.IE.Event(event, currentElement) : new This.Netscape.Event(e, currentElement);
    };

    This.Event.adaptToMouseEvent = function(e, currentElement) {
        return window.event ? new This.IE.MouseEvent(event, currentElement) : new This.Netscape.MouseEvent(e, currentElement);
    };

    This.Event.adaptToKeyEvent = function(e, currentElement) {
        return window.event ? new This.IE.KeyEvent(event, currentElement) : new This.Netscape.KeyEvent(e, currentElement);
    };

    This.Event.adaptToEvent = function(e, currentElement) {
        var capturedEvent = window.event || e;
        if (capturedEvent) {
            var eventType = 'on' + capturedEvent.type;
            var detector = function (name) {
                return name.toLowerCase() == eventType;
            };
            if (Element.prototype.KeyListenerNames.detect(detector)) {
                return This.Event.adaptToKeyEvent(e, currentElement);
            } else if (Element.prototype.MouseListenerNames.detect(detector)) {
                return This.Event.adaptToMouseEvent(e, currentElement);
            } else {
                return This.Event.adaptToPlainEvent(e, currentElement);
            }
        } else {
            return new This.UnknownEvent(currentElement);
        }
    };

    //public call
    window.$event = This.Event.adaptToEvent;
});
[ Ice ].as(function(This) {
    This.Cookie = This.Parameter.Association.subclass({
        initialize: function(name, value) {
            this.name = name;
            this.value = value || '';
            this.save();
        },

        saveValue: function(value) {
            this.value = value;
            this.save();
        },

        loadValue: function() {
            this.load();
            return this.value;
        },

        save: function() {
            document.cookie = this.name + '=' + this.value;
            return this;
        },

        load: function() {
            var foundTuple = This.Cookie.parse().detect(function(tuple) {
                return this.name == tuple[0];
            }.bind(this));
            this.value = foundTuple[1];
            return this;
        },

        remove: function() {
            document.cookie = this.name + '=0; expires=' + (new Date).toGMTString();
        }
    });

    This.Cookie.all = function() {
        return This.Cookie.parse().collect(function(tuple) {
            var name = tuple[0];
            var value = tuple[1];
            return new This.Cookie(name, value);
        });
    };

    This.Cookie.lookup = function(name) {
        var foundTuple = This.Cookie.parse().detect(function(tuple) {
            return name == tuple[0];
        });
        if (foundTuple) {
            return new This.Cookie(foundTuple[0], foundTuple[1]);
        } else {
            throw 'Cannot find cookie named: ' + name;
        }
    };

    //private
    This.Cookie.parse = function() {
        return document.cookie.split('; ').collect(function(tupleDetails) {
            return tupleDetails.contains('=') ? tupleDetails.split('=') : [tupleDetails, ''];
        });
    };
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

function currentConnection() {
    var e = $enumerate(arguments).detect(function(i) {
        return !!i;
    });
    return e.findConnection();
}

function formOf(element) {
    var parent = element.parentNode;
    while (parent) {
        if (parent.tagName && parent.tagName.toLowerCase() == 'form') return parent;
        parent = parent.parentNode;
    }

    throw 'Cannot find enclosing form.';
}

function iceSubmitPartial(form, component, evt) {
    form = (form ? form : component.form);
    var query = new Ice.Parameter.Query();

    if (Ice.InputFileIdPreUpload)
        query.add('ice.inputFile.preUpload', Ice.InputFileIdPreUpload);
    if (Ice.InputFileIdPostUpload)
        query.add('ice.inputFile.postUpload', Ice.InputFileIdPostUpload);
    if (Ice.Menu.menuContext != null)
        query.add('ice.menuContext', Ice.Menu.menuContext);
    if(Ice.FCKeditorUtility) 
        Ice.FCKeditorUtility.saveAll();        
    query.add('ice.submit.partial', true);
    $event(evt, component).serializeOn(query);
    if (form && form.id) {
        $element(form).serializeOn(query);
    }
    if (component && component.id) {
        var c = $element(component);
        if (c.isSubmit()) {
            c.serializeOn(query);
        }
    }
    query.sendOn(currentConnection($element(form), $element(component)));
    resetHiddenFieldsFor(form);
    return false;
}

function iceSubmit(aForm, aComponent, anEvent) {
    aForm = (aForm ? aForm : aComponent.form);
    var event = $event(anEvent, aComponent);
    var form = $element(aForm);
    var query = new Ice.Parameter.Query();
    if (Ice.InputFileIdPreUpload)
        query.add('ice.inputFile.preUpload', Ice.InputFileIdPreUpload);
    if (Ice.InputFileIdPostUpload)
        query.add('ice.inputFile.postUpload', Ice.InputFileIdPostUpload);
    if (Ice.Menu.menuContext != null)
        query.add('ice.menuContext', Ice.Menu.menuContext);
    if(Ice.FCKeditorUtility) 
        Ice.FCKeditorUtility.saveAll();        
    query.add('ice.submit.partial', false);
    //all key events are discarded except when 'enter' is pressed...not good!
    if (event.isKeyEvent()) {
        if (event.isEnterKey()) {
            //find a default submit element to submit the form
            var submit = form ? form.detectDefaultSubmit() : null;
            //cancel the default action to block 'onclick' event on the submit element
            event.cancelDefaultAction();
            event.serializeOn(query);
            if (submit) {
                submit.serializeOn(query);
            }
            if (form) {
                form.serializeOn(query);
            }
            query.sendOn(currentConnection($element(aForm), $element(aComponent)));
        }
    } else {
        var component = aComponent && aComponent.id ? $element(aComponent) : null;
        event.serializeOn(query);
        if (component && component.isSubmit()) {
            component.serializeOn(query);
        }
        if (form) {
            form.serializeOn(query);
        }
        query.sendOn(currentConnection($element(aForm), $element(aComponent)));
    }

    resetHiddenFieldsFor(aForm);
    return false;
}

//todo: determine if the cleanup of hidden fields should be at framework or component level
function resetHiddenFieldsFor(aForm) {
    $enumerate(aForm.elements).each(function(formElement) {
        if (formElement.type == 'hidden' && formElement.id == '') formElement.value = '';
    });
}
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Document = new Object, Ice.ElementModel.Element, Ice.Connection, Ice.Ajax ].as(function(This, Element, Connection, Ajax) {
    This.replaceContainerHTML = function(container, html) {
        var start = new RegExp('\<body[^\<]*\>', 'g').exec(html);
        var end = new RegExp('\<\/body\>', 'g').exec(html);
        var body = html.substring(start.index, end.index + end[0].length)
        var bodyContent = body.substring(body.indexOf('>') + 1, body.lastIndexOf('<'));
        var tag = container.tagName;
        var c = $element(container);
        c.disconnectEventListeners();
        c.replaceHtml(['<', tag, '>', bodyContent, '</', tag, '>'].join(''));
    };

    This.Synchronizer = Object.subclass({
        initialize: function(logger, sessionID, viewID) {
            this.logger = logger.child('synchronizer');
            this.ajax = new Ajax.Client(this.logger);
            var id = 'history-frame:' + sessionID + ':' + viewID;
            try {
                //ICE-3242 under certain circumstances accessing location's hash property can throw an exception in Firefox                
                window.frames[id].location.hash;
                this.historyFrame = window.frames[id];
            } catch (e) {
                //alternative way of looking up the frame
                this.historyFrame = id.asElement().contentWindow;
            }
            if (this.historyFrame.location.hash.length > 0) this.reload();
        },

        synchronize: function() {
            try {
                this.historyFrame.location.hash = '#reload';
                this.logger.debug('mark document as modified');
                this.synchronize = Function.NOOP;
            } catch(e) {
                this.logger.warn('could not mark document as modified', e);
            }
        },

        reload: function() {
            try {
                this.logger.info('synchronize body');
                this.ajax.getAsynchronously(document.URL, '', function(request) {
                    request.setRequestHeader('Connection', 'close');
                    request.on(Connection.OK, function(response) {
                        This.replaceContainerHTML(document.body, response.content());
                    });
                });
            } catch (e) {
                this.logger.error('failed to reload body', e);
            }
        },

        shutdown: function() {
            this.synchronize = Function.NOOP;
        }
    });
});
[ Ice.Command = new Object ].as(function(This) {

    This.Dispatcher = Object.subclass({
        initialize: function() {
            this.commands = new Object;
        },

        register: function(messageName, command) {
            this.commands[messageName] = command;
        },

        deserializeAndExecute: function(message) {
            var messageName = message.tagName;
            for (var commandName in this.commands) {
                if (commandName == messageName) {
                    this.commands[messageName](message);
                    return;
                }
            }

            throw 'Unknown message received: ' + messageName;
        }
    });

    This.SetCookie = function(message) {
        document.cookie = message.firstChild.data;
    };

    This.ParsingError = function(message) {
        logger.error('Parsing error');
        var errorNode = message.firstChild;
        logger.error(errorNode.data);
        var sourceNode = errorNode.firstChild;
        logger.error(sourceNode.data);
    };
});
[ Ice.Script = new Object, Ice.Ajax.Client ].as(function(This, Client) {

    //todo: should this code be part of Element.replaceHtml method?    
    This.Loader = Object.subclass({
        initialize: function(logger) {
            this.logger = logger.child('script-loader');
            this.referencedScripts = [];
            //list of urls
            this.client = new Client(this.logger);
            //the scripts present on the page are already evaluated
            $enumerate(document.documentElement.getElementsByTagName('script')).each(function(script) {
                if (script.src) this.referencedScripts.push(script.src);
            }.bind(this));
        },

        searchAndEvaluateScripts: function(element) {
            $enumerate(element.getElementsByTagName('script')).each(function(s) {
                this.evaluateScript(s);
            }.bind(this));
        },

        evaluateScript: function(script) {
            var uri = script.src;
            if (uri) {
                if (!this.referencedScripts.include(script.src)) {
                    this.logger.debug('loading : ' + uri);
                    this.client.getSynchronously(uri, '', function(request) {
                        request.on(Ice.Connection.OK, function() {
                            this.referencedScripts.push(uri);
                            this.logger.debug('evaluating script at : ' + uri);
                            try {
                                eval(request.content());
                            } catch (e) {
                                this.logger.warn('Failed to evaluate script located at: ' + uri, e);
                            }
                        }.bind(this));
                    }.bind(this));
                }
            } else {
                var code = script.innerHTML;
                this.logger.debug('evaluating script : ' + code);
                try {
                    eval(code);
                } catch (e) {
                    this.logger.warn('Failed to evaluate script: \n' + code, e);
                }
            }
        }
    });
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

//todo: implement focus management!
var currentFocus;
Ice.Focus = new Object();
Ice.Focus.userInterupt = false;

Ice.Focus.userInterupt = function (e) {
    if (window.logger)
        window.logger.debug('Interup pressed');
    if (Ice.Focus.userInterupt == false) {
        if (window.logger)
            window.logger.debug('User action. Set focus will be ignored.');
        Ice.Focus.userInterupt = true;
    }

}

Ice.Focus.setFocus = function(id) {
    // This delay is required for focusing newly rendered components on IE 
    // ICE-1247
    window.setTimeout("Ice.Focus.setFocusNow('" + id + "');", 100);
};

Ice.Focus.setFocusNow = function(id) {
    if ((Ice.Focus.userInterupt == false) && (id != '') && (id != 'undefined')) {
        try {
            id.asExtendedElement().focus();
            setFocus(id);
            var ele = document.getElementById(id);
            if (ele) {
                ele.focus();
            } else {
                if (window.logger)
                    window.logger.info('Cannot set focus, no element for id [' + id + "]");
            }
            if (window.logger)
                window.logger.debug('Focus Set on [' + id + "]");
        } catch(e) {
            if (window.logger)
                window.logger.info('Cannot set focus, no element for id [' + id + ']', e);
        }
    } else {
        if (window.logger)
            window.logger.debug('Focus interupted. Not Set on [' + id + ']');
    }
};


document.onKeyDown = function(listener) {
    var previousListener = document.onkeydown;
    document.onkeydown = previousListener != null ? function(e) {
        listener(Ice.EventModel.Event.adaptToKeyEvent(e));
        previousListener(e);
    } : function(e) {
        listener(Ice.EventModel.Event.adaptToKeyEvent(e));
    };
};

document.onMouseDown = function(listener) {
    var previousListener = document.onmousedown;
    document.onmousedown = previousListener != null ? function(e) {
        listener(e);
        previousListener(e);
    } : function(e) {
        listener(e);
    };
};

document.onKeyDown(Ice.Focus.userInterupt);
document.onMouseDown(Ice.Focus.userInterupt);

function setFocus(id) {
    currentFocus = id;
}

window.onScroll(function() {
    currentFocus = null;
    window.focus();
});

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Status = new Object ].as(function(This) {
    This.NOOPIndicator = {
        on: Function.NOOP,
        off: Function.NOOP
    };

    This.RedirectIndicator = Object.subclass({
        initialize: function(uri) {
            this.uri = uri;
        },

        on: function() {
            window.location.href = this.uri;
        },

        off: Function.NOOP
    });

    This.ElementIndicator = Object.subclass({
        initialize: function(elementID, indicators) {
            this.elementID = elementID;
            this.indicators = indicators;
            this.indicators.push(this);
            this.off();
        },

        on: function() {
            this.indicators.each(function(indicator) {
                if (indicator != this) indicator.off();
            }.bind(this));
            var e = this.elementID.asElement();
            if (e) {
                e.style.visibility = 'visible';
            }
        },

        off: function() {
            var e = this.elementID.asElement();
            if (e) {
                e.style.visibility = 'hidden';
            }
        }
    });

    This.ToggleIndicator = Object.subclass({
        initialize: function(onElement, offElement) {
            this.onElement = onElement;
            this.offElement = offElement;
            this.off();
        },

        on: function() {
            this.onElement.on();
            this.offElement.off();
        },

        off: function() {
            this.onElement.off();
            this.offElement.on();
        }
    });

    This.MuxIndicator = Object.subclass({
        initialize: function(a, b) {
            this.a = a;
            this.b = b;
            this.off();
        },

        on: function() {
            this.a.on();
            this.b.on();
        },

        off: function() {
            this.a.off();
            this.b.off();
        }
    });

    This.PointerIndicator = Object.subclass({
        initialize: function(element) {
            this.toggle = function() {
                //block any other action from triggering the indicator before being in 'off' state again
                this.on = Function.NOOP;
                //prepare cursor shape rollback
                var cursorRollbacks = ['input', 'select', 'textarea', 'button', 'a'].inject([ element ], function(result, type) {
                    return result.concat($enumerate(element.getElementsByTagName(type)).toArray());
                }).collect(function(e) {
                    var c = e.style.cursor;
                    e.style.cursor = 'wait';
                    return function() {
                        e.style.cursor = c;
                    };
                });

                this.off = function() {
                    cursorRollbacks.broadcast();

                    this.on = this.toggle;
                    this.off = Function.NOOP;
                };
            };

            this.on = /Safari/.test(navigator.userAgent) ? Function.NOOP : this.toggle;
            this.off = Function.NOOP;
        }
    });

    This.OverlayIndicator = Object.subclass({
        initialize: function(message, description, buttonText, iconPath, panel) {
            this.message = message;
            this.description = description;
            this.buttonText = buttonText;
            this.iconPath = iconPath;
            this.panel = panel;
        },

        on: function() {
            this.panel.on();
            var messageContainer = document.createElement('div');
            var messageContainerStyle = messageContainer.style;
            messageContainerStyle.position = 'absolute';
            messageContainerStyle.textAlign = 'center';
            messageContainerStyle.zIndex = '28001';
            messageContainerStyle.color = 'black';
            messageContainerStyle.backgroundColor = 'white';
            messageContainerStyle.paddingLeft = '0';
            messageContainerStyle.paddingRight = '0';
            messageContainerStyle.paddingTop = '15px';
            messageContainerStyle.paddingBottom = '15px';
            messageContainerStyle.borderBottomColor = 'gray';
            messageContainerStyle.borderRightColor = 'gray';
            messageContainerStyle.borderTopColor = 'silver';
            messageContainerStyle.borderLeftColor = 'silver';
            messageContainerStyle.borderWidth = '2px';
            messageContainerStyle.borderStyle = 'solid';
            messageContainerStyle.width = '270px';
            document.body.appendChild(messageContainer);

            var messageElement = document.createElement('div');
            messageElement.appendChild(document.createTextNode(this.message));
            var messageElementStyle = messageElement.style;
            messageElementStyle.marginLeft = '30px';
            messageElementStyle.textAlign = 'left';
            messageElementStyle.fontSize = '14px';
            messageElementStyle.fontSize = '14px';
            messageElementStyle.fontWeight = 'bold';
            messageContainer.appendChild(messageElement);

            var descriptionElement = document.createElement('div');
            descriptionElement.appendChild(document.createTextNode(this.description));
            var descriptionElementStyle = descriptionElement.style;
            descriptionElementStyle.fontSize = '11px';
            descriptionElementStyle.marginTop = '7px';
            descriptionElementStyle.marginBottom = '7px';
            descriptionElementStyle.fontWeight = 'normal';
            messageElement.appendChild(descriptionElement);

            var buttonElement = document.createElement('input');
            buttonElement.type = 'button';
            buttonElement.value = this.buttonText;
            var buttonElementStyle = buttonElement.style;
            buttonElementStyle.fontSize = '11px';
            buttonElementStyle.fontWeight = 'normal';
            buttonElement.onclick = function() {
                window.location.reload();
            };
            messageContainer.appendChild(buttonElement);
            var resize = function() {
                messageContainerStyle.left = ((window.width() - messageContainer.clientWidth) / 2) + 'px';
                messageContainerStyle.top = ((window.height() - messageContainer.clientHeight) / 2) + 'px';
            }.bind(this);
            resize();
            window.onResize(resize);
        },

        off: Function.NOOP
    });

    This.DefaultStatusManager = Object.subclass({
        initialize: function(configuration, container) {
            this.container = container;
            this.connectionLostRedirect = configuration.connectionLostRedirectURI ? new This.RedirectIndicator(configuration.connectionLostRedirectURI) : null;
            this.sessionExpiredRedirect = configuration.sessionExpiredRedirectURI ? new This.RedirectIndicator(configuration.sessionExpiredRedirectURI) : null;
            var messages = configuration.messages;
            var sessionExpiredIcon = configuration.connection.context + '/xmlhttp/css/xp/css-images/connect_disconnected.gif';
            var connectionLostIcon = configuration.connection.context + '/xmlhttp/css/xp/css-images/connect_caution.gif';

            this.busy = new This.PointerIndicator(container);
            this.sessionExpired = this.sessionExpiredRedirect ? this.sessionExpiredRedirect : new This.OverlayIndicator(messages.sessionExpired, messages.description, messages.buttonText, sessionExpiredIcon, this)
            this.connectionLost = this.connectionLostRedirect ? this.connectionLostRedirect : new This.OverlayIndicator(messages.connectionLost, messages.description, messages.buttonText, connectionLostIcon, this);
            this.serverError = new This.OverlayIndicator(messages.serverError, messages.description, messages.buttonText, connectionLostIcon, this)
            this.connectionTrouble = { on: Function.NOOP, off: Function.NOOP };
        },

        on: function() {
            var overlay = this.container.ownerDocument.createElement('iframe');
            overlay.setAttribute('src', 'about:blank');
            overlay.setAttribute('frameborder', '0');
            var overlayStyle = overlay.style;
            overlayStyle.position = 'absolute';
            overlayStyle.display = 'block';
            overlayStyle.visibility = 'visible';
            overlayStyle.backgroundColor = 'white';
            overlayStyle.zIndex = '28000';
            overlayStyle.top = '0';
            overlayStyle.left = '0';
            overlayStyle.opacity = 0.22;
            overlayStyle.filter = 'alpha(opacity=22)';
            this.container.appendChild(overlay);

            var resize = this.container.tagName.toLowerCase() == 'body' ?
                         function() {
                             overlayStyle.width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth) + 'px';
                             overlayStyle.height = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight) + 'px';
                         } :
                         function() {
                             overlayStyle.width = this.container.offsetWidth + 'px';
                             overlayStyle.height = this.container.offsetHeight + 'px';
                         };
            resize();
            window.onResize(resize);
        },

        off: Function.NOOP
    });

    This.ComponentStatusManager = Object.subclass({
        initialize: function(workingID, idleID, troubleID, lostID, defaultStatusManager, showPopups, displayHourglassWhenActive) {
            var indicators = [];
            var connectionWorking = new Ice.Status.ElementIndicator(workingID, indicators);
            var connectionIdle = new Ice.Status.ElementIndicator(idleID, indicators);
            var connectionLost = new Ice.Status.ElementIndicator(lostID, indicators);
            var busyIndicator = new Ice.Status.ToggleIndicator(connectionWorking, connectionIdle);

            this.busy = displayHourglassWhenActive ? new Ice.Status.MuxIndicator(defaultStatusManager.busy, busyIndicator) : busyIndicator;
            this.connectionTrouble = new Ice.Status.ElementIndicator(troubleID, indicators);
            if (showPopups) {
                this.dsm = defaultStatusManager;
                this.connectionLost = new Ice.Status.MuxIndicator(connectionLost, this.dsm.connectionLost);
                this.sessionExpired = new Ice.Status.MuxIndicator(connectionLost, this.dsm.sessionExpired);
                this.serverError = new Ice.Status.MuxIndicator(connectionLost, this.dsm.serverError);
            } else {
                this.connectionLost = defaultStatusManager.connectionLostRedirect ? defaultStatusManager.connectionLostRedirect : connectionLost;
                this.sessionExpired = defaultStatusManager.sessionExpiredRedirect ? defaultStatusManager.sessionExpiredRedirect : connectionLost;
                this.serverError = connectionLost;
            }
        },

        on: function() {
            this.dsm.on();
        },

        off: function() {
            [this.busy, this.sessionExpired, this.serverError, this.connectionLost, this.connectionTrouble].eachWithGuard(function(indicator) {
                indicator.off();
            });
        }
    });
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Connection = new Object, Ice.Connection, Ice.Ajax, Ice.Parameter.Query ].as(function(This, Connection, Ajax, Query) {
    This.FormPost = function(request) {
        request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    };

    This.Close = function(response) {
        response.close();
    };

    This.BadResponse = function(response) {
        return response.statusCode() == 0;
    }

    This.ServerError = function(response) {
        var code = response.statusCode();
        return code >= 500 && code < 600;
    }

    This.OK = function(response) {
        return response.statusCode() == 200;
    }

    This.SyncConnection = Object.subclass({
        initialize: function(logger, configuration, defaultQuery) {
            this.logger = logger.child('sync-connection');
            this.channel = new Ajax.Client(this.logger);
            this.defaultQuery = defaultQuery;
            this.onSendListeners = [];
            this.onReceiveListeners = [];
            this.onServerErrorListeners = [];
            this.connectionDownListeners = [];
            this.timeoutBomb = { cancel: Function.NOOP };
            this.logger.info('synchronous mode');
            this.sendURI = configuration.context.current + 'block/send-receive-updates';
            var timeout = configuration.timeout ? configuration.timeout : 60000;

            //clear connectionDownListeners to avoid bogus connection lost messages
            window.onBeforeUnload(function() {
                this.connectionDownListeners.clear();
            }.bind(this));

            this.onSend(function() {
                this.timeoutBomb.cancel();
                this.timeoutBomb = this.connectionDownListeners.broadcaster().delayExecutionFor(timeout);
            }.bind(this));

            this.onReceive(function() {
                this.timeoutBomb.cancel();
            }.bind(this));

            this.whenDown(function() {
                this.timeoutBomb.cancel();
            }.bind(this));

            this.receiveCallback = function(response) {
                try {
                    this.onReceiveListeners.broadcast(response);
                } catch (e) {
                    this.logger.error('receive broadcast failed', e)
                }
            }.bind(this);

            this.badResponseCallback = this.connectionDownListeners.broadcaster();
            this.serverErrorCallback = this.onServerErrorListeners.broadcaster();
        },

        send: function(query) {
            var compoundQuery = new Query();
            compoundQuery.addQuery(query);
            compoundQuery.addQuery(this.defaultQuery);
            compoundQuery.add('ice.focus', window.currentFocus);

            this.logger.debug('send > ' + compoundQuery.asString());
            this.channel.postAsynchronously(this.sendURI, compoundQuery.asURIEncodedString(), function(request) {
                This.FormPost(request);
                request.on(Connection.OK, this.receiveCallback);
                request.on(Connection.BadResponse, this.badResponseCallback);
                request.on(Connection.ServerError, this.serverErrorCallback);
                request.on(Connection.OK, Connection.Close);
                this.onSendListeners.broadcast(request);
            }.bind(this));
        },

        onSend: function(callback) {
            this.onSendListeners.push(callback);
        },

        onReceive: function(callback) {
            this.onReceiveListeners.push(callback);
        },

        onServerError: function(callback) {
            this.onServerErrorListeners.push(callback);
        },

        whenDown: function(callback) {
            this.connectionDownListeners.push(callback);
        },

        whenTrouble: Function.NOOP,

        shutdown: function() {
            //shutdown once
            this.shutdown = Function.NOOP;
            //avoid sending XMLHTTP requests that might create new sessions on the server
            this.send = Function.NOOP;
            [ this.onSendListeners, this.onReceiveListeners, this.onServerErrorListeners, this.connectionDownListeners ].eachWithGuard(function(listeners) {
                listeners.clear();
            });
        }
    });
});

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

Ice.Community = new Object;
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Reliability = new Object ].as(function(This) {
    This.Heartbeat = Object.subclass({
        initialize: function(period, timeout, logger) {
            this.period = period;
            this.logger = logger.child('heartbeat');
            this.pingListeners = [];
            this.lostPongListeners = [];

            this.beat = function() {
                var timeoutBomb = function() {
                    this.logger.warn('pong lost');
                    this.lostPongListeners.each(function(listener) {
                        listener.notify();
                    });
                }.bind(this).delayExecutionFor(timeout);

                this.pingListeners.broadcast(new This.Ping(timeoutBomb, this, this.logger));
            }.bind(this);


            window.onKeyPress(function(e) {
                if (e.keyCode() == 46 && e.isCtrlPressed() && e.isShiftPressed()) {
                    this.beatPID ? this.stop() : this.start();
                }
            }.bind(this));
        },

        start: function() {
            this.beatPID = this.beat.repeatExecutionEvery(this.period);
            this.logger.info('heartbeat started');
            return this;
        },

        stop: function() {
            try {
                this.beatPID.cancel();
                this.beatPID = null;
                this.pingListeners.clear();
                this.lostPongListeners.each(function(listener) {
                    listener.ignoreNotifications();
                });
                this.logger.info('heartbeat stopped');
            } catch (e) {
                this.logger.warn('heartbeat not started', e);
            }
            return this;
        },

        reset: function() {
            this.lostPongListeners.each(function(listener) {
                listener.reset();
            });
        },

        onPing: function(callback) {
            this.pingListeners.push(callback);
        },

        onLostPongs: function(callback, lostPongs) {
            var retries = lostPongs || 1;
            this.lostPongListeners.push(new This.CoalescingListener(retries, callback));
        }
    });

    This.Ping = Object.subclass({
        initialize: function(pid, heartbeat, logger) {
            this.pid = pid;
            this.heartbeat = heartbeat;
            this.logger = logger;
            this.logger.info('ping');
        },

        pong: function() {
            if (this.pid) {
                this.heartbeat.reset();
                this.pid.cancel();
                this.pong = Function.NOOP;
                this.logger.info('pong');
            }
        }
    });

    This.CoalescingListener = Object.subclass({
        initialize: function(retries, callback) {
            this.count = 0;
            this.retries = retries;
            this.callback = callback;
        },

        notify: function() {
            this.count += 1;
            if (this.count == this.retries) {
                this.callback();
                this.reset();
            }
        },

        ignoreNotifications: function() {
            this.notify = Function.NOOP;
        },

        reset: function() {
            this.count = 0;
        }
    });
});
/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

[ Ice.Community.Connection = new Object, Ice.Connection, Ice.Ajax, Ice.Reliability.Heartbeat, Ice.Cookie, Ice.Parameter.Query ].as(function(This, Connection, Ajax, Heartbeat, Cookie, Query) {
    This.AsyncConnection = Object.subclass({
        initialize: function(logger, sessionID, viewID, configuration, defaultQuery, commandDispatcher) {
            this.logger = logger.child('async-connection');
            this.sendChannel = new Ajax.Client(this.logger.child('ui'));
            this.receiveChannel = new Ajax.Client(this.logger.child('blocking'));
            this.defaultQuery = defaultQuery;
            this.onSendListeners = [];
            this.onReceiveListeners = [];
            this.onServerErrorListeners = [];
            this.connectionDownListeners = [];
            this.connectionTroubleListeners = [];

            this.listener = { close: Function.NOOP, abort: Function.NOOP };
            this.listening = { remove: Function.NOOP };
            this.timeoutBomb = { cancel: Function.NOOP };
            this.heartbeat = { stop: Function.NOOP };

            this.pingURI = configuration.context.current + 'block/ping';
            this.getURI = configuration.context.current + 'block/receive-updates';
            this.sendURI = configuration.context.current + 'block/send-receive-updates';
            this.receiveURI = configuration.context.async + 'block/receive-updated-views';

            //clear connectionDownListeners to avoid bogus connection lost messages
            window.onBeforeUnload(function() {
                this.connectionDownListeners.clear();
            }.bind(this));

            var timeout = configuration.timeout ? configuration.timeout : 60000;
            this.onSend(function() {
                this.timeoutBomb.cancel();
                this.timeoutBomb = this.connectionDownListeners.broadcaster().delayExecutionFor(timeout);
            }.bind(this));

            this.onReceive(function() {
                this.timeoutBomb.cancel();
            }.bind(this));

            this.serverErrorCallback = this.onServerErrorListeners.broadcaster();
            this.receiveCallback = function(response) {
                try {
                    this.onReceiveListeners.broadcast(response);
                } catch (e) {
                    this.logger.error('receive broadcast failed', e);
                }
            }.bind(this);
            this.sendXWindowCookie = Function.NOOP;
            this.receiveXWindowCookie = function (response) {
                var xWindowCookie = response.getResponseHeader("X-Set-Window-Cookie");
                if (xWindowCookie) {
                    this.sendXWindowCookie = function(request) {
                        request.setRequestHeader("X-Window-Cookie", xWindowCookie);
                    };
                }
            }.bind(this);

            //read/create cookie that contains the updated views
            try {
                this.updatedViews = Cookie.lookup('updates');
            } catch (e) {
                this.updatedViews = new Cookie('updates', '');
            }

            //register command that handles the updated-views message
            commandDispatcher.register('updated-views', function(message) {
                var views = this.updatedViews.loadValue().split(' ');
                var text = message.firstChild;
                if (text && !text.data.blank()) {
                    this.updatedViews.saveValue(views.concat(text.data.split(' ')).asSet().join(' '));
                } else {
                    this.logger.warn("No updated views were returned.");
                }
            }.bind(this));


            //remove the blocking connection marker so that everytime a new
            //bridge instance is created the blocking connection will
            //be re-established
            //this strategy is mainly employed to fix the window.onunload issue
            //in Opera -- see http://jira.icefaces.org/browse/ICE-1872
            try {
                this.listening = Cookie.lookup('bconn');
                this.listening.remove();
            } catch (e) {
                //do nothing
            }

            this.connect = function() {
                this.logger.debug("closing previous connection...");
                this.listener.close();
                this.logger.debug("connect...");
                var query = new Query();
                window.sessions.each(function(sessionID) {
                    query.add('ice.session', sessionID);
                });
                this.listener = this.receiveChannel.postAsynchronously(this.receiveURI, query.asURIEncodedString(), function(request) {
                    this.sendXWindowCookie(request);
                    Connection.FormPost(request);
                    request.on(Connection.ServerError, this.serverErrorCallback);
                    request.on(Connection.OK, this.receiveXWindowCookie);
                    request.on(Connection.OK, function(response) {
                        if (!response.isEmpty()) {
                            this.receiveCallback(response);
                        }
                        if (response.getResponseHeader('X-Connection') != 'close') {
                            this.connect();
                        }
                    }.bind(this));
                    request.on(Connection.OK, Connection.Close);
                }.bind(this));
            }.bind(this);

            //avoid error messages for 'pong' messages that arrive after blocking connection is closed
            commandDispatcher.register('pong', Function.NOOP);
            //heartbeat setup
            var heartbeatInterval = configuration.heartbeat.interval ? configuration.heartbeat.interval : 50000;
            var heartbeatTimeout = configuration.heartbeat.timeout ? configuration.heartbeat.timeout : 30000;
            var heartbeatRetries = configuration.heartbeat.retries ? configuration.heartbeat.retries : 3;
            var initializeConnection = function() {
                //stop the previous heartbeat instance
                this.heartbeat.stop();
                this.heartbeat = new Heartbeat(heartbeatInterval, heartbeatTimeout, this.logger);
                this.heartbeat.onPing(function(ping) {
                    //re-register a pong command on every ping
                    commandDispatcher.register('pong', function() {
                        ping.pong();
                    });
                    this.sendChannel.postAsynchronously(this.pingURI, this.defaultQuery.asURIEncodedString(), Connection.FormPost);
                }.bind(this));

                this.heartbeat.onLostPongs(this.connectionDownListeners.broadcaster(), heartbeatRetries);
                this.heartbeat.onLostPongs(this.connectionTroubleListeners.broadcaster());
                this.heartbeat.onLostPongs(function() {
                    this.logger.debug('retry to connect...');
                    this.connect();
                }.bind(this));

                this.heartbeat.start();
                this.connect();
            }.bind(this);

            //monitor if the blocking connection needs to be started
            //
            //the blocking connection will be started by the window noticing
            //that the connection is not started
            var fullViewID = sessionID + ':' + viewID;
            this.blockingConnectionMonitor = function() {
                try {
                    this.listening = Cookie.lookup('bconn');
                    if (this.listening.value == fullViewID) {
                        this.listening.saveValue('acquired');
                        //start blocking connection since no other window has started it
                        initializeConnection();
                    }
                } catch (e) {
                    this.listening = new Cookie('bconn', fullViewID);
                }
            }.bind(this).repeatExecutionEvery(1000);

            var pickUpdates = function() {
                this.sendChannel.postAsynchronously(this.getURI, this.defaultQuery.asURIEncodedString(), function(request) {
                    Connection.FormPost(request);
                    request.on(Connection.OK, this.receiveCallback);
                    request.on(Connection.OK, Connection.Close);
                }.bind(this));
            }.bind(this);

            //pick any updates that might be generated in between bridge re-initialization
            //todo: replace heuristic with more exact solution
            pickUpdates.delayExecutionFor(1000);

            //monitor & pick updates for this view
            this.updatesMonitor = function() {
                try {
                    var views = this.updatedViews.loadValue().split(' ');
                    if (views.include(fullViewID)) {
                        pickUpdates();
                        this.updatedViews.saveValue(views.complement([ fullViewID ]).join(' '));
                    }
                } catch (e) {
                    this.logger.warn('failed to listen for updates', e);
                }
            }.bind(this).repeatExecutionEvery(300);

            this.logger.info('asynchronous mode');
        },

        send: function(query) {
            var compoundQuery = new Query();
            compoundQuery.addQuery(query);
            compoundQuery.addQuery(this.defaultQuery);
            compoundQuery.add('ice.focus', window.currentFocus);

            this.logger.debug('send > ' + compoundQuery.asString());
            this.sendChannel.postAsynchronously(this.sendURI, compoundQuery.asURIEncodedString(), function(request) {
                Connection.FormPost(request);
                request.on(Connection.OK, this.receiveCallback);
                request.on(Connection.ServerError, this.serverErrorCallback);
                request.on(Connection.OK, Connection.Close);
                this.onSendListeners.broadcast(request);
            }.bind(this));
        },

        onSend: function(callback) {
            this.onSendListeners.push(callback);
        },

        onReceive: function(callback) {
            this.onReceiveListeners.push(callback);
        },

        onServerError: function(callback) {
            this.onServerErrorListeners.push(callback);
        },

        whenDown: function(callback) {
            this.connectionDownListeners.push(callback);
        },

        whenTrouble: function(callback) {
            this.connectionTroubleListeners.push(callback);
        },

        shutdown: function() {
            try {
                //shutdown once
                this.shutdown = Function.NOOP;
                //avoid sending XMLHTTP requests that might create new sessions on the server
                this.send = Function.NOOP;
                this.connect = Function.NOOP;
                this.heartbeat.stop();
            } catch (e) {
                //ignore, we really need to shutdown
            } finally {
                [ this.onSendListeners, this.onReceiveListeners, this.connectionDownListeners, this.onServerErrorListeners ].eachWithGuard(function(listeners) {
                    listeners.clear();
                });
                this.listener.abort();

                [ this.updatesMonitor, this.blockingConnectionMonitor ].eachWithGuard(function(monitor) {
                    monitor.cancel();
                });
                this.listening.remove();
            }
        }
    });
});

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

window.logger = new Ice.Log.Logger([ 'window' ]);
window.console && window.console.firebug ? new Ice.Log.FirebugLogHandler(window.logger) : new Ice.Log.WindowLogHandler(window.logger, window);

[ Ice.Community ].as(function(This) {
    var views = window.views = window.views ? window.views : [];
    var registerView = function(session, view) {
        views.push(new Ice.Parameter.Association(session, view));
    };

    var deregisterAllViews = function() {
        views.clear();
    };

    var channel = new Ice.Ajax.Client(logger.child('dispose'));
    var sendDisposeViews = function(parameters) {
        if (parameters.isEmpty()) return;
        try {
            var query = parameters.inject(new Ice.Parameter.Query(), function(query, parameter) {
                return query.addParameter(parameter);
            });

            channel.postSynchronously(window.disposeViewsURI, query.asURIEncodedString(), function(request) {
                Ice.Connection.FormPost(request);
                request.on(Ice.Connection.OK, Ice.Connection.Close);
            });
        } catch (e) {
            logger.warn('Failed to notify view disposal', e);
        }
    };

    var disposeView = function(session, view) {
        views = views.reject(function(i) {
            return i.name == session && i.value == view;
        });
        sendDisposeViews([new Ice.Parameter.Association(session, view)]);
    };

    var disposeSessionViews = function(session) {
        var sessionViews = views.select(function(i) {
            return i.name == session;
        });
        views = views.complement(sessionViews);
        sendDisposeViews(sessionViews);
    };

    var disposeWindowViews = function() {
        sendDisposeViews(views);
        views.clear();
    };

    window.onBeforeUnload(disposeWindowViews);

    This.Application = Object.subclass({
        initialize: function(configuration, container) {
            var sessionID = configuration.session;
            var viewID = configuration.view;
            registerView(sessionID, viewID);
            var logger = window.logger.child(sessionID.substring(0, 4) + '#' + viewID);
            var statusManager = new Ice.Status.DefaultStatusManager(configuration, container);
            var scriptLoader = new Ice.Script.Loader(logger);
            var commandDispatcher = new Ice.Command.Dispatcher();
            var documentSynchronizer = new Ice.Document.Synchronizer(window.logger, sessionID, viewID);
            var parameters = Ice.Parameter.Query.create(function(query) {
                query.add('ice.session', sessionID);
                query.add('ice.view', viewID);
            });
            var replaceContainerHTML = function(html) {
                Ice.Document.replaceContainerHTML(container, html);
                scriptLoader.searchAndEvaluateScripts(container);
            };

            var connection = configuration.synchronous ?
                             new Ice.Connection.SyncConnection(logger, configuration.connection, parameters) :
                             new This.Connection.AsyncConnection(logger, sessionID, viewID, configuration.connection, parameters, commandDispatcher);
            var dispose = function() {
                dispose = Function.NOOP;
                documentSynchronizer.shutdown();
                connection.shutdown();
            }

            commandDispatcher.register('noop', Function.NOOP);
            commandDispatcher.register('set-cookie', Ice.Command.SetCookie);
            commandDispatcher.register('parsererror', Ice.Command.ParsingError);
            commandDispatcher.register('redirect', function(element) {
                //replace ampersand entities incorrectly decoded by Safari 2.0.4
                var url = element.getAttribute("url").replace(/&#38;/g, "&");
                logger.info('Redirecting to ' + url);
                //avoid view disposal on navigation rules
                if (url.contains('rvn=')) {
                    deregisterAllViews();
                }
                window.location.href = url;
            });
            commandDispatcher.register('reload', function(element) {
                logger.info('Reloading');
                var url = window.location.href;
                deregisterAllViews();
                if (url.contains('rvn=')) {
                    window.location.reload();
                } else {
                    var view = element.getAttribute('view');
                    var queryPrefix = url.contains('?') ? '&' : '?';
                    window.location.href = url + queryPrefix + 'rvn=' + view;
                }
            });
            commandDispatcher.register('macro', function(message) {
                $enumerate(message.childNodes).each(function(subMessage) {
                    commandDispatcher.deserializeAndExecute(subMessage);
                });
            });
            commandDispatcher.register('updates', function(element) {
                $enumerate(element.getElementsByTagName('update')).each(function(updateElement) {
                    try {
                        var address = updateElement.getAttribute('address');
                        var update = new Ice.ElementModel.Update(updateElement);
                        address.asExtendedElement().updateDOM(update);
                        logger.debug('applied update : ' + update.asString());
                        scriptLoader.searchAndEvaluateScripts(address.asElement());
                        if (Ice.StateMon) {
                            Ice.StateMon.checkAll();
                            Ice.StateMon.rebuild();
                        }
                    } catch (e) {
                        logger.error('failed to insert element: ' + update.asString(), e);
                    }
                });
            });
            commandDispatcher.register('session-expired', function() {
                logger.warn('Session has expired');
                statusManager.sessionExpired.on();
                //implicit protocol for session expiry
                //todo: verify if still needed
                //disposeSessionViews(sessionID);
                dispose();
            });

            window.onUnload(function() {
                dispose();
            });

            connection.onSend(function() {
                Ice.Focus.userInterupt = false;
                statusManager.busy.on();
            });

            connection.onReceive(function(response) {
                statusManager.busy.off();

                var mimeType = response.getResponseHeader('Content-Type');
                if (mimeType.startsWith('text/html')) {
                    replaceContainerHTML(response.content());
                } else if (mimeType.startsWith('text/xml')) {
                    commandDispatcher.deserializeAndExecute(response.contentAsDOM().documentElement);
                    documentSynchronizer.synchronize();
                } else {
                    logger.warn('unknown content in response');
                }
            });

            connection.onServerError(function (response) {
                logger.warn('server side error');
                disposeView(sessionID, viewID);
                if (response.isEmpty()) {
                    statusManager.serverError.on();
                } else {
                    replaceContainerHTML(response.content());
                }
                dispose();
            });

            connection.whenDown(function() {
                logger.warn('connection to server was lost');
                statusManager.connectionLost.on();
                dispose();
            });

            connection.whenTrouble(function() {
                logger.warn('connection in trouble');
                statusManager.connectionTrouble.on();
            });

            //public method used to modify bridge's status manager
            this.attachStatusManager = function(setup) {
                statusManager.off();
                statusManager = setup(new Ice.Status.DefaultStatusManager(configuration, container));
                logger.info("status indicators were updated");
            };
            //public methods
            this.connection = connection;
            this.dispose = dispose;
            this.disposeAndNotify = function() {
                disposeView(sessionID, viewID);
                dispose();
            };
            logger.info('bridge loaded!');
        }
    });
});

window.onKeyPress(function(e) {
    if (e.isEscKey()) e.cancelDefaultAction();
});
if (typeof OpenAjax != 'undefined' && typeof OpenAjax.registerLibrary != 'undefined' && typeof OpenAjax.registerGlobals != 'undefined') {
    OpenAjax.registerLibrary('icefaces-d2d', 'http://www.icefaces.org/', '1.5.3');
    OpenAjax.registerGlobals('icefaces-d2d', ['Class','Enumerable','iceSubmit','$A','resetHiddenFieldsFor','$H','setFocus','property','$R','$break','Hashtable','ObjectRange','$w','Template','current','$continue','PeriodicalExecuter','Try','currentFocus','Abstract','Ice','iceSubmitPartial']);
}

