var pc = navigator.userAgent.toLowerCase();
var ie4_win = (pc.indexOf("win")!=-1) && (pc.indexOf("msie") != -1)
    && (parseInt(navigator.appVersion) >= 4);

// only builds based upon gecko later than Jan 8th support the selectionStart, selectionEnd properly
var is_gecko = pc.indexOf("gecko/") != -1 &&
    parseFloat(pc.substring(pc.indexOf("gecko/") + 6, pc.indexOf("gecko/") + 14)) > 20030108;

function styleTag(tag, endtag, ta) {
    var end = 0;
    var scrollTop = ta.scrollTop;
    var r;
    if (document.selection) {
        if (document.selection.createRange().parentElement().tagName == 'TEXTAREA') {
            var selected = document.selection.createRange().text;

            // now determine the cursor position
            end = getSelectionRangeEnd(ta);

            // r is an array
            r = _markupText(selected, tag, endtag);
            // update text
            document.selection.createRange().text = r[0];
            // update end position
            end += r[1];
        }
    }
    else if (typeof(ta.selectionStart) != 'undefined' && typeof(ta.selectionEnd) != 'undefined') {
        if (ta.selectionStart == ta.selectionEnd) {
            return;
        }
        var selLength = ta.textLength;
        var selStart = ta.selectionStart;
        var selEnd = ta.selectionEnd;
        if (selEnd == 1 || selEnd == 2) {
            selEnd = selLength;
        }
        var s1 = (ta.value).substring(0, selStart);
        var s2 = (ta.value).substring(selStart, selEnd);
        var s3 = (ta.value).substring(selEnd, selLength);

        // r is an array
        r = _markupText(s2, tag, endtag);
        // update text
        ta.value = s1 + r[0] + s3;
        // update end position
        end = selEnd + r[1];
    }
    else {
        return;
    }

    if (end > 0) {
        setCaretTo(ta, end, scrollTop);
    }
}

function _markupText(text, tag, endtag) {
    // trim off leading whitespace from selection (includes newlines)
    var r = trimLeadingSpace(text);
    text = r[0];
    var removeSpace = r[1];

    // trim off trailing whitespace from selection (includes newlines)
    r = trimTrailingSpace(text);
    text = r[0];
    var addSpace = r[1];

    // determine if selection crosses multiple lines
    var addedCharacters = 0;

    if (text.indexOf('\n') > 0) {
        // wrap whole lines in the tag
        var lines = text.split('\n');
        var newText = '';
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];

            r = trimLeadingSpace(line);
            line = r[0];
            var leadingSpaces = r[1];

            // trim off trailing whitespace from selection (includes newlines)
            r = trimTrailingSpace(line);
            line = r[0];
            var trailingSpaces = r[1];

            if (line == '') {
                newText += leadingSpaces + line + trailingSpaces;
            }
            else {
                newText += leadingSpaces + tag + line + endtag + trailingSpaces;
                addedCharacters += (tag.length + endtag.length);
            }

            if (i < lines.length -1) {
                newText += '\n';
            }
        }
        text = removeSpace + newText + addSpace;
    }
    else {
        text = removeSpace + tag + text + endtag + addSpace;
    }

    r = new Array(2);
    r[0] = text;
    r[1] = addedCharacters;

    return r;
}

function trimLeadingSpace(text) {
    var removeSpace = "";
    while (text.length > 0 && 
           (text.charAt(0) == ' ' || 
            text.charAt(0) == '\n' ||
            text.charAt(0) == '\r')) 
    {
        removeSpace += text.charAt(0);
        text = text.substring(1);
    }
    
    var r = new Array(2);
    r[0] = text;
    r[1] = removeSpace;
    return r;
}

function trimTrailingSpace(text) {
    var addSpace = "";
    while (text.length > 0 && 
           (text.charAt(text.length-1) == ' ' || 
            text.charAt(text.length-1) == '\n' || 
           text.charAt(text.length-1) == '\r')) 
    {
        addSpace += text.charAt(text.length-1);        
        text = text.substring(0, text.length-1);
    }
    
    var r = new Array(2);
    r[0] = text;
    r[1] = addSpace;
    return r;
}

function getSelectionRangeText(ta) {
    if (document.selection) {
        // The current selection
        return document.selection.createRange().text;
    }
    else if (is_gecko) {
        var start = ta.selectionStart;
        var end   = ta.selectionEnd;
        return ta.value.substr(start, end-start);
    }
    else {
        return '';
    }
}

function getSelectionRangeEnd(ta) {
    if (document.selection) { 
        // The current selection 
        var range = document.selection.createRange(); 
        // We'll use this as a 'dummy' 
        var stored_range = range.duplicate(); 
        // Select all text 
        stored_range.moveToElementText(ta); 
        // Now move 'dummy' end point to end point of original range 
        stored_range.setEndPoint('EndToEnd', range); 
        
        // Now we can calculate start and end points
        var start = stored_range.text.length - range.text.length; 
        return start + range.text.length;
    }
    else if (is_gecko) {
        return ta.selectionEnd;
    }
    else {
        return 0;
    }
}
function setCaretTo(ta, pos, scrollTop) { 
    ta.focus();
    if (ta.createTextRange) {         
        // we need to determine the number of \r\n in IE because while they are two separate
        // characters they are treated as one for the purposes of position
        var i = 0;
        var count = 0;
        var text = ta.value;
        while (i > -1 && i < pos) {
            i = text.indexOf("\r\n", i);
            if (i >= 0) {
                count++;
                i += 2;
            }
        }
        // knock one off of count 
        if (count > 1) {
            count--;
        }
        
        // position cursor
        var range = ta.createTextRange();        
        range.move("character", (pos - count)); 
        range.select(); 
    } 
    else if (ta.selectionStart) {
        ta.setSelectionRange(pos, pos);        
    }
    
    // scroll to the same location that the textarea was previously scrolled to
    if (scrollTop > 0) {
        ta.scrollTop = scrollTop;
    }
}

function caret(ta) {
    if (ie4_win && ta.createTextRange &&
            document.selection.createRange().parentElement().tagName == 'TEXTAREA')
    {
        ta.caretPos = document.selection.createRange().duplicate();
    }
}

// This function returns the name of a given function. It does this by
// converting the function to a string, then using a regular expression
// to extract the function name from the resulting code.
function funcname(f) {
    var s = f.toString().match(/function (\w*)/)[1];
    if ((s == null) || (s.length == 0)) return "anonymous";
    return s;
}

// This function returns a string that contains a "stack trace."
function stacktrace() {
    var s = "";  // This is the string we'll return.
    // Loop through the stack of functions, using the caller property of
    // one arguments object to refer to the next arguments object on the
    // stack.
    for(var a = arguments.caller; a != null; a = a.caller) {
        // Add the name of the current function to the return value.
        s += funcname(a.callee) + "\n";

        // Because of a bug in Navigator 4.0, we need this line to break.
        // a.caller will equal a rather than null when we reach the end
        // of the stack. The following line works around this.
        if (a.caller == a) break;
    }
    return s;
}

function printStackTrace() {
    alert('stack trace is ' + stacktrace());
}


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

/*
 * JIVE SOFTWARE ENGINEERS
 *
 * please note the following changes to this document:
 * line 3929 -> added conditional checks for element.stopObserving
 *              see svn revision 66086
 */

var Prototype = {
  Version: '1.6.0.2',

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

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

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

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

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


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

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

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

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

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

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

    klass.prototype.constructor = klass;

    return klass;
  }
};

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

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

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

    return this;
  }
};

var Abstract = { };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return returnValue;
  }
};

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

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

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

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

    this.registerCallback();
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return camelized;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var $break = { };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

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

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

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

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

Object.extend(Ajax.Responders, Enumerable);

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

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

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

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

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

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

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

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

    this.parameters = params;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  status:      0,
  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

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

  getHeader: Ajax.Request.prototype.getHeader,

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

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

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

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

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

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

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

    $super(url, options);
  },

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

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

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

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

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

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

    this.start();
  },

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

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

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

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

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

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

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

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

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

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

Element.cache = { };

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

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

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

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

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

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

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

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

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

    var content, insert, tagName, childNodes;

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

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

      content = Object.toHTML(content);

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

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

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

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

    return element;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return element;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return $(document.body);
  },

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

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

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

    } while (element = element.offsetParent);

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

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

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

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

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

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

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

Element.Methods.identify.counter = 1;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  Element._attributeTranslations.has = {};

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

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

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

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

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

    return element;
  };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

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

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

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

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

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

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

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

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    var e = this.expression;

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

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

    return true;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return match;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return matchingInputs;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  cache: { },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      return element;
    },

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

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
            if(element && element.stopObserving){                               // this line added by Jive Software
                element.stopObserving(eventName, wrapper.handler);
            }                                                                   // this line added by Jive Software
        });
        return element;

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

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

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

      destroyWrapper(id, eventName, handler);

      return element;
    },

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

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

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

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

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

Object.extend(Event, Event.Methods);

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

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

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

  var timer;

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

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

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

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

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

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

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

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

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Element.addMethods();

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

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

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

Scriptaculous.load();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return styleRules;
};

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

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

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

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

Element.addMethods(Effect.Methods);


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

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

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

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

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

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

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

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

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

    Element.hide(this.update);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


function jiveToggleTab(thisPanel, otherPanels) {
    var thisPanelElem = $(thisPanel);
    // load the tabs for the panel elements
    if (thisPanelElem) {
        var thisTabElem = $(thisPanelElem.id + "-tab");
    }

    // toggle the tab styles and the panel visibility
    if (thisPanelElem && thisPanelElem.style.display == 'none') {
        if (thisTabElem) {
            thisTabElem.className = "jive-body-tab jive-body-tabcurrent";
            thisPanelElem.style.display = "block";
        }
        for (var i = 0; i < otherPanels.length; i++) {
            var thatPanelElem = $(otherPanels[i]);
            if (thatPanelElem) {
                var thatTabElem = $(thatPanelElem.id + "-tab");
            }
            if (thatTabElem) {
                thatTabElem.className = "jive-body-tab";
                thatPanelElem.style.display = "none";
            }
        }
    }
}

/*
jiveToggleOptions function
Function for toggling the state of an options element.
*/
function jiveToggleOptions(optionName) {
    if ($(optionName + '-form').style.display != 'none') {
        Element.hide(optionName + '-form');
        $(optionName + '-hdr').className = 'jive-compose-hdr-opt-closed';
    }
    else
    {
        $(optionName + '-form').style.display = 'block';
        $(optionName + '-hdr').className = 'jive-compose-hdr-opt';
    }
}

function jiveShowTopicFilter(thisID) {
    if ($(thisID).style.display != 'none') {
        Element.toggle($(thisID));
		//Effect.toggle($(thisID), 'slide', {duration: .4});
    }
    else
    {
        Element.toggle($(thisID));
		//Effect.toggle($(thisID), 'slide', {duration: .4});
    }
}

function jiveToggleSpaceDetails(thisID) {
    Element.toggle($(thisID));

    if ($(thisID).style.display != 'none') {
        $(thisID + '-less').style.display = '';
        $(thisID + '-more').style.display = 'none';
    }
    else
    {
        $(thisID + '-more').style.display = '';
        $(thisID + '-less').style.display = 'none';
    }
}

function jiveToggleSpaceDetails2(thisID) {
    if ($(thisID).className == 'jive-space-namedesc-full') {
        $(thisID).className = '';
        $(thisID + '-more').style.display = '';
        $(thisID + '-less').style.display = 'none';
    }
    else
    {
        $(thisID).className = 'jive-space-namedesc-full';
        $(thisID + '-less').style.display = '';
        $(thisID + '-more').style.display = 'none';
    }
}

function callOnLoad(init) {
    if (window.addEventListener) {
        window.addEventListener("load", init, false);
    }
    else if (window.attachEvent) {
        window.attachEvent("onload", init);
    }
    else
    {
        window.onload = init;
    }
}

Jive = Class.create();
Jive.AlertMessage = function(element) {
    var options = arguments[1] || {} ;

    new Effect.Appear(element, {
        queue: 'front',
        scope: element,
        duration: 1,
        beforeStart:options.beforeStart,
        afterFinish:function(obj) {
            new Pause(1);
            new Effect.Fade(element, {
                queue: 'end',
                scope: obj,
                duration: 1,
                afterFinish:options.afterFinish
            });
        }
    });
};

function Pause(duration, busy) {
    this.duration = duration * 1000;
    this.busywork = null; // function to call while waiting.
    this.runner = 0;

    if (arguments.length == 2) {
        this.busywork = busy;
    }

    this.pause(this.duration);

}

Pause.prototype.pause = function(duration) {
    if ((duration == null) || (duration < 0)) {
        return;
    }

    var later = (new Date()).getTime() + duration;

    while (true) {
        if ((new Date()).getTime() > later) {
            break;
        }

        this.runner++;

        if (this.busywork != null) {
            this.busywork(this.runner);
        }

    } // while

} // pause method

var TimeoutExecutor = Class.create();
TimeoutExecutor.prototype = {
    initialize: function(callback, timeout) {
        this.callback = callback;
        this.timeout = timeout;
        this.currentlyExecuting = false;
        this.registerCallback();
    },
    registerCallback: function() {
        this.timeoutID = setTimeout(this.onTimerEvent.bind(this), this.timeout);
    },
    onTimerEvent: function() {
        try {
            this.currentlyExecuting = true;
            if (this.callback && this.callback instanceof Function) {
                this.callback();
            }
        }
        finally {
            this.currentlyExecuting = false;
            delete this.timeoutID;
        }
    },
    cancel: function() {
        if (!this.currentlyExecuting && this.timeoutID) {
            clearTimeout(this.timeoutID);
            delete this.timeoutID;
        }
    },
    reset: function() {
        if (!this.currentlyExecuting && this.timeoutID) {
            clearTimeout(this.timeoutID);
            delete this.timeoutID;
            this.registerCallback();
        }
    }
}

var QuickUserProfile = Class.create();
QuickUserProfile.prototype = {

/*
* Initialize the QuickUserProfile object.
*/
    initialize: function(userTT, userTTURL, textTTLoading, textTTError)
    {
        this.loadingContent = '<strong class="jive-tooltip2-loading">' + textTTLoading + '</strong>';
        this.userTT = userTT;
        this.userTTURL = userTTURL.indexOf('?') < 0? userTTURL + '?tooltip=true' : userTTURL + '&tooltip=true';
        this.textErrorTT = textTTError;
        this.jiveUserTips = new SuperNote('jiveTT', {showDelay: 700, hideDelay: 30, cssProp: 'visibility', cssVis: 'visible', cssHid: 'hidden'});
    },

    getUserProfileTooltip: function(userID) {
        this.cancelTooltip();
        $(this.userTT).innerHTML = this.loadingContent;
        this.timeoutExecutor = new TimeoutExecutor(this.getUserProfile.bind(this, userID), 700);    
    },

    getUserProfile: function(userID) {
        var instance = this;
        new Ajax.Updater( this.userTT, this.userTTURL, {
            method: 'get',
            parameters: {
                'targetUser': userID
            },
            onError: function() {
                $(this.userTT).innerHTML = instance.textTTError;
            }   
        });
    },

    cancelTooltip: function() {
        if (this.timeoutExecutor) {
            this.timeoutExecutor.cancel();
        }
    }
}


/*
 *
 * Copyright (c) 2004-2005 by Zapatec, Inc.
 * http://www.zapatec.com
 * 1700 MLK Way, Berkeley, California,
 * 94709, U.S.A.
 * All rights reserved.
 *
 *
 */


if(typeof Zapatec=='undefined'){Zapatec=function(){};}
Zapatec.version='07-01';if(typeof Zapatec.zapatecPath=='undefined'){Zapatec.zapatecPath=function(){if(document.documentElement){var aTokens=document.documentElement.innerHTML.match(/<script[^>]+src="([^"]*zapatec(-core|-src)?.js[^"]*)"/i);if(aTokens&&aTokens.length>=2){aTokens=aTokens[1].split('?');aTokens=aTokens[0].split('/');if(Array.prototype.pop){aTokens.pop();}else{aTokens.length-=1;}
return aTokens.length?aTokens.join('/')+'/':'';}}
return'';}();}
if(typeof Zapatec=='undefined'){Zapatec=function(){};}
Zapatec.Utils={};Zapatec.Utils.getAbsolutePos=function(el,scrollOff){var SL=0,ST=0;if(!scrollOff){var is_div=/^div$/i.test(el.tagName);if(is_div&&el.scrollLeft)
SL=el.scrollLeft;if(is_div&&el.scrollTop)
ST=el.scrollTop;}
var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var tmp=this.getAbsolutePos(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;}
return r;};Zapatec.Utils.getElementOffset=function(oEl){var iLeft=iTop=iWidth=iHeight=0;if(oEl.getBoundingClientRect){var oRect=oEl.getBoundingClientRect();iLeft=oRect.left;iTop=oRect.top;iWidth=oRect.right-iLeft;iHeight=oRect.bottom-iTop;iLeft+=Zapatec.Utils.getPageScrollX()-2;iTop+=Zapatec.Utils.getPageScrollY()-2;}else{iWidth=oEl.offsetWidth;iHeight=oEl.offsetHeight;var sPos=Zapatec.Utils.getStyleProperty(oEl,'position');if(sPos=='fixed'){iLeft=oEl.offsetLeft+Zapatec.Utils.getPageScrollX();iTop=oEl.offsetTop+Zapatec.Utils.getPageScrollY();}else if(sPos=='absolute'){while(oEl){var sTag=oEl.tagName;if(sTag){sTag=sTag.toLowerCase();if(sTag!='body'&&sTag!='html'){iLeft+=parseInt(oEl.offsetLeft,10)||0;iTop+=parseInt(oEl.offsetTop,10)||0;}}
oEl=oEl.offsetParent;var sTag=oEl?oEl.tagName:null;if(sTag){sTag=sTag.toLowerCase();if(sTag!='body'&&sTag!='html'){iLeft-=oEl.scrollLeft;iTop-=oEl.scrollTop;}}}}else{var oP=oEl;while(oP){iLeft+=parseInt(oP.offsetLeft,10)||0;iTop+=parseInt(oP.offsetTop,10)||0;oP=oP.offsetParent;}
oP=oEl;while(oP.parentNode){oP=oP.parentNode;var sTag=oP.tagName;if(sTag){sTag=sTag.toLowerCase();if(sTag!='body'&&sTag!='html'&&sTag!='tr'){iLeft-=oP.scrollLeft;iTop-=oP.scrollTop;}}}}}
return{left:iLeft,top:iTop,x:iLeft,y:iTop,width:iWidth,height:iHeight};};Zapatec.Utils.getElementOffsetScrollable=function(oEl){var oPos=Zapatec.Utils.getElementOffset(oEl);if(oEl.scrollLeft){oPos.left-=oEl.scrollLeft;oPos.x=oPos.left;}
if(oEl.scrollTop){oPos.top-=oEl.scrollTop;oPos.y=oPos.top;}
return oPos;};Zapatec.Utils.fixBoxPosition=function(box,leave){var screenX=Zapatec.Utils.getPageScrollX();var screenY=Zapatec.Utils.getPageScrollY();var sizes=Zapatec.Utils.getWindowSize();leave=parseInt(leave,10)||0;if(box.x<screenX){box.x=screenX+leave;}
if(box.y<screenY){box.y=screenY+leave;}
if(box.x+box.width>screenX+sizes.width){box.x=screenX+sizes.width-box.width-leave;}
if(box.y+box.height>screenY+sizes.height){box.y=screenY+sizes.height-box.height-leave;}};Zapatec.Utils.isRelated=function(el,evt){evt||(evt=window.event);var related=evt.relatedTarget;if(!related){var type=evt.type;if(type=="mouseover"){related=evt.fromElement;}else if(type=="mouseout"){related=evt.toElement;}}
try{while(related){if(related==el){return true;}
related=related.parentNode;}}catch(e){};return false;};Zapatec.Utils.removeClass=function(el,className){if(!(el&&el.className)){return;}
var cls=el.className.split(" ");var ar=[];for(var i=cls.length;i>0;){if(cls[--i]!=className){ar[ar.length]=cls[i];}}
el.className=ar.join(" ");};Zapatec.Utils.addClass=function(el,className){Zapatec.Utils.removeClass(el,className);el.className+=" "+className;};Zapatec.Utils.getElement=function(ev){if(Zapatec.is_ie){return window.event.srcElement;}else{return ev.currentTarget;}};Zapatec.Utils.getTargetElement=function(ev){if(Zapatec.is_ie){return window.event.srcElement;}else{return ev.target;}};Zapatec.Utils.getMousePos=function(oEv){oEv||(oEv=window.event);var oPos={pageX:0,pageY:0,clientX:0,clientY:0};if(oEv){var bIsPageX=(typeof oEv.pageX!='undefined');var bIsClientX=(typeof oEv.clientX!='undefined');if(bIsPageX||bIsClientX){if(bIsPageX){oPos.pageX=oEv.pageX;oPos.pageY=oEv.pageY;}else{oPos.pageX=oEv.clientX+Zapatec.Utils.getPageScrollX();oPos.pageY=oEv.clientY+Zapatec.Utils.getPageScrollY();}
if(bIsClientX){oPos.clientX=oEv.clientX;oPos.clientY=oEv.clientY;}else{oPos.clientX=oEv.pageX-Zapatec.Utils.getPageScrollX();oPos.clientY=oEv.pageY-Zapatec.Utils.getPageScrollY();}}}
return oPos;};Zapatec.Utils.stopEvent=function(ev){ev||(ev=window.event);if(ev){if(Zapatec.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}}
return false;};Zapatec.Utils.removeOnUnload=[];Zapatec.Utils.addEvent=function(oElement,sEvent,fListener,bUseCapture){if(oElement.addEventListener){if(!bUseCapture){bUseCapture=false;}
oElement.addEventListener(sEvent,fListener,bUseCapture);}else if(oElement.attachEvent){oElement.detachEvent('on'+sEvent,fListener);oElement.attachEvent('on'+sEvent,fListener);if(bUseCapture){oElement.setCapture(false);}}
Zapatec.Utils.removeOnUnload.push({'element':oElement,'event':sEvent,'listener':fListener,'capture':bUseCapture});};Zapatec.Utils.removeEvent=function(oElement,sEvent,fListener,bUseCapture){if(oElement.removeEventListener){oElement.removeEventListener(sEvent,fListener,bUseCapture);}else if(oElement.detachEvent){oElement.detachEvent('on'+sEvent,fListener);}
for(var iLis=Zapatec.Utils.removeOnUnload.length-1;iLis>=0;iLis--){var oParams=Zapatec.Utils.removeOnUnload[iLis];if(!oParams){continue;}
if(oElement==oParams['element']&&sEvent==oParams['event']&&fListener==oParams['listener']&&bUseCapture==oParams['capture']){Zapatec.Utils.removeOnUnload[iLis]=null;Zapatec.Utils.removeEvent(oParams['element'],oParams['event'],oParams['listener'],oParams['capture']);}}};Zapatec.Utils.createElement=function(type,parent,selectable){var el=null;if(window.self.document.createElementNS)
el=window.self.document.createElementNS("http://www.w3.org/1999/xhtml",type);else
el=document.createElement(type);if(typeof parent!="undefined"&&parent!=null)
parent.appendChild(el);if(!selectable){if(Zapatec.is_ie)
el.setAttribute("unselectable",true);if(Zapatec.is_gecko)
el.style.setProperty("-moz-user-select","none","");}
return el;};Zapatec.Utils.writeCookie=function(name,value,domain,path,exp_days){value=escape(value);var ck=name+"="+value,exp;if(domain)
ck+=";domain="+domain;if(path)
ck+=";path="+path;if(exp_days){exp=new Date();exp.setTime(exp_days*86400000+exp.getTime());ck+=";expires="+exp.toGMTString();}
document.cookie=ck;};Zapatec.Utils.getCookie=function(name){var pattern=name+"=";var tokenPos=0;while(tokenPos<document.cookie.length){var valuePos=tokenPos+pattern.length;if(document.cookie.substring(tokenPos,valuePos)==pattern){var endValuePos=document.cookie.indexOf(";",valuePos);if(endValuePos==-1){endValuePos=document.cookie.length;}
return unescape(document.cookie.substring(valuePos,endValuePos));}
tokenPos=document.cookie.indexOf(" ",tokenPos)+1;if(tokenPos==0){break;}}
return null;};Zapatec.Utils.makePref=function(obj){function stringify(val){if(typeof val=="object"&&!val)
return"null";else if(typeof val=="number"||typeof val=="boolean")
return val;else if(typeof val=="string")
return'"'+val.replace(/\x22/,"\\22")+'"';else return null;};var txt="",i;for(i in obj)
txt+=(txt?",'":"'")+i+"':"+stringify(obj[i]);return txt;};Zapatec.Utils.loadPref=function(sCookie){var oCookie=zapatecTransport.parseJson({strJson:'{'+sCookie+'}'});if(!oCookie||typeof oCookie!='object'){oCookie={};}return oCookie;};Zapatec.Utils.mergeObjects=function(dest,src){for(var i in src)
dest[i]=src[i];};Zapatec.Utils.__wch_id=0;Zapatec.Utils.createWCH=function(element){var f=null;element=element||document.body;if(Zapatec.is_ie&&!Zapatec.is_ie5){var filter='filter:progid:DXImageTransform.Microsoft.alpha(style=0,opacity=0);';var id="WCH"+(++Zapatec.Utils.__wch_id);element.insertAdjacentHTML
('beforeEnd','<iframe id="'+id+'" scrolling="no" frameborder="0" '+'style="z-index:0;position:absolute;visibility:hidden;'+filter+'border:0;top:0;left:0;width:0;height:0" '+'src="javascript:false"></iframe>');f=document.getElementById(id);}
return f;};Zapatec.Utils.setupWCH_el=function(f,el,el2){if(f){var pos=Zapatec.Utils.getAbsolutePos(el),X1=pos.x,Y1=pos.y,X2=X1+el.offsetWidth,Y2=Y1+el.offsetHeight;if(el2){var p2=Zapatec.Utils.getAbsolutePos(el2),XX1=p2.x,YY1=p2.y,XX2=XX1+el2.offsetWidth,YY2=YY1+el2.offsetHeight;if(X1>XX1)
X1=XX1;if(Y1>YY1)
Y1=YY1;if(X2<XX2)
X2=XX2;if(Y2<YY2)
Y2=YY2;}
Zapatec.Utils.setupWCH(f,X1,Y1,X2-X1,Y2-Y1);}};Zapatec.Utils.setupWCH=function(f,x,y,w,h){if(f){var s=f.style;(typeof x!="undefined")&&(s.left=x+"px");(typeof y!="undefined")&&(s.top=y+"px");(typeof w!="undefined")&&(s.width=w+"px");(typeof h!="undefined")&&(s.height=h+"px");s.visibility="inherit";}};Zapatec.Utils.hideWCH=function(f){if(f)
f.style.visibility="hidden";};Zapatec.Utils.getPageScrollY=function(){if(window.pageYOffset){return window.pageYOffset;}else if(document.body&&document.body.scrollTop){return document.body.scrollTop;}else if(document.documentElement&&document.documentElement.scrollTop){return document.documentElement.scrollTop;}
return 0;};Zapatec.Utils.getPageScrollX=function(){if(window.pageXOffset){return window.pageXOffset;}else if(document.body&&document.body.scrollLeft){return document.body.scrollLeft;}else if(document.documentElement&&document.documentElement.scrollLeft){return document.documentElement.scrollLeft;}
return 0;};Zapatec.ScrollWithWindow={};Zapatec.ScrollWithWindow.list=[];Zapatec.ScrollWithWindow.stickiness=0.25;Zapatec.ScrollWithWindow.register=function(oElement){var iTop=oElement.offsetTop||0;var iLeft=oElement.offsetLeft||0;Zapatec.ScrollWithWindow.list.push({node:oElement,origTop:iTop,origLeft:iLeft});if(!Zapatec.ScrollWithWindow.interval){Zapatec.ScrollWithWindow.on();}};Zapatec.ScrollWithWindow.unregister=function(oElement){for(var iItem=0;iItem<Zapatec.ScrollWithWindow.list.length;iItem++){var oItem=Zapatec.ScrollWithWindow.list[iItem];if(oElement==oItem.node){Zapatec.ScrollWithWindow.list.splice(iItem,1);if(!Zapatec.ScrollWithWindow.list.length){Zapatec.ScrollWithWindow.off();}
return;}}};Zapatec.ScrollWithWindow.moveTop=function(iTop){Zapatec.ScrollWithWindow.top+=(iTop-Zapatec.ScrollWithWindow.top)*Zapatec.ScrollWithWindow.stickiness;if(Math.abs(Zapatec.ScrollWithWindow.top-iTop)<=1){Zapatec.ScrollWithWindow.top=iTop;}
for(var iItem=0;iItem<Zapatec.ScrollWithWindow.list.length;iItem++){var oItem=Zapatec.ScrollWithWindow.list[iItem];var oElement=oItem.node;oElement.style.position='absolute';if(!oItem.origTop&&oItem.origTop!==0){oItem.origTop=parseInt(oElement.style.top)||0;}
oElement.style.top=oItem.origTop+
parseInt(Zapatec.ScrollWithWindow.top)+'px';}};Zapatec.ScrollWithWindow.moveLeft=function(iLeft){Zapatec.ScrollWithWindow.left+=(iLeft-Zapatec.ScrollWithWindow.left)*Zapatec.ScrollWithWindow.stickiness;if(Math.abs(Zapatec.ScrollWithWindow.left-iLeft)<=1){Zapatec.ScrollWithWindow.left=iLeft;}
for(var iItem=0;iItem<Zapatec.ScrollWithWindow.list.length;iItem++){var oItem=Zapatec.ScrollWithWindow.list[iItem];var oElement=oItem.node;oElement.style.position='absolute';if(!oItem.origLeft&&oItem.origLeft!==0){oItem.origLeft=parseInt(oElement.style.left)||0;}
oElement.style.left=oItem.origLeft+
parseInt(Zapatec.ScrollWithWindow.left)+'px';}};Zapatec.ScrollWithWindow.cycle=function(){var iTop=Zapatec.Utils.getPageScrollY();var iLeft=Zapatec.Utils.getPageScrollX();if(iTop!=Zapatec.ScrollWithWindow.top){Zapatec.ScrollWithWindow.moveTop(iTop);}
if(iLeft!=Zapatec.ScrollWithWindow.left){Zapatec.ScrollWithWindow.moveLeft(iLeft);}};Zapatec.ScrollWithWindow.on=function(){if(Zapatec.ScrollWithWindow.interval){return;}
Zapatec.ScrollWithWindow.top=Zapatec.Utils.getPageScrollY();Zapatec.ScrollWithWindow.left=Zapatec.Utils.getPageScrollX();Zapatec.ScrollWithWindow.interval=setInterval(Zapatec.ScrollWithWindow.cycle,50);};Zapatec.ScrollWithWindow.off=function(){if(!Zapatec.ScrollWithWindow.interval){return;}
clearInterval(Zapatec.ScrollWithWindow.interval);Zapatec.ScrollWithWindow.interval=null;};Zapatec.FixateOnScreen={};Zapatec.FixateOnScreen.getExpression=function(coord,direction){return"Zapatec.Utils.getPageScroll"+direction.toUpperCase()+"() + "+coord;};Zapatec.FixateOnScreen.parseCoordinates=function(element){if(!this.isRegistered(element)){return false;}
var x=0;var y=0;var style=element.style;if(Zapatec.is_ie&&!Zapatec.is_ie7){x=style.getExpression("left").split(" ");x=parseInt(x[x.length-1],10);y=style.getExpression("top").split(" ");y=parseInt(y[y.length-1],10);}else{x=parseInt(style.left,10);y=parseInt(style.top,10);}
x+=Zapatec.Utils.getPageScrollX();y+=Zapatec.Utils.getPageScrollY();return{x:x,y:y};};Zapatec.FixateOnScreen.correctCoordinates=function(x,y){position={x:x,y:y};if(position.x||position.x===0){position.x-=Zapatec.Utils.getPageScrollX();if(Zapatec.is_ie&&!Zapatec.is_ie7){position.x=this.getExpression(position.x,"X");;}else{position.x+="px";}}
if(position.y||position.y===0){position.y-=Zapatec.Utils.getPageScrollY();if(Zapatec.is_ie&&!Zapatec.is_ie7){position.y=this.getExpression(position.y,"Y");;}else{position.y+="px";}}
return position;};Zapatec.FixateOnScreen.register=function(element){if(!Zapatec.isHtmlElement(element)){return false;}
if(this.isRegistered(element)){return true;}
var pos=Zapatec.Utils.getElementOffset(element);pos={x:parseInt(element.style.left,10)||pos.x,y:parseInt(element.style.top,10)||pos.y}
pos=this.correctCoordinates(pos.x,pos.y);if(!Zapatec.is_ie||Zapatec.is_ie7){var restorer=element.restorer;if(!restorer||!restorer.getObject||restorer.getObject()!=element){restorer=element.restorer=new Zapatec.SRProp(element);}
restorer.saveProp("style.position");element.style.position="fixed";element.style.left=pos.x;element.style.top=pos.y;}else{element.style.setExpression("left",pos.x);element.style.setExpression("top",pos.y);}
element.zpFixed=true;return true;};Zapatec.FixateOnScreen.unregister=function(element){if(!Zapatec.isHtmlElement(element)){return false;}
var pos=this.parseCoordinates(element);if(pos===false){return true;}
if(Zapatec.is_ie&&!Zapatec.is_ie7){element.style.removeExpression("left");element.style.removeExpression("top");}
element.style.left=pos.x+"px";element.style.top=pos.y+"px";if(!Zapatec.is_ie||Zapatec.is_ie7){element.restorer.restoreProp("style.position",true);}
element.zpFixed=false;return true;};Zapatec.FixateOnScreen.isRegistered=function(element){if(element.zpFixed){return true;}
return false;};Zapatec.Utils.destroy=function(el){if(el&&el.parentNode)
el.parentNode.removeChild(el);};Zapatec.Utils.newCenteredWindow=function(url,windowName,width,height,scrollbars){var leftPosition=0;var topPosition=0;if(screen.width)
leftPosition=(screen.width-width)/2;if(screen.height)
topPosition=(screen.height-height)/2;var winArgs='height='+height+',width='+width+',top='+topPosition+',left='+leftPosition+',scrollbars='+scrollbars+',resizable';var win=window.open(url,windowName,winArgs);return win;};Zapatec.Utils.getWindowSize=function(){var iWidth=0;var iHeight=0;if(Zapatec.is_opera){iWidth=document.body.clientWidth||0;iHeight=document.body.clientHeight||0;}else if(Zapatec.is_khtml){iWidth=window.innerWidth||0;iHeight=window.innerHeight||0;}else if(document.compatMode&&document.compatMode=='CSS1Compat'){iWidth=document.documentElement.clientWidth||0;iHeight=document.documentElement.clientHeight||0;}else{iWidth=document.body.clientWidth||0;iHeight=document.body.clientHeight||0;}
return{width:iWidth,height:iHeight};};Zapatec.Utils.selectOption=function(sel,val,call_default){var a=sel.options,i,o;for(i=a.length;--i>=0;){o=a[i];o.selected=(o.value==val);}
sel.value=val;if(call_default){if(typeof sel.onchange=="function")
sel.onchange();else if(typeof sel.onchange=="string")
eval(sel.onchange);}};Zapatec.Utils.getNextSibling=function(el,tag,alternateTag){el=el.nextSibling;if(!tag){return el;}
tag=tag.toLowerCase();if(alternateTag)alternateTag=alternateTag.toLowerCase();while(el){if(el.nodeType==1&&(el.tagName.toLowerCase()==tag||(alternateTag&&el.tagName.toLowerCase()==alternateTag))){return el;}
el=el.nextSibling;}
return el;};Zapatec.Utils.getPreviousSibling=function(el,tag,alternateTag){el=el.previousSibling;if(!tag){return el;}
tag=tag.toLowerCase();if(alternateTag)alternateTag=alternateTag.toLowerCase();while(el){if(el.nodeType==1&&(el.tagName.toLowerCase()==tag||(alternateTag&&el.tagName.toLowerCase()==alternateTag))){return el;}
el=el.previousSibling;}
return el;};Zapatec.Utils.getFirstChild=function(el,tag,alternateTag){if(!el){return null;}
el=el.firstChild;if(!el){return null;}
if(!tag){return el;}
tag=tag.toLowerCase();if(el.nodeType==1){if(el.tagName.toLowerCase()==tag){return el;}else if(alternateTag){alternateTag=alternateTag.toLowerCase();if(el.tagName.toLowerCase()==alternateTag){return el;}}}
return Zapatec.Utils.getNextSibling(el,tag,alternateTag);};Zapatec.Utils.getLastChild=function(el,tag,alternateTag){if(!el){return null;}
el=el.lastChild;if(!el){return null;}
if(!tag){return el;}
tag=tag.toLowerCase();if(el.nodeType==1){if(el.tagName.toLowerCase()==tag){return el;}else if(alternateTag){alternateTag=alternateTag.toLowerCase();if(el.tagName.toLowerCase()==alternateTag){return el;}}}
return Zapatec.Utils.getPreviousSibling(el,tag,alternateTag);};Zapatec.Utils.getChildText=function(objNode){if(objNode==null){return'';}
var arrText=[];var objChild=objNode.firstChild;while(objChild!=null){if(objChild.nodeType==3){arrText.push(objChild.data);}
objChild=objChild.nextSibling;}
return arrText.join(' ');};Zapatec.Utils.insertAfter=function(oldNode,newNode){if(oldNode.nextSibling){oldNode.parentNode.insertBefore(newNode,oldNode.nextSibling);}else{oldNode.parentNode.appendChild(newNode);}}
Zapatec.Utils._ids={};Zapatec.Utils.generateID=function(code,id){if(typeof id=="undefined"){if(typeof this._ids[code]=="undefined")
this._ids[code]=0;id=++this._ids[code];}
return"zapatec-"+code+"-"+id;};Zapatec.Utils.addTooltip=function(target,tooltip){return new Zapatec.Tooltip({target:target,tooltip:tooltip});};Zapatec.isLite=true;Zapatec.Utils.checkLinks=function(){var anchors=document.getElementsByTagName('A');for(var ii=0;ii<anchors.length;ii++){if(Zapatec.Utils.checkLink(anchors[ii])){return true;}}
return false;}
Zapatec.Utils.checkLink=function(lnk){if(!lnk){return false;}
if(!/^https?:\/\/((dev|www)\.)?zapatec\.com/i.test(lnk.href)){return false;}
var textContent=""
for(var ii=0;ii<lnk.childNodes.length;ii++){if(lnk.childNodes[ii].nodeType==3){textContent+=lnk.childNodes[ii].nodeValue;}}
if(textContent.length<4){return false;}
var parent=lnk;while(parent&&parent.nodeName.toLowerCase()!="html"){if(Zapatec.Utils.getStyleProperty(parent,"display")=="none"||Zapatec.Utils.getStyleProperty(parent,"visibility")=="hidden"||Zapatec.Utils.getStyleProperty(parent,"opacity")=="0"||Zapatec.Utils.getStyleProperty(parent,"-moz-opacity")=="0"||/alpha\(opacity=0\)/i.test(Zapatec.Utils.getStyleProperty(parent,"filter"))){return false;}
parent=parent.parentNode;}
var coords=Zapatec.Utils.getElementOffset(lnk);if(coords.left<0||coords.top<0){return false;}
return true;}
Zapatec.Utils.checkActivation=function(){if(!Zapatec.isLite)return true;var arrProducts=[]
add_product=function(script,webdir_in,name_in)
{arrProducts[script]={webdir:webdir_in,name:name_in,bActive:false}}
add_product('calendar.js','prod1','Calendar')
add_product('zpmenu.js','menu','Menu')
add_product('tree.js','prod3','Tree')
add_product('form.js','forms','Forms')
add_product('effects.js','effects','Effects')
add_product('hoverer.js','effects','Effects - Hoverer')
add_product('slideshow.js','effects','Effects - Slideshow')
add_product('zpgrid.js','grid','Grid')
add_product('slider.js','slider','Slider')
add_product('zptabs.js','tabs','Tabs')
add_product('zptime.js','time','Time')
add_product('window.js','windows','Window')
var strName,arrName,i
var bProduct=false
var scripts=document.getElementsByTagName('script');for(i=0;i<scripts.length;i++)
{if(/wizard.js/i.test(scripts[i].src))
return true
arrName=scripts[i].src.split('/')
if(arrName.length==0)
strName=scripts[i]
else
strName=arrName[arrName.length-1]
strName=strName.toLowerCase()
if(typeof arrProducts[strName]!='undefined')
{bProduct=true
arrProducts[strName].bActive=true}}
if(!bProduct||Zapatec.Utils.checkLinks()){return true;}
var strMsg='You are using the Free version of the Zapatec Software.\n'+'While using the Free version, a link to www.zapatec.com in this page is required.'
for(i in arrProducts)
if(arrProducts[i].bActive==true)
strMsg+='\nTo purchase the Zapatec '+arrProducts[i].name+' visit www.zapatec.com/website/main/products/'+arrProducts[i].webdir+'/'
alert(strMsg)
return false;}
Zapatec.Utils.clone=function(oSource){var oClone;if(!oSource&&typeof oSource=='object'){return null;}else if(typeof oSource=='undefined'){return oClone;}
if((oSource instanceof String)||(oSource instanceof Number)||(oSource instanceof Boolean)){oClone=new oSource.constructor(oSource.valueOf());}else{oClone=new oSource.constructor();}
for(var sProperty in oSource){if(typeof oSource[sProperty]=='object'){oClone[sProperty]=Zapatec.Utils.clone(oSource[sProperty],true);}else{oClone[sProperty]=oSource[sProperty];}}
return oClone;};Zapatec.is_opera=/opera/i.test(navigator.userAgent);Zapatec.is_ie=(/msie/i.test(navigator.userAgent)&&!Zapatec.is_opera);Zapatec.is_ie5=(Zapatec.is_ie&&/msie 5\.0/i.test(navigator.userAgent));Zapatec.is_ie7=(Zapatec.is_ie&&/msie 7\.0/i.test(navigator.userAgent));Zapatec.is_mac_ie=(/msie.*mac/i.test(navigator.userAgent)&&!Zapatec.is_opera);Zapatec.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Zapatec.is_konqueror=/Konqueror/i.test(navigator.userAgent);Zapatec.is_gecko=/Gecko/i.test(navigator.userAgent);Zapatec.is_webkit=/WebKit/i.test(navigator.userAgent);Zapatec.webkitVersion=Zapatec.is_webkit?parseInt(navigator.userAgent.replace(/.+WebKit\/([0-9]+)\..+/,"$1")):-1;if(!Object.prototype.hasOwnProperty){Object.prototype.hasOwnProperty=function(strProperty){try{var objPrototype=this.constructor.prototype;while(objPrototype){if(objPrototype[strProperty]==this[strProperty]){return false;}
objPrototype=objPrototype.prototype;}}catch(objException){}
return true;};}
if(!Function.prototype.call){Function.prototype.call=function(){var objThis=arguments[0];objThis._this_func=this;var arrArgs=[];for(var iArg=1;iArg<arguments.length;iArg++){arrArgs[arrArgs.length]='arguments['+iArg+']';}
var ret=eval('objThis._this_func('+arrArgs.join(',')+')');objThis._this_func=null;return ret;};}
if(!Function.prototype.apply){Function.prototype.apply=function(){var objThis=arguments[0];var objArgs=arguments[1];objThis._this_func=this;var arrArgs=[];if(objArgs){for(var iArg=0;iArg<objArgs.length;iArg++){arrArgs[arrArgs.length]='objArgs['+iArg+']';}}
var ret=eval('objThis._this_func('+arrArgs.join(',')+')');objThis._this_func=null;return ret;};}
if(!Array.prototype.pop){Array.prototype.pop=function(){var last;if(this.length){last=this[this.length-1];this.length-=1;}
return last;};}
if(!Array.prototype.push){Array.prototype.push=function(){for(var i=0;i<arguments.length;i++){this[this.length]=arguments[i];}
return this.length;};}
if(!Array.prototype.shift){Array.prototype.shift=function(){var first;if(this.length){first=this[0];for(var i=0;i<this.length-1;i++){this[i]=this[i+1];}
this.length-=1;}
return first;};}
if(!Array.prototype.unshift){Array.prototype.unshift=function(){if(arguments.length){var i,len=arguments.length;for(i=this.length+len-1;i>=len;i--){this[i]=this[i-len];}
for(i=0;i<len;i++){this[i]=arguments[i];}}
return this.length;};}
if(!Array.prototype.splice){Array.prototype.splice=function(index,howMany){var elements=[],removed=[],i;for(i=2;i<arguments.length;i++){elements.push(arguments[i]);}
for(i=index;(i<index+howMany)&&(i<this.length);i++){removed.push(this[i]);}
for(i=index+howMany;i<this.length;i++){this[i-howMany]=this[i];}
this.length-=removed.length;for(i=this.length+elements.length-1;i>=index+elements.length;i--){this[i]=this[i-elements.length];}
for(i=0;i<elements.length;i++){this[index+i]=elements[i];}
return removed;};}
Zapatec.Utils.arrIndexOf=function(arr,searchElement,fromIndex){if(Array.prototype.indexOf){return arr.indexOf(searchElement,fromIndex);}
if(!fromIndex){fromIndex=0;}
for(var iElement=fromIndex;iElement<arr.length;iElement++){if(arr[iElement]==searchElement){return iElement;}}
return-1;};Zapatec.Log=function(objArgs){if(!objArgs){return;}
var strMessage=objArgs.description;if(objArgs.severity){strMessage=objArgs.severity+':\n'+strMessage;}
if(objArgs.type!="warning"){alert(strMessage);}};Zapatec.Utils.Array={};Zapatec.Utils.Array.insertBefore=function(arr,el,key,nextKey){var tmp=new Array();for(var i in arr){if(i==nextKey){if(key){tmp[key]=el;}else{tmp.push(el);}}
tmp[i]=arr[i];}
return tmp;}
Zapatec.inherit=function(oSubClass,oSuperClass,oArg){var Inheritance=function(){};Inheritance.prototype=oSuperClass.prototype;oSubClass.prototype=new Inheritance();oSubClass.prototype.constructor=oSubClass;oSubClass.SUPERconstructor=oSuperClass;oSubClass.SUPERclass=oSuperClass.prototype;if(typeof oSuperClass.path!='undefined'){if(oArg&&oArg.keepPath){oSubClass.path=oSuperClass.path;}else{oSubClass.path=Zapatec.getPath(oSubClass.id);}}};Zapatec.getPath=function(sId){var sSrc;if(typeof sId=='string'){var oScript=document.getElementById(sId);if(oScript){sSrc=oScript.getAttribute('src');}}
if(!sSrc){if(typeof Zapatec.lastLoadedModule=='string'){return Zapatec.lastLoadedModule;}
if(document.documentElement){var sHtml=document.documentElement.innerHTML;var aMatch=sHtml.match(/<script[^>]+src=[^>]+>/gi);if(aMatch&&aMatch.length){sHtml=aMatch[aMatch.length-1];aMatch=sHtml.match(/src="([^"]+)/i);if(aMatch&&aMatch.length==2){sSrc=aMatch[1];}}}
if(!sSrc){return'';}}
sSrc=sSrc.replace(/\\/g,'/');var aTokens=sSrc.split('?');aTokens=aTokens[0].split('/');aTokens=aTokens.slice(0,-1);if(!aTokens.length){return'';}
return aTokens.join('/')+'/';};Zapatec.Utils.setWindowEvent=function(oEvent){if(oEvent){window.event=oEvent;}};Zapatec.Utils.emulateWindowEvent=function(aEventNames){if(document.addEventListener){for(var iEvent=0;iEvent<aEventNames.length;iEvent++){document.addEventListener(aEventNames[iEvent],Zapatec.Utils.setWindowEvent,true);}}};Zapatec.windowLoaded=typeof(document.readyState)!='undefined'?(document.readyState=='loaded'||document.readyState=='complete'):document.getElementsByTagName!=null&&typeof(document.getElementsByTagName('body')[0])!='undefined';Zapatec.Utils.addEvent(window,"load",function(){Zapatec.windowLoaded=true;});Zapatec.Utils.warnUnload=function(msg,win){Zapatec.Utils.warnUnloadFlag=true;if(typeof(msg)!="string"){msg="All your changes will be lost.";}
if(typeof(win)=='undefined'){win=window;}
Zapatec.Utils.addEvent(win,'beforeunload',function(ev){if(Zapatec.Utils.warnUnloadFlag!=true){return true;}
if(typeof(ev)=='undefined'){ev=window.event;}
ev.returnValue=msg;return false;});}
Zapatec.Utils.unwarnUnload=function(msg,win){Zapatec.Utils.warnUnloadFlag=false;}
Zapatec.Utils.warnUnloadFlag=false;Zapatec.Utils.getMaxZindex=function(){if(window.opera||Zapatec.is_khtml){return 2147483583;}else if(Zapatec.is_ie){return 2147483647;}else{return 10737418239;}};Zapatec.Utils.correctCssLength=function(val){if(typeof val=='undefined'||(typeof val=='object'&&!val)){return'auto';}
val+='';if(!val.length){return'auto';}
if(/\d$/.test(val)){val+='px';}
return val;};Zapatec.Utils.destroyOnUnload=[];Zapatec.Utils.addDestroyOnUnload=function(objElement,strProperty){Zapatec.Utils.destroyOnUnload.push([objElement,strProperty]);};Zapatec.Utils.createProperty=function(objElement,strProperty,val){objElement[strProperty]=val;Zapatec.Utils.addDestroyOnUnload(objElement,strProperty);};Zapatec.Utils.addEvent(window,'unload',function(){for(var iObj=Zapatec.Utils.destroyOnUnload.length-1;iObj>=0;iObj--){var objDestroy=Zapatec.Utils.destroyOnUnload[iObj];objDestroy[0][objDestroy[1]]=null;objDestroy[0]=null;}
for(var iLis=Zapatec.Utils.removeOnUnload.length-1;iLis>=0;iLis--){var oParams=Zapatec.Utils.removeOnUnload[iLis];if(!oParams){continue;}
Zapatec.Utils.removeOnUnload[iLis]=null;Zapatec.Utils.removeEvent(oParams['element'],oParams['event'],oParams['listener'],oParams['capture']);}});Zapatec.Utils.htmlEncode=function(str){str=str.replace(/&/ig,"&amp;");str=str.replace(/</ig,"&lt;");str=str.replace(/>/ig,"&gt;");str=str.replace(/\x22/ig,"&quot;");return str;};Zapatec.Utils.applyStyle=function(elRef,style){if(typeof(elRef)=='string'){elRef=document.getElementById(elRef);}
if(elRef==null||style==null||elRef.style==null){return null;}
if(Zapatec.is_opera){var pairs=style.split(";");for(var ii=0;ii<pairs.length;ii++){var kv=pairs[ii].split(":");if(!kv[1]){continue;}
var value=kv[1].replace(/^\s*/,'').replace(/\s*$/,'');var key="";for(var jj=0;jj<kv[0].length;jj++){if(kv[0].charAt(jj)=="-"){jj++;if(jj<kv[0].length){key+=kv[0].charAt(jj).toUpperCase();}
continue;}
key+=kv[0].charAt(jj);}
switch(key){case"float":key="cssFloat";break;}
try{elRef.style[key]=value;}catch(e){}}}else{elRef.style.cssText=style;}
return true;}
Zapatec.Utils.getStyleProperty=function(objElement,strProperty){if(document.defaultView&&document.defaultView.getComputedStyle){strProperty=strProperty.replace(/([A-Z])/g,'-$1').toLowerCase();var computedStyle=document.defaultView.getComputedStyle(objElement,'');if(computedStyle){return computedStyle.getPropertyValue(strProperty);}}else if(objElement.currentStyle){return objElement.currentStyle[strProperty];}
return objElement.style[strProperty];};Zapatec.Utils.getPrecision=function(dFloat){return(dFloat+'').replace(/^\d*\.*/,'').length;};Zapatec.Utils.setPrecision=function(dFloat,iPrecision){dFloat*=1;if(dFloat.toFixed){return(dFloat*1).toFixed(iPrecision)*1;}
var iPow=Math.pow(10,iPrecision);return parseInt(dFloat*iPow,10)/iPow;};Zapatec.Utils.setPrecisionString=function(dFloat,iPrecision){var sFloat=Zapatec.Utils.setPrecision(dFloat,iPrecision)+'';var iZeros=iPrecision-Zapatec.Utils.getPrecision(sFloat);for(var iZero=0;iZero<iZeros;iZero++){sFloat+='0';}
return sFloat;};Zapatec.Utils.createNestedHash=function(parent,keys,value){if(parent==null||keys==null){return null;}
var tmp=parent;for(var ii=0;ii<keys.length;ii++){if(typeof(tmp[keys[ii]])=='undefined'){tmp[keys[ii]]={};}
if(ii==keys.length-1&&typeof(value)!='undefined'){tmp[keys[ii]]=value;}
tmp=tmp[keys[ii]];}}
Zapatec.implement=function(classOrObject,interfaceStr){if(typeof interfaceStr!="string"){return false;}
if(typeof classOrObject=="function"){classOrObject=classOrObject.prototype;}
if(!classOrObject||typeof classOrObject!="object"){return false;}
var interfaceObj=window;var objs=interfaceStr.split(".");try{for(var i=0;i<objs.length;++i){interfaceObj=interfaceObj[objs[i]];}}catch(e){return false;}
if(typeof classOrObject.interfaces!="object"){classOrObject.interfaces={};classOrObject.interfaces[interfaceStr]=true;}else if(classOrObject.interfaces[interfaceStr]!==true){classOrObject.interfaces=Zapatec.Utils.clone(classOrObject.interfaces);classOrObject.interfaces[interfaceStr]=true;}else{return true;}
for(var iProp in interfaceObj){classOrObject[iProp]=interfaceObj[iProp];}
classOrObject.hasInterface=function(interfaceStr){if(this.interfaces[interfaceStr]===true){return true;}
return false;}
return true;};Zapatec.Utils.getCharFromEvent=function(evt){if(!evt){evt=window.event;}
var response={};if(Zapatec.is_gecko&&!Zapatec.is_khtml&&evt.type!="keydown"&&evt.type!="keyup"){if(evt.charCode){response.chr=String.fromCharCode(evt.charCode);}else{response.charCode=evt.keyCode;}}else{response.charCode=evt.keyCode||evt.which;response.chr=String.fromCharCode(response.charCode);}
if(Zapatec.is_opera&&response.charCode==0){response.charCode=null;response.chr=null;}
if(Zapatec.is_khtml&&response.charCode==63272){response.charCode=46;response.chr=null;}
return response;}
if(typeof Zapatec=='undefined'){Zapatec=function(){};}
Zapatec.Transport=function(){};if(typeof ActiveXObject!='undefined'){Zapatec.Transport.XMLDOM=null;Zapatec.Transport.XMLHTTP=null;Zapatec.Transport.pickActiveXVersion=function(aVersions){for(var iVn=0;iVn<aVersions.length;iVn++){try{var oDoc=new ActiveXObject(aVersions[iVn]);if(oDoc){return aVersions[iVn];}}catch(oExpn){};}
return null;};Zapatec.Transport.XMLDOM=Zapatec.Transport.pickActiveXVersion(['Msxml2.DOMDocument.4.0','Msxml2.DOMDocument.3.0','MSXML2.DOMDocument','MSXML.DOMDocument','Microsoft.XMLDOM']);Zapatec.Transport.XMLHTTP=Zapatec.Transport.pickActiveXVersion(['Msxml2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP']);Zapatec.Transport.pickActiveXVersion=null;}
Zapatec.Transport.createXmlHttpRequest=function(){if(typeof ActiveXObject!='undefined'){try{return new ActiveXObject(Zapatec.Transport.XMLHTTP);}catch(oExpn){};}
if(typeof XMLHttpRequest!='undefined'){return new XMLHttpRequest();}
return null;};Zapatec.Transport.isBusy=function(oArg){var oContr=oArg.busyContainer;if(typeof oContr=='string'){oContr=document.getElementById(oContr);}
if(!oContr){return;}
var sImage=oArg.busyImage;if(typeof sImage!='string'){sImage='';}
sImage=sImage.split('/').pop();if(!sImage.length){sImage='zpbusy.gif';}
var oFC=oContr.firstChild;if(oFC){oFC=oFC.firstChild;if(oFC){oFC=oFC.firstChild;if(oFC&&oFC.tagName&&oFC.tagName.toLowerCase()=='img'){var sSrc=oFC.getAttribute('src');if(typeof sSrc=='string'&&sSrc.length){sSrc=sSrc.split('/').pop();if(sSrc==sImage){return true;}}}}}
return false;};Zapatec.Transport.showBusy=function(oArg){if(Zapatec.Transport.isBusy(oArg)){return;}
var oContr=oArg.busyContainer;if(typeof oContr=='string'){oContr=document.getElementById(oContr);}
if(!oContr){return;}
var sImage=oArg.busyImage;var sImageWidth=oArg.busyImageWidth;var sImageHeight=oArg.busyImageHeight;if(typeof sImage!='string'||!sImage.length){sImage='zpbusy.gif';}else{if(typeof sImageWidth=='number'||(typeof sImageWidth=='string'&&/\d$/.test(sImageWidth))){sImageWidth+='px';}
if(typeof sImageHeight=='number'||(typeof sImageHeight=='string'&&/\d$/.test(sImageHeight))){sImageHeight+='px';}}
if(!sImageWidth){sImageWidth='65px';}
if(!sImageHeight){sImageHeight='35px';}
var sPath='';if(sImage.indexOf('/')<0){if(Zapatec.zapatecPath){sPath=Zapatec.zapatecPath;}else{sPath=Zapatec.Transport.getPath('transport.js');}}
var aImg=[];aImg.push('<img src="');aImg.push(sPath);aImg.push(sImage);aImg.push('"');if(sImageWidth||sImageHeight){aImg.push(' style="');if(sImageWidth){aImg.push('width:');aImg.push(sImageWidth);aImg.push(';');}
if(sImageHeight){aImg.push('height:');aImg.push(sImageHeight);}
aImg.push('"');}
aImg.push(' />');var iContainerWidth=oContr.offsetWidth;var iContainerHeight=oContr.offsetHeight;var oBusyContr=Zapatec.Utils.createElement('div');oBusyContr.style.position='relative';oBusyContr.style.zIndex=2147483583;var oBusy=Zapatec.Utils.createElement('div',oBusyContr);oBusy.style.position='absolute';oBusy.innerHTML=aImg.join('');if(oContr.firstChild){oContr.insertBefore(oBusyContr,oContr.firstChild);}else{oContr.appendChild(oBusyContr);}
var iBusyWidth=oBusy.offsetWidth;var iBusyHeight=oBusy.offsetHeight;if(iContainerWidth>iBusyWidth){oBusy.style.left=oContr.scrollLeft+
(iContainerWidth-iBusyWidth)/2+'px';}
if(iContainerHeight>iBusyHeight){oBusy.style.top=oContr.scrollTop+
(iContainerHeight-iBusyHeight)/2+'px';}};Zapatec.Transport.removeBusy=function(oArg){var oContr=oArg.busyContainer;if(typeof oContr=='string'){oContr=document.getElementById(oContr);}
if(!oContr){return;}
if(Zapatec.Transport.isBusy(oArg)){oContr.removeChild(oContr.firstChild);}};Zapatec.Transport.fetch=function(oArg){if(oArg==null||typeof oArg!='object'){return null;}
if(!oArg.url){return null;}
if(!oArg.method){oArg.method='GET';}
if(typeof oArg.async=='undefined'){oArg.async=true;}
if(!oArg.contentType&&oArg.method.toUpperCase()=='POST'){oArg.contentType='application/x-www-form-urlencoded';}
if(!oArg.content){oArg.content=null;}
if(!oArg.onLoad){oArg.onLoad=null;}
if(!oArg.onError){oArg.onError=null;}
var oRequest=Zapatec.Transport.createXmlHttpRequest();if(oRequest==null){return null;}
Zapatec.Transport.showBusy(oArg);var bErrorDisplayed=false;var funcOnReady=function(){Zapatec.Transport.removeBusy(oArg);try{if(oRequest.status==200||oRequest.status==304||(location.protocol=='file:'&&!oRequest.status)){if(typeof oArg.onLoad=='function'){oArg.onLoad(oRequest);}}else if(!bErrorDisplayed){bErrorDisplayed=true;Zapatec.Transport.displayError(oRequest.status,"Error: Can't fetch "+oArg.url+'.\n'+
(oRequest.statusText||''),oArg.onError);}}catch(oExpn){if(!bErrorDisplayed){bErrorDisplayed=true;if(oExpn.name&&oExpn.name=='NS_ERROR_NOT_AVAILABLE'){Zapatec.Transport.displayError(0,"Error: Can't fetch "+oArg.url+'.\nFile not found.',oArg.onError);}else{Zapatec.Transport.displayError(0,"Error: Can't fetch "+oArg.url+'.\n'+
(oExpn.message||''),oArg.onError);}}};};try{if(typeof oArg.username!='undefined'&&typeof oArg.password!='undefined'){oRequest.open(oArg.method,oArg.url,oArg.async,oArg.username,oArg.password);}else{oRequest.open(oArg.method,oArg.url,oArg.async);}
if(oArg.async){oRequest.onreadystatechange=function(){if(oRequest.readyState==4){funcOnReady();oRequest.onreadystatechange={};}};}
if(oArg.contentType){oRequest.setRequestHeader('Content-Type',oArg.contentType);}
oRequest.send(oArg.content);if(!oArg.async){funcOnReady();return oRequest;}}catch(oExpn){Zapatec.Transport.removeBusy(oArg);if(!bErrorDisplayed){bErrorDisplayed=true;if(oExpn.name&&oExpn.name=='NS_ERROR_FILE_NOT_FOUND'){Zapatec.Transport.displayError(0,"Error: Can't fetch "+oArg.url+'.\nFile not found.',oArg.onError);}else{Zapatec.Transport.displayError(0,"Error: Can't fetch "+oArg.url+'.\n'+
(oExpn.message||''),oArg.onError);}}};return null;};Zapatec.Transport.parseHtml=function(sHtml){sHtml+='';sHtml=sHtml.replace(/^\s+/g,'');var oTmpContr;if(document.createElementNS){oTmpContr=document.createElementNS('http://www.w3.org/1999/xhtml','div');}else{oTmpContr=document.createElement('div');}
oTmpContr.innerHTML=sHtml;return oTmpContr;};Zapatec.Transport.evalGlobalScope=function(sScript){if(typeof sScript!='string'||!sScript.match(/\S/)){return;}
if(window.execScript){window.execScript(sScript,'javascript');}else if(window.eval){window.eval(sScript);}};Zapatec.Transport.setInnerHtml=function(oArg){if(!oArg||typeof oArg.html!='string'){return;}
var sHtml=oArg.html;var oContr=null;if(typeof oArg.container=='string'){oContr=document.getElementById(oArg.container);}else if(typeof oArg.container=='object'){oContr=oArg.container;}
var aScripts=[];if(sHtml.match(/<\s*\/\s*script\s*>/i)){var aTokens=sHtml.split(/<\s*\/\s*script\s*>/i);var aHtml=[];for(var iToken=aTokens.length-1;iToken>=0;iToken--){var sToken=aTokens[iToken];if(sToken.match(/\S/)){var aMatch=sToken.match(/<\s*script([^>]*)>/i);if(aMatch){var aCouple=sToken.split(/<\s*script[^>]*>/i);while(aCouple.length<2){if(sToken.match(/^<\s*script[^>]*>/i)){aCouple.unshift('');}else{aCouple.push('');}}
aHtml.unshift(aCouple[0]);var sAttrs=aMatch[1];var srtScript=aCouple[1];if(sAttrs.match(/\s+src\s*=/i)){srtScript='';}else{srtScript=srtScript.replace(/function\s+([^(]+)/g,'$1=function');}
aScripts.push([sAttrs,srtScript]);}else if(iToken<aTokens.length-1){aTokens[iToken-1]+='</script>'+sToken;}else{aHtml.unshift(sToken);}}else{aHtml.unshift(sToken);}}
sHtml=aHtml.join('');}
if(oContr){if(window.opera){oContr.innerHTML='<form></form>';}
oContr.innerHTML=sHtml;}
for(var iScript=0;iScript<aScripts.length;iScript++){if(aScripts[iScript][1].length){Zapatec.Transport.evalGlobalScope(aScripts[iScript][1]);}
var sAttrs=aScripts[iScript][0];sAttrs=sAttrs.replace(/\s+/g,' ').replace(/^\s/,'').replace(/\s$/,'').replace(/ = /g,'=');if(sAttrs.indexOf('src=')>=0){var oContr=document.body;if(!oContr){oContr=document.getElementsByTagName('head')[0];if(!oContr){oContr=document;}}
var aAttrs=sAttrs.split(' ');var oScript=Zapatec.Utils.createElement('script');for(var iAttr=0;iAttr<aAttrs.length;iAttr++){var aAttr=aAttrs[iAttr].split('=');if(aAttr.length>1){oScript.setAttribute(aAttr[0],aAttr[1].match(/^[\s|"|']*([\s|\S]*[^'|"])[\s|"|']*$/)[1]);}else{oScript.setAttribute(aAttr[0],aAttr[0]);}}
oContr.appendChild(oScript);}}};Zapatec.Transport.fetchXmlDoc=function(oArg){if(oArg==null||typeof oArg!='object'){return null;}
if(!oArg.url){return null;}
if(typeof oArg.async=='undefined'){oArg.async=true;}
if(!oArg.onLoad){oArg.onLoad=null;}
if(!oArg.onError){oArg.onError=null;}
if(!oArg.method&&typeof oArg.username=='undefined'&&typeof oArg.password=='undefined'){if(document.implementation&&document.implementation.createDocument){var oDoc=null;if(!oArg.reliable){oArg.reliable=false;}
var oFetchArg={};for(var sKey in oArg){oFetchArg[sKey]=oArg[sKey];}
if(oArg.async){oFetchArg.onLoad=function(oRequest){oFetchArg.onLoad=null;var parser=new DOMParser();oDoc=parser.parseFromString(oRequest.responseText,"text/xml");Zapatec.Transport.removeBusy(oArg);Zapatec.Transport.onXmlDocLoad(oDoc,oArg.onLoad,oArg.onError);};}else{oFetchArg.onLoad=null;}
var oRequest=Zapatec.Transport.fetch(oFetchArg);if(!oArg.async&&oRequest){var parser=new DOMParser();oDoc=parser.parseFromString(oRequest.responseText,"text/xml");Zapatec.Transport.removeBusy(oArg);Zapatec.Transport.onXmlDocLoad(oDoc,oArg.onLoad,oArg.onError);return oDoc;}
return null;}
if(typeof ActiveXObject!='undefined'){Zapatec.Transport.showBusy(oArg);try{var oDoc=new ActiveXObject(Zapatec.Transport.XMLDOM);oDoc.async=oArg.async;if(oArg.async){oDoc.onreadystatechange=function(){if(oDoc.readyState==4){Zapatec.Transport.removeBusy(oArg);Zapatec.Transport.onXmlDocLoad(oDoc,oArg.onLoad,oArg.onError);oDoc.onreadystatechange={};}};}
oDoc.load(oArg.url);if(!oArg.async){Zapatec.Transport.removeBusy(oArg);Zapatec.Transport.onXmlDocLoad(oDoc,oArg.onLoad,oArg.onError);return oDoc;}
return null;}catch(oExpn){Zapatec.Transport.removeBusy(oArg);};}}
var oFetchArg={};for(var sKey in oArg){oFetchArg[sKey]=oArg[sKey];}
if(oArg.async){oFetchArg.onLoad=function(oRequest){Zapatec.Transport.parseXml({strXml:oRequest.responseText,onLoad:oArg.onLoad,onError:oArg.onError});};}else{oFetchArg.onLoad=null;}
var oRequest=Zapatec.Transport.fetch(oFetchArg);if(!oArg.async&&oRequest){return Zapatec.Transport.parseXml({strXml:oRequest.responseText,onLoad:oArg.onLoad,onError:oArg.onError});}
return null;};Zapatec.Transport.parseXml=function(oArg){if(oArg==null||typeof oArg!='object'){return null;}
if(!oArg.strXml){return null;}
if(!oArg.onLoad){oArg.onLoad=null;}
if(!oArg.onError){oArg.onError=null;}
if(window.DOMParser){try{var oDoc=(new DOMParser()).parseFromString(oArg.strXml,'text/xml');Zapatec.Transport.onXmlDocLoad(oDoc,oArg.onLoad,oArg.onError);return oDoc;}catch(oExpn){Zapatec.Transport.displayError(0,"Error: Can't parse.\n"+'String does not appear to be a valid XML fragment.',oArg.onError);};return null;}
if(typeof ActiveXObject!='undefined'){try{var oDoc=new ActiveXObject(Zapatec.Transport.XMLDOM);oDoc.loadXML(oArg.strXml);Zapatec.Transport.onXmlDocLoad(oDoc,oArg.onLoad,oArg.onError);return oDoc;}catch(oExpn){};}
return null;};Zapatec.Transport.onXmlDocLoad=function(oDoc,onLoad,onError){var sError=null;if(oDoc.parseError){sError=oDoc.parseError.reason;if(oDoc.parseError.srcText){sError+='Location: '+oDoc.parseError.url+'\nLine number '+oDoc.parseError.line+', column '+
oDoc.parseError.linepos+':\n'+
oDoc.parseError.srcText+'\n';}}else if(oDoc.documentElement&&oDoc.documentElement.tagName=='parsererror'){sError=oDoc.documentElement.firstChild.data+'\n'+
oDoc.documentElement.firstChild.nextSibling.firstChild.data;}else if(!oDoc.documentElement){sError='String does not appear to be a valid XML fragment.';}
if(sError){Zapatec.Transport.displayError(0,"Error: Can't parse.\n"+sError,onError);}else{if(typeof onLoad=='function'){onLoad(oDoc);}}};Zapatec.Transport.serializeXmlDoc=function(oDoc){if(window.XMLSerializer){return(new XMLSerializer).serializeToString(oDoc);}
if(oDoc.xml){return oDoc.xml;}};Zapatec.Transport.fetchJsonObj=function(oArg){if(oArg==null||typeof oArg!='object'){return null;}
if(!oArg.url){return null;}
if(typeof oArg.async=='undefined'){oArg.async=true;}
if(!oArg.reliable){oArg.reliable=false;}
var oFetchArg={};for(var sKey in oArg){oFetchArg[sKey]=oArg[sKey];}
if(oArg.async){oFetchArg.onLoad=function(oRequest){Zapatec.Transport.parseJson({strJson:oRequest.responseText,reliable:oArg.reliable,onLoad:oArg.onLoad,onError:oArg.onError});};}else{oFetchArg.onLoad=null;}
var oRequest=Zapatec.Transport.fetch(oFetchArg);if(!oArg.async&&oRequest){return Zapatec.Transport.parseJson({strJson:oRequest.responseText,reliable:oArg.reliable,onLoad:oArg.onLoad,onError:oArg.onError});}
return null;};Zapatec.Transport.parseJson=function(oArg){if(oArg==null||typeof oArg!='object'){return null;}
if(!oArg.reliable){oArg.reliable=false;}
if(!oArg.onLoad){oArg.onLoad=null;}
if(!oArg.onError){oArg.onError=null;}
var oJson=null;try{if(oArg.reliable){if(oArg.strJson){oJson=eval('('+oArg.strJson+')');}}else{oJson=Zapatec.Transport.parseJsonStr(oArg.strJson);}}catch(oExpn){var sError="Error: Can't parse.\nString doesn't appear to be a valid JSON fragment: ";sError+=oExpn.message;if(typeof oExpn.text!='undefined'&&oExpn.text.length){sError+='\n'+oExpn.text;}
sError+='\n'+oArg.strJson;Zapatec.Transport.displayError(0,sError,oArg.onError);return null;};if(typeof oArg.onLoad=='function'){oArg.onLoad(oJson);}
return oJson;};Zapatec.Transport.parseJsonStr=function(text){var p=/^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,token,operator;function error(m,t){throw{name:'JSONError',message:m,text:t||operator||token};}
function next(b){if(b&&b!=operator){error("Expected '"+b+"'");}
if(text){var t=p.exec(text);if(t){if(t[2]){token=null;operator=t[2];}else{operator=null;try{token=eval(t[1]);}catch(e){error("Bad token",t[1]);}}
text=text.substring(t[0].length);}else{error("Unrecognized token",text);}}else{token=operator=null;}}
function val(){var k,o;switch(operator){case'{':next('{');o={};if(operator!='}'){for(;;){if(operator||typeof token!='string'){error("Missing key");}
k=token;next();next(':');o[k]=val();if(operator!=','){break;}
next(',');}}
next('}');return o;case'[':next('[');o=[];if(operator!=']'){for(;;){o.push(val());if(operator!=','){break;}
next(',');}}
next(']');return o;default:if(operator!==null){error("Missing value");}
k=token;next();return k;}}
next();return val();};Zapatec.Transport.serializeJsonObj=function(v){var a=[];function e(s){a[a.length]=s;}
function g(x){var c,i,l,v;switch(typeof x){case'object':if(x){if(x instanceof Array){e('[');l=a.length;for(i=0;i<x.length;i+=1){v=x[i];if(typeof v!='undefined'&&typeof v!='function'){if(l<a.length){e(',');}
g(v);}}
e(']');return;}else if(typeof x.toString!='undefined'){e('{');l=a.length;for(i in x){v=x[i];if(x.hasOwnProperty(i)&&typeof v!='undefined'&&typeof v!='function'){if(l<a.length){e(',');}
g(i);e(':');g(v);}}
return e('}');}}
e('null');return;case'number':e(isFinite(x)?+x:'null');return;case'string':l=x.length;e('"');for(i=0;i<l;i+=1){c=x.charAt(i);if(c>=' '){if(c=='\\'||c=='"'){e('\\');}
e(c);}else{switch(c){case'\b':e('\\b');break;case'\f':e('\\f');break;case'\n':e('\\n');break;case'\r':e('\\r');break;case'\t':e('\\t');break;default:c=c.charCodeAt();e('\\u00'+Math.floor(c/16).toString(16)+
(c%16).toString(16));}}}
e('"');return;case'boolean':e(String(x));return;default:e('null');return;}}
g(v);return a.join('');};Zapatec.Transport.displayError=function(iErrCode,sError,onError){if(typeof onError=='function'){onError({errorCode:iErrCode,errorDescription:sError});}else{alert(sError);}};Zapatec.Transport.translateUrl=function(oArg){if(!oArg||!oArg.url){return null;}
var aFullUrl=oArg.url.split('?',2);var sUrl=aFullUrl[0];if(sUrl.charAt(0)=='/'||sUrl.indexOf(':')>=0){return oArg.url;}
var sRelativeTo;if(typeof oArg.relativeTo!='string'){sRelativeTo=document.location.toString().split('?',2)[0];}else{sRelativeTo=oArg.relativeTo.split('?',2)[0];if(sRelativeTo.indexOf('/')<0){sRelativeTo=document.location.toString().split('?',2)[0];}else if(sRelativeTo.charAt(0)!='/'&&sRelativeTo.indexOf(':')<0){sRelativeTo=Zapatec.Transport.translateUrl({url:sRelativeTo});}}
var aUrl=sUrl.split('/');var aRelativeTo=sRelativeTo.split('/');aRelativeTo.pop();for(var iToken=0;iToken<aUrl.length;iToken++){var sToken=aUrl[iToken];if(sToken=='..'){aRelativeTo.pop();}else if(sToken!='.'){aRelativeTo.push(sToken);}}
aFullUrl[0]=aRelativeTo.join('/');return aFullUrl.join('?');};Zapatec.Transport.loading={};Zapatec.Transport.setupEvents=function(oArg){if(!oArg){return{};}
if(oArg.force||!Zapatec.EventDriven||!oArg.url){return{onLoad:oArg.onLoad,onError:oArg.onError};}
var sUrl=oArg.url;if(typeof oArg.onLoad=='function'){Zapatec.EventDriven.addEventListener('zpTransportOnLoad'+sUrl,oArg.onLoad);}
if(typeof oArg.onError=='function'){Zapatec.EventDriven.addEventListener('zpTransportOnError'+sUrl,oArg.onError);}
if(Zapatec.Transport.loading[sUrl]){return{loading:true};}else{Zapatec.Transport.loading[sUrl]=true;return{onLoad:new Function("Zapatec.EventDriven.fireEvent('zpTransportOnLoad"+
sUrl+"');Zapatec.EventDriven.removeEvent('zpTransportOnLoad"+
sUrl+"');Zapatec.EventDriven.removeEvent('zpTransportOnError"+
sUrl+"');Zapatec.Transport.loading['"+sUrl+"'] = false;"),onError:new Function('oError',"Zapatec.EventDriven.fireEvent('zpTransportOnError"+
sUrl+"',oError);Zapatec.EventDriven.removeEvent('zpTransportOnLoad"+
sUrl+"');Zapatec.EventDriven.removeEvent('zpTransportOnError"+
sUrl+"');Zapatec.Transport.loading['"+sUrl+"'] = false;")};}};Zapatec.Transport.loadedJS={};Zapatec.Transport.isLoadedJS=function(sUrl,sAbsUrl){if(typeof sAbsUrl=='undefined'){sAbsUrl=Zapatec.Transport.translateUrl({url:sUrl});}
if(Zapatec.Transport.loadedJS[sAbsUrl]){return true;}
var aScripts=document.getElementsByTagName('script');for(var iScript=0;iScript<aScripts.length;iScript++){var sSrc=aScripts[iScript].getAttribute('src')||'';if(sSrc==sUrl){Zapatec.Transport.loadedJS[sAbsUrl]=true;return true;}}
return false;};Zapatec.Transport.getPath=function(sScriptFileName){var aScripts=document.getElementsByTagName('script');for(var iScript=aScripts.length-1;iScript>=0;iScript--){var sSrc=aScripts[iScript].getAttribute('src')||'';var aTokens=sSrc.split('/');var sLastToken=aTokens.pop();if(sLastToken==sScriptFileName){return aTokens.length?aTokens.join('/')+'/':'';}}
for(var sSrc in Zapatec.Transport.loadedJS){var aTokens=sSrc.split('/');var sLastToken=aTokens.pop();if(sLastToken==sScriptFileName){return aTokens.length?aTokens.join('/')+'/':'';}}
return'';};Zapatec.Transport.include=function(sSrc,sId,bForce){if(Zapatec.doNotInclude){return;}
var sAbsUrl=Zapatec.Transport.translateUrl({url:sSrc});if(!bForce&&Zapatec.Transport.isLoadedJS(sSrc,sAbsUrl)){return;}
document.write('<script type="text/javascript" src="'+sSrc+
(typeof sId=='string'?'" id="'+sId:'')+'"></script>');Zapatec.Transport.loadedJS[sAbsUrl]=true;};Zapatec.include=Zapatec.Transport.include;Zapatec.Transport.includeJS=function(sSrc,sId){setTimeout(function(){var oContr=document.body;if(!oContr){oContr=document.getElementsByTagName('head')[0];if(!oContr){oContr=document;}}
var oScript=document.createElement('script');oScript.type='text/javascript';oScript.src=sSrc;if(typeof sId=='string'){oScript.id=sId;}
oContr.appendChild(oScript);},0);};Zapatec.Transport.loadJS=function(oArg){if(!(oArg instanceof Object)){return;}
if(typeof oArg.async=='undefined'){oArg.async=true;}
var sUrl=null;if(oArg.url){sUrl=oArg.url;}else if(oArg.module){var sPath='';if(typeof oArg.path!='undefined'){sPath=oArg.path;}else if(typeof Zapatec.zapatecPath!='undefined'){sPath=Zapatec.zapatecPath;}
sUrl=sPath+oArg.module+'.js';}else{return;}
var sAbsUrl=Zapatec.Transport.translateUrl({url:sUrl});if(!oArg.onLoad){oArg.onLoad=null;}
if(!oArg.onError){oArg.onError=null;}
if(Zapatec.doNotInclude||(!oArg.force&&Zapatec.Transport.isLoadedJS(sUrl,sAbsUrl))){if(typeof oArg.onLoad=='function'){oArg.onLoad();}
return;}
var oHandlers=Zapatec.Transport.setupEvents({url:sAbsUrl,force:oArg.force,onLoad:oArg.onLoad,onError:oArg.onError});if(oHandlers.loading){return;}
Zapatec.Transport.fetch({url:sUrl,async:oArg.async,onLoad:function(oRequest){if(oArg.force||!Zapatec.Transport.loadedJS[sAbsUrl]){var aTokens=sUrl.split('/');var sLastToken=aTokens.pop();Zapatec.lastLoadedModule=aTokens.join('/')+'/';Zapatec.Transport.evalGlobalScope(oRequest.responseText);Zapatec.lastLoadedModule=null;Zapatec.Transport.loadedJS[sAbsUrl]=true;}
if(typeof oHandlers.onLoad=='function'){oHandlers.onLoad();}},onError:oHandlers.onError});};Zapatec.Transport.includeCSS=function(sHref){var oContr=document.getElementsByTagName('head')[0];if(!oContr){return;}
var oLink=document.createElement('link');oLink.setAttribute('rel','stylesheet');oLink.setAttribute('type','text/css');oLink.setAttribute('href',sHref);oContr.appendChild(oLink);};Zapatec.Transport.loadedCss={};Zapatec.Transport.loadCss=function(oArg){if(Zapatec.StyleSheet){Zapatec.Transport.loadCssWithStyleSheet(oArg);}else{Zapatec.Transport.loadJS({module:'stylesheet',async:oArg.async,onLoad:function(){Zapatec.Transport.loadCssWithStyleSheet(oArg);}});}};Zapatec.Transport.loadCssWithStyleSheet=function(oArg){if(!(oArg instanceof Object)){return;}
if(!oArg.url){return;}
if(typeof oArg.async=='undefined'){oArg.async=true;}
var sAbsUrl=Zapatec.Transport.translateUrl({url:oArg.url});if(!oArg.force){if(Zapatec.Transport.loadedCss[sAbsUrl]){if(typeof oArg.onLoad=='function'){oArg.onLoad();}
return;}
var aLinks=document.getElementsByTagName('link');for(var iLnk=0;iLnk<aLinks.length;iLnk++){var sHref=aLinks[iLnk].getAttribute('href')||'';sHref=Zapatec.Transport.translateUrl({url:sHref});if(sHref==sAbsUrl){Zapatec.Transport.loadedCss[sAbsUrl]=true;if(typeof oArg.onLoad=='function'){oArg.onLoad();}
return;}}}
var oHandlers=Zapatec.Transport.setupEvents({url:sAbsUrl,force:oArg.force,onLoad:oArg.onLoad,onError:oArg.onError});if(oHandlers.loading){return;}
Zapatec.Transport.fetch({url:oArg.url,async:oArg.async,onLoad:function(oRequest){var sCss=oRequest.responseText;var aResultCss=[];var aImgUrls=[];var aCssUrls=[];var iPos=0;var iNextPos=sCss.indexOf('url(',iPos);while(iNextPos>=0){iNextPos+=4;var sToken=sCss.substring(iPos,iNextPos);var bIsImport=/@import\s+url\($/.test(sToken);aResultCss.push(sToken);iPos=iNextPos;iNextPos=sCss.indexOf(')',iPos);if(iNextPos>=0){var sImgUrl=sCss.substring(iPos,iNextPos);sImgUrl=sImgUrl.replace(/['"]/g,'');sImgUrl=Zapatec.Transport.translateUrl({url:sImgUrl,relativeTo:oArg.url});sImgUrl=Zapatec.Transport.translateUrl({url:sImgUrl});aResultCss.push(sImgUrl);if(bIsImport){aCssUrls.push(sImgUrl);}else{aImgUrls.push(sImgUrl);}
iPos=iNextPos;iNextPos=sCss.indexOf('url(',iPos);}}
aResultCss.push(sCss.substr(iPos));sCss=aResultCss.join('');Zapatec.Transport.loadCssList({urls:aCssUrls,async:oArg.async,onLoad:function(){(new Zapatec.StyleSheet()).addParse(sCss);if(typeof oHandlers.onLoad=='function'){oHandlers.onLoad();}}});Zapatec.Transport.loadedCss[sAbsUrl]=true;Zapatec.Transport.preloadImages({urls:aImgUrls,timeout:60000});},onError:oHandlers.onError});};Zapatec.Transport.loadCssList=function(oArg){if(!(oArg instanceof Object)){return;}
if(typeof oArg.async=='undefined'){oArg.async=true;}
if(!oArg.onLoad){oArg.onLoad=null;}
if(!oArg.onError){oArg.onError=null;}
if(!oArg.urls||!oArg.urls.length){if(typeof oArg.onLoad=='function'){oArg.onLoad();}
return;}
var sUrl=oArg.urls.shift();var funcOnLoad=function(){Zapatec.Transport.loadCssList({urls:oArg.urls,async:oArg.async,force:oArg.force,onLoad:oArg.onLoad,onError:oArg.onError});};Zapatec.Transport.loadCss({url:sUrl,async:oArg.async,force:oArg.force,onLoad:funcOnLoad,onError:function(oError){Zapatec.Transport.displayError(oError.errorCode,oError.errorDescription,oArg.onError);funcOnLoad();}});};Zapatec.Transport.imagePreloads=[];Zapatec.Transport.preloadImages=function(oArg){if(Zapatec.PreloadImages){Zapatec.Transport.imagePreloads.push(new Zapatec.PreloadImages(oArg));}else{Zapatec.Transport.loadJS({module:'preloadimages',onLoad:function(){Zapatec.Transport.imagePreloads.push(new Zapatec.PreloadImages(oArg));}});}};if(typeof Zapatec=='undefined'){Zapatec=function(){};}
Zapatec.StyleSheet=function(bUseLast){if(bUseLast){if(document.createStyleSheet){if(document.styleSheets.length){this.styleSheet=document.styleSheets[document.styleSheets.length-1];}}else{var aStyleSheets=document.getElementsByTagName('style');if(aStyleSheets.length){this.styleSheet=aStyleSheets[aStyleSheets.length-1];}}}
if(!this.styleSheet){if(document.createStyleSheet){try{this.styleSheet=document.createStyleSheet();}catch(oException){this.styleSheet=document.styleSheets[document.styleSheets.length-1];};}else{this.styleSheet=document.createElement('style');this.styleSheet.type='text/css';var oHead=document.getElementsByTagName('head')[0];if(!oHead){oHead=document.documentElement;}
if(oHead){oHead.appendChild(this.styleSheet);}}}};Zapatec.StyleSheet.prototype.addRule=function(strSelector,strDeclarations){if(!this.styleSheet){return;}
if(document.createStyleSheet){this.styleSheet.cssText+=strSelector+' { '+strDeclarations+' }';}else{this.styleSheet.appendChild(document.createTextNode(strSelector+' { '+strDeclarations+' }'));}};Zapatec.StyleSheet.prototype.removeRules=function(){if(!this.styleSheet){return;}
if(document.createStyleSheet){var iRules=this.styleSheet.rules.length;for(var iRule=0;iRule<iRules;iRule++){this.styleSheet.removeRule();}}else{while(this.styleSheet.firstChild){this.styleSheet.removeChild(this.styleSheet.firstChild);}}};Zapatec.StyleSheet.prototype.addParse=function(strStyleSheet){var arrClean=[];var arrTokens=strStyleSheet.split('/*');for(var iTok=0;iTok<arrTokens.length;iTok++){var arrTails=arrTokens[iTok].split('*/');arrClean.push(arrTails[arrTails.length-1]);}
strStyleSheet=arrClean.join('');strStyleSheet=strStyleSheet.replace(/@[^{]*;/g,'');var arrStyles=strStyleSheet.split('}');for(var iStl=0;iStl<arrStyles.length;iStl++){var arrRules=arrStyles[iStl].split('{');if(arrRules[0]&&arrRules[1]){var arrSelectors=arrRules[0].split(',');for(var iSel=0;iSel<arrSelectors.length;iSel++){this.addRule(arrSelectors[iSel],arrRules[1]);}}}};Zapatec.ImagePreloader=function(objArgs){this.job=null;this.image=null;if(arguments.length>0)this.init(objArgs);};Zapatec.ImagePreloader.prototype.init=function(objArgs){if(!objArgs||!objArgs.job){return;}
this.job=objArgs.job;this.image=new Image();this.job.images.push(this.image);var objPreloader=this;this.image.onload=function(){objPreloader.job.loadedUrls.push(objArgs.url);setTimeout(function(){objPreloader.onLoad();},0);};this.image.onerror=function(){objPreloader.job.invalidUrls.push(objArgs.url);objPreloader.onLoad();};this.image.onabort=function(){objPreloader.job.abortedUrls.push(objArgs.url);objPreloader.onLoad();};this.image.src=objArgs.url;if(typeof objArgs.timeout=='number'){setTimeout(function(){if(objPreloader.job){if(objPreloader.image.complete){objPreloader.job.loadedUrls.push(objArgs.url);}else{objPreloader.job.abortedUrls.push(objArgs.url);}
objPreloader.onLoad();}},objArgs.timeout);}};Zapatec.ImagePreloader.prototype.onLoad=function(){if(!this.job){return;}
this.image.onload=null;this.image.onerror=null;this.image.onabort=null;var objJob=this.job;this.job=null;objJob.leftToLoad--;if(objJob.leftToLoad==0&&typeof objJob.onLoad=='function'){var funcOnLoad=objJob.onLoad;objJob.onLoad=null;funcOnLoad(objJob);}};Zapatec.PreloadImages=function(objArgs){this.images=[];this.leftToLoad=0;this.loadedUrls=[];this.invalidUrls=[];this.abortedUrls=[];this.onLoad=null;if(arguments.length>0)this.init(objArgs);};Zapatec.PreloadImages.prototype.init=function(objArgs){if(!objArgs){return;}
if(!objArgs.urls||!objArgs.urls.length){if(typeof objArgs.onLoad=='function'){objArgs.onLoad(this);}
return;}
this.images=[];this.leftToLoad=objArgs.urls.length;this.loadedUrls=[];this.invalidUrls=[];this.abortedUrls=[];this.onLoad=objArgs.onLoad;for(var iUrl=0;iUrl<objArgs.urls.length;iUrl++){new Zapatec.ImagePreloader({job:this,url:objArgs.urls[iUrl],timeout:objArgs.timeout});}};if(typeof Zapatec=='undefined'){Zapatec=function(){};}
Zapatec.EventDriven=function(){};Zapatec.EventDriven.prototype.init=function(){this.events={};};Zapatec.EventDriven.prototype.addEventListener=function(sEvent,fListener){if(typeof fListener!="function"){return false;}
if(!this.events[sEvent]){this.events[sEvent]={listeners:[]};}else{this.removeEventListener(sEvent,fListener);}
this.events[sEvent].listeners.push(fListener);};Zapatec.EventDriven.prototype.unshiftEventListener=function(sEvent,fListener){if(typeof fListener!="function"){return false;}
if(!this.events[sEvent]){this.events[sEvent]={listeners:[]};}else{this.removeEventListener(sEvent,fListener);}
this.events[sEvent].listeners.unshift(fListener);};Zapatec.EventDriven.prototype.removeEventListener=function(sEvent,fListener){if(!this.events[sEvent]){return 0;}
var aListeners=this.events[sEvent].listeners;var iRemoved=0;for(var iListener=aListeners.length-1;iListener>=0;iListener--){if(aListeners[iListener]==fListener){aListeners.splice(iListener,1);iRemoved++;}}
return iRemoved;};Zapatec.EventDriven.prototype.getEventListeners=function(sEvent){if(!this.events[sEvent]){return[];}
return this.events[sEvent].listeners;};Zapatec.EventDriven.prototype.isEventListener=function(sEvent,fListener){if(!this.events[sEvent]){return false;}
var aListeners=this.events[sEvent].listeners;for(var iListener=aListeners.length-1;iListener>=0;iListener--){if(aListeners[iListener]==fListener){return true;}}
return false;};Zapatec.EventDriven.prototype.isEvent=function(sEvent){if(this.events[sEvent]){return true;}
return false;};Zapatec.EventDriven.prototype.removeEvent=function(sEvent){if(this.events[sEvent]){var undef;this.events[sEvent]=undef;}};Zapatec.EventDriven.prototype.fireEvent=function(sEvent){if(!this.events[sEvent]){return;}
var aListeners=this.events[sEvent].listeners.slice();for(var iListener=0;iListener<aListeners.length;iListener++){var aArgs=[].slice.call(arguments,1);aListeners[iListener].apply(this,aArgs);}};Zapatec.EventDriven.events={};Zapatec.EventDriven.addEventListener=function(sEvent,fListener){if(typeof fListener!="function"){return false;}
if(!Zapatec.EventDriven.events[sEvent]){Zapatec.EventDriven.events[sEvent]={listeners:[]};}else{Zapatec.EventDriven.removeEventListener(sEvent,fListener);}
Zapatec.EventDriven.events[sEvent].listeners.push(fListener);};Zapatec.EventDriven.unshiftEventListener=function(sEvent,fListener){if(typeof fListener!="function"){return false;}
if(!Zapatec.EventDriven.events[sEvent]){Zapatec.EventDriven.events[sEvent]={listeners:[]};}else{Zapatec.EventDriven.removeEventListener(sEvent,fListener);}
Zapatec.EventDriven.events[sEvent].listeners.unshift(fListener);};Zapatec.EventDriven.removeEventListener=function(sEvent,fListener){if(!Zapatec.EventDriven.events[sEvent]){return 0;}
var aListeners=Zapatec.EventDriven.events[sEvent].listeners;var iRemoved=0;for(var iListener=aListeners.length-1;iListener>=0;iListener--){if(aListeners[iListener]==fListener){aListeners.splice(iListener,1);iRemoved++;}}
return iRemoved;};Zapatec.EventDriven.getEventListeners=function(sEvent){if(!Zapatec.EventDriven.events[sEvent]){return[];}
return Zapatec.EventDriven.events[sEvent].listeners;};Zapatec.EventDriven.isEventListener=function(sEvent,fListener){if(!Zapatec.EventDriven.events[sEvent]){return false;}
var aListeners=Zapatec.EventDriven.events[sEvent].listeners;for(var iListener=aListeners.length-1;iListener>=0;iListener--){if(aListeners[iListener]==fListener){return true;}}
return false;};Zapatec.EventDriven.isEvent=function(sEvent){if(Zapatec.EventDriven.events[sEvent]){return true;}
return false;};Zapatec.EventDriven.removeEvent=function(sEvent){if(Zapatec.EventDriven.events[sEvent]){var undef;Zapatec.EventDriven.events[sEvent]=undef;}};Zapatec.EventDriven.fireEvent=function(sEvent){if(!Zapatec.EventDriven.events[sEvent]){return;}
var aListeners=Zapatec.EventDriven.events[sEvent].listeners.slice();for(var iListener=0;iListener<aListeners.length;iListener++){var aArgs=[].slice.call(arguments,1);aListeners[iListener].apply(aListeners[iListener],aArgs);}};if(typeof Zapatec=='undefined'){Zapatec=function(){};}
Zapatec.Widget=function(oArg){this.config={};Zapatec.Widget.SUPERconstructor.call(this);this.init(oArg);};Zapatec.inherit(Zapatec.Widget,Zapatec.EventDriven);Zapatec.Widget.path=Zapatec.getPath('Zapatec.Widget');Zapatec.Widget.prototype.init=function(oArg){Zapatec.Widget.SUPERclass.init.call(this);if(typeof this.id=='undefined'){var iId=0;while(Zapatec.Widget.all[iId]){iId++;}
this.id=iId;Zapatec.Widget.all[iId]=this;}
this.configure(oArg);this.addUserEventListeners();this.addStandardEventListeners();this.loadTheme();};Zapatec.Widget.prototype.reconfigure=function(oArg){this.configure(oArg);this.loadTheme();};Zapatec.Widget.prototype.configure=function(oArg){this.defineConfigOption('theme','default');if(typeof this.constructor.path!='undefined'){this.defineConfigOption('themePath',this.constructor.path+'../themes/');}else{this.defineConfigOption('themePath','../themes/');}
this.defineConfigOption('asyncTheme',false);this.defineConfigOption('source');this.defineConfigOption('sourceType');this.defineConfigOption('callbackSource');this.defineConfigOption('asyncSource',true);this.defineConfigOption('reliableSource',true);this.defineConfigOption('eventListeners',{});if(oArg){for(var sOption in oArg){if(typeof this.config[sOption]!='undefined'){this.config[sOption]=oArg[sOption];}else{Zapatec.Log({description:"Unknown config option: "+sOption});}}}};Zapatec.Widget.prototype.getConfiguration=function(){return this.config;};Zapatec.Widget.all=[];Zapatec.Widget.getWidgetById=function(iId){return Zapatec.Widget.all[iId];};Zapatec.Widget.prototype.addCircularRef=function(oElement,sProperty){if(!this.widgetCircularRefs){this.widgetCircularRefs=[];}
this.widgetCircularRefs.push([oElement,sProperty]);};Zapatec.Widget.prototype.createProperty=function(oElement,sProperty,val){oElement[sProperty]=val;this.addCircularRef(oElement,sProperty);};Zapatec.Widget.prototype.removeCircularRefs=function(){if(!this.widgetCircularRefs){return;}
for(var iRef=this.widgetCircularRefs.length-1;iRef>=0;iRef--){var oRef=this.widgetCircularRefs[iRef];oRef[0][oRef[1]]=null;oRef[0]=null;}};Zapatec.Widget.prototype.discard=function(){Zapatec.Widget.all[this.id]=null;this.removeCircularRefs();};Zapatec.Widget.removeCircularRefs=function(){for(var iWidget=Zapatec.Widget.all.length-1;iWidget>=0;iWidget--){var oWidget=Zapatec.Widget.all[iWidget];if(oWidget){oWidget.removeCircularRefs();}}};Zapatec.Utils.addEvent(window,'unload',Zapatec.Widget.removeCircularRefs);Zapatec.Widget.prototype.defineConfigOption=function(sOption,val){if(typeof this.config[sOption]=='undefined'){if(typeof val=='undefined'){this.config[sOption]=null;}else{this.config[sOption]=val;}}};Zapatec.Widget.prototype.addUserEventListeners=function(){for(var sEvent in this.config.eventListeners){if(this.config.eventListeners.hasOwnProperty(sEvent)){this.addEventListener(sEvent,this.config.eventListeners[sEvent]);}}};Zapatec.Widget.prototype.addStandardEventListeners=function(){this.addEventListener('loadThemeError',Zapatec.Widget.loadThemeError);};Zapatec.Widget.loadThemeError=function(oError){var sDescription="Can't load theme.";if(oError&&oError.errorDescription){sDescription+=' '+oError.errorDescription;}
Zapatec.Log({description:sDescription});};Zapatec.Widget.prototype.loadTheme=function(){if(typeof this.config.theme=='string'&&this.config.theme.length){var iPos=this.config.theme.lastIndexOf('/');if(iPos>=0){iPos++;this.config.themePath=this.config.theme.substring(0,iPos);this.config.theme=this.config.theme.substring(iPos);}
iPos=this.config.theme.lastIndexOf('.');if(iPos>=0){this.config.theme=this.config.theme.substring(0,iPos);}
this.config.theme=this.config.theme.toLowerCase();}else{this.config.theme='';}
if(this.config.theme){this.fireEvent('loadThemeStart');this.themeLoaded=false;var oWidget=this;var sUrl=this.config.themePath+this.config.theme+'.css';Zapatec.Transport.loadCss({url:sUrl,async:this.config.asyncTheme,onLoad:function(){oWidget.fireEvent('loadThemeEnd');oWidget.themeLoaded=true;oWidget.hideLoader();},onError:function(oError){oWidget.fireEvent('loadThemeEnd');oWidget.fireEvent('loadThemeError',oError);oWidget.themeLoaded=true;oWidget.hideLoader();}});}}
Zapatec.Widget.prototype.getClassName=function(oArg){var aClassName=[];if(oArg&&oArg.prefix){aClassName.push(oArg.prefix);}
if(this.config.theme!=''){aClassName.push(this.config.theme.charAt(0).toUpperCase());aClassName.push(this.config.theme.substr(1));}
if(oArg&&oArg.suffix){aClassName.push(oArg.suffix);}
return aClassName.join('');};Zapatec.Widget.prototype.formElementId=function(oArg){var aId=[];if(oArg&&oArg.prefix){aId.push(oArg.prefix);}else{aId.push('zpWidget');}
aId.push(this.id);if(oArg&&oArg.suffix){aId.push(oArg.suffix);}else{aId.push('-');}
if(typeof this.widgetUniqueIdCounter=='undefined'){this.widgetUniqueIdCounter=0;}else{this.widgetUniqueIdCounter++;}
aId.push(this.widgetUniqueIdCounter);return aId.join('');};Zapatec.Widget.prototype.showLoader=function(message){if(this.container!=null&&this.config.theme&&!this.themeLoaded){if(!Zapatec.windowLoaded){var self=this;Zapatec.Utils.addEvent(window,"load",function(){self.showLoader(message)});return null;}
if(typeof(Zapatec.Indicator)=='undefined'){var self=this;Zapatec.Transport.loadJS({module:'indicator',onLoad:function(){if(self.themeLoaded){return null;}
self.showLoader(message);}});return null;}
this.loader=new Zapatec.Indicator({container:this.container,themePath:Zapatec.zapatecPath+"../zpextra/themes/indicator/"});this.loader.start(message||'loading');this.container.style.visibility='hidden';}}
Zapatec.Widget.prototype.hideLoader=function(){if(this.loader&&this.loader.isActive()){this.container.style.visibility='';this.loader.stop();}}
Zapatec.Widget.prototype.showContainer=function(effects,animSpeed,onFinish){return this.showHideContainer(effects,animSpeed,onFinish,true);}
Zapatec.Widget.prototype.hideContainer=function(effects,animSpeed,onFinish){return this.showHideContainer(effects,animSpeed,onFinish,false);}
Zapatec.Widget.prototype.showHideContainer=function(effects,animSpeed,onFinish,show){if(this.container==null){return null;}
if(effects&&effects.length>0&&typeof(Zapatec.Effects)=='undefined'){var self=this;Zapatec.Transport.loadJS({url:Zapatec.zapatecPath+'../zpeffects/src/effects.js',onLoad:function(){self.showHideContainer(effects,animSpeed,onFinish,show);}});return false;}
if(animSpeed==null&&isNaN(parseInt(animSpeed))){animSpeed=5;}
if(!effects||effects.length==0){if(show){this.container.style.display=this.originalContainerDisplay;this.originalContainerDisplay=null;}else{this.originalContainerDisplay=this.container.style.display;this.container.style.display='none';}
if(onFinish){onFinish();}}else{if(show){Zapatec.Effects.show(this.container,animSpeed,effects,onFinish);}else{Zapatec.Effects.hide(this.container,animSpeed,effects,onFinish);}}
return true;}
Zapatec.Widget.prototype.loadData=function(oArg){if(typeof this.config.callbackSource=='function'){var oSource=this.config.callbackSource(oArg);if(oSource){if(typeof oSource.source!='undefined'){this.config.source=oSource.source;}
if(typeof oSource.sourceType!='undefined'){this.config.sourceType=oSource.sourceType;}}}
if(this.config.source!=null&&this.config.sourceType!=null){var sSourceType=this.config.sourceType.toLowerCase();if(sSourceType=='html'){this.fireEvent('loadDataStart');this.loadDataHtml(Zapatec.Widget.getElementById(this.config.source));this.fireEvent('loadDataEnd');}else if(sSourceType=='html/text'){this.fireEvent('loadDataStart');this.loadDataHtmlText(this.config.source);this.fireEvent('loadDataEnd');}else if(sSourceType=='html/url'){this.fireEvent('fetchSourceStart');var oWidget=this;Zapatec.Transport.fetch({url:this.config.source,async:this.config.asyncSource,onLoad:function(oRequest){oWidget.fireEvent('fetchSourceEnd');oWidget.fireEvent('loadDataStart');oWidget.loadDataHtmlText(oRequest.responseText);oWidget.fireEvent('loadDataEnd');},onError:function(oError){oWidget.fireEvent('fetchSourceError',oError);oWidget.fireEvent('fetchSourceEnd');oWidget.fireEvent('loadDataEnd');}});}else if(sSourceType=='json'){this.fireEvent('loadDataStart');if(typeof this.config.source=='object'){this.loadDataJson(this.config.source);}else if(this.config.reliableSource){this.loadDataJson(eval('('+this.config.source+')'));}else{this.loadDataJson(Zapatec.Transport.parseJson({strJson:this.config.source}));}
this.fireEvent('loadDataEnd');}else if(sSourceType=='json/url'){this.fireEvent('fetchSourceStart');var oWidget=this;Zapatec.Transport.fetchJsonObj({url:this.config.source,async:this.config.asyncSource,reliable:this.config.reliableSource,onLoad:function(oResult){oWidget.fireEvent('fetchSourceEnd');oWidget.fireEvent('loadDataStart');oWidget.loadDataJson(oResult);oWidget.fireEvent('loadDataEnd');},onError:function(oError){oWidget.fireEvent('fetchSourceError',oError);oWidget.fireEvent('fetchSourceEnd');oWidget.fireEvent('loadDataEnd');}});}else if(sSourceType=='xml'){this.fireEvent('loadDataStart');if(typeof this.config.source=='object'){this.loadDataXml(this.config.source);}else{this.loadDataXml(Zapatec.Transport.parseXml({strXml:this.config.source}));}
this.fireEvent('loadDataEnd');}else if(sSourceType=='xml/url'){this.fireEvent('fetchSourceStart');var oWidget=this;Zapatec.Transport.fetchXmlDoc({url:this.config.source,async:this.config.asyncSource,onLoad:function(oResult){oWidget.fireEvent('fetchSourceEnd');oWidget.fireEvent('loadDataStart');oWidget.loadDataXml(oResult);oWidget.fireEvent('loadDataEnd');},onError:function(oError){oWidget.fireEvent('fetchSourceError',oError);oWidget.fireEvent('fetchSourceEnd');oWidget.fireEvent('loadDataEnd');}});}}else{this.fireEvent('loadDataStart');this.loadDataHtml(Zapatec.Widget.getElementById(this.config.source));this.fireEvent('loadDataEnd');}};Zapatec.Widget.prototype.loadDataHtml=function(oSource){};Zapatec.Widget.prototype.loadDataHtmlText=function(sSource){var oTempContainer=Zapatec.Transport.parseHtml(sSource);this.loadDataHtml(oTempContainer.firstChild);};Zapatec.Widget.prototype.loadDataJson=function(oSource){};Zapatec.Widget.prototype.loadDataXml=function(oSource){};Zapatec.Widget.prototype.editData=function(oArg){this.fireEvent('editData',oArg);};Zapatec.Widget.prototype.editDataGet=function(){return null;};Zapatec.Widget.prototype.editDataCancel=function(){this.fireEvent('editDataCancel');if(typeof this.hide=='function'){this.hide();}};Zapatec.Widget.prototype.editDataReturn=function(oArg){this.fireEvent('editDataReturn',oArg);if(!oArg.widget||typeof oArg.widget.editDataReceive!='function'){return;}
oArg.widget.editDataReceive({data:this.editDataGet()});this.editDataCancel();};Zapatec.Widget.prototype.editDataReceive=function(oArg){this.fireEvent('editDataReceive',oArg);};Zapatec.Widget.callMethod=function(iWidgetId,sMethod){var oWidget=Zapatec.Widget.getWidgetById(iWidgetId);if(oWidget&&typeof oWidget[sMethod]=='function'){var aArgs=[].slice.call(arguments,2);return oWidget[sMethod].apply(oWidget,aArgs);}};Zapatec.Widget.getElementById=function(element){if(typeof element=='string'){return document.getElementById(element);}
return element;};Zapatec.Widget.getStyle=function(element){var style=element.getAttribute('style')||'';if(typeof style=='string'){return style;}
return style.cssText;};Zapatec.Drag={};Zapatec.Utils.emulateWindowEvent(['mousedown','mousemove','mouseup']);Zapatec.Drag.currentId=null;Zapatec.Drag.start=function(oEv,sId,oArg){if(Zapatec.Drag.currentId){return true;}
var oEl=document.getElementById(sId);if(!oEl||oEl.zpDrag){return true;}
if(!oArg){oArg={};}
var oPos=Zapatec.Utils.getMousePos(oEv||window.event);Zapatec.EventDriven.fireEvent('dragStart',{id:sId});oEl.zpDrag=true;oEl.zpDragPageX=oPos.pageX;oEl.zpDragPageY=oPos.pageY;if(oEl.offsetParent){var oPos=Zapatec.Utils.getElementOffset(oEl);var oPosParent=Zapatec.Utils.getElementOffset(oEl.offsetParent);oEl.zpDragLeft=oPos.left-oPosParent.left;oEl.zpDragTop=oPos.top-oPosParent.top;}else{oEl.zpDragLeft=oEl.offsetLeft;oEl.zpDragTop=oEl.offsetTop;}
oEl.zpDragPrevLeft=oEl.zpDragLeft;oEl.zpDragPrevTop=oEl.zpDragTop;oEl.zpDragV=oArg.vertical;oEl.zpDragH=oArg.horizontal;oEl.zpDragLimTop=typeof oArg.limitTop=='number'?oArg.limitTop:-Infinity;oEl.zpDragLimBot=typeof oArg.limitBottom=='number'?oArg.limitBottom:Infinity;oEl.zpDragLimLft=typeof oArg.limitLeft=='number'?oArg.limitLeft:-Infinity;oEl.zpDragLimRgh=typeof oArg.limitRight=='number'?oArg.limitRight:Infinity;Zapatec.Drag.currentId=sId;Zapatec.Utils.addEvent(document,'mousemove',Zapatec.Drag.move);Zapatec.Utils.addEvent(document,'mouseup',Zapatec.Drag.end);return true;};Zapatec.Drag.move=function(oEv){oEv||(oEv=window.event);if(!Zapatec.Drag.currentId){return Zapatec.Utils.stopEvent(oEv);}
var oEl=document.getElementById(Zapatec.Drag.currentId);if(!(oEl&&oEl.zpDrag)){return Zapatec.Utils.stopEvent(oEv);}
var oPos=Zapatec.Utils.getMousePos(oEv);var oOffset={id:Zapatec.Drag.currentId,startLeft:oEl.zpDragLeft,startTop:oEl.zpDragTop,prevLeft:oEl.zpDragPrevLeft,prevTop:oEl.zpDragPrevTop,left:0,top:0};if(!oEl.zpDragV){var iLeft=oEl.zpDragLeft+oPos.pageX-oEl.zpDragPageX;if(oEl.zpDragLimLft<=iLeft&&oEl.zpDragLimRgh>=iLeft){oEl.style.right='';oEl.style.left=iLeft+'px';oOffset.left=iLeft;oEl.zpDragPrevLeft=iLeft;}else{oOffset.left=oOffset.prevLeft;}}
if(!oEl.zpDragH){var iTop=oEl.zpDragTop+oPos.pageY-oEl.zpDragPageY;if(oEl.zpDragLimTop<=iTop&&oEl.zpDragLimBot>=iTop){oEl.style.bottom='';oEl.style.top=iTop+'px';oOffset.top=iTop;oEl.zpDragPrevTop=iTop;}else{oOffset.top=oOffset.prevTop;}}
Zapatec.EventDriven.fireEvent('dragMove',oOffset);return Zapatec.Utils.stopEvent(oEv);};Zapatec.Drag.end=function(oEv){oEv||(oEv=window.event);if(!Zapatec.Drag.currentId){return Zapatec.Utils.stopEvent(oEv);}
var oEl=document.getElementById(Zapatec.Drag.currentId);if(!(oEl&&oEl.zpDrag)){return Zapatec.Utils.stopEvent(oEv);}
Zapatec.Utils.removeEvent(document,'mousemove',Zapatec.Drag.move);Zapatec.Utils.removeEvent(document,'mouseup',Zapatec.Drag.end);var oOffset={id:Zapatec.Drag.currentId,startLeft:oEl.zpDragLeft,startTop:oEl.zpDragTop,left:oEl.zpDragPrevLeft,top:oEl.zpDragPrevTop};Zapatec.Drag.currentId=null;oEl.zpDrag=null;oEl.zpDragPageX=null;oEl.zpDragPageY=null;oEl.zpDragLeft=null;oEl.zpDragTop=null;oEl.zpDragPrevLeft=null;oEl.zpDragPrevTop=null;oEl.zpDragV=null;oEl.zpDragH=null;oEl.zpDragLimTop=null;oEl.zpDragLimBot=null;oEl.zpDragLimLft=null;oEl.zpDragLimRgh=null;Zapatec.EventDriven.fireEvent('dragEnd',oOffset);return Zapatec.Utils.stopEvent(oEv);};

/*
 *
 * Copyright (c) 2004-2005 by Zapatec, Inc.
 * http://www.zapatec.com
 * 1700 MLK Way, Berkeley, California,
 * 94709, U.S.A.
 * All rights reserved.
 *
 *
 */
if (!window.Zapatec || (Zapatec && !Zapatec.include)) {
    alert("You need to include zapatec.js file!");
}
else {
    Zapatec.calendarPath = Zapatec.getPath("Zapatec.CalendarWidget");
}
window.calendar = null;
/**< global object that remembers the calendar */ // initialize the preferences object; // embed it in a try/catch so we don't have any surprises try { Zapatec.Calendar.loadPrefs(); } catch(e) {};

Zapatec.Calendar = function(firstDayOfWeek, dateStr, onSelected, onClose) {
    this.bShowHistoryEvent = false;
    this.activeDiv = null;
    this.currentDateEl = null;
    this.getDateStatus = null;
    this.getDateToolTip = null;
    this.getDateText = null;
    this.timeout = null;
    this.onSelected = onSelected || null;
    this.onClose = onClose || null;
    this.onFDOW = null;
    this.dragging = false;
    this.hidden = false;
    this.minYear = 1970;
    this.maxYear = 2050;
    this.minMonth = 0;
    this.maxMonth = 11;
    this.dateFormat = Zapatec.Calendar.i18n("DEF_DATE_FORMAT");
    this.ttDateFormat = Zapatec.Calendar.i18n("TT_DATE_FORMAT");
    this.historyDateFormat = "%B %d, %Y";
    this.isPopup = true;
    this.weekNumbers = true;
    this.noGrab = false;
    if (Zapatec.Calendar.prefs.fdow || (Zapatec.Calendar.prefs.fdow == 0)) {
        this.firstDayOfWeek = parseInt(Zapatec.Calendar.prefs.fdow, 10);
    }
    else {
        var fd = 0;
        if (typeof firstDayOfWeek == "number") {
            fd = firstDayOfWeek;
        }
        else if (typeof Zapatec.Calendar._FD == 'number') {
            fd = Zapatec.Calendar._FD;
        }
        this.firstDayOfWeek = fd;
    }
    this.showsOtherMonths = false;
    this.dateStr = dateStr;
    this.showsTime = false;
    this.sortOrder = "asc";
    this.time24 = true;
    this.timeInterval = null;
    this.yearStep = 2;
    this.hiliteToday = true;
    this.multiple = null;
    this.table = null;
    this.element = null;
    this.tbody = new Array();
    this.firstdayname = null;
    this.monthsCombo = null;
    this.hilitedMonth = null;
    this.activeMonth = null;
    this.yearsCombo = null;
    this.hilitedYear = null;
    this.activeYear = null;
    this.histCombo = null;
    this.hilitedHist = null;
    this.dateClicked = false;
    this.numberMonths = 1;
    this.controlMonth = 1;
    this.vertical = false;
    this.monthsInRow = 1;
    this.titles = new Array();
    this.rowsOfDayNames = new Array();
    this.helpButton = true;
    this.disableFdowClick = true;
    this.disableDrag = false;
    this.yearNav = true;
    this.closeButton = true;
    Zapatec.Calendar._initSDN();
};
Zapatec.Calendar._initSDN = function() {
    if (typeof Zapatec.Calendar._TT._SDN == "undefined") {
        if (typeof Zapatec.Calendar._TT._SDN_len == "undefined")
            Zapatec.Calendar._TT._SDN_len = 3;
        var ar = [];
        for (var i = 8; i > 0;) {
            ar[--i] = Zapatec.Calendar._TT._DN[i].substr(0, Zapatec.Calendar._TT._SDN_len);
        }
        Zapatec.Calendar._TT._SDN = ar;
        if (typeof Zapatec.Calendar._TT._SMN_len == "undefined")
            Zapatec.Calendar._TT._SMN_len = 3;
        ar = [];
        for (var i = 12; i > 0;) {
            ar[--i] = Zapatec.Calendar._TT._MN[i].substr(0, Zapatec.Calendar._TT._SMN_len);
        }
        Zapatec.Calendar._TT._SMN = ar;
    }
    if (typeof Zapatec.Calendar._TT._AMPM == "undefined") {
        Zapatec.Calendar._TT._AMPM = {am:"am",pm:"pm"};
    }
};
Zapatec.Calendar.i18n = function(str, type) {
    var tr = '';
    if (!type) {
        if (Zapatec.Calendar._TT)
            tr = Zapatec.Calendar._TT[str];
        if (!tr && Zapatec.Calendar._TT_en)
            tr = Zapatec.Calendar._TT_en[str];
    }
    else switch (type) {case"dn":tr = Zapatec.Calendar._TT._DN[str];break;case"sdn":tr = Zapatec.Calendar._TT._SDN[str];break;case"mn":tr = Zapatec.Calendar._TT._MN[str];break;case"smn":tr = Zapatec.Calendar._TT._SMN[str];break;case"ampm":tr = Zapatec.Calendar._TT._AMPM[str];break;}
    if (!tr)tr = "" + str;
    return tr;
};
Zapatec.Calendar._C = null;
Zapatec.Calendar.prefs = {fdow:null,history:"",sortOrder:"asc",hsize:9};
Zapatec.Calendar.savePrefs = function() {
    Zapatec.Utils.writeCookie("ZP_CAL", Zapatec.Utils.makePref(this.prefs), null, '/', 30);
};
Zapatec.Calendar.loadPrefs = function() {
    var txt = Zapatec.Utils.getCookie("ZP_CAL"),tmp;
    if (txt) {
        tmp = Zapatec.Utils.loadPref(txt);
        if (tmp)
            Zapatec.Utils.mergeObjects(this.prefs, tmp);
    }
};
Zapatec.Calendar._add_evs = function(el) {
    var C = Zapatec.Calendar;
    Zapatec.Utils.addEvent(el, "mouseover", C.dayMouseOver);
    Zapatec.Utils.addEvent(el, "mousedown", C.dayMouseDown);
    Zapatec.Utils.addEvent(el, "mouseout", C.dayMouseOut);
    if (Zapatec.is_ie)
        Zapatec.Utils.addEvent(el, "dblclick", C.dayMouseDblClick);
};
Zapatec.Calendar._del_evs = function(el) {
    var C = this;
    Zapatec.Utils.removeEvent(el, "mouseover", C.dayMouseOver);
    Zapatec.Utils.removeEvent(el, "mousedown", C.dayMouseDown);
    Zapatec.Utils.removeEvent(el, "mouseout", C.dayMouseOut);
    if (Zapatec.is_ie)
        Zapatec.Utils.removeEvent(el, "dblclick", C.dayMouseDblClick);
};
Zapatec.Calendar.findMonth = function(el) {
    if (typeof el.month != "undefined") {
        return el;
    }
    else if (el.parentNode && typeof el.parentNode.month != "undefined") {
        return el.parentNode;
    }
    return null;
};
Zapatec.Calendar.findHist = function(el) {
    if (typeof el.histDate != "undefined") {
        return el;
    }
    else if (el.parentNode && typeof el.parentNode.histDate != "undefined") {
        return el.parentNode;
    }
    return null;
};
Zapatec.Calendar.findYear = function(el) {
    if (typeof el.year != "undefined") {
        return el;
    }
    else if (el.parentNode && typeof el.parentNode.year != "undefined") {
        return el.parentNode;
    }
    return null;
};
Zapatec.Calendar.showMonthsCombo = function() {
    var cal = Zapatec.Calendar._C;
    if (!cal) {
        return false;
    }
    var cd = cal.activeDiv;
    var mc = cal.monthsCombo;
    var date = cal.date,MM = cal.date.getMonth(),YY = cal.date.getFullYear(),min = (YY == cal.minYear),max = (YY == cal.maxYear);
    for (var i = mc.firstChild; i; i = i.nextSibling) {
        var m = i.month;
        Zapatec.Utils.removeClass(i, "hilite");
        Zapatec.Utils.removeClass(i, "active");
        Zapatec.Utils.removeClass(i, "disabled");
        i.disabled = false;
        if ((min && m < cal.minMonth) || (max && m > cal.maxMonth)) {
            Zapatec.Utils.addClass(i, "disabled");
            i.disabled = true;
        }
        if (m == MM)
            Zapatec.Utils.addClass(cal.activeMonth = i, "active");
    }
    var s = mc.style;
    s.display = "block";
    if (cd.navtype < 0)
        s.left = cd.offsetLeft + "px";
    else {
        var mcw = mc.offsetWidth;
        if (typeof mcw == "undefined")
            mcw = 50;
        s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
    }
    s.top = (cd.offsetTop + cd.offsetHeight) + "px";
    cal.updateWCH(mc);
};
Zapatec.Calendar.showHistoryCombo = function() {
    var cal = Zapatec.Calendar._C,a,h,i,cd,hc,s,tmp,div;
    if (!cal)
        return false;
    hc = cal.histCombo;
    while (hc.firstChild)
        hc.removeChild(hc.lastChild);
    if (Zapatec.Calendar.prefs.history) {
        a = Zapatec.Calendar.prefs.history.split(/,/);
        i = 0;
        while (tmp = a[i++]) {
            tmp = tmp.split(/\//);
            h = Zapatec.Utils.createElement("div");
            h.className = Zapatec.is_ie ? "label-IEfix" : "label";
            h.histDate = new Date(parseInt(tmp[0], 10), parseInt(tmp[1], 10) - 1, parseInt(tmp[2], 10), tmp[3] ? parseInt(tmp[3], 10) : 0, tmp[4] ? parseInt(tmp[4], 10) : 0);
            h.appendChild(window.document.createTextNode(h.histDate.print(cal.historyDateFormat)));
            hc.appendChild(h);
            if (h.histDate.dateEqualsTo(cal.date))
                Zapatec.Utils.addClass(h, "active");
        }
    }
    cd = cal.activeDiv;
    s = hc.style;
    s.display = "block";
    s.left = Math.floor(cd.offsetLeft + (cd.offsetWidth - hc.offsetWidth) / 2) + "px";
    s.top = (cd.offsetTop + cd.offsetHeight) + "px";
    cal.updateWCH(hc);
    cal.bEventShowHistory = true;
};
Zapatec.Calendar.showYearsCombo = function(fwd) {
    var cal = Zapatec.Calendar._C;
    if (!cal) {
        return false;
    }
    var cd = cal.activeDiv;
    var yc = cal.yearsCombo;
    if (cal.hilitedYear) {
        Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
    }
    if (cal.activeYear) {
        Zapatec.Utils.removeClass(cal.activeYear, "active");
    }
    cal.activeYear = null;
    var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
    var yr = yc.firstChild;
    var show = false;
    for (var i = 12; i > 0; --i) {
        if (Y >= cal.minYear && Y <= cal.maxYear) {
            yr.firstChild.data = Y;
            yr.year = Y;
            yr.style.display = "block";
            show = true;
        }
        else {
            yr.style.display = "none";
        }
        yr = yr.nextSibling;
        Y += fwd ? cal.yearStep : -cal.yearStep;
    }
    if (show) {
        var s = yc.style;
        s.display = "block";
        if (cd.navtype < 0)
            s.left = cd.offsetLeft + "px";
        else {
            var ycw = yc.offsetWidth;
            if (typeof ycw == "undefined")
                ycw = 50;
            s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
        }
        s.top = (cd.offsetTop + cd.offsetHeight) + "px";
    }
    cal.updateWCH(yc);
};
Zapatec.Calendar.tableMouseUp = function(ev) {
    var cal = Zapatec.Calendar._C;
    if (!cal) {
        return false;
    }
    if (cal.timeout) {
        clearTimeout(cal.timeout);
    }
    var el = cal.activeDiv;
    if (!el) {
        return false;
    }
    var target = Zapatec.Utils.getTargetElement(ev);
    if (typeof(el.navtype) == "undefined") {
        while (target && !target.calendar) {
            target = target.parentNode;
        }
    }
    ev || (ev = window.event);
    Zapatec.Utils.removeClass(el, "active");
    if (target == el || target.parentNode == el) {
        Zapatec.Calendar.cellClick(el, ev);
    }
    var mon = Zapatec.Calendar.findMonth(target);
    var date = null;
    if (mon) {
        if (!mon.disabled) {
            date = new Date(cal.date);
            if (mon.month != date.getMonth()) {
                date.setMonth(mon.month);
                cal.setDate(date, true);
                cal.dateClicked = false;
                cal.callHandler();
            }
        }
    }
    else {
        var year = Zapatec.Calendar.findYear(target);
        if (year) {
            date = new Date(cal.date);
            if (year.year != date.getFullYear()) {
                date.setFullYear(year.year);
                cal.setDate(date, true);
                cal.dateClicked = false;
                cal.callHandler();
            }
        }
        else {
            var hist = Zapatec.Calendar.findHist(target);
            if (hist && !hist.histDate.dateEqualsTo(cal.date)) {
                date = new Date(hist.histDate);
                cal._init(cal.firstDayOfWeek, cal.date = date);
                cal.dateClicked = false;
                cal.callHandler();
                cal.updateHistory();
            }
        }
    }
    Zapatec.Utils.removeEvent(window.document, "mouseup", Zapatec.Calendar.tableMouseUp);
    Zapatec.Utils.removeEvent(window.document, "mouseover", Zapatec.Calendar.tableMouseOver);
    Zapatec.Utils.removeEvent(window.document, "mousemove", Zapatec.Calendar.tableMouseOver);
    cal._hideCombos();
    Zapatec.Calendar._C = null;
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.tableMouseOver = function(ev) {
    var cal = Zapatec.Calendar._C;
    if (!cal) {
        return;
    }
    var el = cal.activeDiv;
    var target = Zapatec.Utils.getTargetElement(ev);
    if (target == el || target.parentNode == el) {
        Zapatec.Utils.addClass(el, "hilite active");
        Zapatec.Utils.addClass(el.parentNode, "rowhilite");
    }
    else {
        if (typeof el.navtype == "undefined" || (el.navtype != 50 && ((el.navtype == 0 && !cal.histCombo) || Math.abs(el.navtype) > 2)))
            Zapatec.Utils.removeClass(el, "active");
        Zapatec.Utils.removeClass(el, "hilite");
        Zapatec.Utils.removeClass(el.parentNode, "rowhilite");
    }
    ev || (ev = window.event);
    if (el.navtype == 50 && target != el) {
        var pos = Zapatec.Utils.getAbsolutePos(el);
        var w = el.offsetWidth;
        var x = ev.clientX;
        var dx;
        var decrease = true;
        if (x > pos.x + w) {
            dx = x - pos.x - w;
            decrease = false;
        }
        else
            dx = pos.x - x;
        if (dx < 0)dx = 0;
        var range = el._range;
        var current = el._current;
        var date = cal.currentDate;
        var pm = (date.getHours() >= 12);
        var old = el.firstChild.data;
        var count = Math.floor(dx / 10) % range.length;
        for (var i = range.length; --i >= 0;)
            if (range[i] == current)
                break;
        while (count-- > 0)
            if (decrease) {
                if (--i < 0) {
                    i = range.length - 1;
                }
            }
            else if (++i >= range.length) {
                i = 0;
            }
        if (cal.getDateStatus) {
            var minute = null;
            var hour = null;
            var new_date = new Date(date);
            if (el.className.indexOf("ampm", 0) != -1) {
                minute = date.getMinutes();
                if (old != range[i]) {
                    hour = (range[i] == Zapatec.Calendar.i18n("pm", "ampm")) ? ((date.getHours() == 0) ? (12) : (date.getHours() + 12)) : (date.getHours() - 12);
                }
                else {
                    hour = date.getHours();
                }
                new_date.setHours(hour);
            }
            if (el.className.indexOf("hour", 0) != -1) {
                minute = date.getMinutes();
                hour = (!cal.time24) ? ((pm) ? ((range[i] != 12) ? (parseInt(range[i], 10) + 12) : (12)) : ((range[i] != 12) ? (range[i]) : (0))) : (range[i]);
                new_date.setHours(hour);
            }
            if (el.className.indexOf("minute", 0) != -1) {
                hour = date.getHours();
                minute = range[i];
                new_date.setMinutes(minute);
            }
        }
        var status = false;
        if (cal.getDateStatus) {
            status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
        }
        if (status == false) {
            if (!((!cal.time24) && (range[i] == Zapatec.Calendar.i18n("pm", "ampm")) && (hour > 23))) {
                el.firstChild.data = range[i];
            }
        }
        cal.onUpdateTime();
    }
    var mon = Zapatec.Calendar.findMonth(target);
    if (mon) {
        if (!mon.disabled) {
            if (mon.month != cal.date.getMonth()) {
                if (cal.hilitedMonth) {
                    Zapatec.Utils.removeClass(cal.hilitedMonth, "hilite");
                }
                Zapatec.Utils.addClass(mon, "hilite");
                cal.hilitedMonth = mon;
            }
            else if (cal.hilitedMonth) {
                Zapatec.Utils.removeClass(cal.hilitedMonth, "hilite");
            }
        }
    }
    else {
        if (cal.hilitedMonth) {
            Zapatec.Utils.removeClass(cal.hilitedMonth, "hilite");
        }
        var year = Zapatec.Calendar.findYear(target);
        if (year) {
            if (year.year != cal.date.getFullYear()) {
                if (cal.hilitedYear) {
                    Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
                }
                Zapatec.Utils.addClass(year, "hilite");
                cal.hilitedYear = year;
            }
            else if (cal.hilitedYear) {
                Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
            }
        }
        else {
            if (cal.hilitedYear) {
                Zapatec.Utils.removeClass(cal.hilitedYear, "hilite");
            }
            var hist = Zapatec.Calendar.findHist(target);
            if (hist) {
                if (!hist.histDate.dateEqualsTo(cal.date)) {
                    if (cal.hilitedHist) {
                        Zapatec.Utils.removeClass(cal.hilitedHist, "hilite");
                    }
                    Zapatec.Utils.addClass(hist, "hilite");
                    cal.hilitedHist = hist;
                }
                else if (cal.hilitedHist) {
                    Zapatec.Utils.removeClass(cal.hilitedHist, "hilite");
                }
            }
            else if (cal.hilitedHist) {
                Zapatec.Utils.removeClass(cal.hilitedHist, "hilite");
            }
        }
    }
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.tableMouseDown = function(ev) {
    if (Zapatec.Utils.getTargetElement(ev) == Zapatec.Utils.getElement(ev)) {
        return Zapatec.Utils.stopEvent(ev);
    }
};
Zapatec.Calendar.calDragIt = function(ev) {
    ev || (ev = window.event);
    var cal = Zapatec.Calendar._C;
    if (!cal) {
        Zapatec.Calendar.calDragEnd();
    }
    if (!cal.disableDrag) {
        if (!(cal && cal.dragging)) {
            return false;
        }
        var posX = ev.clientX + window.document.body.scrollLeft;
        var posY = ev.clientY + window.document.body.scrollTop;
        cal.hideShowCovered();
        var st = cal.element.style,L = posX - cal.xOffs,T = posY - cal.yOffs;
        st.left = L + "px";
        st.top = T + "px";
        Zapatec.Utils.setupWCH(cal.WCH, L, T);
    }
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.calDragEnd = function(ev) {
    var cal = Zapatec.Calendar._C;
    Zapatec.Utils.removeEvent(window.document, "mousemove", Zapatec.Calendar.calDragIt);
    Zapatec.Utils.removeEvent(window.document, "mouseover", Zapatec.Calendar.calDragIt);
    Zapatec.Utils.removeEvent(window.document, "mouseup", Zapatec.Calendar.calDragEnd);
    if (!cal) {
        return false;
    }
    cal.dragging = false;
    Zapatec.Calendar.tableMouseUp(ev);
    cal.hideShowCovered();
};
Zapatec.Calendar.dayMouseDown = function(ev) {
    var canDrag = true;
    var el = Zapatec.Utils.getElement(ev);
    if (el.className.indexOf("disabled") != -1 || el.className.indexOf("true") != -1) {
        return false;
    }
    var cal = el.calendar;
    while (!cal) {
        el = el.parentNode;
        cal = el.calendar;
    }
    cal.bEventShowHistory = false;
    cal.activeDiv = el;
    Zapatec.Calendar._C = cal;
    if (el.navtype != 300) {
        if (el.navtype == 50) {
            if (!((cal.timeInterval == null) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1)))) {
                canDrag = false;
            }
            el._current = el.firstChild.data;
            if (canDrag) {
                Zapatec.Utils.addEvent(window.document, "mousemove", Zapatec.Calendar.tableMouseOver);
            }
        }
        else {
            if (((el.navtype == 201) || (el.navtype == 202)) && (cal.timeInterval > 30) && (el.timePart.className.indexOf("minute", 0) != -1)) {
                canDrag = false;
            }
            if (canDrag) {
                Zapatec.Utils.addEvent(window.document, Zapatec.is_ie5 ? "mousemove" : "mouseover", Zapatec.Calendar.tableMouseOver);
            }
        }
        if (canDrag) {
            Zapatec.Utils.addClass(el, "hilite active");
        }
        Zapatec.Utils.addEvent(window.document, "mouseup", Zapatec.Calendar.tableMouseUp);
    }
    else if (cal.isPopup) {
        cal._dragStart(ev);
    }
    else {
        Zapatec.Calendar._C = null;
    }
    if (el.navtype == -1 || el.navtype == 1) {
        if (cal.timeout)clearTimeout(cal.timeout);
        cal.timeout = setTimeout("Zapatec.Calendar.showMonthsCombo()", 250);
    }
    else if (el.navtype == -2 || el.navtype == 2) {
        if (cal.timeout)clearTimeout(cal.timeout);
        cal.timeout = setTimeout((el.navtype > 0) ? "Zapatec.Calendar.showYearsCombo(true)" : "Zapatec.Calendar.showYearsCombo(false)", 250);
    }
    else if (el.navtype == 0 && Zapatec.Calendar.prefs.history) {
        if (cal.timeout)clearTimeout(cal.timeout);
        cal.timeout = setTimeout("Zapatec.Calendar.showHistoryCombo()", 250);
    }
    else {
        cal.timeout = null;
    }
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.dayMouseDblClick = function(ev) {
    Zapatec.Calendar.cellClick(Zapatec.Utils.getElement(ev), ev || window.event);
    if (Zapatec.is_ie)
        window.document.selection.empty();
};
Zapatec.Calendar.dayMouseOver = function(ev) {
    var el = Zapatec.Utils.getElement(ev),caldate = el.caldate;
    while (!el.calendar) {
        el = el.parentNode;
        caldate = el.caldate;
    }
    var cal = el.calendar;
    var cel = el.timePart;
    if (caldate) {
        caldate = new Date(caldate[0], caldate[1], caldate[2]);
        if (caldate.getDate() != el.caldate[2])caldate.setDate(el.caldate[2]);
    }
    if (Zapatec.Utils.isRelated(el, ev) || Zapatec.Calendar._C || el.className.indexOf("disabled") != -1 || el.className.indexOf("true") != -1) {
        return false;
    }
    if (el.ttip) {
        if (el.ttip.substr(0, 1) == "_") {
            el.ttip = caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
        }
        el.calendar.showHint(el.ttip);
    }
    if (el.navtype != 300) {
        if (!((cal.timeInterval == null) || (el.className.indexOf("ampm", 0) != -1) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1))) && (el.navtype == 50)) {
            return Zapatec.Utils.stopEvent(ev);
        }
        if (((el.navtype == 201) || (el.navtype == 202)) && (cal.timeInterval > 30) && (cel.className.indexOf("minute", 0) != -1)) {
            return Zapatec.Utils.stopEvent(ev);
        }
        Zapatec.Utils.addClass(el, "hilite");
        if (caldate) {
            Zapatec.Utils.addClass(el.parentNode, "rowhilite");
        }
    }
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.dayMouseOut = function(ev) {
    var el = Zapatec.Utils.getElement(ev);
    while (!el.calendar) {
        el = el.parentNode;
        caldate = el.caldate;
    }
    if (Zapatec.Utils.isRelated(el, ev) || Zapatec.Calendar._C || el.className.indexOf("disabled") != -1 || el.className.indexOf("true") != -1)
        return false;
    Zapatec.Utils.removeClass(el, "hilite");
    if (el.caldate)
        Zapatec.Utils.removeClass(el.parentNode, "rowhilite");
    if (el.calendar)
        el.calendar.showHint(Zapatec.Calendar.i18n("SEL_DATE"));
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.cellClick = function(el, ev) {
    var cal = el.calendar;
    var closing = false;
    var newdate = false;
    var date = null;
    while (!cal) {
        el = el.parentNode;
        cal = el.calendar;
    }
    if (el.className.indexOf("disabled") != -1 || el.className.indexOf("true") != -1) {
        return false;
    }
    if (typeof el.navtype == "undefined") {
        if (cal.currentDateEl) {
            Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
            Zapatec.Utils.addClass(el, "selected");
            closing = (cal.currentDateEl == el);
            if (!closing) {
                cal.currentDateEl = el;
            }
        }
        var tmpDate = new Date(el.caldate[0], el.caldate[1], el.caldate[2]);
        if (tmpDate.getDate() != el.caldate[2]) {
            tmpDate.setDate(el.caldate[2]);
        }
        cal.date.setDateOnly(tmpDate);
        cal.currentDate.setDateOnly(tmpDate);
        date = cal.date;
        cal.dateClicked = true;
        if (cal.multiple)
            cal._toggleMultipleDate(new Date(date));
        newdate = true;
        if (el.otherMonth)
            cal._init(cal.firstDayOfWeek, date);
        cal.onSetTime();
    }
    else {
        if (el.navtype == 200) {
            Zapatec.Utils.removeClass(el, "hilite");
            cal.callCloseHandler();
            return;
        }
        date = new Date(cal.date);
        if (el.navtype == 0 && !cal.bEventShowHistory)
            date.setDateOnly(new Date());
        cal.dateClicked = false;
        var year = date.getFullYear();
        var mon = date.getMonth();
        function setMonth(m) {
            var day = date.getDate();
            var max = date.getMonthDays(m);
            if (day > max) {
                date.setDate(max);
            }
            date.setMonth(m);
        }
        ;
        switch (el.navtype) {case 400:Zapatec.Utils.removeClass(el, "hilite");var text = Zapatec.Calendar.i18n("ABOUT");if (typeof text != "undefined") {
            text += cal.showsTime ? Zapatec.Calendar.i18n("ABOUT_TIME") : "";
        }
        else {
            text = "Help and about box text is not translated into this language.\n" + "If you know this language and you feel generous please update\n" + "the corresponding file in \"lang\" subdir to match calendar-en.js\n" + "and send it back to <support@zapatec.com> to get it into the distribution  ;-)\n\n" + "Thank you!\n" + "http://www.zapatec.com\n";
        }
            alert(text);return;case-2:if (year > cal.minYear) {
            date.setFullYear(year - 1);
        }
            break;case-1:if (mon > 0) {
            setMonth(mon - 1);
        }
        else if (year-- > cal.minYear) {
            date.setFullYear(year);
            setMonth(11);
        }
            break;case 1:if (mon < 11) {
            setMonth(mon + 1);
        }
        else if (year < cal.maxYear) {
            date.setFullYear(year + 1);
            setMonth(0);
        }
            break;case 2:if (year < cal.maxYear) {
            date.setFullYear(year + 1);
        }
            break;case 100:cal.setFirstDayOfWeek(el.fdow);Zapatec.Calendar.prefs.fdow = cal.firstDayOfWeek;Zapatec.Calendar.savePrefs();if (cal.onFDOW)
            cal.onFDOW(cal.firstDayOfWeek);return;case 50:var date = cal.currentDate;if (el.className.indexOf("ampm", 0) >= 0);
        else
            if (!((cal.timeInterval == null) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1)))) {
                break;
            }
            var range = el._range;var current = el.firstChild.data;var pm = (date.getHours() >= 12);for (var i = range.length; --i >= 0;)
            if (range[i] == current)
                break;if (ev && ev.shiftKey) {
            if (--i < 0) {
                i = range.length - 1;
            }
        }
        else if (++i >= range.length) {
            i = 0;
        }
            if (cal.getDateStatus) {
                var minute = null;
                var hour = null;
                var new_date = new Date(date);
                if (el.className.indexOf("ampm", 0) != -1) {
                    minute = date.getMinutes();
                    hour = (range[i] == Zapatec.Calendar.i18n("pm", "ampm")) ? ((date.getHours() == 12) ? (date.getHours()) : (date.getHours() + 12)) : (date.getHours() - 12);
                    if (cal.getDateStatus && cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10))) {
                        var dirrect;
                        if (range[i] == Zapatec.Calendar.i18n("pm", "ampm")) {
                            dirrect = -5;
                        }
                        else {
                            dirrect = 5;
                        }
                        hours = hour;
                        minutes = minute;
                        do{
                            minutes += dirrect;
                            if (minutes >= 60) {
                                minutes -= 60;
                                ++hours;
                                if (hours >= 24)hours -= 24;
                                new_date.setHours(hours);
                            }
                            if (minutes < 0) {
                                minutes += 60;
                                --hours;
                                if (hours < 0)hours += 24;
                                new_date.setHours(hours);
                            }
                            new_date.setMinutes(minutes);
                            if (!cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hours, 10), parseInt(minutes, 10))) {
                                hour = hours;
                                minute = minutes;
                                if (hour > 12)i = 1;
                                else i = 0;
                                cal.date.setHours(hour);
                                cal.date.setMinutes(minute);
                                cal.onSetTime();
                            }
                        }
                        while ((hour != hours) || (minute != minutes));
                    }
                    new_date.setHours(hour);
                }
                if (el.className.indexOf("hour", 0) != -1) {
                    minute = date.getMinutes();
                    hour = (!cal.time24) ? ((pm) ? ((range[i] != 12) ? (parseInt(range[i], 10) + 12) : (12)) : ((range[i] != 12) ? (range[i]) : (0))) : (range[i]);
                    new_date.setHours(hour);
                }
                if (el.className.indexOf("minute", 0) != -1) {
                    hour = date.getHours();
                    minute = range[i];
                    new_date.setMinutes(minute);
                }
            }
            var status = false;if (cal.getDateStatus) {
            status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
        }
            if (!status) {
                el.firstChild.data = range[i];
            }
            cal.onUpdateTime();return;case 201:case 202:var cel = el.timePart;var date = cal.currentDate;if ((cel.className.indexOf("minute", 0) != -1) && (cal.timeInterval > 30)) {
            break;
        }
            var val = parseInt(cel.firstChild.data, 10);var pm = (date.getHours() >= 12);var range = cel._range;for (var i = range.length; --i >= 0;)
            if (val == range[i]) {
                val = i;
                break;
            }
            var step = cel._step;if (el.navtype == 201) {
            val = step * Math.floor(val / step);
            val += step;
            if (val >= range.length)
                val = 0;
        }
        else {
            val = step * Math.ceil(val / step);
            val -= step;
            if (val < 0)
                val = range.length - step;
        }
            if (cal.getDateStatus) {
                var minute = null;
                var hour = null;
                var new_date = new Date(date);
                if (cel.className == "hour") {
                    minute = date.getMinutes();
                    hour = (!cal.time24) ? ((pm) ? ((range[val] != 12) ? (parseInt(range[val], 10) + 12) : (12)) : ((range[val] != 12) ? (range[val]) : (0))) : (range[val]);
                    new_date.setHours(hour);
                }
                if (cel.className == "minute") {
                    hour = date.getHours();
                    minute = val;
                    new_date.setMinutes(range[val]);
                }
            }
            var status = false;if (cal.getDateStatus) {
            status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
        }
            if (!status) {
                cel.firstChild.data = range[val];
            }
            cal.onUpdateTime();return;case 0:if (cal.getDateStatus && ((cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate()) == true) || (cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate()) == "disabled"))) {
            return false;
        }
            break;}
        if (!date.equalsTo(cal.date)) {
            if ((el.navtype >= -2 && el.navtype <= 2) && (el.navtype != 0)) {
                cal._init(cal.firstDayOfWeek, date, true);
                return;
            }
            cal.setDate(date);
            newdate = !(el.navtype && (el.navtype >= -2 && el.navtype <= 2));
        }
    }
    if (newdate) {
        cal.callHandler();
    }
    if (closing) {
        Zapatec.Utils.removeClass(el, "hilite");
        cal.callCloseHandler();
    }
};
Zapatec.Calendar.prototype.create = function(_par) {
    var parent = null;
    if (!_par) {
        parent = window.document.getElementsByTagName("body")[0];
        this.isPopup = true;
        this.WCH = Zapatec.Utils.createWCH();
    }
    else {
        parent = _par;
        this.isPopup = false;
    }
    this.currentDate = this.date = this.dateStr ? new Date(this.dateStr) : new Date();
    var table = Zapatec.Utils.createElement("table");
    this.table = table;
    table.cellSpacing = 0;
    table.cellPadding = 0;
    Zapatec.Utils.createProperty(table, "calendar", this);
    Zapatec.Utils.addEvent(table, "mousedown", Zapatec.Calendar.tableMouseDown);
    var div = Zapatec.Utils.createElement("div");
    this.element = div;
    div.className = "calendar";
    if (Zapatec.is_opera) {
        table.style.width = (this.monthsInRow * ((this.weekNumbers) ? (8) : (7)) * 2 + 4.4 * this.monthsInRow) + "em";
    }
    if (this.isPopup) {
        div.style.position = "absolute";
        div.style.display = "none";
    }
    div.appendChild(table);
    var cell = null;
    var row = null;
    var cal = this;
    var hh = function(text, cs, navtype) {
        cell = Zapatec.Utils.createElement("td", row);
        cell.colSpan = cs;
        cell.className = "button";
        if (Math.abs(navtype) <= 2)
            cell.className += " nav";
        Zapatec.Calendar._add_evs(cell);
        Zapatec.Utils.createProperty(cell, "calendar", cal);
        cell.navtype = navtype;
        if (text.substr(0, 1) != "&") {
            cell.appendChild(document.createTextNode(text));
        }
        else {
            cell.innerHTML = text;
        }
        return cell;
    };
    var hd = function(par, colspan) {
        cell = Zapatec.Utils.createElement("td", par);
        cell.colSpan = colspan;
        cell.className = "button";
        cell.innerHTML = "<div>&nbsp</div>";
        return cell;
    };
    var title_length = ((this.weekNumbers) ? (8) : (7)) * this.monthsInRow - 2;
    var thead = Zapatec.Utils.createElement("thead", table);
    if (this.numberMonths == 1) {
        this.title = thead;
    }
    row = Zapatec.Utils.createElement("tr", thead);
    if (this.helpButton) {
        hh("?", 1, 400).ttip = Zapatec.Calendar.i18n("INFO");
    }
    else {
        hd(row, 1);
    }
    this.title = hh("&nbsp;", title_length, 300);
    this.title.className = "title";
    if (this.isPopup) {
        if (!this.disableDrag) {
            this.title.ttip = Zapatec.Calendar.i18n("DRAG_TO_MOVE");
            this.title.style.cursor = "move";
        }
        if (this.closeButton) {
            hh("&#x00d7;", 1, 200).ttip = Zapatec.Calendar.i18n("CLOSE");
        }
        else {
            hd(row, 1);
        }
    }
    else {
        hd(row, 1);
    }
    row = Zapatec.Utils.createElement("tr", thead);
    this._nav_py = hh("&#x00ab;", 1, -2);
    this._nav_py.ttip = Zapatec.Calendar.i18n("PREV_YEAR");
    this._nav_pm = hh("&#x2039;", 1, -1);
    this._nav_pm.ttip = Zapatec.Calendar.i18n("PREV_MONTH");
    this._nav_now = hh(Zapatec.Calendar.i18n("TODAY"), title_length - 2, 0);
    this._nav_now.ttip = Zapatec.Calendar.i18n("GO_TODAY");
    this._nav_nm = hh("&#x203a;", 1, 1);
    this._nav_nm.ttip = Zapatec.Calendar.i18n("NEXT_MONTH");
    this._nav_ny = hh("&#x00bb;", 1, 2);
    this._nav_ny.ttip = Zapatec.Calendar.i18n("NEXT_YEAR");
    var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
    if (this.numberMonths % this.monthsInRow > 0) {
        ++rowsOfMonths;
    }
    for (var l = 1; l <= rowsOfMonths; ++l) {
        var thead = Zapatec.Utils.createElement("thead", table);
        if (Zapatec.is_opera) {
            thead.style.display = "table-row-group";
        }
        if (this.numberMonths != 1) {
            row = Zapatec.Utils.createElement("tr", thead);
            var title_length = 5;
            this.weekNumbers && ++title_length;
            this.titles[l] = new Array();
            for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
                hd(row, 1);
                this.titles[l][k] = hh("&nbsp;", title_length, 300);
                this.titles[l][k].className = "title";
                hd(row, 1);
            }
        }
        row = Zapatec.Utils.createElement("tr", thead);
        row.className = "daynames";
        for (k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
            if (this.weekNumbers) {
                cell = Zapatec.Utils.createElement("td", row);
                cell.className = "name wn";
                cell.appendChild(window.document.createTextNode(Zapatec.Calendar.i18n("WK")));
                if (k > 1) {
                    Zapatec.Utils.addClass(cell, "month-left-border");
                }
                var cal_wk = Zapatec.Calendar.i18n("WK")
                if (cal_wk == null) {
                    cal_wk = "";
                }
            }
            for (var i = 7; i > 0; --i) {
                cell = Zapatec.Utils.createElement("td", row);
                cell.appendChild(document.createTextNode("&nbsp;"));
            }
        }
        this.firstdayname = row.childNodes[this.weekNumbers ? 1 : 0];
        this.rowsOfDayNames[l] = this.firstdayname;
        this._displayWeekdays();
        var tbody = Zapatec.Utils.createElement("tbody", table);
        this.tbody[l] = tbody;
        for (i = 6; i > 0; --i) {
            row = Zapatec.Utils.createElement("tr", tbody);
            for (k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
                if (this.weekNumbers) {
                    cell = Zapatec.Utils.createElement("td", row);
                    cell.appendChild(document.createTextNode("&nbsp;"));
                }
                for (var j = 7; j > 0; --j) {
                    cell = Zapatec.Utils.createElement("td", row);
                    cell.appendChild(document.createTextNode("&nbsp;"));
                    Zapatec.Utils.createProperty(cell, "calendar", this);
                    Zapatec.Calendar._add_evs(cell);
                }
            }
        }
    }
    var tfoot = Zapatec.Utils.createElement("tfoot", table);
    if (this.showsTime) {
        row = Zapatec.Utils.createElement("tr", tfoot);
        row.className = "time";
        var emptyColspan;
        if (this.monthsInRow != 1) {
            cell = Zapatec.Utils.createElement("td", row);
            emptyColspan = cell.colSpan = Math.ceil((((this.weekNumbers) ? 8 : 7) * (this.monthsInRow - 1)) / 2);
            cell.className = "timetext";
            cell.innerHTML = "&nbsp";
        }
        cell = Zapatec.Utils.createElement("td", row);
        cell.className = "timetext";
        cell.colSpan = this.weekNumbers ? 2 : 1;
        cell.innerHTML = Zapatec.Calendar.i18n("TIME") || "&nbsp;";
        (function() {
            function makeTimePart(className, init, range_start, range_end) {
                var table,tbody,tr,tr2,part;
                if (range_end) {
                    cell = Zapatec.Utils.createElement("td", row);
                    cell.colSpan = 1;
                    if (cal.showsTime != "seconds") {
                        ++cell.colSpan;
                    }
                    cell.className = "parent-" + className;
                    table = Zapatec.Utils.createElement("table", cell);
                    table.cellSpacing = table.cellPadding = 0;
                    if (className == "hour")
                        table.align = "right";
                    table.className = "calendar-time-scroller";
                    tbody = Zapatec.Utils.createElement("tbody", table);
                    tr = Zapatec.Utils.createElement("tr", tbody);
                    tr2 = Zapatec.Utils.createElement("tr", tbody);
                }
                else
                    tr = row;
                part = Zapatec.Utils.createElement("td", tr);
                part.className = className;
                part.appendChild(window.document.createTextNode(init));
                Zapatec.Utils.createProperty(part, "calendar", cal);
                part.ttip = Zapatec.Calendar.i18n("TIME_PART");
                part.navtype = 50;
                part._range = [];
                if (!range_end)
                    part._range = range_start;
                else {
                    part.rowSpan = 2;
                    for (var i = range_start; i <= range_end; ++i) {
                        var txt;
                        if (i < 10 && range_end >= 10)txt = '0' + i;
                        else txt = '' + i;
                        part._range[part._range.length] = txt;
                    }
                    var up = Zapatec.Utils.createElement("td", tr);
                    up.className = "up";
                    up.navtype = 201;
                    Zapatec.Utils.createProperty(up, "calendar", cal);
                    up.timePart = part;
                    if (Zapatec.is_khtml)
                        up.innerHTML = "&nbsp;";
                    Zapatec.Calendar._add_evs(up);
                    var down = Zapatec.Utils.createElement("td", tr2);
                    down.className = "down";
                    down.navtype = 202;
                    Zapatec.Utils.createProperty(down, "calendar", cal);
                    down.timePart = part;
                    if (Zapatec.is_khtml)
                        down.innerHTML = "&nbsp;";
                    Zapatec.Calendar._add_evs(down);
                }
                Zapatec.Calendar._add_evs(part);
                return part;
            }
            ;
            var hrs = cal.currentDate.getHours();
            var mins = cal.currentDate.getMinutes();
            if (cal.showsTime == "seconds") {
                var secs = cal.currentDate.getSeconds();
            }
            var t12 = !cal.time24;
            var pm = (hrs > 12);
            if (t12 && pm)hrs -= 12;
            var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
            H._step = (cal.timeInterval > 30) ? (cal.timeInterval / 60) : 1;
            cell = Zapatec.Utils.createElement("td", row);
            cell.innerHTML = ":";
            cell.className = "colon";
            var M = makeTimePart("minute", mins, 0, 59);
            M._step = ((cal.timeInterval) && (cal.timeInterval < 60)) ? (cal.timeInterval) : 5;
            if (cal.showsTime == "seconds") {
                cell = Zapatec.Utils.createElement("td", row);
                cell.innerHTML = ":";
                cell.className = "colon";
                var S = makeTimePart("minute", secs, 0, 59);
                S._step = 5;
            }
            var AP = null;
            if (t12) {
                AP = makeTimePart("ampm", pm ? Zapatec.Calendar.i18n("pm", "ampm") : Zapatec.Calendar.i18n("am", "ampm"), [Zapatec.Calendar.i18n("am", "ampm"),Zapatec.Calendar.i18n("pm", "ampm")]);
                AP.className += " button";
            }
            else
                Zapatec.Utils.createElement("td", row).innerHTML = "&nbsp;";
            cal.onSetTime = function() {
                var hrs = this.currentDate.getHours();
                var mins = this.currentDate.getMinutes();
                if (this.showsTime == "seconds") {
                    var secs = cal.currentDate.getSeconds();
                }
                if (this.timeInterval) {
                    mins += this.timeInterval - ((mins - 1 + this.timeInterval) % this.timeInterval) - 1;
                }
                while (mins >= 60) {
                    mins -= 60;
                    ++hrs;
                }
                if (this.timeInterval > 60) {
                    var interval = this.timeInterval / 60;
                    if (hrs % interval != 0) {
                        hrs += interval - ((hrs - 1 + interval) % interval) - 1;
                    }
                    if (hrs >= 24) {
                        hrs -= 24;
                    }
                }
                var new_date = new Date(this.currentDate);
                if (this.getDateStatus && this.getDateStatus(this.currentDate, this.currentDate.getFullYear(), this.currentDate.getMonth(), this.currentDate.getDate(), hrs, mins)) {
                    hours = hrs;
                    minutes = mins;
                    do{
                        if (this.timeInterval) {
                            if (this.timeInterval < 60) {
                                minutes += this.timeInterval;
                            }
                            else {
                                hrs += this.timeInterval / 60;
                            }
                        }
                        else {
                            minutes += 5;
                        }
                        if (minutes >= 60) {
                            minutes -= 60;
                            hours += 1;
                        }
                        if (hours >= 24) {
                            hours -= 24;
                        }
                        new_date.setMinutes(minutes);
                        new_date.setHours(hours);
                        if (!this.getDateStatus(new_date, this.currentDate.getFullYear(), this.currentDate.getMonth(), this.currentDate.getDate(), hours, minutes)) {
                            hrs = hours;
                            mins = minutes;
                        }
                    }
                    while ((hrs != hours) || (mins != minutes));
                }
                this.currentDate.setMinutes(mins);
                this.currentDate.setHours(hrs);
                var pm = (hrs >= 12);
                if (pm && t12 && hrs != 12)hrs -= 12;
                if (!pm && t12 && hrs == 0)hrs = 12;
                H.firstChild.data = (hrs < 10) ? ("0" + hrs) : hrs;
                M.firstChild.data = (mins < 10) ? ("0" + mins) : mins;
                if (this.showsTime == "seconds") {
                    S.firstChild.data = (secs < 10) ? ("0" + secs) : secs;
                }
                if (t12)
                    AP.firstChild.data = pm ? Zapatec.Calendar.i18n("pm", "ampm") : Zapatec.Calendar.i18n("am", "ampm");
            };
            cal.onUpdateTime = function() {
                var date = this.currentDate;
                var h = parseInt(H.firstChild.data, 10);
                if (t12) {
                    if (/pm/i.test(AP.firstChild.data) && h < 12)
                        h += 12;
                    else if (/am/i.test(AP.firstChild.data) && h == 12)
                        h = 0;
                }
                var d = date.getDate();
                var m = date.getMonth();
                var y = date.getFullYear();
                date.setHours(h);
                date.setMinutes(parseInt(M.firstChild.data, 10));
                if (this.showsTime == "seconds") {
                    date.setSeconds(parseInt(S.firstChild.data, 10));
                }
                date.setFullYear(y);
                date.setMonth(m);
                date.setDate(d);
                this.dateClicked = false;
                this.callHandler();
            };
        })();
        if (this.monthsInRow != 1) {
            cell = Zapatec.Utils.createElement("td", row);
            cell.colSpan = ((this.weekNumbers) ? 8 : 7) * (this.monthsInRow - 1) - Math.ceil(emptyColspan);
            cell.className = "timetext";
            cell.innerHTML = "&nbsp";
        }
    }
    else {
        this.onSetTime = this.onUpdateTime = function() {
        };
    }
    row = Zapatec.Utils.createElement("tr", tfoot);
    row.className = "footrow";
    cell = hh(Zapatec.Calendar.i18n("SEL_DATE"), this.weekNumbers ? (8 * this.numberMonths) : (7 * this.numberMonths), 300);
    cell.className = "ttip";
    if (this.isPopup && !this.disableDrag) {
        cell.ttip = Zapatec.Calendar.i18n("DRAG_TO_MOVE");
        cell.style.cursor = "move";
    }
    this.tooltips = cell;
    div = this.monthsCombo = Zapatec.Utils.createElement("div", this.element);
    div.className = "combo";
    for (i = 0; i < 12; ++i) {
        var mn = Zapatec.Utils.createElement("div");
        mn.className = Zapatec.is_ie ? "label-IEfix" : "label";
        mn.month = i;
        mn.appendChild(window.document.createTextNode(Zapatec.Calendar.i18n(i, "smn")));
        div.appendChild(mn);
    }
    div = this.yearsCombo = Zapatec.Utils.createElement("div", this.element);
    div.className = "combo";
    for (i = 12; i > 0; --i) {
        var yr = Zapatec.Utils.createElement("div");
        yr.className = Zapatec.is_ie ? "label-IEfix" : "label";
        yr.appendChild(window.document.createTextNode("&nbsp;"));
        div.appendChild(yr);
    }
    div = this.histCombo = Zapatec.Utils.createElement("div", this.element);
    div.className = "combo history";
    this._init(this.firstDayOfWeek, this.date);
    parent.appendChild(this.element);
};
Zapatec.Calendar._keyEvent = function(ev) {
    if (!window.calendar) {
        return false;
    }
    (Zapatec.is_ie) && (ev = window.event);
    var cal = window.calendar;
    var act = (Zapatec.is_ie || ev.type == "keypress");
    var K = ev.keyCode;
    var date = new Date(cal.date);
    if (ev.ctrlKey) {
        switch (K) {case 37:act && Zapatec.Calendar.cellClick(cal._nav_pm);break;case 38:act && Zapatec.Calendar.cellClick(cal._nav_py);break;case 39:act && Zapatec.Calendar.cellClick(cal._nav_nm);break;case 40:act && Zapatec.Calendar.cellClick(cal._nav_ny);break;default:return false;}
    }
    else switch (K) {case 32:Zapatec.Calendar.cellClick(cal._nav_now);break;case 27:act && cal.callCloseHandler();break;case 37:if (act && !cal.multiple) {
        date.setTime(date.getTime() - 86400000);
        cal.setDate(date);
    }
        break;case 38:if (act && !cal.multiple) {
        date.setTime(date.getTime() - 7 * 86400000);
        cal.setDate(date);
    }
        break;case 39:if (act && !cal.multiple) {
        date.setTime(date.getTime() + 86400000);
        cal.setDate(date);
    }
        break;case 40:if (act && !cal.multiple) {
        date.setTime(date.getTime() + 7 * 86400000);
        cal.setDate(date);
    }
        break;case 13:if (act) {
        Zapatec.Calendar.cellClick(cal.currentDateEl);
    }
        break;default:return false;}
    return Zapatec.Utils.stopEvent(ev);
};
Zapatec.Calendar.prototype._init = function(firstDayOfWeek, date, last) {
    var
            today = new Date(),TD = today.getDate(),TY = today.getFullYear(),TM = today.getMonth();
    if (this.getDateStatus && !last) {
        var status = this.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate());
        var backupDate = new Date(date);
        while (((status == true) || (status == "disabled")) && (backupDate.getMonth() == date.getMonth())) {
            date.setTime(date.getTime() + 86400000);
            var status = this.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate());
        }
        if (backupDate.getMonth() != date.getMonth()) {
            date = new Date(backupDate);
            while (((status == true) || (status == "disabled")) && (backupDate.getMonth() == date.getMonth())) {
                date.setTime(date.getTime() - 86400000);
                var status = this.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate());
            }
        }
        if (backupDate.getMonth() != date.getMonth()) {
            last = true;
            date = new Date(backupDate);
        }
    }
    var year = date.getFullYear();
    var month = date.getMonth();
    var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
    var minMonth;
    var diffMonth,last_row,before_control;
    if (!this.vertical) {
        diffMonth = (this.controlMonth - 1);
        minMonth = month - diffMonth;
    }
    else {
        last_row = ((this.numberMonths - 1) % this.monthsInRow) + 1;
        before_control = (this.controlMonth - 1) % this.monthsInRow;
        bottom = (before_control >= (last_row) ? (last_row) : (before_control));
        diffMonth = (before_control) * (rowsOfMonths - 1) + Math.floor((this.controlMonth - 1) / this.monthsInRow) + bottom;
        minMonth = month - diffMonth;
    }
    var minYear = year;
    if (minMonth < 0) {
        minMonth += 12;
        --minYear;
    }
    var maxMonth = minMonth + this.numberMonths - 1;
    var maxYear = minYear;
    if (maxMonth > 11) {
        maxMonth -= 12;
        ++maxYear;
    }
    function disableControl(ctrl) {
        Zapatec.Calendar._del_evs(ctrl);
        ctrl.disabled = true;
        ctrl.className = "button";
        ctrl.innerHTML = "<div>&nbsp</div>";
    }
    function enableControl(ctrl, sign) {
        Zapatec.Calendar._add_evs(ctrl);
        ctrl.disabled = false;
        ctrl.className = "button nav";
        ctrl.innerHTML = sign;
    }
    if ((minYear <= this.minYear) || !this.yearNav) {
        if (!this._nav_py.disabled) {
            disableControl(this._nav_py);
        }
    }
    else {
        if (this._nav_py.disabled) {
            enableControl(this._nav_py, "&#x00ab;");
        }
    }
    if (maxYear >= this.maxYear || !this.yearNav) {
        if (!this._nav_ny.disabled) {
            disableControl(this._nav_ny);
        }
    }
    else {
        if (this._nav_ny.disabled) {
            enableControl(this._nav_ny, "&#x00bb;");
        }
    }
    if (((minYear == this.minYear) && (minMonth <= this.minMonth)) || (minYear < this.minYear)) {
        if (!this._nav_pm.disabled) {
            disableControl(this._nav_pm);
        }
    }
    else {
        if (this._nav_pm.disabled) {
            enableControl(this._nav_pm, "&#x2039;");
        }
    }
    if (((maxYear == this.maxYear) && (maxMonth >= this.maxMonth)) || (maxYear > this.maxYear)) {
        if (!this._nav_nm.disabled) {
            disableControl(this._nav_nm);
        }
    }
    else {
        if (this._nav_nm.disabled) {
            enableControl(this._nav_nm, "&#x203a;");
        }
    }
    upperMonth = this.maxMonth + 1;
    upperYear = this.maxYear;
    if (upperMonth > 11) {
        upperMonth -= 12;
        ++upperYear;
    }
    bottomMonth = this.minMonth - 1;
    bottomYear = this.minYear;
    if (bottomMonth < 0) {
        bottomMonth += 12;
        --bottomYear;
    }
    maxDate1 = new Date(maxYear, maxMonth, date.getMonthDays(maxMonth), 23, 59, 59, 999);
    maxDate2 = new Date(upperYear, upperMonth, 1, 0, 0, 0, 0);
    minDate1 = new Date(minYear, minMonth, 1, 0, 0, 0, 0);
    minDate2 = new Date(bottomYear, bottomMonth, date.getMonthDays(bottomMonth), 23, 59, 59, 999);
    if (maxDate1.getTime() > maxDate2.getTime()) {
        date.setTime(date.getTime() - (maxDate1.getTime() - maxDate2.getTime()));
    }
    if (minDate1.getTime() < minDate2.getTime()) {
        date.setTime(date.getTime() + (minDate2.getTime() - minDate1.getTime()) + 1);
    }
    delete maxDate1;
    delete maxDate2;
    delete minDate1;
    delete minDate2;
    this.firstDayOfWeek = firstDayOfWeek;
    if (!last) {
        this.currentDate = date;
    }
    this.date = date;
    (this.date = new Date(this.date)).setDateOnly(date);
    year = this.date.getFullYear();
    month = this.date.getMonth();
    var initMonth = date.getMonth();
    var mday = this.date.getDate();
    var no_days = date.getMonthDays();
    var months = new Array();
    if (this.numberMonths % this.monthsInRow > 0) {
        ++rowsOfMonths;
    }
    for (var l = 1; l <= rowsOfMonths; ++l) {
        months[l] = new Array();
        for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
            var tmpDate = new Date(date);
            if (this.vertical) {
                var validMonth = date.getMonth() - diffMonth + ((k - 1) * (rowsOfMonths - 1) + (l - 1) + ((last_row < k) ? (last_row) : (k - 1)));
            }
            else {
                var validMonth = date.getMonth() - diffMonth + (l - 1) * this.monthsInRow + k - 1;
            }
            if (validMonth < 0) {
                tmpDate.setFullYear(tmpDate.getFullYear() - 1);
                validMonth = 12 + validMonth;
            }
            if (validMonth > 11) {
                tmpDate.setFullYear(tmpDate.getFullYear() + 1);
                validMonth = validMonth - 12;
            }
            tmpDate.setDate(1);
            tmpDate.setMonth(validMonth);
            var day1 = (tmpDate.getDay() - this.firstDayOfWeek) % 7;
            if (day1 < 0)
                day1 += 7;
            var hrs = tmpDate.getHours();
            tmpDate.setDate(-day1);
            tmpDate.setDate(tmpDate.getDate() + 1);
            if (hrs != tmpDate.getHours()) {
                tmpDate.setDate(1);
                tmpDate.setMonth(validMonth);
                tmpDate.setDate(-day1);
                tmpDate.setDate(tmpDate.getDate() + 1);
            }
            months[l][k] = tmpDate;
        }
    }
    var MN = Zapatec.Calendar.i18n(month, "smn");
    var weekend = Zapatec.Calendar.i18n("WEEKEND");
    var dates = this.multiple ? (this.datesCells = {}) : null;
    var DATETXT = this.getDateText;
    for (var l = 1; l <= rowsOfMonths; ++l) {
        var row = this.tbody[l].firstChild;
        for (var i = 7; --i > 0; row = row.nextSibling) {
            var cell = row.firstChild;
            var hasdays = false;
            for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
                date = months[l][k];
                if (this.weekNumbers) {
                    cell.className = " day wn";
                    cell.innerHTML = date.getWeekNumber();
                    if (k > 1) {
                        Zapatec.Utils.addClass(cell, "month-left-border");
                    }
                    cell = cell.nextSibling;
                }
                row.className = "daysrow";
                var iday;
                for (j = 7; cell && (iday = date.getDate()) && (j > 0); date.setDate(iday + 1),((date.getDate() == iday) ? (date.setHours(1) && date.setDate(iday + 1)) : (false)),cell = cell.nextSibling,--j) {
                    var
                            wday = date.getDay(),dmonth = date.getMonth(),dyear = date.getFullYear();
                    cell.className = " day";
                    if ((!this.weekNumbers) && (j == 7) && (k != 1)) {
                        Zapatec.Utils.addClass(cell, "month-left-border");
                    }
                    if ((j == 1) && (k != this.monthsInRow)) {
                        Zapatec.Utils.addClass(cell, "month-right-border");
                    }
                    if (this.vertical) {
                        validMonth = initMonth - diffMonth + ((k - 1) * (rowsOfMonths - 1) + (l - 1) + ((last_row < k) ? (last_row) : (k - 1)));
                    }
                    else {
                        validMonth = initMonth - diffMonth + ((l - 1) * this.monthsInRow + k - 1);
                    }
                    if (validMonth < 0) {
                        validMonth = 12 + validMonth;
                    }
                    if (validMonth > 11) {
                        validMonth = validMonth - 12;
                    }
                    var current_month = !(cell.otherMonth = !(dmonth == validMonth));
                    if (!current_month) {
                        if (this.showsOtherMonths)
                            cell.className += " othermonth";
                        else {
                            cell.className += " true";
                            cell.innerHTML = "<div>&nbsp;</div>";
                            continue;
                        }
                    }
                    else
                        hasdays = true;
                    cell.innerHTML = DATETXT ? DATETXT(date, dyear, dmonth, iday) : iday;
                    dates && (dates[date.print("%Y%m%d")] = cell);
                    if (this.getDateStatus) {
                        var status = this.getDateStatus(date, dyear, dmonth, iday);
                        if (this.getDateToolTip) {
                            var toolTip = this.getDateToolTip(date, dyear, dmonth, iday);
                            if (toolTip)
                                cell.title = toolTip;
                        }
                        if (status == true) {
                            cell.className += " disabled";
                        }
                        else {
                            cell.className += " " + status;
                        }
                    }
                    if (!cell.disabled) {
                        cell.caldate = [dyear,dmonth,iday];
                        cell.ttip = "_";
                        if (!this.multiple && current_month && iday == this.currentDate.getDate() && this.hiliteToday && (dmonth == this.currentDate.getMonth()) && (dyear == this.currentDate.getFullYear())) {
                            cell.className += " selected";
                            this.currentDateEl = cell;
                        }
                        if (dyear == TY && dmonth == TM && iday == TD) {
                            cell.className += " today";
                            cell.ttip += Zapatec.Calendar.i18n("PART_TODAY");
                        }
                        if ((weekend != null) && (weekend.indexOf(wday.toString()) != -1)) {
                            cell.className += cell.otherMonth ? " oweekend" : " weekend";
                        }
                    }
                }
                if (!(hasdays || this.showsOtherMonths))
                    row.className = "emptyrow";
            }
            if ((i == 1) && (l < rowsOfMonths)) {
                if (row.className == "emptyrow") {
                    row = row.previousSibling;
                }
                cell = row.firstChild;
                while (cell != null) {
                    Zapatec.Utils.addClass(cell, "month-bottom-border");
                    cell = cell.nextSibling;
                }
            }
        }
    }
    if (this.numberMonths == 1) {
        this.title.innerHTML = Zapatec.Calendar.i18n(month, "mn") + ", " + year;
        if (this.params && this.params.titleHtml)
            if (typeof this.params.titleHtml == 'function')
                this.title.innerHTML = this.params.titleHtml(this.title.innerHTML, month, year)
            else
                this.title.innerHTML += this.params.titleHtml
    }
    else {
        if (this.params && this.params.titleHtml)
            if (typeof this.params.titleHtml == 'function')
                this.title.innerHTML = this.params.titleHtml(Zapatec.Calendar.i18n(month, "mn") + ", " + year, month, year)
            else
                this.title.innerHTML = this.params.titleHtml
        for (var l = 1; l <= rowsOfMonths; ++l) {
            for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
                if (this.vertical) {
                    validMonth = month - diffMonth + ((k - 1) * (rowsOfMonths - 1) + (l - 1) + ((last_row < k) ? (last_row) : (k - 1)));
                }
                else {
                    validMonth = month - diffMonth + (l - 1) * this.monthsInRow + k - 1;
                }
                validYear = year;
                if (validMonth < 0) {
                    --validYear;
                    validMonth = 12 + validMonth;
                }
                if (validMonth > 11) {
                    ++validYear;
                    validMonth = validMonth - 12;
                }
                this.titles[l][k].innerHTML = Zapatec.Calendar.i18n(validMonth, "mn") + ", " + validYear;
            }
        }
    }
    this.onSetTime();
    this._initMultipleDates();
    this.updateWCH();
};
Zapatec.Calendar.prototype._initMultipleDates = function() {
    if (this.multiple) {
        for (var i in this.multiple) {
            var cell = this.datesCells[i];
            var d = this.multiple[i];
            if (!d)
                continue;
            if (cell)
                cell.className += " selected";
        }
    }
};
Zapatec.Calendar.prototype._toggleMultipleDate = function(date) {
    if (this.multiple) {
        var ds = date.print("%Y%m%d");
        var cell = this.datesCells[ds];
        if (cell) {
            var d = this.multiple[ds];
            if (!d) {
                Zapatec.Utils.addClass(cell, "selected");
                this.multiple[ds] = date;
            }
            else {
                Zapatec.Utils.removeClass(cell, "selected");
                delete this.multiple[ds];
            }
        }
    }
};
Zapatec.Calendar.prototype.setDateToolTipHandler = function(unaryFunction) {
    this.getDateToolTip = unaryFunction;
};
Zapatec.Calendar.prototype.setDate = function(date, justInit) {
    if (!date)
        date = new Date();
    if (!date.equalsTo(this.date)) {
        var year = date.getFullYear(),m = date.getMonth();
        if (year < this.minYear || (year == this.minYear && m < this.minMonth))
            this.showHint("<div class='error'>" + Zapatec.Calendar.i18n("E_RANGE") + " Â»Â»Â»</div>");
        else if (year > this.maxYear || (year == this.maxYear && m > this.maxMonth))
            this.showHint("<div class='error'>Â«Â«Â« " + Zapatec.Calendar.i18n("E_RANGE") + "</div>");
        this._init(this.firstDayOfWeek, date, justInit);
    }
};
Zapatec.Calendar.prototype.showHint = function(text) {
    this.tooltips.innerHTML = text;
};
Zapatec.Calendar.prototype.reinit = function() {
    this._init(this.firstDayOfWeek, this.date);
};
Zapatec.Calendar.prototype.refresh = function() {
    var p = this.isPopup ? null : this.element.parentNode;
    var x = parseInt(this.element.style.left);
    var y = parseInt(this.element.style.top);
    this.destroy();
    this.dateStr = this.date;
    this.create(p);
    if (this.isPopup)
        this.showAt(x, y);
    else
        this.show();
};
Zapatec.Calendar.prototype.setFirstDayOfWeek = function(firstDayOfWeek) {
    if (this.firstDayOfWeek != firstDayOfWeek) {
        this._init(firstDayOfWeek, this.date);
        var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
        if (this.numberMonths % this.monthsInRow > 0) {
            ++rowsOfMonths;
        }
        for (var l = 1; l <= rowsOfMonths; ++l) {
            this.firstdayname = this.rowsOfDayNames[l];
            this._displayWeekdays();
        }
    }
};
Zapatec.Calendar.prototype.setDateStatusHandler = Zapatec.Calendar.prototype.setDisabledHandler = function(unaryFunction) {
    this.getDateStatus = unaryFunction;
};
Zapatec.Calendar.prototype.setRange = function(A, Z) {
    var m,a = Math.min(A, Z),z = Math.max(A, Z);
    this.minYear = m = Math.floor(a);
    this.minMonth = (m == a) ? 0 : Math.ceil((a - m) * 100 - 1);
    this.maxYear = m = Math.floor(z);
    this.maxMonth = (m == z) ? 11 : Math.ceil((z - m) * 100 - 1);
};
Zapatec.Calendar.prototype.setMultipleDates = function(multiple) {
    if (!multiple || typeof multiple == "undefined")return;
    this.multiple = {};
    for (var i = multiple.length; --i >= 0;) {
        var d = multiple[i];
        var ds = d.print("%Y%m%d");
        this.multiple[ds] = d;
    }
};
Zapatec.Calendar.prototype.submitFlatDates = function()
{
    if (typeof this.params.flatCallback == "function") {
        Zapatec.Utils.sortOrder = (this.sortOrder != "asc" && this.sortOrder != "desc" && this.sortOrder != "none") ? "none" : this.sortOrder;
        if (this.multiple && (Zapatec.Utils.sortOrder != "none")) {
            var dateArray = new Array();
            for (var i in this.multiple) {
                var currentDate = this.multiple[i];
                if (currentDate) {
                    dateArray[dateArray.length] = currentDate;
                }
                dateArray.sort(Zapatec.Utils.compareDates);
            }
            this.multiple = {};
            for (var i = 0; i < dateArray.length; i++) {
                var d = dateArray[i];
                var ds = d.print("%Y%m%d");
                this.multiple[ds] = d;
            }
        }
        this.params.flatCallback(this);
    }
}
Zapatec.Calendar.prototype.callHandler = function() {
    if (this.onSelected) {
        this.onSelected(this, this.date.print(this.dateFormat));
    }
};
Zapatec.Calendar.prototype.updateHistory = function() {
    var a,i,d,tmp,s,str = "",len = Zapatec.Calendar.prefs.hsize - 1;
    if (Zapatec.Calendar.prefs.history) {
        a = Zapatec.Calendar.prefs.history.split(/,/);
        i = 0;
        while (i < len && (tmp = a[i++])) {
            s = tmp.split(/\//);
            d = new Date(parseInt(s[0], 10), parseInt(s[1], 10) - 1, parseInt(s[2], 10), parseInt(s[3], 10), parseInt(s[4], 10));
            if (!d.dateEqualsTo(this.date))
                str += "," + tmp;
        }
    }
    Zapatec.Calendar.prefs.history = this.date.print("%Y/%m/%d/%H/%M") + str;
    Zapatec.Calendar.savePrefs();
};
Zapatec.Calendar.prototype.callCloseHandler = function() {
    if (this.dateClicked) {
        this.updateHistory();
    }
    if (this.onClose) {
        this.onClose(this);
    }
    this.hideShowCovered();
};
Zapatec.Calendar.prototype.destroy = function() {
    this.hide();
    Zapatec.Utils.destroy(this.element);
    Zapatec.Utils.destroy(this.WCH);
    Zapatec.Calendar._C = null;
    window.calendar = null;
};
Zapatec.Calendar.prototype.reparent = function(new_parent) {
    var el = this.element;
    el.parentNode.removeChild(el);
    new_parent.appendChild(el);
};
Zapatec.Calendar._checkCalendar = function(ev) {
    if (!window.calendar) {
        return false;
    }
    var el = Zapatec.is_ie ? Zapatec.Utils.getElement(ev) : Zapatec.Utils.getTargetElement(ev);
    for (; el != null && el != calendar.element; el = el.parentNode);
    if (el == null) {
        window.calendar.callCloseHandler();
    }
};
Zapatec.Calendar.prototype.updateWCH = function(other_el) {
    Zapatec.Utils.setupWCH_el(this.WCH, this.element, other_el);
};
Zapatec.Calendar.prototype.show = function() {
    var rows = this.table.getElementsByTagName("tr");
    for (var i = rows.length; i > 0;) {
        var row = rows[--i];
        Zapatec.Utils.removeClass(row, "rowhilite");
        var cells = row.getElementsByTagName("td");
        for (var j = cells.length; j > 0;) {
            var cell = cells[--j];
            Zapatec.Utils.removeClass(cell, "hilite");
            Zapatec.Utils.removeClass(cell, "active");
        }
    }
    if (this.element.style.display != "block") {
        this.element.style.display = "block";
    }
    this.hidden = false;
    if (this.isPopup) {
        this.updateWCH();
        window.calendar = this;
        if (!this.noGrab) {
            Zapatec.Utils.addEvent(window.document, "keydown", Zapatec.Calendar._keyEvent);
            Zapatec.Utils.addEvent(window.document, "keypress", Zapatec.Calendar._keyEvent);
            Zapatec.Utils.addEvent(window.document, "mousedown", Zapatec.Calendar._checkCalendar);
        }
    }
    this.hideShowCovered();
};
Zapatec.Calendar.prototype.hide = function() {
    if (this.isPopup) {
        Zapatec.Utils.removeEvent(window.document, "keydown", Zapatec.Calendar._keyEvent);
        Zapatec.Utils.removeEvent(window.document, "keypress", Zapatec.Calendar._keyEvent);
        Zapatec.Utils.removeEvent(window.document, "mousedown", Zapatec.Calendar._checkCalendar);
    }
    this.element.style.display = "none";
    Zapatec.Utils.hideWCH(this.WCH);
    this.hidden = true;
    this.hideShowCovered();
};
Zapatec.Calendar.prototype.showAt = function(x, y) {
    var s = this.element.style;
    s.left = x + "px";
    s.top = y + "px";
    this.show();
};
Zapatec.Calendar.prototype.showAtElement = function(el, opts) {
    var self = this;
    var p = Zapatec.Utils.getElementOffset(el);
    if (!opts || typeof opts != "string") {
        this.showAt(p.x, p.y + el.offsetHeight);
        return true;
    }
    this.element.style.display = "block";
    var w = self.element.offsetWidth;
    var h = self.element.offsetHeight;
    self.element.style.display = "none";
    var valign = opts.substr(0, 1);
    var halign = "l";
    if (opts.length > 1) {
        halign = opts.substr(1, 1);
    }
    switch (valign) {case"T":p.y -= h;break;case"B":p.y += el.offsetHeight;break;case"C":p.y += (el.offsetHeight - h) / 2;break;case"t":p.y += el.offsetHeight - h;break;case"b":break;}
    switch (halign) {case"L":p.x -= w;break;case"R":p.x += el.offsetWidth;break;case"C":p.x += (el.offsetWidth - w) / 2;break;case"l":p.x += el.offsetWidth - w;break;case"r":break;}
    p.width = w;
    p.height = h;
    self.monthsCombo.style.display = "none";
    Zapatec.Utils.fixBoxPosition(p, 10);
    self.showAt(p.x, p.y);
};
Zapatec.Calendar.prototype.setDateFormat = function(str) {
    this.dateFormat = str;
};
Zapatec.Calendar.prototype.setTtDateFormat = function(str) {
    this.ttDateFormat = str;
};
Zapatec.Calendar.prototype.parseDate = function(str, fmt) {
    if (!str)
        return this.setDate(this.date);
    if (!fmt)
        fmt = this.dateFormat;
    var date = Date.parseDate(str, fmt);
    return this.setDate(date);
};
Zapatec.Calendar.prototype.hideShowCovered = function() {
    if (!Zapatec.is_ie5)
        return;
    var self = this;
    function getVisib(obj) {
        var value = obj.style.visibility;
        if (!value) {
            if (window.document.defaultView && typeof(window.document.defaultView.getComputedStyle) == "function") {
                if (!Zapatec.is_khtml)
                    value = window.document.defaultView.getComputedStyle(obj, "").getPropertyValue("visibility");
                else
                    value = '';
            }
            else if (obj.currentStyle) {
                value = obj.currentStyle.visibility;
            }
            else
                value = '';
        }
        return value;
    }
    ;
    var tags = ["applet","iframe","select"];
    var el = self.element;
    var p = Zapatec.Utils.getAbsolutePos(el);
    var EX1 = p.x;
    var EX2 = el.offsetWidth + EX1;
    var EY1 = p.y;
    var EY2 = el.offsetHeight + EY1;
    for (var k = tags.length; k > 0;) {
        var ar = window.document.getElementsByTagName(tags[--k]);
        var cc = null;
        for (var i = ar.length; i > 0;) {
            cc = ar[--i];
            p = Zapatec.Utils.getAbsolutePos(cc);
            var CX1 = p.x;
            var CX2 = cc.offsetWidth + CX1;
            var CY1 = p.y;
            var CY2 = cc.offsetHeight + CY1;
            if (self.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
                if (!cc.__msh_save_visibility) {
                    cc.__msh_save_visibility = getVisib(cc);
                }
                cc.style.visibility = cc.__msh_save_visibility;
            }
            else {
                if (!cc.__msh_save_visibility) {
                    cc.__msh_save_visibility = getVisib(cc);
                }
                cc.style.visibility = "hidden";
            }
        }
    }
};
Zapatec.Calendar.prototype._displayWeekdays = function() {
    var fdow = this.firstDayOfWeek;
    var cell = this.firstdayname;
    var weekend = Zapatec.Calendar.i18n("WEEKEND");
    for (k = 1; (k <= this.monthsInRow) && (cell); ++k) {
        for (var i = 0; i < 7; ++i) {
            cell.className = " day name";
            if ((!this.weekNumbers) && (i == 0) && (k != 1)) {
                Zapatec.Utils.addClass(cell, "month-left-border");
            }
            if ((i == 6) && (k != this.monthsInRow)) {
                Zapatec.Utils.addClass(cell, "month-right-border");
            }
            var realday = (i + fdow) % 7;
            if ((!this.disableFdowClick) && ((this.params && this.params.fdowClick) || i)) {
                if (Zapatec.Calendar.i18n("DAY_FIRST") != null) {
                    cell.ttip = Zapatec.Calendar.i18n("DAY_FIRST").replace("%s", Zapatec.Calendar.i18n(realday, "dn"));
                }
                cell.navtype = 100;
                cell.calendar = this;
                cell.fdow = realday;
                Zapatec.Calendar._add_evs(cell);
            }
            if ((weekend != null) && (weekend.indexOf(realday.toString()) != -1)) {
                Zapatec.Utils.addClass(cell, "weekend");
            }
            cell.innerHTML = Zapatec.Calendar.i18n((i + fdow) % 7, "sdn");
            cell = cell.nextSibling;
        }
        if (this.weekNumbers && cell) {
            cell = cell.nextSibling;
        }
    }
};
Zapatec.Utils.compareDates = function(date1, date2)
{
    if (Zapatec.Calendar.prefs.sortOrder == "asc")
        return date1 - date2;
    else
        return date2 - date1;
}
Zapatec.Calendar.prototype._hideCombos = function() {
    this.monthsCombo.style.display = "none";
    this.yearsCombo.style.display = "none";
    this.histCombo.style.display = "none";
    this.updateWCH();
};
Zapatec.Calendar.prototype._dragStart = function(ev) {
    ev || (ev = window.event);
    if (this.dragging) {
        return;
    }
    this.dragging = true;
    var posX = ev.clientX + window.document.body.scrollLeft;
    var posY = ev.clientY + window.document.body.scrollTop;
    var st = this.element.style;
    this.xOffs = posX - parseInt(st.left);
    this.yOffs = posY - parseInt(st.top);
    Zapatec.Utils.addEvent(window.document, "mousemove", Zapatec.Calendar.calDragIt);
    Zapatec.Utils.addEvent(window.document, "mouseover", Zapatec.Calendar.calDragIt);
    Zapatec.Utils.addEvent(window.document, "mouseup", Zapatec.Calendar.calDragEnd);
};
Date._MD = [31,28,31,30,31,30,31,31,30,31,30,31];
Date.SECOND = 1000;
Date.MINUTE = 60 * Date.SECOND;
Date.HOUR = 60 * Date.MINUTE;
Date.DAY = 24 * Date.HOUR;
Date.WEEK = 7 * Date.DAY;
Date.prototype.getMonthDays = function(month) {
    var year = this.getFullYear();
    if (typeof month == "undefined") {
        month = this.getMonth();
    }
    if (((0 == (year % 4)) && ((0 != (year % 100)) || (0 == (year % 400)))) && month == 1) {
        return 29;
    }
    else {
        return Date._MD[month];
    }
};
Date.prototype.getDayOfYear = function() {
    var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
    var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
    var time = now - then;
    return Math.round(time / Date.DAY);
};
Date.prototype.getWeekNumber = function() {
    var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
    var DoW = d.getDay();
    d.setDate(d.getDate() - (DoW + 6) % 7 + 3);
    var ms = d.valueOf();
    d.setMonth(0);
    d.setDate(4);
    return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};
Date.prototype.equalsTo = function(date) {
    return((this.getFullYear() == date.getFullYear()) && (this.getMonth() == date.getMonth()) && (this.getDate() == date.getDate()) && (this.getHours() == date.getHours()) && (this.getMinutes() == date.getMinutes()));
};
Date.prototype.dateEqualsTo = function(date) {
    return((this.getFullYear() == date.getFullYear()) && (this.getMonth() == date.getMonth()) && (this.getDate() == date.getDate()));
};
Date.prototype.setDateOnly = function(date) {
    var tmp = new Date(date);
    this.setDate(1);
    this.setFullYear(tmp.getFullYear());
    this.setMonth(tmp.getMonth());
    this.setDate(tmp.getDate());
};
Date.prototype.print = function(str) {
    var m = this.getMonth();
    var d = this.getDate();
    var y = this.getFullYear();
    var wn = this.getWeekNumber();
    var w = this.getDay();
    var s = {};
    var hr = this.getHours();
    var pm = (hr >= 12);
    var ir = (pm) ? (hr - 12) : hr;
    var dy = this.getDayOfYear();
    if (ir == 0)
        ir = 12;
    var min = this.getMinutes();
    var sec = this.getSeconds();
    s["%a"] = Zapatec.Calendar.i18n(w, "sdn");
    s["%A"] = Zapatec.Calendar.i18n(w, "dn");
    s["%b"] = Zapatec.Calendar.i18n(m, "smn");
    s["%B"] = Zapatec.Calendar.i18n(m, "mn");
    s["%C"] = 1 + Math.floor(y / 100);
    s["%d"] = (d < 10) ? ("0" + d) : d;
    s["%e"] = d;
    s["%H"] = (hr < 10) ? ("0" + hr) : hr;
    s["%I"] = (ir < 10) ? ("0" + ir) : ir;
    s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy;
    s["%k"] = hr ? hr : "0";
    s["%l"] = ir;
    s["%m"] = (m < 9) ? ("0" + (1 + m)) : (1 + m);
    s["%M"] = (min < 10) ? ("0" + min) : min;
    s["%n"] = "\n";
    s["%p"] = pm ? "PM" : "AM";
    s["%P"] = pm ? "pm" : "am";
    s["%s"] = Math.floor(this.getTime() / 1000);
    s["%S"] = (sec < 10) ? ("0" + sec) : sec;
    s["%t"] = "\t";
    s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
    s["%u"] = (w == 0) ? 7 : w;
    s["%w"] = w ? w : "0";
    s["%y"] = '' + y % 100;
    if (s["%y"] < 10) {
        s["%y"] = "0" + s["%y"];
    }
    s["%Y"] = y;
    s["%%"] = "%";
    var re = /%./g;
    var a = str.match(re) || [];
    for (var i = 0; i < a.length; i++) {
        var tmp = s[a[i]];
        if (tmp) {
            re = new RegExp(a[i], 'g');
            str = str.replace(re, tmp);
        }
    }
    return str;
};
Date.parseDate = function(str, format) {
    var fmt = format,strPointer = 0,token = null,parseFunc = null,valueLength = null,valueRange = null,valueType = null,date = new Date(),values = {};
    var numberRules = ["%d","%H","%I","%m","%M","%S","%s","%W","%u","%w","%y","%e","%k","%l","%s","%Y","%C"];
    function isNumberRule(rule) {
        if (Zapatec.Utils.arrIndexOf(numberRules, rule) != -1) {
            return true;
        }
        return false;
    }
    function parseString() {
        for (var iString = valueRange[0]; iString < valueRange[1]; ++iString) {
            var value = Zapatec.Calendar.i18n(iString, valueType);
            if (!value) {
                return null;
            }
            if (value == str.substr(strPointer, value.length)) {
                valueLength = value.length;
                return iString;
            }
        }
        return null;
    }
    function parseNumber() {
        var val = str.substr(strPointer, valueLength);
        if (val.length != valueLength || /$\d+^/.test(val)) {
            return null;
        }
        return parseInt(val, 10);
    }
    function parseAMPM() {
        var result = (str.substr(strPointer, valueLength).toLowerCase() == Zapatec.Calendar.i18n("pm", "ampm")) ? true : false;
        return result || ((str.substr(strPointer, valueLength).toLowerCase() == Zapatec.Calendar.i18n("am", "ampm")) ? false : null);
    }
    function parseCharacter() {
        return"";
    }
    function parseRule(rule) {
        return(values[rule] = parseFunc());
    }
    function wasParsed(rule) {
        if (typeof rule == "undefined" || rule === null) {
            return false;
        }
        return true;
    }
    function getValue() {
        for (var i = 0; i < arguments.length; ++i) {
            if (arguments[i] !== null && typeof arguments[i] != "undefined" && !isNaN(arguments[i])) {
                return arguments[i];
            }
        }
        return null;
    }
    if (typeof fmt != "string" || typeof str != "string" || str == "" || fmt == "") {
        return null;
    }
    while (fmt) {
        parseFunc = parseNumber;
        valueLength = fmt.indexOf("%");
        valueLength = (valueLength == -1) ? fmt.length : valueLength;
        token = fmt.slice(0, valueLength);
        if (token != str.substr(strPointer, valueLength)) {
            return null;
        }
        strPointer += valueLength;
        fmt = fmt.slice(valueLength);
        if (fmt == "") {
            break;
        }
        token = fmt.slice(0, 2);
        valueLength = 2;
        switch (token) {case"%A":case"%a":{
            valueType = (token == "%A") ? "dn" : "sdn";
            valueRange = [0,7];
            parseFunc = parseString;
            break;
        }
            case"%B":case"%b":{
            valueType = (token == "%B") ? "mn" : "smn";
            valueRange = [0,12];
            parseFunc = parseString;
            break;
        }
            case"%p":case"%P":{
            parseFunc = parseAMPM;
            break;
        }
            case"%Y":{
                valueLength = 4;
                if (isNumberRule(fmt.substr(2, 2))) {
                    return null;
                }
                while (isNaN(parseInt(str.charAt(strPointer + valueLength - 1))) && valueLength > 0) {
                    --valueLength;
                }
                if (valueLength == 0) {
                    break;
                }
                break;
            }
            case"%C":case"%s":{
            valueLength = 1;
            if (isNumberRule(fmt.substr(2, 2))) {
                return null;
            }
            while (!isNaN(parseInt(str.charAt(strPointer + valueLength)))) {
                ++valueLength;
            }
            break;
        }
            case"%k":case"%l":case"%e":{
            valueLength = 1;
            if (isNumberRule(fmt.substr(2, 2))) {
                return null;
            }
            if (!isNaN(parseInt(str.charAt(strPointer + 1)))) {
                ++valueLength;
            }
            break;
        }
            case"%j":valueLength = 3;break;case"%u":case"%w":valueLength = 1;case"%y":case"%m":case"%d":case"%W":case"%H":case"%I":case"%M":case"%S":{
            break;
        }}
        if (parseRule(token) === null) {
            return null;
        }
        strPointer += valueLength;
        fmt = fmt.slice(2);
    }
    if (wasParsed(values["%s"])) {
        date.setTime(values["%s"] * 1000);
    }
    else {
        var year = getValue(values["%Y"], values["%y"] + --values["%C"] * 100, values["%y"] + (date.getFullYear() - date.getFullYear() % 100), values["%C"] * 100 + date.getFullYear() % 100);
        var month = getValue(values["%m"] - 1, values["%b"], values["%B"]);
        var day = getValue(values["%d"] || values["%e"]);
        if (day === null || month === null) {
            var dayOfWeek = getValue(values["%a"], values["%A"], values["%u"] == 7 ? 0 : values["%u"], values["%w"]);
        }
        var hour = getValue(values["%H"], values["%k"]);
        if (hour === null && (wasParsed(values["%p"]) || wasParsed(values["%P"]))) {
            var pm = getValue(values["%p"], values["%P"]);
            hour = getValue(values["%I"], values["%l"]);
            hour = pm ? ((hour == 12) ? 12 : (hour + 12)) : ((hour == 12) ? (0) : hour);
        }
        if (year || year === 0) {
            date.setFullYear(year);
        }
        if (month || month === 0) {
            date.setMonth(month);
        }
        if (day || day === 0) {
            date.setDate(day);
        }
        if (wasParsed(values["%j"])) {
            date.setMonth(0);
            date.setDate(1);
            date.setDate(values["%j"]);
        }
        if (wasParsed(dayOfWeek)) {
            date.setDate(date.getDate() + (dayOfWeek - date.getDay()));
        }
        if (wasParsed(values["%W"])) {
            var weekNumber = date.getWeekNumber();
            if (weekNumber != values["%W"]) {
                date.setDate(date.getDate() + (values["%W"] - weekNumber) * 7);
            }
        }
        if (hour !== null) {
            date.setHours(hour);
        }
        if (wasParsed(values["%M"])) {
            date.setMinutes(values["%M"]);
        }
        if (wasParsed(values["%S"])) {
            date.setSeconds(values["%S"]);
        }
    }
    if (date.print(format) != str) {
        return null;
    }
    return date;
};
Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
Date.prototype.setFullYear = function(y) {
    var d = new Date(this);
    d.__msh_oldSetFullYear(y);
    if (d.getMonth() != this.getMonth())
        this.setDate(28);
    this.__msh_oldSetFullYear(y);
};
Date.prototype.compareDatesOnly = function(date1, date2) {
    var year1 = date1.getYear();
    var year2 = date2.getYear();
    var month1 = date1.getMonth();
    var month2 = date2.getMonth();
    var day1 = date1.getDate();
    var day2 = date2.getDate();
    if (year1 > year2) {
        return-1;
    }
    if (year2 > year1) {
        return 1;
    }
    if (month1 > month2) {
        return-1;
    }
    if (month2 > month1) {
        return 1;
    }
    if (day1 > day2) {
        return-1;
    }
    if (day2 > day1) {
        return 1;
    }
    return 0;
}
Zapatec.Setup = function() {
};
Zapatec.Setup.test = true;
Zapatec.Calendar.setup = function(params) {
    paramsList = ["id"];
    function param_default(pname, def) {
        if (typeof params[pname] == "undefined") {
            params[pname] = def;
        }
        paramsList.push(pname);
    }
    ;
    params.id = Zapatec.Utils.generateID("calendar");
    param_default("inputField", null);
    param_default("displayArea", null);
    param_default("button", null);
    param_default("eventName", "click");
    param_default("closeEventName", null);
    param_default("ifFormat", Zapatec.Calendar.i18n("DEF_DATE_FORMAT"));
    param_default("daFormat", "%Y/%m/%d");
    param_default("singleClick", true);
    param_default("disableFunc", null);
    param_default("dateStatusFunc", params["disableFunc"]);
    param_default("dateText", null);
    param_default("firstDay", null);
    param_default("align", "Br");
    param_default("range", [1900,2999]);
    param_default("weekNumbers", true);
    param_default("flat", null);
    param_default("flatCallback", null);
    param_default("onSelect", null);
    param_default("onClose", null);
    param_default("onUpdate", null);
    param_default("date", null);
    param_default("showsTime", false);
    param_default("sortOrder", "asc");
    param_default("timeFormat", "24");
    param_default("timeInterval", null);
    param_default("electric", true);
    param_default("step", 2);
    param_default("position", null);
    param_default("cache", false);
    param_default("showOthers", false);
    param_default("multiple", null);
    param_default("saveDate", null);
    param_default("fdowClick", false);
    param_default("titleHtml", null);
    param_default("noHelp", false);
    param_default("noCloseButton", false);
    param_default("disableYearNav", false);
    param_default("disableFdowChange", false);
    if (params.weekNumbers) {
        params.disableFdowChange = true;
        params.firstDay = 1;
    }
    param_default("disableDrag", false);
    param_default("numberMonths", 1);
    if ((params.numberMonths > 12) || (params.numberMonths < 1)) {
        params.numberMonths = 1;
    }
    if (params.numberMonths > 1) {
        params.showOthers = false;
    }
    params.numberMonths = parseInt(params.numberMonths, 10);
    param_default("controlMonth", 1);
    if ((params.controlMonth > params.numberMonths) || (params.controlMonth < 1)) {
        params.controlMonth = 1;
    }
    params.controlMonth = parseInt(params.controlMonth, 10);
    param_default("vertical", false);
    if (params.monthsInRow > params.numberMonths) {
        params.monthsInRow = params.numberMonths;
    }
    param_default("monthsInRow", params.numberMonths);
    params.monthsInRow = parseInt(params.monthsInRow, 10);
    param_default("multiple", false);
    if (params.multiple) {
        params.singleClick = false;
    }
    param_default("canType", false);
    var tmp = ["inputField","displayArea","button"];
    for (var i in tmp) {
        if (typeof params[tmp[i]] == "string") {
            params[tmp[i]] = document.getElementById(params[tmp[i]]);
        }
    }
    if (!params.inputField) {
        params.canType = false;
    }
    else {
        params.inputField.setAttribute("autocomplete", "off");
    }
    if (!(params.flat || params.multiple || params.inputField || params.displayArea || params.button)) {
        alert("Calendar.setup '" + params.id + "':\n  Nothing to setup (no fields found).  Please check your code");
        return false;
    }
    if (((params.timeInterval) && ((params.timeInterval !== Math.floor(params.timeInterval)) || ((60 % params.timeInterval !== 0) && (params.timeInterval % 60 !== 0)))) || (params.timeInterval > 360)) {
        alert("'" + params.id + "': timeInterval option can only have the following number of minutes:\n1, 2, 3, 4, 5, 6, 10, 15, 30,  60, 120, 180, 240, 300, 360 ");
        params.timeInterval = null;
    }
    if (params.date && !Date.parse(params.date)) {
        alert("'" + params.id + "' Start Date Invalid: " + params.date + ".\nSee date option.\nDefaulting to today.");
        params.date = null;
    }
    if (params.saveDate) {
        param_default("cookiePrefix", window.location.href + "--" + params.button.id);
        var cookieName = params.cookiePrefix;
        var newdate = Zapatec.Utils.getCookie(cookieName);
        if (newdate != null) {
            document.getElementById(params.inputField.id).value = newdate;
        }
    }
    for (var ii in params) {
        if (typeof params.constructor.prototype[ii] != "undefined") {
            continue;
        }
        if (Zapatec.Utils.arrIndexOf(paramsList, ii) == -1) {
            alert("Wrong config option: " + ii);
        }
    }
    function onSelect(cal) {
        var p = cal.params;
        var update = (cal.dateClicked || p.electric);
        if (update && p.flat) {
            if (typeof p.flatCallback == "function")
            {
                if (!p.multiple)
                    p.flatCallback(cal);
            }
            else
                alert("'" + cal.id + "': No flatCallback given -- doing nothing.");
            return false;
        }
        if (update && p.inputField) {
            p.inputField.value = cal.currentDate.print(p.ifFormat);
            if (typeof p.inputField.onchange == "function")
                p.inputField.onchange();
        }
        if (update && p.displayArea)
            p.displayArea.innerHTML = cal.currentDate.print(p.daFormat);
        if (update && p.singleClick && cal.dateClicked)
            cal.callCloseHandler();
        if (update && typeof p.onUpdate == "function")
            p.onUpdate(cal);
        if (p.saveDate) {
            var cookieName = p.cookiePrefix;
            Zapatec.Utils.writeCookie(cookieName, p.inputField.value, null, '/', p.saveDate);
        }
    }
    ;
    if (params.flat != null) {
        if (typeof params.flat == "string")
            params.flat = document.getElementById(params.flat);
        if (!params.flat) {
            alert("Calendar.setup '" + params.id + "':\n  Flat specified but can't find parent.");
            return false;
        }
        var cal = new Zapatec.Calendar(params.firstDay, params.date, params.onSelect || onSelect);
        cal.id = params.id;
        cal.disableFdowClick = params.disableFdowChange;
        cal.showsOtherMonths = params.showOthers;
        cal.showsTime = params.showsTime;
        cal.time24 = (params.timeFormat == "24");
        cal.timeInterval = params.timeInterval;
        cal.params = params;
        cal.weekNumbers = params.weekNumbers;
        cal.sortOrder = params.sortOrder.toLowerCase();
        cal.setRange(params.range[0], params.range[1]);
        cal.setDateStatusHandler(params.dateStatusFunc);
        cal.getDateText = params.dateText;
        cal.numberMonths = params.numberMonths;
        cal.controlMonth = params.controlMonth;
        cal.vertical = params.vertical;
        cal.yearStep = params.step;
        cal.monthsInRow = params.monthsInRow;
        cal.helpButton = !params.noHelp;
        cal.closeButton = !params.noCloseButton;
        cal.yearNav = !params.disableYearNav;
        if (params.ifFormat) {
            cal.setDateFormat(params.ifFormat);
        }
        if (params.inputField && params.inputField.type == "text" && typeof params.inputField.value == "string") {
            cal.parseDate(params.inputField.value);
        }
        if (params.multiple) {
            cal.setMultipleDates(params.multiple);
        }
        cal.create(params.flat);
        cal.show();
        return cal;
    }
    var triggerEl = params.button || params.displayArea || params.inputField;
    if (params.canType) {
        function cancelBubble(ev) {
            ev = ev || window.event;
            if (Zapatec.is_ie) {
                ev.cancelBubble = true;
            }
            else {
                ev.stopPropagation();
            }
        }
        Zapatec.Utils.addEvent(params.inputField, "mousedown", cancelBubble);
        Zapatec.Utils.addEvent(params.inputField, "keydown", cancelBubble);
        Zapatec.Utils.addEvent(params.inputField, "keypress", cancelBubble);
        Zapatec.Utils.addEvent(params.inputField, "keyup", function(ev) {
            var format = params.inputField ? params.ifFormat : params.daFormat;
            var parsedDate = Date.parseDate(params.inputField.value, format);
            var cal = window.calendar;
            if (cal && parsedDate && !cal.hidden) {
                cal.setDate(parsedDate);
            }
        });
    }
    triggerEl["on" + params.eventName] = function() {
        var dateEl = params.inputField || params.displayArea;
        if ((!params.canType || params.inputField != triggerEl) && triggerEl.blur) {
            triggerEl.blur();
        }
        var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
        var mustCreate = false;
        var cal = window.calendar;
        if (params.canType && (params.inputField == triggerEl) && cal && !cal.hidden) {
            return;
        }
        if (!(cal && params.cache)) {
            window.calendar = cal = new Zapatec.Calendar(params.firstDay, params.date, params.onSelect || onSelect, params.onClose || function(cal) {
                if (params.cache)
                    cal.hide();
                else
                    cal.destroy();
            });
            cal.id = params.id;
            cal.disableFdowClick = params.disableFdowChange;
            cal.showsTime = params.showsTime;
            cal.time24 = (params.timeFormat == "24");
            cal.timeInterval = params.timeInterval;
            cal.weekNumbers = params.weekNumbers;
            cal.numberMonths = params.numberMonths;
            cal.controlMonth = params.controlMonth;
            cal.vertical = params.vertical;
            cal.monthsInRow = params.monthsInRow;
            cal.historyDateFormat = params.ifFormat || params.daFormat;
            cal.helpButton = !params.noHelp;
            cal.disableDrag = params.disableDrag;
            cal.closeButton = !params.noCloseButton;
            cal.yearNav = !params.disableYearNav;
            cal.sortOrder = params.sortOrder.toLowerCase();
            mustCreate = true;
        }
        else {
            if (params.date)
                cal.setDate(params.date);
            cal.hide();
        }
        if (params.multiple) {
            cal.setMultipleDates(params.multiple);
        }
        cal.showsOtherMonths = params.showOthers;
        cal.yearStep = params.step;
        cal.setRange(params.range[0], params.range[1]);
        cal.params = params;
        cal.setDateStatusHandler(params.dateStatusFunc);
        cal.getDateText = params.dateText;
        cal.setDateFormat(dateFmt);
        if (mustCreate)
            cal.create();
        if (dateEl) {
            var dateValue;
            if (dateEl.value) {
                dateValue = dateEl.value;
            }
            else {
                dateValue = dateEl.innerHTML;
            }
            if (dateValue != "") {
                var parsedDate = Date.parseDate(dateEl.value || dateEl.innerHTML, dateFmt);
                if (parsedDate != null) {
                    cal.setDate(parsedDate);
                }
            }
        }
        if (!params.position)
            cal.showAtElement(params.button || params.displayArea || params.inputField, params.align);
        else
            cal.showAt(params.position[0], params.position[1]);
        return false;
    };
    if (params.closeEventName) {
        triggerEl["on" + params.closeEventName] = function() {
            if (window.calendar)
                window.calendar.callCloseHandler();
        };
    }
    return cal;
};

// $Id: calendar-en.js 6573 2007-03-09 08:36:16Z slip $
// ** I18N

// Calendar EN language
// Author: Mihai Bazon, <mihai_bazon@yahoo.com>
// Encoding: any
// Distributed under the same terms as the calendar itself.

// For translators: please use UTF-8 if possible.  We strongly believe that
// Unicode is the answer to a real internationalized world.  Also please
// include your contact information in the header, as can be seen above.

// full day names
Zapatec.Calendar._DN = new Array
("Sunday",
 "Monday",
 "Tuesday",
 "Wednesday",
 "Thursday",
 "Friday",
 "Saturday",
 "Sunday");

// Please note that the following array of short day names (and the same goes
// for short month names, _SMN) isn't absolutely necessary.  We give it here
// for exemplification on how one can customize the short day names, but if
// they are simply the first N letters of the full name you can simply say:
//
//   Zapatec.Calendar._SDN_len = N; // short day name length
//   Zapatec.Calendar._SMN_len = N; // short month name length
//
// If N = 3 then this is not needed either since we assume a value of 3 if not
// present, to be compatible with translation files that were written before
// this feature.

// short day names
Zapatec.Calendar._SDN = new Array
("Sun",
 "Mon",
 "Tue",
 "Wed",
 "Thu",
 "Fri",
 "Sat",
 "Sun");

// First day of the week. "0" means display Sunday first, "1" means display
// Monday first, etc.
Zapatec.Calendar._FD = 0;

// full month names
Zapatec.Calendar._MN = new Array
("January",
 "February",
 "March",
 "April",
 "May",
 "June",
 "July",
 "August",
 "September",
 "October",
 "November",
 "December");

// short month names
Zapatec.Calendar._SMN = new Array
("Jan",
 "Feb",
 "Mar",
 "Apr",
 "May",
 "Jun",
 "Jul",
 "Aug",
 "Sep",
 "Oct",
 "Nov",
 "Dec");

// tooltips
Zapatec.Calendar._TT_en = Zapatec.Calendar._TT = {};
Zapatec.Calendar._TT["INFO"] = "About the calendar";

Zapatec.Calendar._TT["ABOUT"] =
"DHTML Date/Time Selector\n" +
"(c) zapatec.com 2002-2007\n" + // don't translate this this ;-)
"For latest version visit: http://www.zapatec.com/\n" +
"\n\n" +
"Date selection:\n" +
"- Use the \xab, \xbb buttons to select year\n" +
"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
"- Hold mouse button on any of the above buttons for faster selection.";
Zapatec.Calendar._TT["ABOUT_TIME"] = "\n\n" +
"Time selection:\n" +
"- Click on any of the time parts to increase it\n" +
"- or Shift-click to decrease it\n" +
"- or click and drag for faster selection.";

Zapatec.Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
Zapatec.Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
Zapatec.Calendar._TT["GO_TODAY"] = "Go Today (hold for history)";
Zapatec.Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
Zapatec.Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
Zapatec.Calendar._TT["SEL_DATE"] = "Select date";
Zapatec.Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
Zapatec.Calendar._TT["PART_TODAY"] = " (today)";

// the following is to inform that "%s" is to be the first day of week
// %s will be replaced with the day name.
Zapatec.Calendar._TT["DAY_FIRST"] = "Display %s first";

// This may be locale-dependent.  It specifies the week-end days, as an array
// of comma-separated numbers.  The numbers are from 0 to 6: 0 means Sunday, 1
// means Monday, etc.
Zapatec.Calendar._TT["WEEKEND"] = "0,6";

Zapatec.Calendar._TT["CLOSE"] = "Close";
Zapatec.Calendar._TT["TODAY"] = "Today";
Zapatec.Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";

// date formats
Zapatec.Calendar._TT["DEF_DATE_FORMAT"] = "%m/%d/%Y";
Zapatec.Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";

Zapatec.Calendar._TT["WK"] = "wk";
Zapatec.Calendar._TT["TIME"] = "Time:";

Zapatec.Calendar._TT["E_RANGE"] = "Outside the range";

Zapatec.Calendar._TT._AMPM = {am : "am", pm : "pm"};

/* Preserve data */
	if(Zapatec.Calendar._DN) Zapatec.Calendar._TT._DN = Zapatec.Calendar._DN;
	if(Zapatec.Calendar._SDN) Zapatec.Calendar._TT._SDN = Zapatec.Calendar._SDN;
	if(Zapatec.Calendar._SDN_len) Zapatec.Calendar._TT._SDN_len = Zapatec.Calendar._SDN_len;
	if(Zapatec.Calendar._MN) Zapatec.Calendar._TT._MN = Zapatec.Calendar._MN;
	if(Zapatec.Calendar._SMN) Zapatec.Calendar._TT._SMN = Zapatec.Calendar._SMN;
	if(Zapatec.Calendar._SMN_len) Zapatec.Calendar._TT._SMN_len = Zapatec.Calendar._SMN_len;
	Zapatec.Calendar._DN = Zapatec.Calendar._SDN = Zapatec.Calendar._SDN_len = Zapatec.Calendar._MN = Zapatec.Calendar._SMN = Zapatec.Calendar._SMN_len = null


/*

SUPERNOTE v1.0beta (c) 2005-2006 Angus Turnbull, http://www.twinhelix.com
Altering this notice or redistributing this file is prohibited.

*/

if(typeof addEvent!='function'){var addEvent=function(o,t,f,l){var d='addEventListener',n='on'+t,rO=o,rT=t,rF=f,rL=l;if(o[d]&&!l)return o[d](t,f,false);if(!o._evts)o._evts={};if(!o._evts[t]){o._evts[t]=o[n]?{b:o[n]}:{};o[n]=new Function('e','var r=true,o=this,a=o._evts["'+t+'"],i;for(i in a){o._f=a[i];r=o._f(e||window.event)!=false&&r;o._f=null}return r');if(t!='unload')addEvent(window,'unload',function(){removeEvent(rO,rT,rF,rL)})}if(!f._i)f._i=addEvent._i++;o._evts[t][f._i]=f};addEvent._i=1;var removeEvent=function(o,t,f,l){var d='removeEventListener';if(o[d]&&!l)return o[d](t,f,false);if(o._evts&&o._evts[t]&&f._i)delete o._evts[t][f._i]}}function cancelEvent(e,c){e.returnValue=false;if(e.preventDefault)e.preventDefault();if(c){e.cancelBubble=true;if(e.stopPropagation)e.stopPropagation()}};function SuperNote(myName,config){var defaults={myName:myName,allowNesting:false,cssProp:'visibility',cssVis:'inherit',cssHid:'hidden',IESelectBoxFix:true,showDelay:0,hideDelay:500,animInSpeed:0.1,animOutSpeed:0.1,animations:[],mouseX:0,mouseY:0,notes:{},rootElm:null,onshow:null,onhide:null};for(var p in defaults)this[p]=(typeof config[p]=='undefined')?defaults[p]:config[p];var obj=this;addEvent(document,'mouseover',function(evt){obj.mouseHandler(evt,1)});/*addEvent(document,'click',function(evt){obj.mouseHandler(evt,2)});*/addEvent(document,'mousemove',function(evt){obj.mouseTrack(evt)});addEvent(document,'mouseout',function(evt){obj.mouseHandler(evt,0)});this.instance=SuperNote.instances.length;SuperNote.instances[this.instance]=this}SuperNote.instances=[];SuperNote.prototype.bTypes={};SuperNote.prototype.pTypes={};SuperNote.prototype.pTypes.mouseoffset=function(obj,noteID,nextVis,nextAnim){with(obj){var note=notes[noteID];if(nextVis&&!note.animating&&!note.visible){note.ref.style.left=checkWinX(mouseX,note)+'px';note.ref.style.top=checkWinY(mouseY,note)+'px'}}};SuperNote.prototype.pTypes.mousetrack=function(obj,noteID,nextVis,nextAnim){with(obj){var note=notes[noteID];if(nextVis&&!note.animating&&!note.visible){var posString='with('+myName+'){var note=notes["'+noteID+'"];note.ref.style.left=checkWinX(mouseX,note)+"px";note.ref.style.top=checkWinY(mouseY,note)+"px"}';eval(posString);obj.IEFrameFix(noteID,1);if(!note.trackTimer)note.trackTimer=setInterval(posString,50)}else if(!nextVis&&!nextAnim){clearInterval(note.trackTimer);note.trackTimer=null}}};SuperNote.prototype.pTypes.triggeroffset=function(obj,noteID,nextVis,nextAnim){with(obj){var note=notes[noteID];if(nextVis&&!note.animating&&!note.visible){var x=0,y=0,elm=note.trigRef;while(elm){x+=elm.offsetLeft;y+=elm.offsetTop;elm=elm.offsetParent}note.ref.style.left=checkWinX(x,note)+'px';note.ref.style.top=checkWinY(y,note)+'px'}}};SuperNote.prototype.bTypes.pinned=function(obj,noteID,nextVis){with(obj){return(!nextVis)?false:true}};SuperNote.prototype.docBody=function(){return document[(document.compatMode&&document.compatMode.indexOf('CSS')>-1)?'documentElement':'body']};SuperNote.prototype.getWinW=function(){return this.docBody().clientWidth||window.innerWidth||0};SuperNote.prototype.getWinH=function(){return this.docBody().clientHeight||window.innerHeight||0};SuperNote.prototype.getScrX=function(){return this.docBody().scrollLeft||window.scrollX||0};SuperNote.prototype.getScrY=function(){return this.docBody().scrollTop||window.scrollY||0};SuperNote.prototype.checkWinX=function(newX,note){with(this){return Math.max(getScrX(),Math.min(newX,getScrX()+getWinW()-note.ref.offsetWidth-8))}};SuperNote.prototype.checkWinY=function(newY,note){with(this){return Math.max(getScrY(),Math.min(newY,getScrY()+getWinH()-note.ref.offsetHeight-8))}};SuperNote.prototype.mouseTrack=function(evt){with(this){mouseX=evt.pageX||evt.clientX+getScrX()||0;mouseY=evt.pageY||evt.clientY+getScrY()||0}};SuperNote.prototype.mouseHandler=function(evt,show){with(this){if(!document.documentElement)return true;var srcElm=evt.target||evt.srcElement,trigRE=new RegExp(myName+'-(hover|click)-([a-z0-9]+)','i'),targRE=new RegExp(myName+'-(note)-([a-z0-9]+)','i'),trigFind=1,foundNotes={};if(srcElm.nodeType!=1)srcElm=srcElm.parentNode;var elm=srcElm;while(elm&&elm!=rootElm){if(targRE.test(elm.id)||(trigFind&&trigRE.test(elm.className))){if(!allowNesting)trigFind=0;var click=RegExp.$1=='click'?1:0,noteID=RegExp.$2,ref=document.getElementById(myName+'-note-'+noteID),trigRef=trigRE.test(elm.className)?elm:null;if(ref){if(!notes[noteID]){notes[noteID]={click:click,ref:ref,trigRef:null,visible:0,animating:0,timer:null};ref._sn_obj=this;ref._sn_id=noteID}var note=notes[noteID];if(!note.click||(trigRef!=srcElm))foundNotes[noteID]=true;if(!note.click||(show==2)){if(trigRef)notes[noteID].trigRef=notes[noteID].ref._sn_trig=elm;display(noteID,show);if(note.click&&(srcElm==trigRef))cancelEvent(evt)}}}if(elm._sn_trig){trigFind=1;elm=elm._sn_trig}else{elm=elm.parentNode}}if(show==2)for(var n in notes){if(notes[n].click&&notes[n].visible&&!foundNotes[n])display(n,0)}}};SuperNote.prototype.display=function(noteID,show){with(this){with(notes[noteID]){clearTimeout(timer);if(!animating||(show?!visible:visible)){var tmt=animating?1:(show?showDelay||1:hideDelay||1);timer=setTimeout('SuperNote.instances['+instance+'].setVis("'+noteID+'",'+show+',false)',tmt)}}}};SuperNote.prototype.checkType=function(noteID,nextVis,nextAnim){with(this){var note=notes[noteID],bType,pType;if((/snp-([a-z]+)/).test(note.ref.className))pType=RegExp.$1;if((/snb-([a-z]+)/).test(note.ref.className))bType=RegExp.$1;if(nextAnim&&bType&&bTypes[bType]&&(bTypes[bType](this,noteID,nextVis)==false))return false;if(pType&&pTypes[pType])pTypes[pType](this,noteID,nextVis,nextAnim);return true}};SuperNote.prototype.setVis=function(noteID,show,now){with(this){var note=notes[noteID];if(note&&checkType(noteID,show,1)||now){note.visible=show;note.animating=1;animate(noteID,show,now)}}};SuperNote.prototype.animate=function(noteID,show,now){with(this){var note=notes[noteID];if(!note.animTimer)note.animTimer=0;if(!note.animC)note.animC=0;with(note){clearTimeout(animTimer);var speed=(animations.length&&!now)?(show?animInSpeed:animOutSpeed):1;if(show&&!animC){if(onshow)this.onshow(noteID);IEFrameFix(noteID,1);ref.style[cssProp]=cssVis}animC=Math.max(0,Math.min(1,animC+speed*(show?1:-1)));if(document.getElementById&&speed<1)for(var a=0;a<animations.length;a++)animations[a](ref,animC);if(!show&&!animC){if(onhide)this.onhide(noteID);IEFrameFix(noteID,0);ref.style[cssProp]=cssHid}if(animC!=parseInt(animC)){animTimer=setTimeout(myName+'.animate("'+noteID+'",'+show+')',50)}else{checkType(noteID,animC?1:0,0);note.animating=0}}}};SuperNote.prototype.IEFrameFix=function(noteID,show){with(this){if(!window.createPopup||!IESelectBoxFix)return;var note=notes[noteID],ifr=note.iframe;if(!ifr){ifr=notes[noteID].iframe=document.createElement('iframe');ifr.setAttribute("src", CS_BASE_URL + "/resources/blank.html");ifr.style.filter='progid:DXImageTransform.Microsoft.Alpha(opacity=0)';ifr.style.position='absolute';ifr.style.borderWidth='0';note.ref.parentNode.insertBefore(ifr,note.ref.parentNode.firstChild)}if(show){ifr.style.left=note.ref.offsetLeft+'px';ifr.style.top=note.ref.offsetTop+'px';ifr.style.width=note.ref.offsetWidth+'px';ifr.style.height=note.ref.offsetHeight+'px';ifr.style.visibility='inherit'}else{ifr.style.visibility='hidden'}}};


// This file provides the default namespaces for the Jive JavaScript Library

var HOSTURL = "/"
var AJAXPATH = "";

if(typeof(jive) == "undefined"){
    var jive = new Object();
    jive.gui = new Object();
    jive.model = new Object();
    jive.ext = new Object();
    jive.ext.y = new Object();
    jive.ext.x = new Object();
    jive.xml = new Object();
    jive.rte = new Object();
    jive.rte.macros = new Array();
}

if(typeof(console) == "undefined"){
    /*
    var win = window.open("",name,"width=250,height=700,scrollbars=1,resize=1");
    win.document.write("<html><head><title>History</title>" +
    "<style>ol{padding-left: 12px;} div{ font-family:verdana;font-size:8pt; margin-bottom:10px; }</style>" +
    "</head><body>");
    win.document.write("</body></html>");
    win.document.close();

    var arrayHolder = win.document.createElement('DIV');
    win.document.body.appendChild(arrayHolder);
    var arrayList = win.document.createElement('OL');
    arrayHolder.appendChild(arrayList);

    var log = win.document.createElement('DIV');
    win.document.body.appendChild(log);
    console = new Object();
    console.log = function(str){ log.appendChild(win.document.createTextNode(str)); log.appendChild(win.document.createElement('BR'));};
*/
    console = new Object();
    console.log = function(str){ };
}
if(typeof(console.debug) != "function"){
    console.debug = console.log;
}

// x_core.js
// X v3.15.1, Cross-Browser DHTML Library from Cross-Browser.com
// Copyright (c) 2002,2003,2004 Michael Foster (mike@cross-browser.com)
// This library is distributed under the terms of the LGPL (gnu.org)

// Variables:
jive.ext.x.xMac = (navigator.appVersion.indexOf('Mac') != -1);
jive.ext.x.xWindows = !jive.ext.x.xMac;
jive.ext.x.xVersion='3.15.1';
jive.ext.x.xNN4=false;
jive.ext.x.xOp7=false;
jive.ext.x.xOp5or6=false;
jive.ext.x.xIE4Up=false;
jive.ext.x.xIE4=false;
jive.ext.x.xIE5=false;
jive.ext.x.xUA=navigator.userAgent.toLowerCase();
jive.ext.x.xIE = false;
jive.ext.x.xSafari = false;
if(window.opera){
  jive.ext.x.xOp7=(jive.ext.x.xUA.indexOf('opera 7')!=-1 || jive.ext.x.xUA.indexOf('opera/7')!=-1);
  if (!jive.ext.x.xOp7) jive.ext.x.xOp5or6=(jive.ext.x.xUA.indexOf('opera 5')!=-1 || jive.ext.x.xUA.indexOf('opera/5')!=-1 || jive.ext.x.xUA.indexOf('opera 6')!=-1 || jive.ext.x.xUA.indexOf('opera/6')!=-1);
}
else if (document.all) {
  jive.ext.x.xIE4Up=jive.ext.x.xUA.indexOf('msie')!=-1 && parseInt(navigator.appVersion)>=4;
  jive.ext.x.xIE4=jive.ext.x.xUA.indexOf('msie 4')!=-1;
  jive.ext.x.xIE5=jive.ext.x.xUA.indexOf('msie 5')!=-1;
  jive.ext.x.xIE6=jive.ext.x.xUA.indexOf('msie 6')!=-1;
  jive.ext.x.xIE7=jive.ext.x.xUA.indexOf('msie 7')!=-1;
  jive.ext.x.xIE4Up=jive.ext.x.xIE4 || jive.ext.x.xIE5 || jive.ext.x.xIE6;
  jive.ext.x.xIE = true;
}
if(jive.ext.x.xUA.indexOf('safari') != -1 || jive.ext.x.xUA.indexOf('Safari') != -1){
  jive.ext.x.xSafari = true;
}
// Object:
jive.ext.x.xGetElementById = function(e,doc) {
  if(!$obj(doc)) doc = e.ownerDocument;
  if(e == null) return e;
  if(typeof(e)!='string') return e;
  if(doc.getElementById) e=doc.getElementById(e);
  else if(doc.all) e=doc.all[e];
  else e=null;
  return e;
}
jive.ext.x.xParent = function(e,bNode){
  if (!(e=jive.ext.x.xGetElementById(e))) return null;
  var p=null;
  if (!bNode && $def(e.offsetParent)) p=e.offsetParent;
  else if ($def(e.parentNode)) p=e.parentNode;
  else if ($def(e.parentElement)) p=e.parentElement;
  return p;
}
var $def = function(theItem) {
  return (typeof(theItem)!='undefined');
}
// yObj
// returns true if all the arguments are objects
var $obj = function(item)
{
  return (typeof(item) == 'object');
}
// yArr
// returns true if all the arguments are arrays
var $arr = function(item)
{
	return item != null && $obj(item) && $def(item.splice);
}
$str = function(s) {
  return typeof(s)=='string';
}
var $num = function(n) {
  return typeof(n)=='number';
}
// Appearance:
jive.ext.x.xShow = function(e) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  if(e.style && $def(e.style.visibility)) e.style.visibility='visible';
}
jive.ext.x.xHide = function(e) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  if(e.style && $def(e.style.visibility)) e.style.visibility='hidden';
}
jive.ext.x.xDisplay = function(e,s)
{
  if(!(e=jive.ext.x.xGetElementById(e))) return null;
  if(e.style && $def(e.style.display)) {
    if ($str(s)) e.style.display = s;
    return e.style.display;
  }
  return null;
}
jive.ext.x.xDisplayNone = function(e) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  if(e.style && $def(e.style.display)) e.style.display='none';
}
jive.ext.x.xDisplayBlock = function(e) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  if(e.style && $def(e.style.display)) e.style.display='block';
}
jive.ext.x.xDisplayInline = function(e) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  if(e.style && $def(e.style.display)) e.style.display='inline';
}
// xVisibility, Copyright 2003-2005 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

jive.ext.x.xZIndex = function(e,uZ)
{
  if(!(e=jive.ext.x.xGetElementById(e))) return 0;
  if(e.style && $def(e.style.zIndex)) {
    if($num(uZ)) e.style.zIndex=uZ;
    uZ=parseInt(e.style.zIndex);
  }
  return uZ;
}
// Position:
jive.ext.x.xMoveTo = function(e,iX,iY) {
  jive.ext.x.xLeft(e,iX);
  jive.ext.x.xTop(e,iY);
}
jive.ext.x.xLeft = function(e,iX) {
  if(!(e=jive.ext.x.xGetElementById(e))) return 0;
  var css=$def(e.style);
  if (css && $str(e.style.left)) {
    if($num(iX)) e.style.left=iX+'px';
    else {
      iX=parseInt(e.style.left);
      if(isNaN(iX)) iX=0;
    }
  }
  else if(css && $def(e.style.pixelLeft)) {
    if($num(iX)) e.style.pixelLeft=iX;
    else iX=e.style.pixelLeft;
  }
  return iX;
}
jive.ext.x.xTop = function(e,iY) {
  if(!(e=jive.ext.x.xGetElementById(e))) return 0;
  var css=$def(e.style);
  if(css && $str(e.style.top)) {
    if($num(iY)) e.style.top=iY+'px';
    else {
      iY=parseInt(e.style.top);
      if(isNaN(iY)) iY=0;
    }
  }
  else if(css && $def(e.style.pixelTop)) {
    if($num(iY)) e.style.pixelTop=iY;
    else iY=e.style.pixelTop;
  }
  return iY;
}
jive.ext.x.xPageX = function(obj) {
    var curleft = 0;
    if(obj.offsetParent)
        while(1)
        {
          curleft += obj.offsetLeft;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.x)
        curleft += obj.x;
    return curleft;
  }

jive.ext.x.xPageY = function(obj){
    var curtop = 0;
    if(obj.offsetParent)
        while(1)
        {
          curtop += obj.offsetTop;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.y)
        curtop += obj.y;
    return curtop;
}
jive.ext.x.xScrollLeft = function(e) {
  var offset=0, doc = e.ownerDocument;
  if (!(e=jive.ext.x.xGetElementById(e))) {
    if(doc.documentElement && doc.documentElement.scrollLeft) offset=doc.documentElement.scrollLeft;
    else if(doc.body && $def(doc.body.scrollLeft)) offset=doc.body.scrollLeft;
  }
  else { if ($num(e.scrollLeft)) offset = e.scrollLeft; }
  return offset;
}
jive.ext.x.xScrollTop = function(e) {
  var offset=0, doc = e.ownerDocument;
  if (!(e=jive.ext.x.xGetElementById(e))) {
    if(doc.documentElement && doc.documentElement.scrollTop) offset=doc.documentElement.scrollTop;
    else if(doc.body && $def(doc.body.scrollTop)) offset=doc.body.scrollTop;
  }
  else { if ($num(e.scrollTop)) offset = e.scrollTop; }
  return offset;
}
jive.ext.x.xWidth = function(e,w)
{
  if(!(e=jive.ext.x.xGetElementById(e))) return 0;
  if ($num(w)) {
    if (w<0) w = 0;
    else w=Math.round(w);
  }
  else w=-1;
  var css=$def(e.style);
  if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
    w = jive.ext.x.xClientWidth();
  }
  else if(css && $def(e.offsetWidth) && $str(e.style.width)) {
    if(w>=0) {
      var pl=0,pr=0,bl=0,br=0;
      if (document.compatMode=='CSS1Compat') {
        var gcs = jive.ext.x.xGetCS;
        pl=gcs(e,'padding-left',1);
        if (pl !== null) {
          pr=gcs(e,'padding-right',1);
          bl=gcs(e,'border-left-width',1);
          br=gcs(e,'border-right-width',1);
        }
        // Should we try this as a last resort?
        // At this point getComputedStyle and currentStyle do not exist.
        else if($def(e.offsetWidth,e.style.width)){
          e.style.width=w+'px';
          pl=e.offsetWidth-w;
        }
      }
      w-=(pl+pr+bl+br);
      if(isNaN(w)||w<0) return;
      else e.style.width=w+'px';
    }
    w=e.offsetWidth;
  }
  else if(css && $def(e.style.pixelWidth)) {
    if(w>=0) e.style.pixelWidth=w;
    w=e.style.pixelWidth;
  }
  return w;
}
jive.ext.x.xCamelize = function(cssPropStr)
{
  var i, c, a = cssPropStr.split('-');
  var s = a[0];
  for (i=1; i<a.length; ++i) {
    c = a[i].charAt(0);
    s += a[i].replace(c, c.toUpperCase());
  }
  return s;
}
jive.ext.x.xGetCS = function(e, p)
{
    try{
      if(!(e=jive.ext.x.xGetElementById(e))) return null;
      var s, v = 'undefined', dv = e.ownerDocument.defaultView;
      if(dv && dv.getComputedStyle){
        s = dv.getComputedStyle(e,'');
        if (s) v = s.getPropertyValue(p);
      }
      else if(e.currentStyle) {
        v = e.currentStyle[jive.ext.x.xCamelize(p)];
      }
      else return null;
        if($str(v) && v.indexOf("px") > 0){
            v = v.substr(0,v.indexOf("px"));
            v = parseInt(v);
        }
      return v;
    }catch(e){
        return "";
    }
}
jive.ext.x.xSetCH = function(ele,uH){
  var pt=0,pb=0,bt=0,bb=0,doc = ele.ownerDocument
  if($def(doc.defaultView) && $def(doc.defaultView.getComputedStyle)){
    pt=jive.ext.x.xGetCS(ele,'padding-top');
    pb=jive.ext.x.xGetCS(ele,'padding-bottom');
    bt=jive.ext.x.xGetCS(ele,'border-top-width');
    bb=jive.ext.x.xGetCS(ele,'border-bottom-width');
  }
  else if($def(ele.currentStyle,doc.compatMode)){
    if(doc.compatMode=='CSS1Compat'){
      pt=parseInt(ele.currentStyle.paddingTop);
      pb=parseInt(ele.currentStyle.paddingBottom);
      bt=parseInt(ele.currentStyle.borderTopWidth);
      bb=parseInt(ele.currentStyle.borderBottomWidth);
    }
  }
  else if($def(ele.offsetHeight,ele.style.height)){ // ?
    ele.style.height=uH+'px';
    pt=ele.offsetHeight-uH;
  }
  if(isNaN(pt)) pt=0; if(isNaN(pb)) pb=0; if(isNaN(bt)) bt=0; if(isNaN(bb)) bb=0;
  var cssH=uH-(pt+pb+bt+bb);
  if(isNaN(cssH)||cssH<0) return;
  else ele.style.height=cssH+'px';
}
jive.ext.x.xHeight = function(e,uH) {
  if(!(e=jive.ext.x.xGetElementById(e))) return 0;
  if ($num(uH)) {
    if (uH<0) uH = 0;
    else uH=Math.round(uH);
  }
  else uH=0;
  var css=$def(e.style);
  if(css && $def(e.offsetHeight) && $str(e.style.height)) {
    if(uH) jive.ext.x.xSetCH(e, uH);
    uH=e.offsetHeight;
  }
  else if(css && $def(e.style.pixelHeight)) {
    if(uH) e.style.pixelHeight=uH;
    uH=e.style.pixelHeight;
  }
  return uH;
}
jive.ext.x.xHasPoint = function(ele, iLeft, iTop, iClpT, iClpR, iClpB, iClpL) {
  if (!$num(iClpT)){iClpT=iClpR=iClpB=iClpL=0;}
  else if (!$num(iClpR)){iClpR=iClpB=iClpL=iClpT;}
  else if (!$num(iClpB)){iClpL=iClpR; iClpB=iClpT;}
  var thisX = jive.ext.x.xPageX(ele), thisY = jive.ext.x.xPageY(ele);
  return (iLeft >= thisX + iClpL && iLeft <= thisX + jive.ext.x.xWidth(ele) - iClpR &&
          iTop >=thisY + iClpT && iTop <= thisY + jive.ext.x.xHeight(ele) - iClpB );
}
// Window:
jive.ext.x.xClientWidth = function() {
  var w=0;
  if(jive.ext.x.xOp5or6) w=window.innerWidth;
  else if(!window.opera && document.documentElement && document.documentElement.clientWidth) // v3.12
    w=document.documentElement.clientWidth;
  else if(document.body && document.body.clientWidth)
    w=document.body.clientWidth;
  else if($def(window.innerWidth,window.innerHeight,document.height)) {
    w=window.innerWidth;
    if(document.height>window.innerHeight) w-=16;
  }
  return w;
}
jive.ext.x.xClientHeight = function() {
  var h=0;
  if(jive.ext.x.xOp5or6) h=window.innerHeight;
  else if(!window.opera && document.documentElement && document.documentElement.clientHeight) // v3.12
    h=document.documentElement.clientHeight;
  else if(document.body && document.body.clientHeight)
    h=document.body.clientHeight;
  else if($def(window.innerWidth,window.innerHeight,document.width)) {
    h=window.innerHeight;
    if(document.width>window.innerWidth) h-=16;
  }
  return h;
}



jive.ext.x.xDocHeight = function(doc) {
    if(doc)
        var b=doc.body, e=doc.documentElement;
    else
        var b=document.body, e=document.documentElement;
    var esh=0, eoh=0, bsh=0, boh=0;
    if (e) {
        esh = e.scrollHeight;
        eoh = e.offsetHeight;
    }
    if (b) {
        bsh = b.scrollHeight;
        boh = b.offsetHeight;
    }
    return Math.max(esh,eoh,bsh,boh);
}

// x_event.js
// X v3.15, Cross-Browser DHTML Library from Cross-Browser.com
// Copyright (c) 2002,2003,2004 Michael Foster (mike@cross-browser.com)
// This library is distributed under the terms of the LGPL (gnu.org)

jive.ext.x.xAddEventListener = function(e,eventType,eventListener,useCapture) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  eventType=eventType.toLowerCase();
  if((!jive.ext.x.xIE4Up && !jive.ext.x.xOp7) && e==window) {
    if(eventType=='resize') { jive.ext.x.xPCW=jive.ext.x.xClientWidth(); jive.ext.x.xPCH=jive.ext.x.xClientHeight(); jive.ext.x.xREL=eventListener; jive.ext.x.xResizeEvent(); return; }
    if(eventType=='scroll') { jive.ext.x.xPSL=jive.ext.x.xScrollLeft(); jive.ext.x.xPST=jive.ext.x.xScrollTop(); jive.ext.x.xSEL=eventListener; jive.ext.x.xScrollEvent(); return; }
  }
  if(e.addEventListener) e.addEventListener(eventType,eventListener,useCapture);
  else if(e.attachEvent) e.attachEvent('on'+eventType,eventListener);
  else if(e.captureEvents) {
    if(useCapture||(eventType.indexOf('mousemove')!=-1)) { e.captureEvents(eval('Event.'+eventType.toUpperCase())); }
    var eh='e.on'+eventType+'=eventListener';
    eval(eh);
  }
  else{
	var eh='e.on'+eventType+'=eventListener';
  	eval(eh);
  }
}
jive.ext.x.xRemoveEventListener = function(e,eventType,eventListener,useCapture) {
  if(!(e=jive.ext.x.xGetElementById(e))) return;
  eventType=eventType.toLowerCase();
  if((!jive.ext.x.xIE4Up && !jive.ext.x.xOp7) && e==window) {
    if(eventType=='resize') { jive.ext.x.xREL=null; return; }
    if(eventType=='scroll') { jive.ext.x.xSEL=null; return; }
  }
  var eh='e.on'+eventType+'=null';
  if(e.removeEventListener) e.removeEventListener(eventType,eventListener,useCapture);
  else if(e.detachEvent) e.detachEvent('on'+eventType,eventListener);
  else if(e.releaseEvents) {
    if(useCapture||(eventType.indexOf('mousemove')!=-1)) { e.releaseEvents(eval('Event.'+eventType.toUpperCase())); }
    eval(eh);
  }
  else eval(eh);
}
// xStopPropagation, Copyright 2004-2006 Michael Foster (Cross-Browser.com)
// Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL

jive.ext.x.xStopPropagation = function(evt)
{
  if (evt && evt.stopPropagation) evt.stopPropagation();
  else if (window.event) window.event.cancelBubble = true;
}
jive.ext.x.xEvent = function(evt) { // cross-browser event object prototype
  this.type = '';
  this.target = null;
  this.keyCode = 0;
  var e = evt ? evt : window.event;
  if(!e) return;
  if(e.type) this.type = e.type;
  if(e.target) this.target = e.target;
  else if(e.srcElement) this.target = e.srcElement;

  var pageX = null;
  var pageY = null;

  this.pageX = function(){
  	if(pageX == null){
		if(jive.ext.x.xOp5or6) { pageX = e.clientX; pageY = e.clientY; }
		else if(jive.ext.x.xDef(e.clientX,e.clientY)) { pageX = e.clientX + jive.ext.x.xScrollLeft(); pageY = e.clientY + jive.ext.x.xScrollTop(); }
  	}
  	return pageX;
  }

  this.pageY = function(){
  	if(pageY == null){
		if(jive.ext.x.xOp5or6) { pageX = e.clientX; pageY = e.clientY; }
		else if(jive.ext.x.xDef(e.clientX,e.clientY)) { pageX = e.clientX + jive.ext.x.xScrollLeft(); pageY = e.clientY + jive.ext.x.xScrollTop(); }
  	}
  	return pageY;
  }

  var offsetX = null;
  var offsetY = null;
  this.offsetX = function(){
  	if(offsetX == null){
		if(jive.ext.x.xDef(e.layerX,e.layerY)) { offsetX = e.layerX; offsetY = e.layerY; }
		else if(jive.ext.x.xDef(e.offsetX,e.offsetY)) { offsetX = e.offsetX; offsetY = e.offsetY; }
		else { offsetX = this.pageX - jive.ext.x.xPageX(this.target); offsetY = this.pageY - jive.ext.x.xPageY(this.target); }
  	}
  	return offsetX;
  }

  this.offsetY = function(){
  	if(offsetY == null){
		if(jive.ext.x.xDef(e.layerX,e.layerY)) { offsetX = e.layerX; offsetY = e.layerY; }
		else if(jive.ext.x.xDef(e.offsetX,e.offsetY)) { offsetX = e.offsetX; offsetY = e.offsetY; }
		else { offsetX = this.pageX - jive.ext.x.xPageX(this.target); offsetY = this.pageY - jive.ext.x.xPageY(this.target); }
  	}
  	return offsetY;
  }

  if (e.keyCode) { this.keyCode = e.keyCode; } // for moz/fb, if keyCode==0 use which
  else if (jive.ext.x.xDef(e.which)) { this.keyCode = e.which; }
}
jive.ext.x.xResizeEvent = function() { // window resize event simulation
  if (jive.ext.x.xREL) setTimeout('jive.ext.x.xResizeEvent()', 250);
  var cw = jive.ext.x.xClientWidth(), ch = jive.ext.x.xClientHeight();
  if (jive.ext.x.xPCW != cw || jive.ext.x.xPCH != ch) { jive.ext.x.xPCW = cw; jive.ext.x.xPCH = ch; if (jive.ext.x.xREL) jive.ext.x.xREL(); }
}
jive.ext.x.xScrollEvent = function() { // window scroll event simulation
  if (jive.ext.x.xSEL) setTimeout('jive.ext.x.xScrollEvent()', 250);
  var sl = jive.ext.x.xScrollLeft(), st = jive.ext.x.xScrollTop();
  if (jive.ext.x.xPSL != sl || jive.ext.x.xPST != st) { jive.ext.x.xPSL = sl; jive.ext.x.xPST = st; if (jive.ext.x.xSEL) jive.ext.x.xSEL(); }
}
// end x_event.js




// x_timer.js
// X v3.15, Cross-Browser DHTML Library from Cross-Browser.com
// Copyright (c) 2002,2003,2004 Michael Foster (mike@cross-browser.com)
// This library is distributed under the terms of the LGPL (gnu.org)

jive.ext.x.xTimerMgr = function() // object prototype
{
  // Public Methods
  this.set = function(type, obj, sMethod, uTime, data) // type: 'interval' or 'timeout'
  {
    return (this.timers[this.timers.length] = new xTimerObj(type, obj, sMethod, uTime, data));
  }
  // Private Properties
  this.timers = new Array();
  // Private Methods
  this.running = false;
  this.run = function()
  {
    if(this.running) return;
    this.running = true;
    var i, t, d = new Date(), now = d.getTime();
    for (i = 0; i < this.timers.length; ++i) {
      t = this.timers[i];
      if (t && t.running) {
        t.elapsed = now - t.time0;
        if (t.elapsed >= t.preset) { // timer event on t
          t.obj[t.mthd](t); // pass listener this xTimerObj
          if (t.type.charAt(0) == 'i') { t.time0 = now; }
          else { t.stop(); }
        }
      }
    }
    this.running = false;
  }
  // Private Object Prototype
  function xTimerObj(type, obj, mthd, preset, data)
  {
    // Public Methods
    this.stop = function() { this.running = false; }
    this.start = function() { this.running = true; } // continue after a stop
    this.reset = function()
    {
      var d = new Date();
      this.time0 = d.getTime();
      this.elapsed = 0;
      this.running = true;
    }
    // Public Properties
    this.data = data;
    // Read-only Properties
    this.type = type; // 'interval' or 'timeout'
    this.obj = obj;
    this.mthd = mthd; // string
    this.preset = preset;
    this.reset();
  } // end xTimerObj
} // end xTimerMgr

jive.ext.x.xTimer = new jive.ext.x.xTimerMgr(); // applications assume global name is 'xTimer'
jive.ext.x.xAddEventListener(window, "load", function(){
    setInterval('jive.ext.x.xTimer.run()', 250);
})

// end x_timer.js



// The remaining functions are utility functions added by Adam Wulf
// on June of 2004

/**
 * store an object in a hashtable.
 * hash key must be provided
 * currently O(1) for insert
 * O(n) for retrive/clear
 * we can optimize this later
 *
 * ex
 * var cal = new jotlet.model.Calendar();
 * var key = 224451 // any key you want to use to save/lookup the cal object
 * var table = new jotlet.external.y.HashTable();
 * table.put(key, cal);
 * table.get(key) // gives us back the cal object
 */
jive.ext.y.HashTable = function(){
	var that = this;

	var count = 0;

	this.getCount = function(){
		return count;
	}

	this.undefined = new Object();

	this.cache = new Array();

	/**
	 * add an item to the hashtable
	 */
	this.put = function(index, item){
		that.clear(index);
		that.cache[index] = item;
		count = count + 1;
	}

	/**
	 * retrieve an item from the hashtable
	 */
	this.get = function(index){
		if(typeof(that.cache[index])!='undefined' && that.cache[index] != that.undefined){
			return that.cache[index];
		}else{
			return false;
		}
	}

	/**
	 * clear the hashtable
	 */
	this.clear = function(index){
		if(that.cache[index] != that.undefined &&
		   that.cache[index] != null){
			   // decrease count only if we're
			   // clearing a legit item
			   count = count - 1;
		}
		that.cache[index] = that.undefined;
	}

	/**
	 * return an array of this Hashtable
	 * (values only, no keys)
	 */
	this.toArray = function(func){
		var a = new Array();
		for (var i in that.cache){
			if(typeof(func) == "function" &&
			   func(that.cache[i])){
			   	a.push(that.cache[i]);
			}else
			if(typeof(func) != "function" &&
			      that.cache[i] != that.undefined &&
				that.cache[i] != Array.prototype.find &&
				that.cache[i] != Array.prototype.jFind &&
				that.cache[i] != Array.prototype.remove &&
				that.cache[i] != Array.prototype.______array &&
				that.cache[i] != null){
				a.push(that.cache[i]);
			}
		}
		return a;
	}

	/**
	 * return the keys of a Hashtable
	 */
	this.toKeysArray = function(func){
		var a = new Array();
		for (var i in that.cache){
			a.push(i);
		}
		return i;
	}
}


jive.ext.y.yBottom = function(e,iY) {
  if(!(e=jive.ext.x.xGetElementById(e))) return 0;
  var css=$def(e.style);
  if(css && $str(e.style.bottom)) {
    if($num(iY)) e.style.bottom=iY+'px';
    else {
      iY=parseInt(e.style.bottom);
      if(isNaN(iY)) iY=0;
    }
  }
  else if(css && $def(e.style.pixelBottom)) {
    if($num(iY)) e.style.pixelBottom=iY;
    else iY=e.style.pixelBottom;
  }
  return iY;
}


/**
 * opacity is between 0-100
 */
jive.ext.y.yOpacity = function(e, op){
  if (!(e=jive.ext.x.xGetElementById(e))) return;
  if (jotlet.external.x.xNum(op)) {
    if (op<0) op = 0;
    else if(op > 100) op = 100;
    else op=Math.round(op);

    if(jotlet.external.x.xDef(e.style.MozOpacity)){
	  e.style.MozOpacity = (op/100.0);
	  return e.style.MozOpacity * 100.0;
    }
    if(jotlet.external.x.xDef(e.style.opacity)){
	  e.style.opacity = (op/100.0);
	  return e.style.opacity * 100.0;
    }

    if(jotlet.external.x.xStr(e.style.filter)){ // ie
//    if(jotlet.external.x.xDef(e.filters) && jotlet.external.x.xDef(e.filters.alpha) && jotlet.external.x.xDef(e.filters.alpha.opacity)){
    	e.style.filter = 'alpha(opacity=' + (op) + ')';
//	e.filters.alpha.opacity = op;
	e.yOpacity = op;
	return e.yOpacity;
    }
  }
  if(jotlet.external.x.xDef(e.style.MozOpacity)){
	  return e.style.MozOpacity * 100.0;
  }
  if(jotlet.external.x.xDef(e.style.opacity)){
	  return e.style.opacity * 100.0;
  }
  if(jotlet.external.x.xDef(e.filters) && jotlet.external.x.xDef(e.filters.alpha) && jotlet.external.x.xDef(e.filters.alpha.opacity)){
	  return e.filters.alpha.opacity;
  }
}



// var ajax = new yAjax("index.php");
// ajax.POST(parameters);


// url is a string
jive.ext.y.yAjax = function(rdyFun, errFun){
	var http_request = false;

	if (window.XMLHttpRequest) { // Mozilla, Safari,...
		http_request = new XMLHttpRequest();
		if (http_request.overrideMimeType) {
			http_request.overrideMimeType('text/xml');
		}
	} else if (window.ActiveXObject) { // IE
		try {
			http_request = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				http_request = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {}
		}
	}
	if (!http_request) {
		return false;
	}

	http_request.onreadystatechange = alertContents;

	function alertContents() {
		try{
			if (http_request.readyState == 4) {
				if (http_request.status == 200) {
					rdyFun(http_request.responseText);
				} else {
					errFun();
				}
			}
		}catch(e){
			// alert(e);
		}
	}

	// parameters is a form
	this.POST = function (url, parameters) {
			http_request.open('POST', url, true);
			http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			http_request.setRequestHeader("Content-length", parameters.length);
			http_request.setRequestHeader("Connection", "close");
			http_request.send(parameters);
	}
}


// Ver .91 Feb 21 1998
//////////////////////////////////////////////////////////////
//
//	Copyright 1998 Jeremie
//	Free for public non-commercial use and modification
//	as long as this header is kept intact and unmodified.
//	Please see http://www.jeremie.com for more information
//	or email jer@jeremie.com with questions/suggestions.
//
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
////////// Simple XML Processing Library //////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
////   Fully complies to the XML 1.0 spec
////   as a well-formed processor, with the
////   exception of full error reporting and
////   the document type declaration(and it's
///   related features, internal entities, etc).
///////////////////////////////////////////////////////////////

// global vars to track element UID's for the index

jive.xml._Xparse_count = 0;
jive.xml._Xparse_index = new Array();

//////////////////////
//// util to replace internal entities in input string
jive.xml._entity = function(str)
{
	var A = new Array();

	A = str.split("&l" + "t;");
	str = A.join("<");
	A = str.split("&g" + "t;");
	str = A.join(">");
	A = str.split("&qu" + "ot;");
	str = A.join("\"");
	A = str.split("&ap" + "os;");
	str = A.join("\'");
	A = str.split("&a" + "mp;");
	str = A.join("&");

	return str;
}
/////////////////////////
//////////////////////
//// util to remove white characters from input string
jive.xml._strip = function(str)
{
	var A = new Array();

	A = str.split("\n");
	str = A.join("");
	A = str.split(" ");
	str = A.join("");
	A = str.split("\t");
	str = A.join("");
	return str;
}

//////////////////////
//// util to replace white characters in input string
jive.xml._normalize = function(str)
{
	var A = new Array();

	A = str.split("\n");
	str = A.join(" ");
	A = str.split("\t");
	str = A.join(" ");
	return str;
}
//////////////////
/////////////////////////
//// the object constructors for the hybrid DOM
jive.xml._element = function()
{
	this.type = "element";
	this.tagName = new String();
	this.attributes = new Array();
	this.childNodes = new Array();
	this.nodeValue = "";
	this.uid = jive.xml._Xparse_count++;
	jive.xml._Xparse_index[this.uid]=this;
}

jive.xml._chardata = function()
{
	this.type = "chardata";
	this.value = new String();
}

jive.xml._pi = function()
{
	this.type = "pi";
	this.value = new String();
}

jive.xml._comment = function()
{
	this.type = "comment";
	this.value = new String();
}

// an internal fragment that is passed between functions
jive.xml._frag = function()
{
	this.str = new String();
	this.ary = new Array();
	this.end = new String();
}

/////////////////////////

///////////////////////
//// functions to process different tags

jive.xml._tag_element = function(frag)
{
	// initialize some temporary variables for manipulating the tag
	var close = frag.str.indexOf(">");
	var empty = (frag.str.substring(close - 1,close) == "/");
	if(empty)
	{
		close -= 1;
	}

	// split up the name and attributes
	var starttag = jive.xml._normalize(frag.str.substring(1,close));
	var nextspace = starttag.indexOf(" ");
	var attribs = new String();
	var name = new String();
	if(nextspace != -1)
	{
		name = starttag.substring(0,nextspace);
		attribs = starttag.substring(nextspace + 1,starttag.length);
	}
	else
	{
		name = starttag;
	}

	var thisary = frag.ary.length;
	frag.ary[thisary] = new jive.xml._element();
	frag.ary[thisary].tagName = jive.xml._strip(name);
	if(attribs.length > 0)
	{
		frag.ary[thisary].attributes = jive.xml._attribution(attribs);
	}
	if(!empty)
	{
		// !!!! important,
		// take the contents of the tag and parse them
		var contents = new jive.xml._frag();
		contents.str = frag.str.substring(close + 1,frag.str.length);
		contents.end = name;
		var val = contents;
		contents = jive.xml._compile(contents);
		frag.ary[thisary].childNodes = contents.ary;
		frag.ary[thisary].nodeValue = val;
		frag.str = contents.str;
	}
	else
	{
		frag.str = frag.str.substring(close + 2,frag.str.length);
	}
	return frag;
}

jive.xml._tag_pi = function(frag)
{
	var close = frag.str.indexOf("?" + ">");
	var val = frag.str.substring(2,close);
	var thisary = frag.ary.length;
	frag.ary[thisary] = new jive.xml._pi();
	frag.ary[thisary].nodeValue = val;
	frag.str = frag.str.substring(close + 2,frag.str.length);
	return frag;
}


jive.xml._tag_comment = function(frag)
{
	var close = frag.str.indexOf("--" + ">");
	var val = frag.str.substring(4,close);
	var thisary = frag.ary.length;
	frag.ary[thisary] = new jive.xml._comment();
	frag.ary[thisary].nodeValue = val;
	frag.str = frag.str.substring(close + 3,frag.str.length);
	return frag;
}

jive.xml._tag_cdata = function(frag)
{
	var close = frag.str.indexOf("]" + "]>");
	var val = frag.str.substring(9,close);
	var thisary = frag.ary.length;
	frag.ary[thisary] = new jive.xml._chardata();
	frag.ary[thisary].nodeValue = val;
	frag.str = frag.str.substring(close + 3,frag.str.length);
	return frag;
}

/////////////////////////


//////////////////
//// util for element attribute parsing
//// returns an array of all of the keys = values
jive.xml._attribution = function(str)
{
	var all = new Array();
	while(1)
	{
		var eq = str.indexOf("=");
		if(str.length == 0 || eq == -1)
		{
			return all;
		}

		var id1 = str.indexOf("\'");
		var id2 = str.indexOf("\"");
		var ids = new Number();
		var id = new String();
		if((id1 < id2 && id1 != -1) || id2 == -1)
		{
			ids = id1;
			id = "\'";
		}
		if((id2 < id1 || id1 == -1) && id2 != -1)
		{
			ids = id2;
			id = "\"";
		}
		var nextid = str.indexOf(id,ids + 1);
		var val = str.substring(ids + 1,nextid);

		var name = jive.xml._strip(str.substring(0,eq));
		all[name] = jive.xml._entity(val);
		str = str.substring(nextid + 1,str.length);
	}
	return "";
}

////////////////////

/////////////////////////
//// transforms raw text input into a multilevel array
jive.xml._compile = function(frag)
{
	// keep circling and eating the str
	while(1)
	{
		// when the str is empty, return the fragment
		if(frag.str.length == 0)
		{
			return frag;
		}

		var TagStart = frag.str.indexOf("<");

		if(TagStart != 0)
		{
			// theres a chunk of characters here, store it and go on
			var thisary = frag.ary.length;
			frag.ary[thisary] = new jive.xml._chardata();
			if(TagStart == -1)
			{
				frag.ary[thisary].nodeValue = jive.xml._entity(frag.str);
				frag.str = "";
			}
			else
			{
				frag.ary[thisary].nodeValue = jive.xml._entity(frag.str.substring(0,TagStart));
				frag.str = frag.str.substring(TagStart,frag.str.length);
			}
		}
		else
		{
			// determine what the next section is, and process it
			if(frag.str.substring(1,2) == "?")
			{
				frag = jive.xml._tag_pi(frag);
			}
			else
			{
				if(frag.str.substring(1,4) == "!" + "--")
				{
					frag = jive.xml._tag_comment(frag);
				}
				else
				{
					if(frag.str.substring(1,9) == "!" + "[CDA" + "TA[")
					{
						frag = jive.xml._tag_cdata(frag);
					}
					else
					{
						if(frag.str.substring(1,frag.end.length + 3) == "/" + frag.end + ">" || jive.xml._strip(frag.str.substring(1,frag.end.length + 3)) == "/" + frag.end)
						{
							// found the end of the current tag, end the recursive process and return
							frag.str = frag.str.substring(frag.end.length + 3,frag.str.length);
							frag.end = "";
							return frag;
						}
						else
						{
							frag = jive.xml._tag_element(frag);
						}
					}
				}
			}
		}
	}
	return "";
}

///////////////////////


//////////////////////
//// util to remove \r characters from input string
//// and return xml string without a prolog
jive.xml._prolog = function(str)
{
	var A = new Array();

	A = str.split("\r\n");
	str = A.join("\n");
	A = str.split("\r");
	str = A.join("\n");

	var start = str.indexOf("<");
	if(str.substring(start,start + 3) == "<" + "?x" || str.substring(start,start + 3) == "<" + "?X" )
	{
		var close = str.indexOf("?" + ">");
		str = str.substring(close + 2,str.length);
	}
	var start = str.indexOf("<!DOC" + "TYPE");
	if(start != -1)
	{
		var close = str.indexOf(">",start) + 1;
		var dp = str.indexOf("[",start);
		if(dp < close && dp != -1)
		{
			close = str.indexOf("]" + ">",start) + 2;
		}
		str = str.substring(close,str.length);
	}
	return str;
}


//// Main public function that is called to
//// parse the XML string and return a root element object
jive.xml.Xparse = function(src)
{
	var frag = new jive.xml._frag();
	// remove bad \r characters and the prolog
	frag.str = jive.xml._prolog(src);
	// create a root element to contain the document
	var root = new Object();
	// main recursive function to process the xml
	frag = jive.xml._compile(frag);
	// all done, lets return the root element + index + document
	if(frag.ary.length > 0){
		root.documentElement = frag.ary[0];
	}else{
		root.documentElement = null;
	}
	root.tagName = "RO" + "OT";
	root.index = jive.xml._Xparse_index;
	jive.xml._Xparse_index = new Array();
	return root;
}

/////////////////////////

//////////////////////////////////////////////////////////////

//	End Copyright 1998 Jeremie

//////////////////////////////////////////////////////////////




// Ver .91 Feb 21 1998
//////////////////////////////////////////////////////////////
//
//	Copyright 1998 Jeremie
//	Free for public non-commercial use and modification
//	as long as this header is kept intact and unmodified.
//	Please see http://www.jeremie.com for more information
//	or email jer@jeremie.com with questions/suggestions.
//
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
////////// Simple XML Processing Library //////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
////   Fully complies to the XML 1.0 spec
////   as a well-formed processor, with the
////   exception of full error reporting and
////   the document type declaration(and it's
////   related features, internal entities, etc).
///////////////////////////////////////////////////////////////



jive.xml.XMLParser = function(){

	var parser = null;

//	alert(XMLDom);
	if (window.ActiveXObject){

		var ARR_ACTIVEX = ["MSXML4.DOMDocument",
                   "MSXML3.DOMDocument",
                   "MSXML2.DOMDocument",
                   "MSXML.DOMDocument",
                   "Microsoft.XmlDom"]

            var xmlDoc = null;


            if(xmlDoc == null){
			var bFound = false;
			for(var i=0;i<ARR_ACTIVEX.length && !bFound;i++){
				try{
					xmlDoc=new ActiveXObject(ARR_ACTIVEX[i]);
					bFound = true;
				}catch(e){
				}
			}
		}

		if(xmlDoc == null){
			alert("No XML parser available");
			return;
		}
		parser = function(str){
			xmlDoc.async="false";
			xmlDoc.loadXML(str);
			return xmlDoc;
		}


	}

	if(parser == null && window.DOMParser){
		var xmlDoc = new DOMParser();

		parser = function(str){
			var doc = xmlDoc.parseFromString(str, "text/xml");
			var roottag = doc.documentElement;
			if ((roottag.tagName == "parserError") ||
			    (roottag.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml")){
				    return null;
			}
			return doc;
		}

	}else if (parser == null && document.implementation && document.implementation.createDocument){
		//create the DOM Document the standards way
		var xmlDoc = document.implementation.createDocument("","", null);
		parser = function(str){
			xmlDoc.async="false";
			xmlDoc.loadXML(str);
			return xmlDoc;
		}
	}else{
        parser = function(str){
            return jive.xml.Xparse(str);
        }
	}


	this.parse = function(str){
		if(parser != null){
			return parser(str);
		}else{
			throw "no xml parser defined"
		}
	}

}








// url is a string
// control is the controller
// rdyFun will be called when a successful result is returned from the server
// errFun will be called if anything goes wrong
jive.model.Ajax = function(control, rdyFun, errFun){
	var that = this;

	// parameters is a form
	this.POST = function (url, parameters) {

		var readyFunction = function(reply){
			try{
				// reply is the message from the server
				// check to see if the server sent back JSON
				var list = null;
                if(!$obj(list) || list == null || !$obj(list.documentElement) || list.documentElement == null){
                    var parser = new jive.xml.XMLParser();
                    try{
                        list = parser.parse(reply);
                    }catch(e){
                        errFun("XML Parse exception");
                        return;
                    }
                }
				// list is a ROOT xml object
				// and holds the actual document in the
				// .contents property
				if($obj(list) && list != null && $obj(list.documentElement) && list.documentElement != null){
					// if it returned results at all
					// then get them
					list = list.documentElement;
				}else{
					// otherwise its an error
					errFun("XML Parse exception");
					return;
				}

				if(list.tagName == "br"){
					// an exception
					//
					// we should log this somehow ?
					errFun("Server Exception");
				}else
				if(list.tagName == "NotLoggedInException"){
					control.handleLogIn(function(){
						that.POST(url, parameters);
					});
				}else{
					control.poke();
					rdyFun(list);
				}
			}catch(e){
				alert("ajax error:" + e);
			}
		};

		var errorFun = function(){
//			500 error
			errFun("500 Status");
		}

		var ajax = new jive.ext.y.yAjax(readyFunction, errorFun);
		ajax.POST(url, parameters);
	}
}


jive.model.Controller = function(){

    var that = this;

    //
    // fake default language for now
    //
    var def_lang = new Object();
    def_lang.childNodes = new Array();


    //
    // return an object to help us ajax
    this.newAjax = function(rdyFun, errFun){
        return new jive.model.Ajax(that, rdyFun, errFun);
    };

    //
    //
    // settings / etc managers here
    //
    var login_manager = new jive.model.LoginManager(that);

    this.getLoginManager = function(){
        return login_manager;
    }

    var settings_manager = new jive.model.SettingsManager(that);

    this.getSettingsManager = function(){
        return settings_manager;
    }

    var refresh_manager = new jive.model.RefreshManager(that);

    this.getRefreshManager = function(){
        return refresh_manager;
    }

    var language_manager = new jive.model.LanguageManager(that, default_lang);

    this.getLanguageManager = function(){
        return language_manager;
    }




    //
    //
    // model below here
    //

    var project_cache = new jive.model.ProjectCache(that);

    this.getProjectCache = function(){
        return project_cache;
    }

    var user_cache = new jive.model.UserCache(that);

    this.getUserCache = function(){
        return user_cache;
    }

    var task_cache = new jive.model.TaskCache(that);

    this.getTaskCache = function(){
        return task_cache;
    }


    //
    // we found out that we're logged out
    // when we tried to thunk(). log back
    // in, and thunk() again.
    this.handleLogIn = function(thunk){
        // uh oh, tell the refresh manager
        // that we're logged out

        that.getRefreshManager().loggedOut();

        //
        // optionally let the user login again via ajax
        // if successful, be sure to call thunk();

    }

    // an ajax call succeeded
    // tell the refresh manager
    this.poke = function(){
        that.getRefreshManager().poke();
    }

    //
    // return true if the calendar/project is
    // visible, false otherwise
    this.isCalendarVisibleHuh = function(id){
        return true;
    }

    this.isReadOnly = function(){
        return false;
    }




    /******************************************
     * listener functions
     ******************************************/
    this.addListener = function(list){
        listeners.push(list);
    }

    var listeners = new Array();
    var listener_actions = new Array();
    /**
     * act must be a thunk (a function without arguments)
     * it will be executed after either
     * notifyLoadFinish or notifyLoadFail
     */
    this.addListenerAction = function(act){
        listener_actions.push(act);
    }

    /**
     * private
     * executes all the listener actions
     */
    this.executeListenerActions = function(){
        while(listener_actions.length > 0){
            listener_actions[0]();
            listener_actions.splice(0,1);
        }
    }

    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }

    /**
     * notification functions
     */
    this.notifyTinyMCELoaded = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].tinyMCELoaded();
        }
        that.executeListenerActions();
    }
}

/**
 * checks if Date d1 is <= Date d2
 * by checking day of the month/year
 * only, (hours/min/sec/mil are ignored)
 *
 * ex
 * var d1 = new Date(); d1.setFullYear(2004);
 * var d2 = new Date(); d1.setFullYear(2005);
 * date(LTEQ(d1, d2)); // true
 * date(LTEQ(d2, d1)); // false
 */
jive.model.dateLTEQ = function(d1, d2){
	return (d1.getFullYear() < d2.getFullYear() || d1.getFullYear() == d2.getFullYear() && (
	        d1.getMonth() < d2.getMonth()  || d1.getMonth() == d2.getMonth() && (
	        d1.getDate() <= d2.getDate())));
}
jive.model.dateLT = function(d1, d2){
	return (d1.getFullYear() < d2.getFullYear() || d1.getFullYear() == d2.getFullYear() && (
	        d1.getMonth() < d2.getMonth()  || d1.getMonth() == d2.getMonth() && (
	        d1.getDate() < d2.getDate())));
}


jive.model.dateGT = function(d1, d2){
	return jive.model.dateLT(d2, d1);
}
jive.model.dateGTEQ = function(d1, d2){
	return jive.model.dateLTEQ(d2, d1);
}
/*
 * compares if two dates have equal month and year
 */
jive.model.monthYearEQ = function(d1, d2){
	return d1.getMonth() == d2.getMonth() &&
	        d1.getFullYear() == d2.getFullYear();
}
jive.model.dateEQ = function(d1, d2){
	return (d1.getDate() == d2.getDate() &&
	        d1.getMonth() == d2.getMonth() &&
	        d1.getFullYear() == d2.getFullYear());
}
/*
 * compares if two dates are equal (including time HH:MM only)
 */
jive.model.datetimeEQ = function(d1, d2){
	return (d1.getFullYear() == d2.getFullYear() &&
	        d1.getMonth() == d2.getMonth() &&
	        d1.getDate() == d2.getDate() &&
		d1.getHours() == d2.getHours() &&
		d1.getMinutes() == d2.getMinutes());
}
/*
 * compares if two dates are less than or equal (including time HH:MM only)
 */
jive.model.datetimeLTEQ = function(d1, d2){
	return (d1.getFullYear() < d2.getFullYear() || (d1.getFullYear() == d2.getFullYear() &&
	        (d1.getMonth() < d2.getMonth() || d1.getMonth() == d2.getMonth() &&
	        (d1.getDate() < d2.getDate() || d1.getDate() == d2.getDate() &&
		(d1.getHours() < d2.getHours() || d1.getHours() == d2.getHours() &&
		d1.getMinutes() <= d2.getMinutes())))));
}
/**
 * this subtracts one month from a date
 *
 * THIS FIXES A SAFARI BUG (OR 'FEATURE' :)
 *
 * this also fixes subtracting a month if the date >= 29
 * ie, if it's march 29th - 1 month = feb 29th = march 1st :(
 * instead, it'll subtract a month, and if the month is still the same
 * it'll subtract 1 day until it's different
 */
jive.model.dateMinusMonth = function(d){
	var m = d.getMonth();
	if(d.getMonth() == 0){
		d.setFullYear(d.getFullYear() - 1);
		d.setMonth(11);
	}else{
		d.setMonth(d.getMonth()-1);
	}
	while(d.getMonth() == m){
		d.setDate(d.getDate() - 1);
	}
}
/**
 * this subtracts one week from a date
 *
 * THIS FIXES A SAFARI BUG (OR 'FEATURE' :)
 */
jive.model.dateMinusWeek = function(d){
	d.setDate(d.getDate()-7);
}
/**
 * this subtracts one day from a date
 *
 * THIS FIXES A SAFARI BUG (OR 'FEATURE' :)
 */
jive.model.dateMinusDay = function(d){
	if(d.getDate() == 0 && d.getDate() == 1){
		d.setFullYear(d.getFullYear() - 1);
		d.setMonth(11);
		d.setDate(31);
	}else{
		d.setDate(d.getDate()-1);
	}
}


/**
 * helps format common date strings
 */
jive.model.DateHelper = function(control){

	var settings = control.getSettingsManager();
	var lm = control.getLanguageManager();
	var that = this;

//	var shortDayNames = new Array("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
//	var dayNames = new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
//	var longMonth = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
//	var shortMonth = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");


	/**
	 * returns the human readable difference
	 * between 2 datetimes
	 * ie, 2 days, or about an hour
	 *
	 * it should be able to complete the sentance:
	 * the event starts _________
	 * "in 4 minutes"
	 * "15 minutes ago"
	 */
	this.readableDifference = function(d1, d2){
		var secs = d1.getTime() - d2.getTime();
		secs = secs / 1000;

		var ago = false;
		if(secs <= 0){
			ago = true;
			secs = -1 * secs;
		}

		var ret = "";

		if(secs < 10){
			return "just now";
		}else if(secs < 20){
			ret = "a few seconds";
		}else if(secs < 60){
			ret = "less than a minute";
		}else if(secs < 90){
			ret = "about a minute";
		}else{
			var mins = Math.ceil(secs / 60.0);
			if(mins <=50){
				var s = (mins == 1) ? "" : "s";
				ret = mins + " minute" + s;
			}else{
				var hours = Math.ceil(mins / 60.0);
				if(hours < 20){
					var s = (hours == 1) ? "" : "s";
					ret = hours + " hour" + s;
				}else{
					var days = Math.round(hours / 24.0);
					if(days < 7){
						var s = (days == 1) ? "" : "s";
						ret = days + " day" + s;
					}else{
						var weeks = Math.ceil(days / 7.0);
						var s = (weeks == 1) ? "" : "s";
						ret = weeks + " week" + s;
					}
				}
			}
		}

		if(ago){
			return ret + " ago";
		}else{
			return "in " + ret;
		}
	}


	/**
	 * returns the human readable difference
	 * between 2 datetimes
	 * ie, 2 days (in days/weeks, not hours/minutes)
	 *
	 * it should be able to complete the sentance:
	 * the event starts _________
	 * "in 4 days"
	 * "15 weeks ago"
	 */
	this.readableDateDifference = function(d11, d22){

		var d1 = new Date();
		d1.setTime(d11.getTime());
		d1.setHours(0);
		d1.setMinutes(0);
		d1.setSeconds(0);
		d1.setMilliseconds(0);

		var d2 = new Date();
		d2.setTime(d22.getTime());
		d2.setHours(0);
		d2.setMinutes(0);
		d2.setSeconds(0);
		d2.setMilliseconds(0);


		var secs = d1.getTime() - d2.getTime();
		secs = secs / 1000;

		var ago = false;
		if(jive.model.dateLT(d1, d2)){
			ago = true;
		}
		secs = Math.abs(secs);

		var ret = "";

			var mins = Math.ceil(secs / 60.0);
			var hours = Math.ceil(mins / 60.0);
			var days = Math.floor(hours / 24.0);
			if(days == 0){
				ret = "today";
			}else if(days == 1 && ago){
				ret = "yesterday";
			}else if(days == 1 && !ago){
				ret = "tomorrow";
			}else if(days < 7){
				var s = (days == 1) ? "" : "s";
				ret = days + " day" + s;
				if(ago){
					ret = ret + " ago";
				}else{
					ret = "in " + ret;
				}
			}else{
				var weeks = Math.ceil(days / 7.0);
				var s = (weeks == 1) ? "" : "s";
				ret = weeks + " week" + s;
				if(ago){
					ret = ret + " ago";
				}else{
					ret = "in " + ret;
				}
			}
		return ret;
	}

	/**
	 * returns time formatted
	 * yyyy-mm-dd hh:ii:ss
	 */
	this.formatToDateTime = function(d){
		var year = d.getFullYear();
		var month = d.getMonth() + 1;
		if(month < 10) month = "0" + month;
		var day = d.getDate();
		if(day < 10) day = "0" + day;

		var hour = d.getHours();
		if(hour < 10) hour = "0" + hour;
		var minute = d.getMinutes();
		if(minute < 10) minute = "0" + minute;
		var second = d.getSeconds();
		if(second < 10) second = "0" + second;

		return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
	}

	/**
	 * returns time formatted
	 * as [h]h:mma
	 */
	this.formatTo12HourTime = function(d){
		var format = settings.getTimeFormat();
		if(format == "3:00p"){
			var hour = d.getHours();
			var minute = d.getMinutes();
			if(minute < 10) minute = "0" + minute;
			var ampm = "a";

			if(hour >= 12){
				ampm = "p";
				hour -= 12;
			}
			if(hour == 0){
				hour = 12;
			}
			return hour + ":" + minute + ampm;
		}else{ // 15:00
			var hour = d.getHours();
			var minute = d.getMinutes();
			if(minute < 10) minute = "0" + minute;
			return hour + ":" + minute;
		}
	}

	/**
	 * returns time formatted
	 * as [h]ha
	 */
	this.formatToHourTime = function(d){
		var format = settings.getTimeFormat();
		if(format == "3:00p"){
			var hour = d.getHours();
			var ampm = "a";

			if(hour >= 12){
				ampm = "p";
				hour -= 12;
			}
			if(hour == 0){
				hour = 12;
			}
			return hour + ampm;
		}else{ // 15
			var hour = d.getHours();
			return hour;
		}
	}

	/**
	 * returns time formatted
	 * yyyy-mm-dd
	 */
	this.formatToStandardTime = function(d){
		var year = "" + d.getFullYear();
		var month = d.getMonth() + 1;
		if(month < 10) month = "0" + month;
		var day = d.getDate();
		if(day < 10) day = "0" + day;

		var hour = d.getHours();
		if(hour < 10) hour = "0" + hour;
		var minute = d.getMinutes();
		if(minute < 10) minute = "0" + minute;
		var second = d.getSeconds();
		if(second < 10) second = "0" + second;
		return year + "-" + month + "-" + day;
	}

	/**
	 * returns date formatted
	 * as mm/dd
	 */
	this.formatToShortDate = function(d){
		var month = d.getMonth()+1;
		var day = d.getDate();

		var format = settings.getDateFormat();
		if(format == "4/30"){
			return month + "/" + day;
		}else{ // 30/4
			return day + "/" + month;
		}
	}

	/**
	 * returns date formatted
	 * as Monday, February 14th, 2005
	 */
	this.formatToLongDate = function(d){
		var tlang = lm.getActiveLanguage();
		var str = tlang.longDay(d.getDay()) + ", ";

		var month = tlang.longMonth(d.getMonth());
		var day = d.getDate();


		var format = settings.getDateFormat();
		if(format == "4/30"){
			if(d.getDate() == 1 || d.getDate() == 21 || d.getDate() == 31){
				day += "st";
			}else
			if(d.getDate() == 2 || d.getDate() == 22){
				day += "nd";
			}else
			if(d.getDate() == 3 || d.getDate() == 23){
				day += "rd";
			}else{
				day += "th";
			}
			str += month + " " + day;
		}else{ // 30/4
			str += day + " " + month;
		}
		str += ", " + d.getFullYear();

		return str;
	}

	/**
	 * returns date formatted
	 * as Mon, Feb 14th, 2005
	 */
	this.formatToMediumDate = function(d){
		var str = that.formatToMediumDateNoYear(d);
		str += ", " + d.getFullYear();

		return str;
	}

	/**
	 * returns date formatted
	 * as Mon, Feb 14th
	 */
	this.formatToMediumDateNoYear = function(d){
		var tlang = lm.getActiveLanguage();
		var str = tlang.shortDay(d.getDay()) + ", ";

		var month = tlang.shortMonth(d.getMonth());
		var day = d.getDate();


		var format = settings.getDateFormat();
		if(format == "4/30"){
			if(d.getDate() == 1 || d.getDate() == 21 || d.getDate() == 31){
				day += "st";
			}else
			if(d.getDate() == 2 || d.getDate() == 22){
				day += "nd";
			}else
			if(d.getDate() == 3 || d.getDate() == 23){
				day += "rd";
			}else{
				day += "th";
			}
			str += month + " " + day;
		}else{ // 30/4
			str += day + " " + month;
		}

		return str;
	}

	/**
	 * returns date formatted
	 * as Feb 14th
	 */
	this.formatToMedDate = function(d){
		var tlang = lm.getActiveLanguage();

		var month = tlang.shortMonth(d.getMonth());
		var day = d.getDate();
		var str = "";
		var format = settings.getDateFormat();
		if(format == "4/30"){
			var sfx = "";
			if(d.getDate() == 1 || d.getDate() == 21 || d.getDate() == 31){
				sfx = "st";
			}else
			if(d.getDate() == 2 || d.getDate() == 22){
				sfx = "nd";
			}else
			if(d.getDate() == 3 || d.getDate() == 23){
				sfx = "rd";
			}else{
				sfx = "th";
			}
			str = month + " " + day + sfx;
		}else{
			str = day + " " + month;
		}

		return str;
	}

	/**
	 * returns date formatted
	 * as Febuary 14th
	 */
	this.formatToMedLongDate = function(d){
		var tlang = lm.getActiveLanguage();

		var month = tlang.longMonth(d.getMonth());
		var day = d.getDate();
		var str = "";
		var format = settings.getDateFormat();
		if(format == "4/30"){
			var sfx = "";
			if(d.getDate() == 1 || d.getDate() == 21 || d.getDate() == 31){
				sfx = "st";
			}else
			if(d.getDate() == 2 || d.getDate() == 22){
				sfx = "nd";
			}else
			if(d.getDate() == 3 || d.getDate() == 23){
				sfx = "rd";
			}else{
				sfx = "th";
			}
			str = month + " " + day + sfx;
		}else{
			str = day + " " + month;
		}

		return str;
	}

	this.getMonthName = function(d){
		var tlang = lm.getActiveLanguage();
		return tlang.longMonth(d.getMonth());
	}
}


var default_lang = {
"childNodes": [
	{
	"childNodes": [
		{
		"tagName": "lang_id",
		"childNodes": [{ "nodeValue": "1"
}]
		},
		{
		"tagName": "name",
		"childNodes": [{ "nodeValue": "English"
}]
		},
		{
		"childNodes": [
			{
			"tagName": "eng_name",
			"childNodes": [{ "nodeValue": "English"
}]
			},
			{
			"tagName": "name",
			"childNodes": [{ "nodeValue": "English"
}]
			},
			{
			"tagName": "january",
			"childNodes": [{ "nodeValue": "January"
}]
			},
			{
			"tagName": "february",
			"childNodes": [{ "nodeValue": "February"
}]
			},
			{
			"tagName": "march",
			"childNodes": [{ "nodeValue": "March"
}]
			},
			{
			"tagName": "april",
			"childNodes": [{ "nodeValue": "April"
}]
			},
			{
			"tagName": "may",
			"childNodes": [{ "nodeValue": "May"
}]
			},
			{
			"tagName": "june",
			"childNodes": [{ "nodeValue": "June"
}]
			},
			{
			"tagName": "july",
			"childNodes": [{ "nodeValue": "July"
}]
			},
			{
			"tagName": "august",
			"childNodes": [{ "nodeValue": "August"
}]
			},
			{
			"tagName": "september",
			"childNodes": [{ "nodeValue": "September"
}]
			},
			{
			"tagName": "october",
			"childNodes": [{ "nodeValue": "October"
}]
			},
			{
			"tagName": "november",
			"childNodes": [{ "nodeValue": "November"
}]
			},
			{
			"tagName": "december",
			"childNodes": [{ "nodeValue": "December"
}]
			},
			{
			"tagName": "sunday",
			"childNodes": [{ "nodeValue": "Sunday"
}]
			},
			{
			"tagName": "monday",
			"childNodes": [{ "nodeValue": "Monday"
}]
			},
			{
			"tagName": "tuesday",
			"childNodes": [{ "nodeValue": "Tuesday"
}]
			},
			{
			"tagName": "wednesday",
			"childNodes": [{ "nodeValue": "Wednesday"
}]
			},
			{
			"tagName": "thursday",
			"childNodes": [{ "nodeValue": "Thursday"
}]
			},
			{
			"tagName": "friday",
			"childNodes": [{ "nodeValue": "Friday"
}]
			},
			{
			"tagName": "saturday",
			"childNodes": [{ "nodeValue": "Saturday"
}]
			},
			{
			"tagName": "sh_january",
			"childNodes": [{ "nodeValue": "Jan"
}]
			},
			{
			"tagName": "sh_february",
			"childNodes": [{ "nodeValue": "Feb"
}]
			},
			{
			"tagName": "sh_march",
			"childNodes": [{ "nodeValue": "Mar"
}]
			},
			{
			"tagName": "sh_april",
			"childNodes": [{ "nodeValue": "Apr"
}]
			},
			{
			"tagName": "sh_may",
			"childNodes": [{ "nodeValue": "May"
}]
			},
			{
			"tagName": "sh_june",
			"childNodes": [{ "nodeValue": "Jun"
}]
			},
			{
			"tagName": "sh_july",
			"childNodes": [{ "nodeValue": "Jul"
}]
			},
			{
			"tagName": "sh_august",
			"childNodes": [{ "nodeValue": "Aug"
}]
			},
			{
			"tagName": "sh_september",
			"childNodes": [{ "nodeValue": "Sep"
}]
			},
			{
			"tagName": "sh_october",
			"childNodes": [{ "nodeValue": "Oct"
}]
			},
			{
			"tagName": "sh_november",
			"childNodes": [{ "nodeValue": "Nov"
}]
			},
			{
			"tagName": "sh_december",
			"childNodes": [{ "nodeValue": "Dec"
}]
			},
			{
			"tagName": "sh_sunday",
			"childNodes": [{ "nodeValue": "Sun"
}]
			},
			{
			"tagName": "sh_monday",
			"childNodes": [{ "nodeValue": "Mon"
}]
			},
			{
			"tagName": "sh_tuesday",
			"childNodes": [{ "nodeValue": "Tue"
}]
			},
			{
			"tagName": "sh_wednesday",
			"childNodes": [{ "nodeValue": "Wed"
}]
			},
			{
			"tagName": "sh_thursday",
			"childNodes": [{ "nodeValue": "Thu"
}]
			},
			{
			"tagName": "sh_friday",
			"childNodes": [{ "nodeValue": "Fri"
}]
			},
			{
			"tagName": "sh_saturday",
			"childNodes": [{ "nodeValue": "Sat"
}]
			},
			{
			"tagName": "loading",
			"childNodes": [{ "nodeValue": "Loading..."
}]
			},
			{
			"tagName": "sb_actions",
			"childNodes": [{ "nodeValue": "Actions"
}]
			},
			{
			"tagName": "sb_my_calendars",
			"childNodes": [{ "nodeValue": "My Calendars"
}]
			},
			{
			"tagName": "sb_other_calendars",
			"childNodes": [{ "nodeValue": "Other Calendars"
}]
			},
			{
			"tagName": "sb_alerts",
			"childNodes": [{ "nodeValue": "Alerts"
}]
			},
			{
			"tagName": "sb_general_tasks",
			"childNodes": [{ "nodeValue": "General Tasks"
}]
			},
			{
			"tagName": "sb_no_tasks",
			"childNodes": [{ "nodeValue": "no tasks"
}]
			},
			{
			"tagName": "sb_reminder",
			"childNodes": [{ "nodeValue": "Reminder"
}]
			},
			{
			"tagName": "sb_reminders",
			"childNodes": [{ "nodeValue": "Reminders"
}]
			},
			{
			"tagName": "sb_alert",
			"childNodes": [{ "nodeValue": "Alert"
}]
			},
			{
			"tagName": "motto",
			"childNodes": [{ "nodeValue": "Simply Spectacular Time Management"
}]
			},
			{
			"tagName": "nav_month",
			"childNodes": [{ "nodeValue": "month"
}]
			},
			{
			"tagName": "nav_week",
			"childNodes": [{ "nodeValue": "week"
}]
			},
			{
			"tagName": "nav_day",
			"childNodes": [{ "nodeValue": "day"
}]
			},
			{
			"tagName": "nav_today",
			"childNodes": [{ "nodeValue": "today"
}]
			},
			{
			"tagName": "nav_tomorrow",
			"childNodes": [{ "nodeValue": "tomorrow"
}]
			},
			{
			"tagName": "nav_refresh",
			"childNodes": [{ "nodeValue": "refresh"
}]
			},
			{
			"tagName": "nav_list",
			"childNodes": [{ "nodeValue": "list"
}]
			},
			{
			"tagName": "nav_overview",
			"childNodes": [{ "nodeValue": "overview"
}]
			},
			{
			"tagName": "nav_feedback",
			"childNodes": [{ "nodeValue": "feedback"
}]
			},
			{
			"tagName": "nav_send",
			"childNodes": [{ "nodeValue": "Send"
}]
			},
			{
			"tagName": "nav_settings",
			"childNodes": [{ "nodeValue": "settings"
}]
			},
			{
			"tagName": "nav_advanced",
			"childNodes": [{ "nodeValue": "advanced"
}]
			},
			{
			"tagName": "nav_logout",
			"childNodes": [{ "nodeValue": "logout"
}]
			},
			{
			"tagName": "nav_prelogout",
			"childNodes": [{ "nodeValue": "Any unsaved changes will be lost!"
}]
			},
			{
			"tagName": "nav_back",
			"childNodes": [{ "nodeValue": "Back to:"
}]
			},
			{
			"tagName": "nav_event",
			"childNodes": [{ "nodeValue": "event"
}]
			},
			{
			"tagName": "nav_task",
			"childNodes": [{ "nodeValue": "task"
}]
			},
			{
			"tagName": "nav_invite",
			"childNodes": [{ "nodeValue": "Invite!"
}]
			},
			{
			"tagName": "nav_save",
			"childNodes": [{ "nodeValue": "Save"
}]
			},
			{
			"tagName": "nav_filter",
			"childNodes": [{ "nodeValue": "filter"
}]
			},
			{
			"tagName": "feedback_title",
			"childNodes": [{ "nodeValue": "What do you think about Jotlet?"
}]
			},
			{
			"tagName": "feedback_name",
			"childNodes": [{ "nodeValue": "your name:"
}]
			},
			{
			"tagName": "feedback_body",
			"childNodes": [{ "nodeValue": "we welcome brutal honesty:"
}]
			},
			{
			"tagName": "feedback_error",
			"childNodes": [{ "nodeValue": "There was an error sending your feedback. Please try again in a few minutes."
}]
			},
			{
			"tagName": "feedback_thanks",
			"childNodes": [{ "nodeValue": "Thanks for your feedback!"
}]
			},
			{
			"tagName": "cal_create",
			"childNodes": [{ "nodeValue": "create a calendar"
}]
			},
			{
			"tagName": "cal_add",
			"childNodes": [{ "nodeValue": "Add Calendar"
}]
			},
			{
			"tagName": "cal_edit",
			"childNodes": [{ "nodeValue": "Edit Calendar"
}]
			},
			{
			"tagName": "cal_delete",
			"childNodes": [{ "nodeValue": "Delete Calendar"
}]
			},
			{
			"tagName": "cal_remove",
			"childNodes": [{ "nodeValue": "Remove Calendar"
}]
			},
			{
			"tagName": "cal_name",
			"childNodes": [{ "nodeValue": "calendar name"
}]
			},
			{
			"tagName": "cal_color",
			"childNodes": [{ "nodeValue": "select a color for this calendar:"
}]
			},
			{
			"tagName": "color_red",
			"childNodes": [{ "nodeValue": "red"
}]
			},
			{
			"tagName": "color_blue",
			"childNodes": [{ "nodeValue": "blue"
}]
			},
			{
			"tagName": "color_green",
			"childNodes": [{ "nodeValue": "green"
}]
			},
			{
			"tagName": "color_pink",
			"childNodes": [{ "nodeValue": "pink"
}]
			},
			{
			"tagName": "color_purple",
			"childNodes": [{ "nodeValue": "purple"
}]
			},
			{
			"tagName": "color_orange",
			"childNodes": [{ "nodeValue": "orange"
}]
			},
			{
			"tagName": "color_yellow",
			"childNodes": [{ "nodeValue": "yellow"
}]
			},
			{
			"tagName": "color_grey",
			"childNodes": [{ "nodeValue": "grey"
}]
			},
			{
			"tagName": "nav_close",
			"childNodes": [{ "nodeValue": "Close"
}]
			},
			{
			"tagName": "nav_cancel",
			"childNodes": [{ "nodeValue": "Cancel"
}]
			},
			{
			"tagName": "event_menu",
			"childNodes": [{ "nodeValue": "Add Event to..."
}]
			},
			{
			"tagName": "event_edit",
			"childNodes": [{ "nodeValue": "edit event"
}]
			},
			{
			"tagName": "event_edit_cap",
			"childNodes": [{ "nodeValue": "Edit Event"
}]
			},
			{
			"tagName": "event_loading",
			"childNodes": [{ "nodeValue": "Loading Add Event Page..."
}]
			},
			{
			"tagName": "event_add",
			"childNodes": [{ "nodeValue": "add event"
}]
			},
			{
			"tagName": "event_title",
			"childNodes": [{ "nodeValue": "event title"
}]
			},
			{
			"tagName": "event_dt",
			"childNodes": [{ "nodeValue": "date &amp; time"
}]
			},
			{
			"tagName": "event_begins",
			"childNodes": [{ "nodeValue": "begins"
}]
			},
			{
			"tagName": "event_ends",
			"childNodes": [{ "nodeValue": "ends"
}]
			},
			{
			"tagName": "event_at",
			"childNodes": [{ "nodeValue": "at"
}]
			},
			{
			"tagName": "event_allday",
			"childNodes": [{ "nodeValue": "This event is an all day event"
}]
			},
			{
			"tagName": "event_repeats",
			"childNodes": [{ "nodeValue": "This event repeats"
}]
			},
			{
			"tagName": "event_desc",
			"childNodes": [{ "nodeValue": "description"
}]
			},
			{
			"tagName": "event_add_cap",
			"childNodes": [{ "nodeValue": "Add Event"
}]
			},
			{
			"tagName": "event_update",
			"childNodes": [{ "nodeValue": "Update Event"
}]
			},
			{
			"tagName": "event_remind",
			"childNodes": [{ "nodeValue": "Add Reminder"
}]
			},
			{
			"tagName": "event_sb_delete",
			"childNodes": [{ "nodeValue": "Delete this event"
}]
			},
			{
			"tagName": "event_sb_delete_series",
			"childNodes": [{ "nodeValue": "Delete this series"
}]
			},
			{
			"tagName": "event_sb_edit",
			"childNodes": [{ "nodeValue": "Edit this event"
}]
			},
			{
			"tagName": "event_sb_export",
			"childNodes": [{ "nodeValue": "Download this event"
}]
			},
			{
			"tagName": "event_sb_perm",
			"childNodes": [{ "nodeValue": "This is a shared calendar. You do not have permission to edit or delete information."
}]
			},
			{
			"tagName": "task_menu",
			"childNodes": [{ "nodeValue": "Add Task to..."
}]
			},
			{
			"tagName": "task_edit",
			"childNodes": [{ "nodeValue": "edit task"
}]
			},
			{
			"tagName": "task_loading",
			"childNodes": [{ "nodeValue": "Loading Add Task Page..."
}]
			},
			{
			"tagName": "task_title",
			"childNodes": [{ "nodeValue": "task title"
}]
			},
			{
			"tagName": "task_due_date",
			"childNodes": [{ "nodeValue": "due date"
}]
			},
			{
			"tagName": "task_due",
			"childNodes": [{ "nodeValue": "due"
}]
			},
			{
			"tagName": "task_no_due",
			"childNodes": [{ "nodeValue": "This task does not have a due date"
}]
			},
			{
			"tagName": "task_repeats",
			"childNodes": [{ "nodeValue": "This task repeats"
}]
			},
			{
			"tagName": "task_add_cap",
			"childNodes": [{ "nodeValue": "Add Task"
}]
			},
			{
			"tagName": "task_update",
			"childNodes": [{ "nodeValue": "Update Task"
}]
			},
			{
			"tagName": "task_add",
			"childNodes": [{ "nodeValue": "add task"
}]
			},
			{
			"tagName": "task_sb_delete",
			"childNodes": [{ "nodeValue": "Delete this task"
}]
			},
			{
			"tagName": "task_sb_delete_series",
			"childNodes": [{ "nodeValue": "Delete this series"
}]
			},
			{
			"tagName": "task_sb_edit",
			"childNodes": [{ "nodeValue": "Edit this task"
}]
			},
			{
			"tagName": "task_sb_export",
			"childNodes": [{ "nodeValue": "Download this task"
}]
			},
			{
			"tagName": "task_sb_perm",
			"childNodes": [{ "nodeValue": "This is a shared calendar. You do not have permission to edit   or delete information."
}]
			},
			{
			"tagName": "info_in_cal",
			"childNodes": [{ "nodeValue": "in the %sub% calendar"
}]
			},
			{
			"tagName": "info_no_desc",
			"childNodes": [{ "nodeValue": "no description"
}]
			},
			{
			"tagName": "info_on",
			"childNodes": [{ "nodeValue": "on"
}]
			},
			{
			"tagName": "info_never",
			"childNodes": [{ "nodeValue": "never"
}]
			},
			{
			"tagName": "info_no_title",
			"childNodes": [{ "nodeValue": "no title"
}]
			},
			{
			"tagName": "info_more",
			"childNodes": [{ "nodeValue": "More Info"
}]
			},
			{
			"tagName": "info_allday",
			"childNodes": [{ "nodeValue": "All Day"
}]
			},
			{
			"tagName": "info_minutes",
			"childNodes": [{ "nodeValue": "minutes"
}]
			},
			{
			"tagName": "info_hours",
			"childNodes": [{ "nodeValue": "hours"
}]
			},
			{
			"tagName": "info_duration",
			"childNodes": [{ "nodeValue": "duration"
}]
			},
			{
			"tagName": "day_notes",
			"childNodes": [{ "nodeValue": "click to add daily notes"
}]
			},
			{
			"tagName": "day_tasks",
			"childNodes": [{ "nodeValue": "Tasks for the Day"
}]
			},
			{
			"tagName": "day_add_task",
			"childNodes": [{ "nodeValue": "add new task"
}]
			},
			{
			"tagName": "day_all_day",
			"childNodes": [{ "nodeValue": "All Day Events"
}]
			},
			{
			"tagName": "day_click",
			"childNodes": [{ "nodeValue": "click here to add some notes"
}]
			},
			{
			"tagName": "day_saving",
			"childNodes": [{ "nodeValue": "saving..."
}]
			},
			{
			"tagName": "day_loading",
			"childNodes": [{ "nodeValue": "loading..."
}]
			},
			{
			"tagName": "manage_cal",
			"childNodes": [{ "nodeValue": "Calendar Management"
}]
			},
			{
			"tagName": "manage_prop",
			"childNodes": [{ "nodeValue": "calendar properties"
}]
			},
			{
			"tagName": "manage_share",
			"childNodes": [{ "nodeValue": "share calendar"
}]
			},
			{
			"tagName": "manage_export",
			"childNodes": [{ "nodeValue": "export calendar"
}]
			},
			{
			"tagName": "manage_delete",
			"childNodes": [{ "nodeValue": "delete calendar: "
}]
			},
			{
			"tagName": "manage_remove",
			"childNodes": [{ "nodeValue": "remove calendar: "
}]
			},
			{
			"tagName": "manage_edit",
			"childNodes": [{ "nodeValue": "edit a calendar"
}]
			},
			{
			"tagName": "manage_select_buddies",
			"childNodes": [{ "nodeValue": "Select which buddies to share with from the list on the right"
}]
			},
			{
			"tagName": "manage_add_buddy",
			"childNodes": [{ "nodeValue": "Add Buddy"
}]
			},
			{
			"tagName": "manage_add_buddy_link",
			"childNodes": [{ "nodeValue": "Add a Buddy!"
}]
			},
			{
			"tagName": "manage_no_buddies",
			"childNodes": [{ "nodeValue": "No Buddies Here :("
}]
			},
			{
			"tagName": "manage_buddy_email",
			"childNodes": [{ "nodeValue": "enter buddy's email address"
}]
			},
			{
			"tagName": "manage_buddy_name",
			"childNodes": [{ "nodeValue": "Buddy Name:"
}]
			},
			{
			"tagName": "manage_invite_buddy",
			"childNodes": [{ "nodeValue": "Invite Your Buddy!"
}]
			},
			{
			"tagName": "manage_invite_buddy_to",
			"childNodes": [{ "nodeValue": "Invite %sub% to Jotlet!"
}]
			},
			{
			"tagName": "manage_go_back",
			"childNodes": [{ "nodeValue": "Go Back"
}]
			},
			{
			"tagName": "manage_share_bang",
			"childNodes": [{ "nodeValue": "Share!"
}]
			},
			{
			"tagName": "manage_share_self",
			"childNodes": [{ "nodeValue": "You can't share with yourself! :)"
}]
			},
			{
			"tagName": "manage_buddy_in_list",
			"childNodes": [{ "nodeValue": "%sub% is already in your buddy list"
}]
			},
			{
			"tagName": "manage_valid_email",
			"childNodes": [{ "nodeValue": "Please enter a valid email address"
}]
			},
			{
			"tagName": "manage_searching",
			"childNodes": [{ "nodeValue": "Searching..."
}]
			},
			{
			"tagName": "manage_adding_buddy",
			"childNodes": [{ "nodeValue": "Adding Buddy..."
}]
			},
			{
			"tagName": "manage_done_sharing",
			"childNodes": [{ "nodeValue": "Done Sharing!"
}]
			},
			{
			"tagName": "manage_error",
			"childNodes": [{ "nodeValue": "There was an error trying to share"
}]
			},
			{
			"tagName": "manage_sharing",
			"childNodes": [{ "nodeValue": "Sharing with Buddy..."
}]
			},
			{
			"tagName": "manage_add_fail",
			"childNodes": [{ "nodeValue": "could not add buddy :("
}]
			},
			{
			"tagName": "manage_inviting",
			"childNodes": [{ "nodeValue": "Inviting Buddy..."
}]
			},
			{
			"tagName": "manage_done_inviting",
			"childNodes": [{ "nodeValue": "Done Inviting!"
}]
			},
			{
			"tagName": "manage_fail_inviting",
			"childNodes": [{ "nodeValue": "Error Inviting"
}]
			},
			{
			"tagName": "manage_done_invite_msg",
			"childNodes": [{ "nodeValue": "We'll let you know when your buddy has accepted the invitation to Jotlet."
}]
			},
			{
			"tagName": "manage_fail_invite_msg",
			"childNodes": [{ "nodeValue": "There was an error inviting your buddy. Please try again in a few minutes."
}]
			},
			{
			"tagName": "manage_subscribe",
			"childNodes": [{ "nodeValue": "Subscribe"
}]
			},
			{
			"tagName": "manage_export_bang",
			"childNodes": [{ "nodeValue": "Export"
}]
			},
			{
			"tagName": "confirm_del_cal",
			"childNodes": [{ "nodeValue": "Delete calendar \"%sub%\"?\nThis cannot be undone!"
}]
			},
			{
			"tagName": "confirm_rem_cal",
			"childNodes": [{ "nodeValue": "Remove calendar \"%sub%\"?\nThis cannot be undone!"
}]
			},
			{
			"tagName": "confirm_del_event",
			"childNodes": [{ "nodeValue": "Delete event \"%sub%\"?\nThis cannot be undone!"
}]
			},
			{
			"tagName": "confirm_del_event_s",
			"childNodes": [{ "nodeValue": "Delete event series containing \"%sub%\"?\nThis cannot be undone!"
}]
			},
			{
			"tagName": "confirm_del_task",
			"childNodes": [{ "nodeValue": "Delete task \"%sub%\"?\nThis cannot be undone!"
}]
			},
			{
			"tagName": "confirm_del_task_s",
			"childNodes": [{ "nodeValue": "Delete task series containing \"%sub%\"?\nThis cannot be undone!"
}]
			},
			{
			"tagName": "setting_title",
			"childNodes": [{ "nodeValue": "Personal Settings"
}]
			},
			{
			"tagName": "setting_first",
			"childNodes": [{ "nodeValue": "First Name"
}]
			},
			{
			"tagName": "setting_last",
			"childNodes": [{ "nodeValue": "Last Name"
}]
			},
			{
			"tagName": "setting_email",
			"childNodes": [{ "nodeValue": "Email"
}]
			},
			{
			"tagName": "setting_sms",
			"childNodes": [{ "nodeValue": "SMS"
}]
			},
			{
			"tagName": "setting_zip",
			"childNodes": [{ "nodeValue": "Postal Code"
}]
			},
			{
			"tagName": "setting_language",
			"childNodes": [{ "nodeValue": "Language"
}]
			},
			{
			"tagName": "setting_save",
			"childNodes": [{ "nodeValue": "Save Profile"
}]
			},
			{
			"tagName": "setting_old_pass",
			"childNodes": [{ "nodeValue": "Old Password"
}]
			},
			{
			"tagName": "setting_new_pass",
			"childNodes": [{ "nodeValue": "New Password"
}]
			},
			{
			"tagName": "setting_confirm",
			"childNodes": [{ "nodeValue": "Confirm"
}]
			},
			{
			"tagName": "setting_save_pass",
			"childNodes": [{ "nodeValue": "Update Password"
}]
			},
			{
			"tagName": "setting_zone",
			"childNodes": [{ "nodeValue": "Time zone"
}]
			},
			{
			"tagName": "setting_zone_desc",
			"childNodes": [{ "nodeValue": "Time zones are listed with an * if they respect Daylight Savings Time"
}]
			},
			{
			"tagName": "setting_save_zone",
			"childNodes": [{ "nodeValue": "Save Local Settings"
}]
			},
			{
			"tagName": "setting_zoom",
			"childNodes": [{ "nodeValue": "Zoom Level"
}]
			},
			{
			"tagName": "setting_zoom_desc",
			"childNodes": [{ "nodeValue": "The zoom level affects how much time each row in day view represents. The 1 hour zoom will show a compact view, while the 15 minute zoom will show more detail."
}]
			},
			{
			"tagName": "setting_zoom_15_min",
			"childNodes": [{ "nodeValue": "15 minutes"
}]
			},
			{
			"tagName": "setting_zoom_30_min",
			"childNodes": [{ "nodeValue": "30 minutes"
}]
			},
			{
			"tagName": "setting_zoom_1_hour",
			"childNodes": [{ "nodeValue": "1 hour"
}]
			},
			{
			"tagName": "setting_save_zoom",
			"childNodes": [{ "nodeValue": "Save Zoom"
}]
			},
			{
			"tagName": "setting_shade",
			"childNodes": [{ "nodeValue": "Smart-Shading day cells"
}]
			},
			{
			"tagName": "setting_shade_desc",
			"childNodes": [{ "nodeValue": "Smart-Shading will automatically darken the background of busier days in month view. The busier the day, the darker the cell."
}]
			},
			{
			"tagName": "setting_save_shade",
			"childNodes": [{ "nodeValue": "Save Month Settings"
}]
			},
			{
			"tagName": "setting_profile",
			"childNodes": [{ "nodeValue": "user profile"
}]
			},
			{
			"tagName": "setting_change_pass",
			"childNodes": [{ "nodeValue": "change password"
}]
			},
			{
			"tagName": "setting_time_zones",
			"childNodes": [{ "nodeValue": "time zones"
}]
			},
			{
			"tagName": "setting_day_view",
			"childNodes": [{ "nodeValue": "day view"
}]
			},
			{
			"tagName": "setting_month_view",
			"childNodes": [{ "nodeValue": "month view"
}]
			},
			{
			"tagName": "setting_start_week",
			"childNodes": [{ "nodeValue": "Start of Week:  "
}]
			},
			{
			"tagName": "err_cal_name",
			"childNodes": [{ "nodeValue": "Please enter a name for the calendar"
}]
			},
			{
			"tagName": "email_invite",
			"childNodes": [{ "nodeValue": "Hey!\n\nCheck out Jotlet Calendar at www.jotlet.net! It's a great looking and easy to use online calendar.\n\nSign up so I can share my schedule with you!"
}]
			},
			{
			"tagName": "err_task_title",
			"childNodes": [{ "nodeValue": "Please enter a title for your task."
}]
			},
			{
			"tagName": "err_event_title",
			"childNodes": [{ "nodeValue": "Please enter a title for your event."
}]
			},
			{
			"tagName": "remind_adding",
			"childNodes": [{ "nodeValue": "Adding Reminder..."
}]
			},
			{
			"tagName": "remind_loading",
			"childNodes": [{ "nodeValue": "Loading Reminder..."
}]
			},
			{
			"tagName": "remind_email",
			"childNodes": [{ "nodeValue": "Email"
}]
			},
			{
			"tagName": "remind_sms",
			"childNodes": [{ "nodeValue": "Text Message"
}]
			},
			{
			"tagName": "remind_both",
			"childNodes": [{ "nodeValue": "Both"
}]
			},
			{
			"tagName": "remind_5_min",
			"childNodes": [{ "nodeValue": "5 minutes"
}]
			},
			{
			"tagName": "remind_4_hour",
			"childNodes": [{ "nodeValue": "4 hours"
}]
			},
			{
			"tagName": "remind_0_day",
			"childNodes": [{ "nodeValue": "the same day"
}]
			},
			{
			"tagName": "remind_1_day",
			"childNodes": [{ "nodeValue": "the day before"
}]
			},
			{
			"tagName": "remind_2_day",
			"childNodes": [{ "nodeValue": "2 days before"
}]
			},
			{
			"tagName": "remind_3_day",
			"childNodes": [{ "nodeValue": "3 days before"
}]
			},
			{
			"tagName": "remind_4_day",
			"childNodes": [{ "nodeValue": "4 days before"
}]
			},
			{
			"tagName": "remind_5_day",
			"childNodes": [{ "nodeValue": "5 days before"
}]
			},
			{
			"tagName": "remind_1_week",
			"childNodes": [{ "nodeValue": "1 week before"
}]
			},
			{
			"tagName": "remind_2_week",
			"childNodes": [{ "nodeValue": "2 weeks before"
}]
			},
			{
			"tagName": "remind_event",
			"childNodes": [{ "nodeValue": "%email% me %time% before this event"
}]
			},
			{
			"tagName": "remind_task_due",
			"childNodes": [{ "nodeValue": "%email% me at %time%%date% its due"
}]
			},
			{
			"tagName": "remind_task_no",
			"childNodes": [{ "nodeValue": "%email% me at %time% on %date%"
}]
			},
			{
			"tagName": "recur_daily",
			"childNodes": [{ "nodeValue": "daily"
}]
			},
			{
			"tagName": "recur_daily_num",
			"childNodes": [{ "nodeValue": "every %num% days"
}]
			},
			{
			"tagName": "recur_daily_weekday",
			"childNodes": [{ "nodeValue": "every weekday"
}]
			},
			{
			"tagName": "recur_weekly",
			"childNodes": [{ "nodeValue": "weekly"
}]
			},
			{
			"tagName": "recur_weekly_num",
			"childNodes": [{ "nodeValue": "every %num% weeks on:"
}]
			},
			{
			"tagName": "recur_monthly",
			"childNodes": [{ "nodeValue": "monthly"
}]
			},
			{
			"tagName": "recur_monthly_num",
			"childNodes": [{ "nodeValue": "day %num% of every %num2% months"
}]
			},
			{
			"tagName": "recur_monthly_date",
			"childNodes": [{ "nodeValue": "the %first% %weekday% of every %num% months"
}]
			},
			{
			"tagName": "recur_yearly",
			"childNodes": [{ "nodeValue": "yearly"
}]
			},
			{
			"tagName": "recur_yearly_exact",
			"childNodes": [{ "nodeValue": "every %month% %day%"
}]
			},
			{
			"tagName": "recur_yearly_rel",
			"childNodes": [{ "nodeValue": "the %first% %weekday% of %month%"
}]
			},
			{
			"tagName": "recur_custom",
			"childNodes": [{ "nodeValue": "custom"
}]
			},
			{
			"tagName": "recur_custom_desc",
			"childNodes": [{ "nodeValue": "Select your custom series of dates from the small calendar on the left."
}]
			},
			{
			"tagName": "recur_custom_dates",
			"childNodes": [{ "nodeValue": "Selected Dates: "
}]
			},
			{
			"tagName": "recur_end",
			"childNodes": [{ "nodeValue": "End Series:"
}]
			},
			{
			"tagName": "recur_end_after_e",
			"childNodes": [{ "nodeValue": "End after %num% events"
}]
			},
			{
			"tagName": "recur_end_after_t",
			"childNodes": [{ "nodeValue": "End after %num% tasks"
}]
			},
			{
			"tagName": "recur_end_by",
			"childNodes": [{ "nodeValue": "by %date%"
}]
			},
			{
			"tagName": "recur_event",
			"childNodes": [{ "nodeValue": "Repeat this event: "
}]
			},
			{
			"tagName": "recur_task",
			"childNodes": [{ "nodeValue": "Repeat this task: "
}]
			},
			{
			"tagName": "recur_first",
			"childNodes": [{ "nodeValue": "first"
}]
			},
			{
			"tagName": "recur_second",
			"childNodes": [{ "nodeValue": "second"
}]
			},
			{
			"tagName": "recur_third",
			"childNodes": [{ "nodeValue": "third"
}]
			},
			{
			"tagName": "recur_fourth",
			"childNodes": [{ "nodeValue": "fourth"
}]
			},
			{
			"tagName": "recur_fifth",
			"childNodes": [{ "nodeValue": "fifth"
}]
			},
			{
			"tagName": "recur_last",
			"childNodes": [{ "nodeValue": "last"
}]
			}
		],
		"tagName": "lang_table"
		}
	],
	"tagName": "language"
	}
],
"tagName": "languages"
};

jive.model.isLanguage = function(lang){
	return $def(lang) && $obj(lang) && lang != null && $def(lang.translate) && $def(lang.getId);
}

/**
 * represents a single language
 */
jive.model.Language = function(id, name, hash){

	var that = this;

	this.getId = function(){
		return id;
	}

	this.getName = function(){
		return name;
	}

	this.translate = function(key){
		var val = hash.get(key);
		if(val == false){
			alert("Language Exception: key \"" + key + "\" not found");
		}else{
			if(val == "_"){
				return " ";
			}else{
				val = val.replace(/#xD#xA/g,"\r\n");
				return val;
			}
		}
	}

    var colors = new Array();
    colors.push("red"),
    colors.push("blue"),
    colors.push("green"),
    colors.push("pink"),
    colors.push("purple"),
    colors.push("orange"),
    colors.push("yellow"),
    colors.push("grey");
	this.color = function(i){
		return that.translate("color_" + colors[i]);
	}

	this.longMonth = function(i){
		var names = new Array();
		names.push("january");
		names.push("february");
		names.push("march");
		names.push("april");
		names.push("may");
		names.push("june");
		names.push("july");
		names.push("august");
		names.push("september");
		names.push("october");
		names.push("november");
		names.push("december");
		return that.translate(names[i]);
	}

	this.shortMonth = function(i){
		var names = new Array();
		names.push("sh_january");
		names.push("sh_february");
		names.push("sh_march");
		names.push("sh_april");
		names.push("sh_may");
		names.push("sh_june");
		names.push("sh_july");
		names.push("sh_august");
		names.push("sh_september");
		names.push("sh_october");
		names.push("sh_november");
		names.push("sh_december");
		return that.translate(names[i]);
	}

	this.longDay = function(i){
		var names = new Array();
		names.push("sunday");
		names.push("monday");
		names.push("tuesday");
		names.push("wednesday");
		names.push("thursday");
		names.push("friday");
		names.push("saturday");
		return that.translate(names[i]);
	}

	this.shortDay = function(i){
		var names = new Array();
		names.push("sh_sunday");
		names.push("sh_monday");
		names.push("sh_tuesday");
		names.push("sh_wednesday");
		names.push("sh_thursday");
		names.push("sh_friday");
		names.push("sh_saturday");
		return that.translate(names[i]);
	}

	this.weekNumber = function(i){
		var names = new Array();
		names.push("recur_first");
		names.push("recur_second");
		names.push("recur_third");
		names.push("recur_fourth");
		names.push("recur_fifth");
		names.push("recur_last");
		return that.translate(names[i]);
	}
}


// checks for the existance of var default_lang
//
// if present, it parses it and stores it as the active language



/**
 * caches all day's comments objects that might show up in main views
 * (especially day view)
 */
jive.model.LanguageManager = function(control, default_lang){

	/**
	 * to reference other functions in this object
	 * from functions in this object,
	 * use that.func() sytax
	 */
	var that = this;

	/**
	 * the currently active language
	 * this will be what jotlet uses to translate the UIs
	 */
	var active_lang = null;


	/**
	 * the list of allowed languages
	 */
	var language_list = new Array();

	/**
	 * the cache of buddy objects
	 */
	var cache = new jive.ext.y.HashTable();


	/**
	 * the array of CommentCacheListener objects
	 *
	 * the object must have the following functions
	 *
	 * beginLoadingComments() // called when loading begins
	 * loadingCommentsFailed() // called when loading fails
	 * loadComment(comment) // the event parameter has been [re]loaded
	 * doneLoadingComment() // called when all calendars have been loaded
	 */
	var listeners = new Array();

	/**
	 * returns the active language
	 */
	this.getActiveLanguage = function(){
		return active_lang;
	}

	/**
	 * sets the active language
	 */
	this.setActiveLanguage = function(lang){
		if(jive.model.isLanguage(lang)){
			active_lang = lang;
			that.notifyLanguageChanged(active_lang);
		}else{
			return false;
		}
	}

	/**
	 * get languages that are loaded in the cache
	 */
	this.getLanguageList = function(){
		return cache.toArray();
	}

	/**
	 * get the available languages
	 */
	this.getSilentLanguages = function(){
		return language_list;
	}

	/**
	 * load buddies from DB and save in cache
	 */
	this.loadLanguage = function(lang){
		that.notifyLoadBegin();
		var a = control.newAjax(that.loadOk,that.loadFail);
		a.POST(HOSTURL + AJAXPATH + "?load_language","lang_id=" + lang);
	}

	/**
	 * save the active language
	 * and load it as active if it's in the cache,
	 * otherwise parse it
	 */
	this.saveLanguage = function(lang_id){
		that.notifyLoadBegin();
		// default load function will parse the language
		// and set it as active
		var okfun = function(lang_id){ return function(list){
			that.loadOk(list);
			var lang = cache.get(lang_id);
			if($obj(lang) && lang != null){
				that.setActiveLanguage(lang);
			}
		}}(lang_id);
		// if it's already parsed though, then just load it
		// from the cache
		var lang = cache.get(lang_id);
		if($obj(lang) && lang != null){
			okfun = function (lang_id){ return function(list){
				that.setActiveLanguage(cache.get(lang_id));
			}}(lang_id);
		}
		var a = control.newAjax(okfun, that.loadFail);
		a.POST(HOSTURL + AJAXPATH + "?save_language","lang_id=" + lang_id);
	}

	function parse(list){
		// reply is the message from the server
		// it should contain info about the calendars
		var tlang;

		if(list.childNodes.length > 0){
			if(list.childNodes[0].tagName == "language"){
				// it's a new language
				if(list.childNodes[0].childNodes.length > 0){
					lang = list.childNodes[0];
					var name = "";
					var id = 0;
					var hash = new jive.ext.y.HashTable();
					for(var j=0;j<lang.childNodes.length;j++){
						if(lang.childNodes[j].tagName == "name"){
							name = lang.childNodes[j].childNodes[0].nodeValue;
						}else
						if(lang.childNodes[j].tagName == "lang_id"){
							id = lang.childNodes[j].childNodes[0].nodeValue;
						}else
						if(lang.childNodes[j].tagName == "lang_table"){
							var table = lang.childNodes[j];
							for(var k=0;k<table.childNodes.length;k++){
								hash.put(table.childNodes[k].tagName, table.childNodes[k].childNodes[0].nodeValue);
							}
						}
					}
					lang = new jive.model.Language(id, name, hash);
					cache.put(lang.getId(), lang);
				}else{
					// fail, we loaded a blank language
					return false;
				}
			}else{
				return false;
			}
		}else{
			return false;
		}
		return lang;
	}


	/**
	 * loading buddies is successful
	 */
	this.loadOk = function(list){
		if(!parse(list)){
			that.notifyLoadFail();
		}else{
			that.notifyLoadFinish();
		}
	}

	/**
	 * loading buddies failed
	 */
	this.loadFail = function(){
		// notify listeners that the loading failed
		that.notifyLoadFail();
	}


	/******************************************
	 * listener functions
	 ******************************************/
	this.addListener = function(list){
		listeners.push(list);
	}

	this.removeListener = function(list){
		for(var i=0;i<listeners.length;i++){
			if(listeners[i] == list){
				listeners.splice(i, 1);
			}
		}
	}

	this.notifyLoadBegin = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].beginLoadingLanguages();
		}
	}

	this.notifyLoad = function(lang){
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadLanguage(lang);
		}
	}

	this.notifyLoadFinish = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].doneLoadingLanguages();
		}
	}

	this.notifyLoadFail = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadingLanguagesFailed();
		}
	}

	this.notifyLanguageChanged = function(lang){
		for(var i=0;i<listeners.length;i++){
			listeners[i].languageChanged(lang);
		}
	}



	try{
		/**
		 * we're checking to see if a language variable is
		 * already defined for us
		 * this way we don't have to ajax in the language
		 */
		if($def(default_lang)){
			var lang = parse(default_lang);
			if($obj(lang) && lang != null){
				that.setActiveLanguage(lang);
			}else{
				alert("error parsing");
			}
		}else{
			alert("no default langauge");
		}
	}catch(e){
		alert("language: " + e);
	}

    
//	try{
//		/**
//		 * get the list of default languages
//		 * this variable is defined at run time,
//		 * so we don't have to load in the list of languages
//		 * by ajax
//		 */
//		if($def(lang_list) && $def(lang_list[1]) && $def(lang_list.length)){
//			language_list = lang_list;
//		}else{
//			alert("empty language list");
//		}
//	}catch(e){
//		alert("language: " + e);
//	}

}




jive.model.LoginManager = function(control){

	var that = this;

	var listeners = new Array();

	/**
	 * if listeners want to modify themselves
	 * (ie, take itself out of the list)
	 * then they have to add a listener action
	 * which will be executed after all listeners
	 * have been notified of all events
	 * ie, these will only be executed after
	 * notifyLoadFinish and notifyLoadFail
	 */
	var listener_actions = new Array();

	function loginOk(reply){
		try{
			// reply is the message from the server
			// it should contain info about the calendars
			var parser = new jive.xml.XMLParser();
			var list = parser.parse(reply);
			// list is a ROOT xml object
			// and holds the actual document in the
			// documentElement attribute
			if($obj(list.documentElement) && list.documentElement != null){
				// if it returned results at all
				// then get them
				list = list.documentElement;
			}else{
				// otherwise show empty results
				list = new Object();
				list.childNodes = new Array();
				list.tagName = "failed";
			}
			if(list.tagName == "success"){
				that.notifyLoginOk();
			}else{
				that.notifyLoginFail();
			}
		}catch(e){
			alert(e);
		}
	}

	function loginFail(){
		that.notifyLoginFail();
	}

	this.login = function(password){
		var a = new jotlet.external.y.yAjax(loginOk, loginFail);
        alert("logging in via ajax");
//        a.POST(HOSTURL + AJAXPATH + "?login","username=" + control.getSettingsManager().getUserName() + "&password=" + password);
	}

	/******************************************
	 * listener functions
	 ******************************************/
	var lock = false;

	function executeListenerActions(){
		while(listener_actions.length > 0){
			listener_actions[0]();
			listener_actions.splice(0,1);
		}
	}

	this.addListener = function(list){
		if(!lock){
			listeners.push(list);
		}else{
			listener_actions.push(function(){that.addListener(list);});
		}
	}

	/**
	 * removes a listener
	 */
	this.removeListener = function(list){
		if(!lock){
			for(var i=0;i<listeners.length;i++){
				if(listeners[i] == list){
					listeners.splice(i, 1);
				}
			}
		}else{
			listener_actions.push(function(){that.removeListener(list);});
		}
	}


	/**
	 * notify listeners that the user is logged in
	 */
	this.notifyLoginOk = function(){
		lock = true;
		for(var i=0;i<listeners.length;i++){
			listeners[i].loginOk();
		}
		lock = false;
		executeListenerActions();
	}

	/**
	 * notify listeners that the user is logged in
	 */
	this.notifyLoginFail = function(){
		lock = true;
		for(var i=0;i<listeners.length;i++){
			listeners[i].loginFail();
		}
		lock = false;
		executeListenerActions();
	}
	/******************************************
	 * end listener functions
	 ******************************************/
}

jive.model.RefreshManager = function(control){

	var that = this;

	// listeners who want to know
	// what' we're doing
	var listeners = new Array();

	//
	// we need to keep track of the last time we
	// *know* that we were logged in
	// so that if we get logged out, we can
	// refresh all things that've been changed
	// since the last time we were logged in.
	//
	var last_session_date = control.getSettingsManager().getGMT();

	// lets keep track of the last time that we successfully
	// refreshed everything.
	var last_refresh = control.getSettingsManager().getGMT();

	// this tells whether we need to update last_login_date
	// when we're poked.
	//
	// we should not track login times if we have just been
	// asked to login again.
	//
	// once we know that we're logged out. we shouldn't track
	// login times until we've refreshed using the
	// refresh manager (which gets all changes since the
	// last time we *know* we're logged in).
	var tracking_session = true;

	// this is called after every successful ajax attempt
	// where we were logged in.
	//
	// if we had to re-login for the ajax to succeed,
	// then this is not called.
	//
	// this way, after every ajax call, we know the last
	// time that we were definately logged in.
	this.poke = function(){
		try{
			if(tracking_session){
				last_session_date = control.getSettingsManager().getGMT();
			}
		}catch(e){
			alert("refresh error:" + e);
		}
	}


	// the controller is letting us know that we're logged out
	// now all ajax calls that succeed will be after we've re-logged in
	this.loggedOut = function(){
		tracking_session = false;
	}



	function sendAjaxRefresh(dt, allHuh){
		try{
			that.notifyRefreshing();
			// subtract 5 seconds,
			// so that if anything, we sync more than
			// we need to
			dt.setTime(dt.getTime() - 5);
			var dh = new jive.model.DateHelper(control);
			var datestr = dh.formatToDateTime(dt);
			var a = control.newAjax(loadXML, loadFail);

			var min = dh.formatToDateTime(control.getEventCache().getMinTime());
			var max = dh.formatToDateTime(control.getEventCache().getMaxTime());

//			alert("min: " + min);

			a.POST(HOSTURL + AJAXPATH + "?refresh","dt=" + encodeURIComponent(datestr) + "&mindt=" + min + "&maxdt=" + max + (allHuh ? "&all" : ""));
		}catch(e){
			alert("refreshing: " + e);
		}
	}


	this.refresh = function(){
		// refresh from last_refresh
		// but only if we think we're logged in.
		//
		// there's no use in refreshing if we're
		// not logged in
		if(tracking_session){
			// normal login code here.
			sendAjaxRefresh(last_refresh, false);
		}
	}



	/**
	 * load in some refresh xml from somewhere else
	 */
	this.reload = function(list){
		var last_refresh_temp = last_refresh;
		that.notifyRefreshing();
		loadXML(list);
		last_refresh = last_refresh_temp;
	}


	var caches = new jive.ext.y.HashTable();
	this.getCustomCache = function(name){
		var c = caches.get(name);
		if(!$obj(c)){
			c = new jive.ext.y.HashTable();
			caches.put(name, c);
		}
		return c;
	}

	function resetCache(name){
		var c = new jive.ext.y.HashTable()
		caches.put(name, c);
		return c;
	}



	function loadEventCacheXML(list){
		for(var i=0;i<list.childNodes.length;i++){
			var c = resetCache(list.childNodes[i].tagName);
			for(var j=0;j<list.childNodes[i].childNodes.length;j++){
				var id = parseInt(list.childNodes[i].childNodes[j].childNodes[0].nodeValue);
				c.put(id, true);
			}
		}
	}


	/**
	 * loads an xml reply
	 *
	 * this will parse out the various responses,
	 * and send them to the approprate objects for
	 * continued refreshing.
	 *
	 * ie, the response will contain a
	 * <calendars></calendars> node
	 * and we'll send this node to the
	 * calendar manager, which will handle
	 * actually refreshing the calendar objects
	 * and notifying everyone of the change
	 */
	function loadXML(list){
		try{
			// now that we've refreshed, we can
			// refresh normally again
			tracking_session = true;


			for(var i=0;i<list.childNodes.length;i++){
				if(list.childNodes[i].tagName == "projects"){
					if(list.childNodes[i].childNodes.length > 0){
						control.getProjectCache().loadExternalProjects(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "events"){
					if(list.childNodes[i].childNodes.length > 0){
						control.getEventCache().reloadEvents(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "del_cals"){
					//
					// these are calendars that have been deleted
					//
					try{
						if(list.childNodes[i].childNodes.length > 0){
							control.getCalendarCache().unloadCalendars(list.childNodes[i]);
						}
					}catch(e){
						alert("error unloading calendars: " + e);
					}
				}else if(list.childNodes[i].tagName == "del_events"){
					//
					// these are events that have been deleted
					//
					try{
						if(list.childNodes[i].childNodes.length > 0){
							control.getEventCache().unloadEvents(list.childNodes[i]);
						}
					}catch(e){
						alert("error unloading calendars: " + e);
					}
				}else if(list.childNodes[i].tagName == "event_cache"){
					//
					// these are event caches
					//
					try{
						if(list.childNodes[i].childNodes.length > 0){
							loadEventCacheXML(list.childNodes[i]);
						}
					}catch(e){
						alert("error unloading calendars: " + e);
					}
				}else if(list.childNodes[i].tagName == "reminders"){
					if(list.childNodes[i].childNodes.length > 0){
						control.getReminderCache().reloadReminders(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "comments"){
					if(list.childNodes[i].childNodes.length > 0){
						control.getCommentCache().reloadComments(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "forms"){
					if(list.childNodes[i].childNodes.length > 0){
						control.getFormManager().reloadForms(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "sync"){
					if($def(control.getSyncManager)){
						control.getSyncManager().reloadSync(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "calendars"){
					if(list.childNodes[i].childNodes.length > 0){
						control.getCalendarCache().reloadCalendars(list.childNodes[i]);
					}
				}else if(list.childNodes[i].tagName == "settings"){
					control.getSettingsManager().reloadSettings(list.childNodes[i]);
				}else if(list.childNodes[i].tagName == "deleted"){
					// here we refresh items that have been deleted
					//
					// the xml is of the format <type>id</type>
					// for instance,
					// <event>3423</event>
					// or <calendar>452</calendar>
					try{
						var list2 = list.childNodes[i];
						for(var j=0;j<list2.childNodes.length;j++){
							if(list2.childNodes[i].tagName == "event"){
								var event_id = parseInt(list2.childNodes[j].nodeValue);
								// delete event event_id
								var event = control.getEventCache().getTaskSilent(task_id);
								control.getEventCache().notifyDeletingEvent(event);
								control.getEventCache().notifyDoneDeletingEvent(event);
							}else if(list2.childNodes[i].tagName == "task"){
								var task_id = parseInt(list2.childNodes[j].nodeValue);
								// delete event event_id
								var task = control.getEventCache().getTaskSilent(task_id);
								control.getEventCache().notifyDeletingTask(task);
								control.getEventCache().notifyDoneDeletingTask(task);
							}else if(list2.childNodes[i].tagName == "calendar"){
								var cal_id = parseInt(list2.childNodes[j].nodeValue);
								// delete event event_id
								var cal = control.getCalendarCache().getCalendar(cal_id);
								if($obj(cal) && cal != null){
									control.getCalendarCache().notifyDeletingCalendar(cal);
									control.getCalendarCache().notifyDoneDeletingCalendar(cal);
								}
							}

						}
					}catch(e){
						alert("exception refreshing deleted items: " + e);
					}
				}
			}

			// lets keep track of the last time that we successfully
			// refreshed everything.
			last_refresh = control.getSettingsManager().getGMT();
			that.notifyDoneRefreshing();
//			alert("refreshed at:" + last_refresh);
		}catch(e){
			alert("refresh.js:loadXML: " + e);
		}
	}

	function loadFail(){
		that.notifyRefreshingFailed();
		// bummmer!
		//
		// we won't do anything here yet :(
	}

	this.loadRemoteXML = function(list){
		that.notifyRefreshing();
		loadXML(list);
	}


	/******************************************
	 * listener functions
	 ******************************************/
	this.addListener = function(list){
		listeners.push(list);
	}

	/**
	 * removes a listener
	 */
	this.removeListener = function(list){
		for(var i=0;i<listeners.length;i++){
			if(listeners[i] == list){
				listeners.splice(i, 1);
			}
		}
	}


	/**
	 * notify listeners that we're refreshing
	 */
	this.notifyRefreshing = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].refreshing();
		}
	}

	/**
	 * notify listeners that we're done refreshing
	 */
	this.notifyDoneRefreshing = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].doneRefreshing();
		}
	}

	/**
	 * notify listeners that we're refreshing failed
	 */
	this.notifyRefreshingFailed = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].refreshingFailed();
		}
	}


	//
	//
	// listen to the login manager
	//
	//
	var list = new Object();
	list.loginOk = function (){
		// we've logged in successfully
		// after being logged out.
		//
		// now we need to refresh from
		// last_session_date
		//
		// actually, we don't need to
		// b/c we were trying to refresh
		// when we found out that we're
		// logged out, and that request
		// will get resent automatically
		// when we get logged back in,
		// so lets not do it twice.
		//


		var dh = new jive.model.DateHelper(control);
		if(last_session_date.getTime() < last_refresh.getTime()){
			var dt = last_session_date;
		}else{
			var dt = last_refresh;
		}
		sendAjaxRefresh(dt, true);
	}
	list.loginFail = function(){
		// we don't care about this.
	}
	control.getLoginManager().addListener(list);

}

jive.model.SettingsManager = function(control){
    /**
     * returns the current GMT time
     */
    var last_gmt = null;
    var last_gmt_stamp = (new Date()).getTime();
    this.getGMT = function(){
        var d = new Date();
        if(last_gmt != null && d.getTime() < last_gmt_stamp + 500){
            return last_gmt;
        }
        var d = new Date();
        var now = new Date();
        // get the current GMT time
        // but chop off teh "GMT" at the end of the toString
        now.setTime(Date.parse(d.toUTCString().substring(0, d.toUTCString().length - 3)));
//		var now = new Date(d.toUTCString());
//		now.setFullYear(d.getUTCFullYear());
//		now.setMonth(d.getUTCMonth());
//		now.setDate(d.getUTCDate());
//		now.setHours(d.getUTCHours());
//		now.setMinutes(d.getUTCMinutes());
//		now.setSeconds(d.getUTCSeconds());

//		now.setFullYear(d.getUTCFullYear());
//		now.setMonth(d.getUTCMonth());
//		now.setDate(d.getUTCDate());
//		now.setHours(d.getUTCHours());
//		now.setMinutes(d.getUTCMinutes());
//		now.setSeconds(d.getUTCSeconds());

        last_gmt = now;
        return now;
    }

    this.getNOW = function(){
        return new Date();
    }

    this.getStartWeekOn = function(){
        return 1;
    }

    this.getSmartShading = function(){
        return true;
    }

    /**
     * return the URL to the weather icon for the input date
     * @param dt
     */
    this.getWeatherImage = function(dt){
        return "";
    }

    this.getDateFormat = function(){
        return "4/30";
    }

    this.getPreferredEditorMode = function() {
        if (preferredMode == "" && $def(WikiTextConverter)) {
            WikiTextConverter.getPreferredEditorMode(
                {
                    callback: function(mode) {
                        preferredMode = mode;
                    },
                    timeout: DWRTimeout, // 20 seconds
                    errorHandler: editorErrorHandler
                }
            );
        }

        if (preferredMode == "") {
            preferredMode = "text";
        }
        return preferredMode;
    }
    
}

jive.model.User = function(){

    var that = this;

    /**
     * if saving the settings fails,
     * then the revert() will be called
     * and we'll need to revert all our values
     *
     * this array will hold thunks that can
     * revert the changes
     */
    var revert_actions = new Array();

    function createRevertAction(func, value){
        return function(){ func(value); }
    }
    /**
     * we don't need to save our revert actions anymore
     * probably b/c a save to DB went ok,
     * so set it as an emtpy array
     */
    this.clearRevertActions = function(){
        revert_actions = new Array();
    }
    /**
     * we need to revert to our old values
     * probably b/c a save to the DB when wrong
     */
    this.revert = function(){
        while(revert_actions.length > 0){
            revert_actions[0]();
            revert_actions.splice(0,1);
        }
    }

    //
    // properties
    var id;
    var username;
    var fullname;
    var url;

    this.getID = function(){
        return id;
    }
    this.getUsername = function(){
        return username;
    }
    this.getFullName = function(){
        return fullname;
    }
    this.getURL = function(){
        return url;
    }

    this.setID = function(i){
        id = i;
    }
    this.setUsername = function(n){
        username = n;
    }
    this.setFullName= function(n){
        revert_actions.push(createRevertAction(function(val){ fullname = val; }, fullname));
        fullname = n;
    }
    this.setURL = function(u){
        url = u;
    }
    /**
     * This function removes all setters for
     * properties that should never be reset after
     * the object has been loaded into the cache.
     *
     * physically removing these functions after
     * init will help us stop devs from calling
     * them accidentally.
     */
    this.cleanAfterInit = function(){
        that.clearRevertActions();
        that.setID = null;
        that.setUsername = null;
        this.setURL = null;
    }
}


jive.model.UserCache = function(control){

    var that = this;

    var cache = new jive.ext.y.HashTable();


    function refreshUserInCache(user){
        var u = cache.get(user.getID());
        if($obj(u)){
            u.setFullName(user.getFullName());
            u.clearRevertActions();
        }else{
            cache.put(user.getID(), user);
        }
        that.notifyLoadUser(user);
    }

    function loadUserXML(list){
        var user = new jive.model.User();
        for(var j=0;j<list.childNodes.length;j++){
            if(list.childNodes[j].tagName == "i"){
                if(list.childNodes[j].childNodes.length > 0)
                    user.setID(parseInt(list.childNodes[j].childNodes[0].nodeValue));
            }else if(list.childNodes[j].tagName == "u"){
                user.setUsername(list.childNodes[j].childNodes[0].nodeValue);
            }else if(list.childNodes[j].tagName == "n"){
                user.setFullName(list.childNodes[j].childNodes[0].nodeValue);
            }else if(list.childNodes[j].tagName == "url"){
                user.setURL(list.childNodes[j].childNodes[0].nodeValue);
            }
        }
        user.cleanAfterInit();
        refreshUserInCache(user);
        return user;
    }


    //
    // expects a <user> tag
    this.loadExternalUser = function(list){
        that.notifyLoadBegin();
        try{
            var u = loadUserXML(list);
            that.notifyLoadFinish();
            return u;
        }catch(e){
            that.notifyLoadFail();
        }
        return null;
    }

    /******************************************
     * listener functions
     ******************************************/
    this.addListener = function(list){
        listeners.push(list);
    }

    var listeners = new Array();
    var listener_actions = new Array();
    /**
     * act must be a thunk (a function without arguments)
     * it will be executed after either
     * notifyLoadFinish or notifyLoadFail
     */
    this.addListenerAction = function(act){
        listener_actions.push(act);
    }

    /**
     * private
     * executes all the listener actions
     */
    this.executeListenerActions = function(){
        while(listener_actions.length > 0){
            listener_actions[0]();
            listener_actions.splice(0,1);
        }
    }

    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }

    /**
     * notification functions
     */
    this.notifyLoadUser = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].loadUser(p);
        }
        that.executeListenerActions();
    }

    this.notifyLoadBegin = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].beginLoadingUsers();
        }
        that.executeListenerActions();
    }

    this.notifyLoadFinish = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneLoadingUsers();
        }
        that.executeListenerActions();
    }

	this.notifyLoadFail = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadingUsersFailed();
		}
		that.executeListenerActions();
	}

}

jive.model.isCheckPoint = function(cp){
    return $def(cp) && $obj(cp) && cp != null && $def(cp.isCheckPoint);
}

jive.model.CheckPoint = function(p){
    var that = this;

    /**
     * if saving the settings fails,
     * then the revert() will be called
     * and we'll need to revert all our values
     *
     * this array will hold thunks that can
     * revert the changes
     */
    var revert_actions = new Array();

    function createRevertAction(func, value){
        return function(){ func(value); }
    }
    /**
     * we don't need to save our revert actions anymore
     * probably b/c a save to DB went ok,
     * so set it as an emtpy array
     */
    this.clearRevertActions = function(){
        revert_actions = new Array();
    }
    /**
     * we need to revert to our old values
     * probably b/c a save to the DB when wrong
     */
    this.revert = function(){
        while(revert_actions.length > 0){
            revert_actions[0]();
            revert_actions.splice(0,1);
        }
    }

    var proj = p;

    this.getProject = function(){
        return proj;
    }

    var id;
    var created_on = null;
    var last_modified = null;
    var name = "";
    var desc = "";
    var due = null;

    this.isCheckPoint = function(){ return true; };    

    this.getID = function(){
        return id;
    }
    this.getCreatedOn = function(){
        return created_on;
    }
    this.getLastModifiedOn = function(){
        return last_modified;
    }
    this.getName = function(){
        return name;
    }
    this.getDescription = function(){
        return desc;
    }
    this.getDueDate = function(){
        return due;
    }

    this.setID = function(i){
        id = i;
    }
    this.setCreatedOn = function(n){
        created_on = n;
    }
    this.setLastModifiedOn = function(n){
        last_modified = n;
    }
    this.setName = function(n){
        revert_actions.push(createRevertAction(function(val){ name = val; }, name));
        name = n;
    }
    this.setDescription = function(d){
        revert_actions.push(createRevertAction(function(val){ desc = val; }, desc));
        desc = d;
    }
    this.setDueDate = function(d){
        revert_actions.push(createRevertAction(function(val){ due = val; }, due));
        due = d;
    }


    /**
     * This function removes all setters for
     * properties that should never be reset after
     * the object has been loaded into the cache.
     *
     * physically removing these functions after
     * init will help us stop devs from calling
     * them accidentally.
     */
    this.cleanAfterInit = function(){
        that.clearRevertActions();
        that.setID = null;
        that.setCreatedOn = null;
        that.setLastModifiedOn = null;
    }

}


jive.model.Project = function(){

    var that = this;

    /**
     * if saving the settings fails,
     * then the revert() will be called
     * and we'll need to revert all our values
     *
     * this array will hold thunks that can
     * revert the changes
     */
    var revert_actions = new Array();

    function createRevertAction(func, value){
        return function(){ func(value); }
    }
    /**
     * we don't need to save our revert actions anymore
     * probably b/c a save to DB went ok,
     * so set it as an emtpy array
     */
    this.clearRevertActions = function(){
        revert_actions = new Array();
    }
    /**
     * we need to revert to our old values
     * probably b/c a save to the DB when wrong
     */
    this.revert = function(){
        while(revert_actions.length > 0){
            revert_actions[0]();
            revert_actions.splice(0,1);
        }
    }

    //
    // properties
    var id;
    var name = "";
    var desc = "";
    var creator;
    var due;
    var last_modified;
    var tasks;
    var editable = false;
    var cps = new Array();

    this.getID = function(){
        return id;
    }
    this.getCreator = function(){
        return creator;
    }
    this.getLastModifiedOn = function(){
        return last_modified;
    }
    this.getName = function(){
        return name;
    }
    this.getDescription = function(){
        return desc;
    }
    this.getDueDate = function(){
        return due;
    }
    this.getTasks = function(){
        return tasks;
    }
    this.isEditable = function(){
        return editable;
    }
    this.getCheckPoints = function(){
        return cps;
    }

    this.setID = function(i){
        id = i;
    }
    this.setCreator = function(i){
        creator = i;
    }
    this.setEditable = function(b){
        editable = b;
    }
    this.setLastModifiedOn = function(n){
        last_modified = n;
    }
    this.setName = function(n){
        revert_actions.push(createRevertAction(function(val){ name = val; }, name));
        name = n;
    }
    this.setDescription = function(d){
        revert_actions.push(createRevertAction(function(val){ desc = val; }, desc));
        desc = d;
    }
    this.setDueDate = function(d){
        revert_actions.push(createRevertAction(function(val){ due = val; }, due));
        due = d;
    }
    this.setTasks = function(t){
        tasks = t;
    }
    this.setCheckPoints = function(c){
        cps = c;
    }

    /**
     * This function removes all setters for
     * properties that should never be reset after
     * the object has been loaded into the cache.
     *
     * physically removing these functions after
     * init will help us stop devs from calling
     * them accidentally.
     */
    this.cleanAfterInit = function(){
        that.clearRevertActions();
        that.setID = null;
        that.setCreator = null;
        that.setEditable = null;
        that.setLastModifiedOn = null;
        that.setCheckPoints = null;
    }
}


jive.model.ProjectCache = function(control){

    var that = this;

    var cache = new jive.ext.y.HashTable();

    this.getProject = function(pID){
        return cache.get(pID);
    }

    function refreshProjectInCache(proj){
        var p = cache.get(proj.getID());
        if($obj(p)){
            p.setCreator(proj.getCreator());
            p.setDescription(proj.getDescription());
            p.setDueDate(proj.getDueDate());
            p.clearRevertActions();
        }else{
            cache.put(proj.getID(), proj);
        }
        that.notifyLoadProject(proj);
    }

    function loadProjectsXML(list){
        var ret = new Array();
        for(var i=0;i<list.childNodes.length;i++){
            var proj = new jive.model.Project();
            var list2 = list.childNodes[i];
            var cps = new Array();
            for(var j=0;j<list2.childNodes.length;j++){
                if(list2.childNodes[j].tagName == "id"){
                    if(list2.childNodes[j].childNodes.length > 0)
                        proj.setID(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "name"){
                    if(list2.childNodes[j].childNodes.length > 0)
                        proj.setName(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "desc"){
                    if(list2.childNodes[j].childNodes.length > 0)
                        proj.setDescription(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "creator"){
                    var list3 = list2.childNodes[j];
                    var u = control.getUserCache().loadExternalUser(list3.childNodes[0]);
                    proj.setCreator(u);
                }else if(list2.childNodes[j].tagName == "d_on"){
                    var dt = list2.childNodes[j].childNodes[0].nodeValue;
                    if(dt != null){
                        proj.setDueDate(new Date(dt.replace(/-/g,"/")));
                    }else{
                        proj.setDueDate(null)
                    }
                }else if(list2.childNodes[j].tagName == "m_on"){
                    var dt = list2.childNodes[j].childNodes[0].nodeValue;
                    if(dt != null){
                        proj.setLastModifiedOn(new Date(dt.replace(/-/g,"/")));
                    }else{
                        proj.setLastModifiedOn(null)
                    }
                }else if(list2.childNodes[j].tagName == "editable"){
                    proj.setEditable(true);
                }else if(list2.childNodes[j].tagName == "tasks"){
                    var list3 = list2.childNodes[j];
                    var u = control.getTaskCache().loadExternalTasks(list3);
                    proj.setTasks(u);
                }else if(list2.childNodes[j].tagName == "cps"){
                    var list3 = list2.childNodes[j];
                    for(var k=0;k<list3.childNodes.length;k++){
                        var listcp = list3.childNodes[k];
                        var cp = new jive.model.CheckPoint(proj);
                        for(var l=0;l<listcp.childNodes.length;l++){
                            if(listcp.childNodes[l].tagName == "id"){
                                if(listcp.childNodes[l].childNodes.length > 0)
                                    cp.setID(listcp.childNodes[l].childNodes[0].nodeValue);
                            }else if(listcp.childNodes[l].tagName == "c_on"){
                                var dt = listcp.childNodes[l].childNodes[0].nodeValue;
                                if(dt != null){
                                    cp.setCreatedOn(new Date(dt.replace(/-/g,"/")));
                                }else{
                                    cp.setCreatedOn(null)
                                }
                            }else if(listcp.childNodes[l].tagName == "m_on"){
                                var dt = listcp.childNodes[l].childNodes[0].nodeValue;
                                if(dt != null){
                                    cp.setLastModifiedOn(new Date(dt.replace(/-/g,"/")));
                                }else{
                                    cp.setLastModifiedOn(null)
                                }
                            }else if(listcp.childNodes[l].tagName == "nm"){
                                if(listcp.childNodes[l].childNodes.length > 0)
                                    cp.setName(listcp.childNodes[l].childNodes[0].nodeValue);
                            }else if(listcp.childNodes[l].tagName == "desc"){
                                if(listcp.childNodes[l].childNodes.length > 0)
                                    cp.setName(listcp.childNodes[l].childNodes[0].nodeValue);
                            }else if(listcp.childNodes[l].tagName == "due"){
                                if(listcp.childNodes[l].childNodes.length > 0){
                                    var dt = listcp.childNodes[l].childNodes[0].nodeValue;
                                    if(dt != null){
                                        cp.setDueDate(new Date(dt.replace(/-/g,"/")));
                                    }else{
                                        cp.setDueDate(null)
                                    }
                                }
                            }
                        }
                        cps.push(cp);
                        proj.setCheckPoints(cps);
                    }
                }
            }
            proj.cleanAfterInit();
            refreshProjectInCache(proj);
            ret.push(proj);
        }
        return ret;
    }

    //
    // expects a <projects> tag
    this.loadExternalProjects = function(list){
        that.notifyLoadBegin();
        try{
            var ret = loadProjectsXML(list);
            that.notifyLoadFinish();
            return ret;
        }catch(e){
            that.notifyLoadFail();
        }
        return null;
    }

    /**
     * return true if the user can edit the project
     * false otherwise
     * @param pID the id of the project
     */
    this.canEditProjectHuh = function(pID){
        if(pID == 0) return true;
    }

    /******************************************
     * listener functions
     ******************************************/
    this.addListener = function(list){
        listeners.push(list);
    }

    var listeners = new Array();
    var working = 0;
    var listener_actions = new Array();
    /**
     * act must be a thunk (a function without arguments)
     * it will be executed after either
     * notifyLoadFinish or notifyLoadFail
     */
    this.addListenerAction = function(act){
        listener_actions.push(act);
    }

    /**
     * private
     * executes all the listener actions
     */
    this.executeListenerActions = function(){
        while(listener_actions.length > 0){
            listener_actions[0]();
            listener_actions.splice(0,1);
        }
    }

    this.removeListener = function(list){
        if(working == 0){
            for(var i=0;i<listeners.length;i++){
                if(listeners[i] == list){
                    listeners.splice(i, 1);
                }
            }
        }else{
            that.addListenerAction(function(list){
                return function(){
                   that.removeListener(list);
                }
            }(list));
        }
    }

    /**
     * notification functions
     */
    this.notifyLoadProject = function(p){
        working++;
        for(var i=0;i<listeners.length;i++){
            listeners[i].loadProject(p);
        }
        working--;
        that.executeListenerActions();
    }

    this.notifyLoadBegin = function(){
        working++;
        for(var i=0;i<listeners.length;i++){
            listeners[i].beginLoadingProjects();
        }
        working--;
        that.executeListenerActions();
    }

    this.notifyLoadFinish = function(){
        working++;
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneLoadingProjects();
        }
        working--;
        that.executeListenerActions();
    }

	this.notifyLoadFail = function(){
        working++;
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadingProjectsFailed();
		}
        working--;
		that.executeListenerActions();
	}

}

jive.model.ProjectCacheListener = function(){
    this.loadProject = function(p){ }
    this.beginLoadingProjects = function(){ }
    this.doneLoadingProjects = function(){ }
    this.loadingProjectsFailed = function(){ }
}

jive.model.isDocument = function(t){
    return $obj(t) && t != null && $def(t.getBody) && $def(t.getSubject)
}

jive.model.Document = function(){

    var that = this;


    /**
     * if saving the settings fails,
     * then the revert() will be called
     * and we'll need to revert all our values
     *
     * this array will hold thunks that can
     * revert the changes
     */
    var revert_actions = new Array();

    function createRevertAction(func, value){
        return function(){ func(value); }
    }
    /**
     * we don't need to save our revert actions anymore
     * probably b/c a save to DB went ok,
     * so set it as an emtpy array
     */
    this.clearRevertActions = function(){
        revert_actions = new Array();
    }
    /**
     * we need to revert to our old values
     * probably b/c a save to the DB when wrong
     */
    this.revert = function(){
        while(revert_actions.length > 0){
            revert_actions[0]();
            revert_actions.splice(0,1);
        }
    }

    this.confirm = function(){
        that.notifyTaskChanged();
    }
    
    //
    // properties
    var id;
    var html;

    this.getID = function(){
        return id;
    }
    this.getHTML = function(){
        return html;
    }

    this.setID = function(i){
        id = i;
    }
    this.setHTML = function(h){
        revert_actions.push(createRevertAction(function(val){ html = val; }, html));
        html = h;
    }
    /**
     * This function removes all setters for
     * properties that should never be reset after
     * the object has been loaded into the cache.
     *
     * physically removing these functions after
     * init will help us stop devs from calling
     * them accidentally.
     */
    this.cleanAfterInit = function(){
        that.clearRevertActions();
        that.setID = null;
    }

    /**
     * converts this document's HTML to wiki
     * format
     * @param list
     */
    this.convertToWiki = function(){
        objectLookupSessionKey
    }

    /******************************************
     * listener functions
     ******************************************/
    var listeners = new Array();
    
    this.addListener = function(list){
        listeners.push(list);
    }

    /**
     * removes a listener
     */
    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }


    /**
     * notify listeners that the task has changed
     */
    this.notifyDocumentChanged = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].documentChanged(that);
        }
    }
    /******************************************
     * end listener functions
     ******************************************/


}


jive.model.DocumentCache = function(control){

    var that = this;

    var cache = new jive.ext.y.HashTable();

    /**
     * define a task listener class
     * this will listen to task objects that are
     * added to this cache.
     *
     * if any task changes, then we'll update our listeners
     * about it
     */
    function DocumentListener(){
        this.documentChanged = function(doc){
            that.notifyDocumentChanged(doc);
        }
    }

    function refreshDocumentInCache(ttask){
        var t = cache.get(ttask.getID());
        if($obj(t)){
            //
            //  update properties of doc  here
            //
            t.clearRevertActions();
        }else{
            ttask.addListener(new DocumentListener());
            cache.put(ttask.getID(), ttask);
        }
        that.notifyLoadDocument(ttask);
    }

    /**
     * save the calendar to the DB
     */
    this.saveDocument= function(ttask){
        that.notifySavingDocument(ttask);
        try{
            var settings = control.getSettingsManager();
            var dh = new jive.model.DateHelper(control);
            var a = control.newAjax(
                function(list){
                    try{
                        if(list.tagName == "success"){
                            that.notifyDoneSavingDocument(ttask);
                        }else{
                            that.notifySavingDocumentFailed(ttask);
                        }
                    }catch(e){
                        alert(e);
                    }
                },
                function(){
                    try{
                        that.notifySavingDocumentFailed(ttask);
                    }catch(e){
                        alert("saving failed: " + e);
                    }
                });

            // save document here
//            a.POST(HOSTURL + AJAXPATH + "?save_task","task_id=" + encodeURIComponent(ttask.getID()) + "&due=" + encodeURIComponent(due) + "&status=" + encodeURIComponent(status) + "&title=" + encodeURIComponent(title) + "&description=" + encodeURIComponent(description) + "&never_due=" + encodeURIComponent(nd ? "1" : "0") + "&project_id=" + projID);
        }catch(e){
            that.notifySavingDocumentFailed(ttask);
        }
    }

    function newDocumentFromWikiHelper(html){
        that.notifyNewDocumentFromWiki(new jive.model.Document("",html));
    }

    /**
     * Creates a document object from wiki markup
     * (the document is loaded in after AJAX
     * @param list
     */
    this.newDocumentFromWiki = function(wiki){
        if(!$def(window.objectLookupSessionKey)){
            throw "window.objectLookupSessionKey must be defined to use newDocumentFromWiki()";
        }
        if(!$def(WikiTextConverter)){
            throw "WikiTextConverter must be defined to use newDocumentFromWiki()";
        }
        WikiTextConverter.convertFromWiki(wiki, window.objectLookupSessionKey,
            {
                callback: newDocumentFromWikiHelper,
                timeout: DWRTimeout, // 20 seconds
                errorHandler: that.notifyNewDocumentFromWikiFailed
            }
        );
    }


    function loadTasksXML(list){
        var ret = new Array();
        for(var i=0;i<list.childNodes.length;i++){
            var task = new jive.model.Document();
            var list2 = list.childNodes[i];
            for(var j=0;j<list2.childNodes.length;j++){
                if(list2.childNodes[j].tagName == "id"){
                    if(list2.childNodes[j].childNodes.length > 0)
                        task.setID(list2.childNodes[j].childNodes[0].nodeValue);
                }
            }
            ret.push(task);
            task.cleanAfterInit();
            refreshDocumentInCache(task);
        }
        return ret;
    }


    //
    // expects a <tasks> tag
    this.loadExternalDocuments = function(list){
        that.notifyLoadBegin();
        try{
            var u = loadDocumentsXML(list);
            that.notifyLoadFinish();
            return u;
        }catch(e){
            that.notifyLoadFail();
        }
        return null;
    }

    /******************************************
     * listener functions
     ******************************************/
    this.addListener = function(list){
        listeners.push(list);
    }

    var listeners = new Array();
    var listener_actions = new Array();
    /**
     * act must be a thunk (a function without arguments)
     * it will be executed after either
     * notifyLoadFinish or notifyLoadFail
     */
    this.addListenerAction = function(act){
        listener_actions.push(act);
    }

    /**
     * private
     * executes all the listener actions
     */
    this.executeListenerActions = function(){
        while(listener_actions.length > 0){
            listener_actions[0]();
            listener_actions.splice(0,1);
        }
    }

    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }

    /**
     * notification functions
     */
    this.notifyDocumentChanged = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].documentChanged(p);
        }
        that.executeListenerActions();
    }

    this.notifyLoadDocument = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].loadDocument(p);
        }
        that.executeListenerActions();
    }

    this.notifyLoadBegin = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].beginLoadingDocuments();
        }
        that.executeListenerActions();
    }

    this.notifyLoadFinish = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneLoadingDocuments();
        }
        that.executeListenerActions();
    }

	this.notifyLoadFail = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadingDocumentsFailed();
		}
		that.executeListenerActions();
	}

    this.notifySavingDocument = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].savingDocument(p);
        }
        that.executeListenerActions();
    }

    this.notifyDoneSavingDocument = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneSavingDocument(p);
        }
        that.executeListenerActions();
    }

    this.notifySavingDocumentFailed = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].savingDocumentFailed(p);
        }
        that.executeListenerActions();
    }
    this.notifyNewDocumentFromWiki = function(doc){
        for(var i=0;i<listeners.length;i++){
            listeners[i].newDocumentFromWiki(doc);
        }
        that.executeListenerActions();
    }
    this.notifyNewDocumentFromWikiFailed = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].newDocumentFromWikiFailed(p);
        }
        that.executeListenerActions();
    }

    var list = new jive.model.DocumentCacheListener();
    list.documentChanged = function(t){
        that.saveDocument(t);
    }
    that.addListener(list);

}

jive.model.DocumentCacheListener = function(){
    this.loadingDocumentsFailed = function(){ }
    this.doneLoadingDocuments = function(){ }
    this.beginLoadingDocuments = function(){ }
    this.loadDocument = function(){ }
    this.documentChanged = function(){ }
    this.savingDocument = function(){ }
    this.doneSavingDocument = function(){ }
    this.savingDocumentFailed = function(){ }
    this.newDocumentFromWikiFailed = function(){ }
}

jive.model.TaskCacheListener = function(){
    this.loadingTasksFailed = function(){ }
    this.doneLoadingTasks = function(){ }
    this.beginLoadingTasks = function(){ }
    this.loadTask = function(){ }
    this.taskChanged = function(){ }
}

jive.model.isEvent = function(t){
    return $obj(t) && t != null && $def(t.getStart) && $def(t.getEnd)
}

jive.model.isTask = function(t){
    return $obj(t) && t != null && $def(t.getDueDate) && $def(t.getProjectID)
}

jive.model.Task = function(){

    var that = this;

    var PERSONAL = 0;

    /**
     * if saving the settings fails,
     * then the revert() will be called
     * and we'll need to revert all our values
     *
     * this array will hold thunks that can
     * revert the changes
     */
    var revert_actions = new Array();

    function createRevertAction(func, value){
        return function(){ func(value); }
    }
    /**
     * we don't need to save our revert actions anymore
     * probably b/c a save to DB went ok,
     * so set it as an emtpy array
     */
    this.clearRevertActions = function(){
        revert_actions = new Array();
    }
    /**
     * we need to revert to our old values
     * probably b/c a save to the DB when wrong
     */
    this.revert = function(){
        while(revert_actions.length > 0){
            revert_actions[0]();
            revert_actions.splice(0,1);
        }
    }

    this.confirm = function(){
        that.notifyTaskChanged();
    }
    
    //
    // properties
    var id;
    var project_id = PERSONAL; // personal task
    var due = null;
    var subject;
    var description;
    var created_by;
    var created_on;
    var assigned_to;
    var assigned_by;
    var complete;
    var url;

    this.getID = function(){
        return id;
    }
    this.getProjectID = function(){
        return project_id;
    }
    this.getDueDate = function(){
        return due;
    }
    this.hasDueDate = function(){
        return due != null;
    }
    this.getSubject = function(){
        return subject;
    }
    this.getDescription = function(){
        return description;
    }
    this.getCreatedBy = function(){
        return created_by;
    }
    this.getCreatedOn = function(){
        return created_on;
    }
    this.getAssignedBy = function(){
        return assigned_by;
    }
    this.getAssignedTo = function(){
        return assigned_to;
    }
    this.getURL = function(){
        return url;
    }
    this.isComplete = function(){
        return complete;
    }

    this.setID = function(i){
        id = i;
    }
    this.setProjectID = function(i){
        project_id = i;
    }
    this.setCreatedBy = function(n){
        created_by = n;
    }
    this.setCreatedOn = function(n){
        created_on = n;
    }
    this.setComplete = function(b){
        complete = b;
    }
    this.setURL = function(u){
        url = u;
    }


    this.setDueDate = function(d){
        revert_actions.push(createRevertAction(function(val){ due = val; }, due));
        due = d;
    }
    this.setSubject = function(n){
        revert_actions.push(createRevertAction(function(val){ subject = val; }, subject));
        subject = n;
    }
    this.setDescription = function(d){
        revert_actions.push(createRevertAction(function(val){ description = val; }, description));
        description = d;
    }
    this.setAssignedBy = function(u){
        revert_actions.push(createRevertAction(function(val){ assigned_by = val; }, assigned_by));
        assigned_by = u;
    }
    this.setAssignedTo = function(d){
        revert_actions.push(createRevertAction(function(val){ assigned_to = val; }, assigned_to));
        assigned_to = d;
    }
    /**
     * This function removes all setters for
     * properties that should never be reset after
     * the object has been loaded into the cache.
     *
     * physically removing these functions after
     * init will help us stop devs from calling
     * them accidentally.
     */
    this.cleanAfterInit = function(){
        that.clearRevertActions();
        that.setID = null;
        that.setCreatedBy = null;
        that.setCreatedOn = null;
        that.setURL = null;
    }


    /******************************************
     * listener functions
     ******************************************/
    var listeners = new Array();
    
    this.addListener = function(list){
        listeners.push(list);
    }

    /**
     * removes a listener
     */
    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }


    /**
     * notify listeners that the task has changed
     */
    this.notifyTaskChanged = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].taskChanged(that);
        }
    }
    /******************************************
     * end listener functions
     ******************************************/


}


jive.model.TaskCache = function(control){

    var that = this;

    var cache = new jive.ext.y.HashTable();

    /**
     * define a task listener class
     * this will listen to task objects that are
     * added to this cache.
     *
     * if any task changes, then we'll update our listeners
     * about it
     */
    function TaskListener(){
        this.taskChanged = function(ttask){
            that.notifyTaskChanged(ttask);
        }
    }

    function refreshTaskInCache(ttask){
        var t = cache.get(ttask.getID());
        if($obj(t)){
            t.setDueDate(ttask.getDueDate());
            t.setSubject(ttask.getSubject());
            t.setDescription(ttask.getDescription());
            t.setAssignedTo(ttask.getAssignedTo());
            t.setAssignedBy(ttask.getAssignedBy());
            t.clearRevertActions();
        }else{
            ttask.addListener(new TaskListener());
            cache.put(ttask.getID(), ttask);
        }
        that.notifyLoadTask(ttask);
    }

    /**
     * save the calendar to the DB
     */
    this.saveTask= function(ttask){
        that.notifySavingTask(ttask);
        try{
            var settings = control.getSettingsManager();
            var dh = new jive.model.DateHelper(control);
            var a = control.newAjax(
                function(list){
                    try{
                        if(list.tagName == "success"){
                            that.notifyDoneSavingTask(ttask);
                        }else{
                            that.notifySavingTaskFailed(ttask);
                        }
                    }catch(e){
                        alert(e);
                    }
                },
                function(){
                    try{
                        that.notifySavingTaskFailed(ttask);
                    }catch(e){
                        alert("saving failed: " + e);
                    }
                });
            var due      = ttask.getDueDate();
            due          = (due != null) ? dh.formatToDateTime(due) : "";
            var nd = ! ttask.hasDueDate();
            var status   = ttask.getStatus();
            var title = ttask.getSubject();
            var description = ttask.getDescription();
            var projID = ttask.getProjectID();

            a.POST(HOSTURL + AJAXPATH + "?save_task","task_id=" + encodeURIComponent(ttask.getID()) + "&due=" + encodeURIComponent(due) + "&status=" + encodeURIComponent(status) + "&title=" + encodeURIComponent(title) + "&description=" + encodeURIComponent(description) + "&never_due=" + encodeURIComponent(nd ? "1" : "0") + "&project_id=" + projID);
        }catch(e){
            that.notifySavingTaskFailed(ttask);
        }
    }



    function loadTasksXML(list){
        var ret = new Array();
        for(var i=0;i<list.childNodes.length;i++){
            var task = new jive.model.Task();
            var list2 = list.childNodes[i];
            for(var j=0;j<list2.childNodes.length;j++){
                if(list2.childNodes[j].tagName == "id"){
                    if(list2.childNodes[j].childNodes.length > 0)
                        task.setID(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "pid"){
                        if(list2.childNodes[j].childNodes.length > 0)
                            task.setProjectID(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "due"){
                    var dt = list2.childNodes[j].childNodes[0].nodeValue;
                    if(dt != null){
                        task.setDueDate(new Date(dt.replace(/-/g,"/")));
                    }else{
                        task.setDueDate(null)
                    }
                }else if(list2.childNodes[j].tagName == "subj"){
                    task.setSubject(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "desc"){
                    task.setDescription(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "c_on"){
                    var dt = list2.childNodes[j].childNodes[0].nodeValue;
                    if(dt != null){
                        task.setCreatedOn(new Date(dt.replace(/-/g,"/")));
                    }else{
                        task.setCreatedOn(null)
                    }
                }else if(list2.childNodes[j].tagName == "c_by"){
                    task.setCreatedBy(control.getUserCache().loadExternalUser(list2.childNodes[j].childNodes[0]));
                }else if(list2.childNodes[j].tagName == "a_by"){
                    task.setAssignedBy(control.getUserCache().loadExternalUser(list2.childNodes[j].childNodes[0]));
                }else if(list2.childNodes[j].tagName == "a_to"){
                    task.setAssignedTo(control.getUserCache().loadExternalUser(list2.childNodes[j].childNodes[0]));
                }else if(list2.childNodes[j].tagName == "url"){
                    task.setURL(list2.childNodes[j].childNodes[0].nodeValue);
                }else if(list2.childNodes[j].tagName == "status"){
                    task.setComplete(list2.childNodes[j].nodeValue == "c");
                }
            }
            ret.push(task);
            task.cleanAfterInit();
            refreshTaskInCache(task);
        }
        return ret;
    }


    //
    // expects a <tasks> tag
    this.loadExternalTasks = function(list){
        that.notifyLoadBegin();
        try{
            var u = loadTasksXML(list);
            that.notifyLoadFinish();
            return u;
        }catch(e){
            that.notifyLoadFail();
        }
        return null;
    }

    /******************************************
     * listener functions
     ******************************************/
    this.addListener = function(list){
        listeners.push(list);
    }

    var listeners = new Array();
    var listener_actions = new Array();
    /**
     * act must be a thunk (a function without arguments)
     * it will be executed after either
     * notifyLoadFinish or notifyLoadFail
     */
    this.addListenerAction = function(act){
        listener_actions.push(act);
    }

    /**
     * private
     * executes all the listener actions
     */
    this.executeListenerActions = function(){
        while(listener_actions.length > 0){
            listener_actions[0]();
            listener_actions.splice(0,1);
        }
    }

    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }

    /**
     * notification functions
     */
    this.notifyTaskChanged = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].taskChanged(p);
        }
        that.executeListenerActions();
    }

    this.notifyLoadTask = function(p){
        for(var i=0;i<listeners.length;i++){
            listeners[i].loadTask(p);
        }
        that.executeListenerActions();
    }

    this.notifyLoadBegin = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].beginLoadingTasks();
        }
        that.executeListenerActions();
    }

    this.notifyLoadFinish = function(){
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneLoadingTasks();
        }
        that.executeListenerActions();
    }

	this.notifyLoadFail = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadingTasksFailed();
		}
		that.executeListenerActions();
	}


    var list = new jive.model.TaskCacheListener();
    list.taskChanged = function(t){
        that.saveTask(t);
    }
    that.addListener(list);

}

jive.rte.settings = function(id){

    function computeStyle(str){
        for(var i=0;i<jive.rte.defaultStyles.length;i++){
            str += "," + jive.rte.defaultStyles[i];
        }
        return str;
    }


    if(id=="mini-w-quote"){
        var _jive_image_picker_url = false;
        var ret = jive.rte.settings(0);
        ret.content_css = computeStyle(CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/content.css");
        ret.theme_advanced_statusbar_location = "none";
        ret.theme_advanced_buttons1 = "bold,italic,underline,strikethrough,spacerbutton,bullist,numlist,spacerbutton,jiveimage,jivevideo,spacerbutton,jivelink,jiveemoticons,jivequote,spellchecker,html";
        delete ret.theme_advanced_buttons2;
        delete ret.theme_advanced_buttons3;
        ret.default_height = 29;
        return ret;
    }else if(id=="mini"){
        var _jive_image_picker_url = false;
        var ret = jive.rte.settings(0);
        ret.content_css = computeStyle(CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/content.css");
        ret.theme_advanced_statusbar_location = "none";
        ret.theme_advanced_buttons1 = "bold,italic,underline,strikethrough,spacerbutton,bullist,numlist,spacerbutton,jiveimage,jivevideo,spacerbutton,jivelink,jiveemoticons,spellchecker,html";
        delete ret.theme_advanced_buttons2;
        delete ret.theme_advanced_buttons3;
        ret.default_height = 29;
        return ret;
    }else if(id=="widget"){
        var _jive_image_picker_url = false;
        var ret = jive.rte.settings(0);
        ret.content_css = computeStyle(CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/content.css");
        ret.theme_advanced_statusbar_location = "none";
//        ret.theme_advanced_buttons1 = "bold,italic,underline,strikethrough,spacerbutton,bullist,numlist,spacerbutton,jiveimage,jivevideo,spacerbutton,jivelink,jiveemoticons,spellchecker,html";
//        delete ret.theme_advanced_buttons2;
//        delete ret.theme_advanced_buttons3;
        return ret;
    }else if(id=="wiki"){
        var ret = jive.rte.settings(0);
        ret.content_css = computeStyle(CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/wiki.css");
        return ret;
    }else if(id=="blog"){
        var ret = jive.rte.settings(0);
        ret.content_css = computeStyle(CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/blog.css");
        return ret;
    }else if(id=="thread"){
        var ret = jive.rte.settings(0);
        ret.content_css = computeStyle(CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/thread.css");
        if(_jive_gui_quote_text && _jive_gui_quote_text.length > 0){
            ret.theme_advanced_buttons1 = "fontselect, fontsizeselect, removeformat, magicspacer, spacerbutton,bullist, numlist, outdent, indent, spacerbutton,jivevideo,spacerbutton,jivelink,tabletoolbar,extra,jivequote,spellchecker,html";
        }
        return ret;
    }else if(id==0){
        return {
        keep_values : true,
        convert_urls : false,
        default_height : 58,
        theme_advanced_buttons1 : "fontselect, fontsizeselect, removeformat, magicspacer, spacerbutton,bullist, numlist, outdent, indent, spacerbutton,jivevideo,spacerbutton,jivelink,tabletoolbar,extra,spellchecker,html",
        theme_advanced_buttons2 : "bold,italic,underline,strikethrough,forecolor,jivestyle, magicspacer, spacerbutton, justifyleft,justifycenter,justifyright,justifyfull, spacerbutton,jiveimage,spacerbutton,jiveemoticons, jivemacros ",
        theme_advanced_buttons3 : "tablecontrols",
        fix_list_elements : false,
        save_callback : "RawHTMLSaveFunction",
        convert_fonts_to_spans : true,
        font_size_style_values : "8pt,10pt,12pt,14pt,18pt,24pt,36pt",
        strict_loading_mode : true,
        body_class : "jive-widget-formattedtext",
        theme : "advanced",
        // jivemacros must be at the end, b/c it's context menu must override all other plugins
        plugins : "jivebuttons,jiveemoticons,jivestyle,jivelink,jivequote,jivevideo,jiveimage,alignment,safari,spellchecker,html,style,jivelists,table,save,advimage,advlink,iespell,inlinepopups,media,contextmenu,tabletoolbar,jivemacros,jiveutil",
        doctype : '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
        theme_advanced_toolbar_location : "top",
        theme_advanced_toolbar_align : "left",
        theme_advanced_statusbar_location : "bottom",
        content_css : CS_BASE_URL +  "/resources/scripts/tiny_mce3/themes/advanced/skins/default/wiki.css",
        theme_advanced_resize_horizontal : false,
        theme_advanced_resizing : true,
        apply_source_formatting : true,
        spellchecker_languages : SPELL_LANGS,
        spellchecker_rpc_url : CS_BASE_URL + "/spellcheck.jspa",
        jive_image_picker_url: _jive_image_picker_url,
        jive_video_picker_url: _jive_image_picker_url,
        relative_urls : false, // this prevents TinyMCE from converting absolute URLs to relative one's in the editor
        valid_elements : ""
                    +"a[accesskey|charset|class|coords|dir<ltr?rtl|href|hreflang|id|lang|name"
                      +"|onblur|onclick|ondblclick|onfocus|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|rel|rev"
                      +"|shape<circle?default?poly?rect|style|tabindex|title|target|type|jivemacro|_.*],"
                    +"abbr[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"acronym[class|dir<ltr?rtl|id|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"address[class|align|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"applet[align<bottom?left?middle?right?top|alt|archive|class|code|codebase"
                      +"|height|hspace|id|name|object|style|title|vspace|width],"
                    +"area[accesskey|alt|class|coords|dir<ltr?rtl|href|id|lang|nohref<nohref"
                      +"|onblur|onclick|ondblclick|onfocus|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup"
                      +"|shape<circle?default?poly?rect|style|tabindex|title|target],"
                    +"base[href|target],"
                    +"basefont[color|face|id|size],"
                    +"bdo[class|dir<ltr?rtl|id|lang|style|title],"
                    +"big[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"blockquote[cite|class|dir<ltr?rtl|id|lang|onclick|ondblclick"
                      +"|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout"
                      +"|onmouseover|onmouseup|style|title],"
                    +"body[alink|background|bgcolor|class|dir<ltr?rtl|id|lang|link|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onload|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|onunload|style|title|text|vlink],"
                    +"br[class|clear<all?left?none?right|id|style|title],"
                    +"button[accesskey|class|dir<ltr?rtl|disabled<disabled|id|lang|name|onblur"
                      +"|onclick|ondblclick|onfocus|onkeydown|onkeypress|onkeyup|onmousedown"
                      +"|onmousemove|onmouseout|onmouseover|onmouseup|style|tabindex|title|type"
                      +"|value],"
                    +"caption[align<bottom?left?right?top|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"center[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"cite[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"code[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"col[align<center?char?justify?left?right|char|charoff|class|dir<ltr?rtl|id"
                      +"|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown"
                      +"|onmousemove|onmouseout|onmouseover|onmouseup|span|style|title"
                      +"|valign<baseline?bottom?middle?top|width],"
                    +"colgroup[align<center?char?justify?left?right|char|charoff|class|dir<ltr?rtl"
                      +"|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown"
                      +"|onmousemove|onmouseout|onmouseover|onmouseup|span|style|title"
                      +"|valign<baseline?bottom?middle?top|width],"
                    +"dd[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
                    +"del[cite|class|datetime|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"dfn[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"dir[class|compact<compact|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"div[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"dl[class|compact<compact|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"dt[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
                    +"em/i[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"embed[width|height|src|pluginspage|name|swliveconnect|play<true?false|loop<true?false"
                      + "|menu<true?false|quality<low?autolow?autohigh?high?medium?high?best"
                      + "|scale<default?exact?noorder|salign<l?t?r?b?tl?tr?bl?br|wmode<window?opaque?transparent"
                      + "|bgcolor|base|flashvars|type|allowfullscreen],"
                    +"fieldset[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"font[class|color|dir<ltr?rtl|face|id|lang|size|style|title],"
                    +"form[accept|accept-charset|action|class|dir<ltr?rtl|enctype|id|lang"
                      +"|method<get?post|name|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onreset|onsubmit"
                      +"|style|title|target],"
                    +"frame[class|frameborder|id|longdesc|marginheight|marginwidth|name"
                      +"|noresize<noresize|scrolling<auto?no?yes|src|style|title],"
                    +"frameset[class|cols|id|onload|onunload|rows|style|title],"
                    +"h1[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"h2[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"h3[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"h4[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"h5[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"h6[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"head[dir<ltr?rtl|lang|profile],"
                    +"hr[align<center?left?right|class|dir<ltr?rtl|id|lang|noshade<noshade|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|size|style|title|width],"
                    +"html[dir<ltr?rtl|lang|version],"
                    +"iframe[align<bottom?left?middle?right?top|class|frameborder|height|id"
                      +"|longdesc|marginheight|marginwidth|name|scrolling<auto?no?yes|src|style"
                      +"|title|width],"
                    +"img[align<bottom?left?middle?right?top|alt|border|class|dir<ltr?rtl|height"
                      +"|hspace|id|ismap<ismap|lang|longdesc|name|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|src|style|title|usemap|vspace|width|jivemacro|_.*|param_.*],"
                    +"input[accept|accesskey|align<bottom?left?middle?right?top|alt"
                      +"|checked<checked|class|dir<ltr?rtl|disabled<disabled|id|ismap<ismap|lang"
                      +"|maxlength|name|onblur|onclick|ondblclick|onfocus|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onselect"
                      +"|readonly<readonly|size|src|style|tabindex|title"
                      +"|type<button?checkbox?file?hidden?image?password?radio?reset?submit?text"
                      +"|usemap|value],"
                    +"ins[cite|class|datetime|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"isindex[class|dir<ltr?rtl|id|lang|prompt|style|title],"
                    +"kbd[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"label[accesskey|class|dir<ltr?rtl|for|id|lang|onblur|onclick|ondblclick"
                      +"|onfocus|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout"
                      +"|onmouseover|onmouseup|style|title],"
                    +"legend[align<bottom?left?right?top|accesskey|class|dir<ltr?rtl|id|lang"
                      +"|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"li[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title|type"
                      +"|value],"
                    +"link[charset|class|dir<ltr?rtl|href|hreflang|id|lang|media|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|rel|rev|style|title|target|type],"
                    +"map[class|dir<ltr?rtl|id|lang|name|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"menu[class|compact<compact|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"meta[content|dir<ltr?rtl|http-equiv|lang|name|scheme],"
                    +"noframes[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"noscript[class|dir<ltr?rtl|id|lang|style|title],"
                    +"object[align<bottom?left?middle?right?top|archive|border|class|classid"
                      +"|codebase|codetype|data|declare|dir<ltr?rtl|height|hspace|id|lang|name"
                      +"|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|standby|style|tabindex|title|type|usemap"
                      +"|vspace|width],"
                    +"ol[class|compact<compact|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|start|style|title|type],"
                    +"optgroup[class|dir<ltr?rtl|disabled<disabled|id|label|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"option[class|dir<ltr?rtl|disabled<disabled|id|label|lang|onclick|ondblclick"
                      +"|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout"
                      +"|onmouseover|onmouseup|selected<selected|style|title|value],"
                    +"p[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|style|title],"
                    +"param[id|name|type|value|valuetype<DATA?OBJECT?REF],"
                    +"pre/listing/plaintext/xmp[align|class|dir<ltr?rtl|id|lang|onclick|ondblclick"
                      +"|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout"
                      +"|onmouseover|onmouseup|style|title|width|jivemacro|_.*],"
                    +"q[cite|class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"s[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
                    +"samp[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"script[charset|defer|language|src|type],"
                    +"select[class|dir<ltr?rtl|disabled<disabled|id|lang|multiple<multiple|name"
                      +"|onblur|onchange|onclick|ondblclick|onfocus|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|size|style"
                      +"|tabindex|title],"
                    +"small[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"span[align<center?justify?left?right|class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title|jivemacro|_.*],"
                    +"strike[class|class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title],"
                    +"strong/b[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"style[dir<ltr?rtl|lang|media|title|type],"
                    +"sub[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"sup[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title],"
                    +"table[align<center?left?right|bgcolor|border|cellpadding|cellspacing|class"
                      +"|dir<ltr?rtl|frame|height|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|rules"
                      +"|style|summary|title|width],"
                    +"tbody[align<center?char?justify?left?right|char|class|charoff|dir<ltr?rtl|id"
                      +"|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown"
                      +"|onmousemove|onmouseout|onmouseover|onmouseup|style|title"
                      +"|valign<baseline?bottom?middle?top],"
                    +"td[abbr|align<center?char?justify?left?right|axis|bgcolor|char|charoff|class"
                      +"|colspan|dir<ltr?rtl|headers|height|id|lang|nowrap<nowrap|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|rowspan|scope<col?colgroup?row?rowgroup"
                      +"|style|title|valign<baseline?bottom?middle?top|width],"
                    +"textarea[accesskey|class|cols|dir<ltr?rtl|disabled<disabled|id|lang|name"
                      +"|onblur|onclick|ondblclick|onfocus|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onselect"
                      +"|readonly<readonly|rows|style|tabindex|title],"
                    +"tfoot[align<center?char?justify?left?right|char|charoff|class|dir<ltr?rtl|id"
                      +"|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown"
                      +"|onmousemove|onmouseout|onmouseover|onmouseup|style|title"
                      +"|valign<baseline?bottom?middle?top],"
                    +"th[abbr|align<center?char?justify?left?right|axis|bgcolor|char|charoff|class"
                      +"|colspan|dir<ltr?rtl|headers|height|id|lang|nowrap<nowrap|onclick"
                      +"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
                      +"|onmouseout|onmouseover|onmouseup|rowspan|scope<col?colgroup?row?rowgroup"
                      +"|style|title|valign<baseline?bottom?middle?top|width],"
                    +"thead[align<center?char?justify?left?right|char|charoff|class|dir<ltr?rtl|id"
                      +"|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown"
                      +"|onmousemove|onmouseout|onmouseover|onmouseup|style|title"
                      +"|valign<baseline?bottom?middle?top],"
                    +"title[dir<ltr?rtl|lang],"
                    +"tr[abbr|align<center?char?justify?left?right|bgcolor|char|charoff|class"
                      +"|rowspan|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title|valign<baseline?bottom?middle?top],"
                    +"tt[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
                    +"u[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
                      +"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
                    +"ul[class|compact<compact|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown"
                      +"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
                      +"|onmouseup|style|title|type],"
                    +"var[class|dir<ltr?rtl|id|lang|onclick|ondblclick|onkeydown|onkeypress"
                      +"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style"
                      +"|title]"

        };
    }
}


/**
 * a paramter set for macros
 */
jive.rte.ParamSet = function(){

    var that = this;
    this.name = "";
    this.deleteAll = false;
    this.params = new Array();

    this.addParam = function(n, v){
        that.params.push({ name: n, value : v})
    }
    
}

/**
 * a generic Rich Text Editor used in ClearSpace
 * @param domID: the id of the textarea to replace with a RTE
 */
jive.rte.RTE = function(control, domID, settings_id){
    var that = this;
    var textonly = false;
    var tinyText = $(domID);
    var textbox = document.createElement('TEXTAREA');
    textbox.style.display = "none";
    tinyText.parentNode.insertBefore(textbox, tinyText);

    if(!$def(settings_id)){
        settings_id = 0;
    }

    var settings = jive.rte.settings(settings_id);
    settings.mode = "exact";
    settings.elements = domID;
    settings.images_enabled = window._images_enabled;
    try{
        if(!settings.jive_image_picker_url && _jive_image_picker_url){
            settings.jive_image_picker_url = _jive_image_picker_url;
        }
    }catch(e){ }
    if(typeof(tinyMCE) == "undefined"){
        // safari 2!
        textonly = true;
        textbox.style.display = "none";
        tinyText.style.display = "block";
        tinyText.style.height = "200px";
    }else{
        try{
        tinyMCE.init(settings);
        }catch(e){
            textonly = true;
            textbox.style.display = "none";
            tinyText.style.display = "block";
            tinyText.style.height = "200px";
        }
    }

    this.isTextOnly = function(){
        return textonly;
    }


    var my_editor = null;
    var ignore_change = true;

    // true if the spell checker is running
    var is_spelling = false;

    /**
     * returns the tinymce editor
     *
     * this function is private for a reason, and
     * the tinymce editor shouldn't be exposed outside
     * of this class.
     */
    var ignoreResize = true;

    function getEditor(){
        if(!that.isTextOnly() && my_editor == null){
            my_editor = tinyMCE.getInstanceById(domID);
            if(!$def(my_editor) || my_editor == null){
                return null;
            }
            my_editor.getContainer().childNodes[0].style.width = "";
            my_editor.onKeyUp.add(function(ed, e){ that.notifyOnKeyUp(e.keyCode); });
            my_editor.onChange.add(function(ed, e){ that.notifyOnChange(e.keyCode); });
            my_editor.onInit.add(function(that){ return function(ed, e){
                if(ed.id == domID) that.notifyInitFinished();
            }}(that));
            my_editor.onBeginSpelling.add( function(){ is_spelling = true; });
            my_editor.onEndSpelling.add(function(){ is_spelling = false; });
            my_editor.theme.onResize.add(function(){
                if(ignoreResize){
                    ignoreResize = false;
                    that.resizeTo(that.getHeight()+that.getToolbarHeight()+1);
                    my_editor.theme.onResize.dispatch();
                }else{
                    ignoreResize = true;
                    that.notifyResized();
                }
            });
            my_editor.plugins.html.registerToggleFunction(that.toggleEditorMode);
            //
            // sometimes the onInit() event doesn't fire from tinymce,
            // so this is our backup
            window.setTimeout(function() {
                that.notifyInitFinished();
            }, 5000);


            if(window.autoSave){
                var list = {
                    navigateAway : function(){
                        if(window && window.autoSave && that.restoreNavigateAway){
                            window.autoSave.confirmation = that.restoreNavigateAway;
                            that.restoreNavigateAway = false;
                        }
                    }
                }
                window.autoSave.addListener(list);
            }
        }

        return my_editor;
    }

    this.isSpellChecking = function(){
        return is_spelling;
    }

    this.toggleSpellChecker = function(){
        getEditor().execCommand("mceSpellCheck");
    }

    this.closeAllDialogs = function(){
        var windows = getEditor().windowManager.windows;
        var keys = Object.keys(windows);
        for(var i=0;i<keys.length;i++){
            getEditor().windowManager.close(null, windows[keys[i]].id);
        }
        return windows;
    }

    /**
     * adds a macro to the RTE w/ the default paramset
     * @param macro
     * @param paramset a parameter set. these can be created w/ the jive.rte.ParamSet class
     */
    this.addMacro = function(macro, paramset){
        var ed = getEditor();
        var collapsed = ed.selection.isCollapsed();
        var pre = ed.plugins.jivemacros.insertMacro(macro);
        ed.plugins.jivemacros.applyParameterSet(pre, macro, paramset);
        if(collapsed){
            pre.setAttribute("id", "__sel_me__");
            ed.selection.setNode(pre);
            pre = ed.getDoc().getElementById("__sel_me__");
            pre.removeAttribute("id");
        }
        ed.nodeChanged();
        return pre;
    }

    /**
     * destroys an editor
     * @param str
     * @param def
     */
    this.destroy = function(){
        tinyMCE.remove(domID);
        textbox.parentNode.removeChild(textbox);
        getEditor().getContainer().parentNode.removeChild(my_editor.getContainer());
    }

    /**
     * returns the i18n value for the input string, or returns the default if none
     * @param str
     * @param def
     */
    this.getLang = function(str, def){
        return getEditor().getLang(str, def);
    }

    /**
     *  returns true if the editor has been fully loaded
     *  returns false otherwise
     *
     * don't count on any other functions working until this
     * guy returns true...
     */
    this.isReady = function(){
        if(that.isTextOnly()) return true;
        var ed = getEditor();
        return ed != null;
    }


    /**
     * returns the DOM container for this editor
     */
    this.getDOM = function(){
        return getEditor().getContainer();
    }

    /**
     * returns the ID of the DOM that this editor was initialized with
     * @param html
     */
    this.getID = function(){
        return domID;
    }

    /**
     * sets the XHTML data for this editor
     * @param html
     */
    this.setHTML = function(html){
        if(that.isTextOnly()){
            return tinyText.value = html;
        }
        textbox.value = html;
        return getEditor().setContent(html);
    }

    /**
     * returns the XHTML data from this editor
     */
    this.getHTML = function(){
        if(that.isTextOnly()){
            return tinyText.value;
        }
        if(this.isHidden()){
            this.setHTML(textbox.value);
        }
        var body = getEditor().getContent();

        if((this.trim(body).length > 0) && body.indexOf("<body") != 0){
            body = "<body>" + body + "</body>";
        }
        while(body.indexOf("  ") >= 0){
            body = body.replace("  ", "&nbsp; ");
        }
        return body;
    }

    /**
     * returns the trimmed string
     */

    this.trim = function(input){
        var	str = input.replace(/^\s\s*/, '');
        var ws = /\s/;
        var i = str.length;
        while (ws.test(str.charAt(--i)));
        return str.slice(0, i + 1);
    }


    /**
     * shows the editor in the page if it's hidden
     */
    this.show = function(){
        getEditor().show();
        that.notifyShowing();
    }

    /**
     *  returns the window object that this RTE is
     *  inside of
     */
    this.getWindow = function(){
        return getEditor().getWin();
    }

    /**
     * returns the document body of which this
     * RTE is a part
     */
    this.getBody = function(){
        return getEditor().getBody();
    }

    /**
     * focuses the cursor in the RTE
     */
    this.focus = function(){
        try{
            if(this.isHidden()){
                textbox.focus();
            }else{
                getEditor().focus();
            }
        }catch(e){ }
    }

    /**
     * collapse the editor's cursor to a point
     */
    this.collapseSelection = function(){
        var ed = getEditor();
        ed.selection.select(ed.selection.getNode());
        ed.selection.collapse();
    }

    /**
     * hides the RTE from view
     */
    this.hide = function(){
        getEditor().hide();
    }

    /**
     * returns true of the RTE is hidden from view
     */
    this.isHidden = function(){
        return textbox.style.display == "block";
    }

    /**
     * resize the editor to the input height
     * @param height
     */
    this.resizeTo = function(height){
        var contain = getEditor().getContainer();
        var table = contain.firstChild;
        if(table.nodeName.toLowerCase() != "table") table = table.childNodes[0]; // IE 6
        var h = jive.ext.x.xHeight(table.rows[0].cells[0]);
        if(table.rows.length > 2){
            h += jive.ext.x.xHeight(table.rows[table.rows.length-1].cells[0]);
        }
        var box = getEditor().getContentAreaContainer();
        var ifr = getEditor().getContentAreaContainer().firstChild;
        tinymce.DOM.setStyle(ifr, 'height', height-h); // Resize iframe
//        tinymce.DOM.setStyle(box, 'height', height-h); // Resize td around the iframe
        tinymce.DOM.setStyle(table, 'height', height); // Resize table
    }

    /**
     * returns the height in px of the editor
     */
    this.getHeight = function(){
        var ifr = getEditor().getContentAreaContainer().firstChild;
        return jive.ext.x.xHeight(ifr);
    }

    this.getToolbarHeight = function(){
        var contain = getEditor().getContainer();
        var table = contain.firstChild;
        if(table.nodeName.toLowerCase() != "table") table = table.childNodes[0]; // IE 6
        var h = jive.ext.x.xHeight(table.rows[0].cells[0]);
        if(table.rows.length > 2){
            h += jive.ext.x.xHeight(table.rows[table.rows.length-1].cells[0]);
        }
        return h;
    }

    /**
     * toggles the editor between rich and raw html
     */
    this.toggleEditorMode = function(edid){
        if(edid == domID){
            if(window && window.autoSave){
                that.restoreNavigateAway = window.autoSave.confirmation;
                window.autoSave.confirmation = false;
            }
            var tiny = $(domID + '_parent');
            if(that.isHidden()){
                var h = jive.ext.x.xHeight(textbox);
                textbox.style.display = "none";
                that.show();
                that.setHTML(textbox.value);
                that.focus();
                that.resizeTo(h);
                getEditor().plugins.jivemacros.removeDuplicateMacros(getEditor(), getEditor().getBody());
            }else{
                var tinyt = $(domID + '_tbl');
                var h = jive.ext.x.xHeight(tinyt);
                textbox.value = that.getHTML(true);
                that.hide();
                textbox.style.display = "block";
                // never show tinyMCE's text box to the user
                // only show our own text box
                tinyText.style.display = "none";
                jive.ext.x.xHeight(textbox,h);
                that.focus();
            }
            that.notifyDoneTogglingMode();
        }
    }


    /******************************************
     * listener functions
     ******************************************/
    this.addListener = function(list){
        if($def(list.onShow)) list.onShow();
        listeners.push(list);
    }

    var listeners = new Array();
    var listener_actions = new Array();
    /**
     * act must be a thunk (a function without arguments)
     * it will be executed after either
     * notifyLoadFinish or notifyLoadFail
     */
    this.addListenerAction = function(act){
        listener_actions.push(act);
    }

    /**
     * private
     * executes all the listener actions
     */
    this.executeListenerActions = function(){
        while(listener_actions.length > 0){
            listener_actions[0]();
            listener_actions.splice(0,1);
        }
    }

    this.removeListener = function(list){
        for(var i=0;i<listeners.length;i++){
            if(listeners[i] == list){
                listeners.splice(i, 1);
            }
        }
    }

    this.initted = false;
    this.notifyInitFinished = function(){
        if(!that.initted){
            that.initted = true;
            for(var i=0;i<listeners.length;i++){
                listeners[i].initFinished();
            }
            that.executeListenerActions();
        }
    }

    this.notifyOnKeyUp = function(keyCode){
        // the user typed a key!
        for(var i=0;i<listeners.length;i++){
            listeners[i].onKeyUp(keyCode);
        }
        that.executeListenerActions();
    }

    this.notifyOnChange = function(){
        if(ignore_change){
            ignore_change = false;
            return;
        }
        // the user typed a key!
        for(var i=0;i<listeners.length;i++){
            listeners[i].onChange();
        }
        that.executeListenerActions();
    }

    this.notifyResized = function(){
        // the user typed a key!
        for(var i=0;i<listeners.length;i++){
            listeners[i].onResize();
        }
        that.executeListenerActions();
    }

    this.notifyShowing = function(){
        // the user typed a key!
        for(var i=0;i<listeners.length;i++){
            listeners[i].onShow();
        }
        that.executeListenerActions();
    }

    this.notifyDoneTogglingMode = function(){
        // the user typed a key!
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneTogglingMode();
        }
        that.executeListenerActions();
    }
}


/**
 * defines a simple Macro interface to mimic the RenderMacro class on the server
 */
jive.rte.Macro = function(shortname, url, macrotag, settingsHuh, displayHuh, paramSets, params, enabled, button){
    var that = this;

    /**
     * gets the unique name for this macro
     * i.e. "code" or "youtube"
     */
    this.getName = function(){
        return shortname;
    }

    /**
     * gets the optional url for this macro
     */
    this.getUrl = function(){
        return url;
    }

    /**
     * returns true if it should be a button or not
     */
    this.isButton = function(){
        return button;
    }

    this.isEnabled = function(){
        return enabled;
    }

    this.isShowSettings = function(){
        return settingsHuh;
    }

    /**
     * Display in RTE Insert List?
     */
    this.isShowInMacroList = function(){
        return displayHuh;
    }
    
    /**
     * returns true if this macro accepts
     * raw text input, like a code macro,
     * or false if it doesn't, like
     * a youtube macro
     */
    this.getMacroType = function(){
        return macrotag;
    }

    /**
     * returns all param sets for this macro
     */
    this.getParameterSets = function(){
        return paramSets;
    }

    /**
     * returns an array of allowed parameters
     */
    this.getAllowedParameters = function(){
        return params;
    }

    /**
     * update the element's display w/ the latest
     * parameter value.
     */
    this.refresh = function(rte, ele){
        if(ele.getAttribute("jivemacro") == this.getName()){
            // i'm a span or an img
            if(this.getMacroType().toLowerCase() == "inline"){
                var str = ele.getAttribute("_title");
                if($def(str) && str != null && str.length > 0){
                    ele.innerHTML = str;
                    ele.attributes.removeNamedItem("_title");
                }else if(tinyMCE.activeEditor.dom.hasClass(ele, "default_title")){
                    var type = ele.getAttribute("jivemacro");
                    var id = ele.getAttribute("___default_attr");
                    var title = tinyMCE.activeEditor.plugins.jivemacros.getTitleFor(type, id);
                    if(title && ele.innerHTML != title[0]){
                        ele.innerHTML = title[0];
                    }
                    if(ele.innerHTML == ""){
                        ele.innerHTML = "unknown";
                    }
                }
                /*
                while(ele.childNodes.length > 1) ele.removeChild(ele.childNodes[0]);
                if(ele.childNodes.length == 0) ele.appendChild(document.createTextNode(""));
                var params = this.getAllowedParameters();
                for(var i=0;i<params.length;i++){
                    var val = ele.getAttribute("_" + params[i].name);
                    if(val != null && val.length > 0){
                        ele.childNodes[0].nodeValue = val;
//                            ele.appendChild(document.createTextNode(val));
                        return;
                    }
                }
                ele.childNodes[0].nodeValue = rte.getLang("jivemacros.macro." + this.getName(), this.getName());
                */
            }else if(this.getMacroType().toLowerCase() == "image"){
                var src = window.CS_BASE_URL + "/resources/scripts/tiny_mce3/plugins/jiveemoticons/images/spacer.gif";
                ele.setAttribute("src", src);
                ele.setAttribute("mce_src", src);
            }
        }
    }
}

/**
 * since emoticons are encoded in the XHTML as macros,
 * but implemented onthe server as a filter, this class
 * will define the client side "macro" class for emotes,
 * since once is not generated on the fly from the
 * server
 *
 * alternatively, i could have refactored emoticons into
 * a macro from a filter on the server, but don't fix
 * what isn't broken :)
 */
jive.rte.EmoticonMacro = function(){

    var that = this;

    var params = new Array();
    var paramSets = new Array();
    params.push({
        name: "__jive_emoticon_name",
        value: ["happy","laugh","silly","wink","plain","angry","blush","confused","cool","cry","devil","grin","love","mischief","sad","shocked"]
    });

    for(var i=0;i<params[0].value.length;i++){
        paramSets.push({
            name : params[0].value[i],
            deleteAll: true,
            params: [ {
                name: params[0].name,
                value: params[0].value[i]
            }]
        });
    }
    var macro = new jive.rte.Macro("emoticon", "", "img", false, true, paramSets, params, true, false)

    this.getName = macro.getName;

    this.getUrl = macro.getUrl;

    this.isShowInMacroList = macro.isShowInMacroList;

    this.isShowSettings = macro.isShowSettings;

    this.getMacroType = macro.getMacroType;

    this.getParameterSets = macro.getParameterSets;

    this.getAllowedParameters = macro.getAllowedParameters;

    this.refresh = function(rte, ele){
        macro.refresh(rte, ele);
        var grin = ele.getAttribute("_" + params[0].name);
        // also, update the icon
        ele.setAttribute("class","jive_macro jive_emote");
        ele.setAttribute("src", window.CS_BASE_URL + "/images/emoticons/" + grin + ".gif");
        ele.setAttribute("mce_src", window.CS_BASE_URL + "/images/emoticons/" + grin + ".gif");
    }

}
jive.rte.macros.push(new jive.rte.EmoticonMacro());

jive.rte.RTEListener = function(){
    this.onKeyUp = function(key){ }
    this.onChange = function(){ }
    this.onResize = function(){ }
    this.onShow = function(){ }
    this.doneTogglingMode = function(){ }
    this.initFinished = function(){ }
}

jive.ext.y._sliders_on = true;

jive.ext.y._sliders = new jive.ext.y.HashTable();

/**
 * uTime is the total time for the animation
 * e is the optional object to store settings to
 * (useful to share settings between multiople animations)
 */
jive.ext.y.ySlider = function(uTime, e, name){

	/**
	 * tickers have a tick(p)
	 * init()
	 * and stop() methods
	 * tick() is given the period (between 0 and 1)?
	 */
	var tickers = new Array();

	var that = this;

	this.hash = 0;
	this.name = name;
	this.moving = false;

	if(!$obj(e) || e == null){
		e = new Object();
	}
	if (!$def(e.timeout) || e.timeout == null) e.timeout = false;

	/**
	 * the mai function
	 */
	this.go = function() {
		try{
			while(that.hash == 0 || $obj(jive.ext.y._sliders.get(that.hash))){
				that.hash = Math.round(Math.random()*10000) + 1;
			}
			jive.ext.y._sliders.put(that.hash, this);
			if (!e.timeout) e.timeout = 40;

			for(var i=0;i<tickers.length;i++){
				tickers[i].init();
			}

			e.slideTime = uTime;
			e.stop = false;
			e.B = Math.PI / (2 * e.slideTime); // B = period
			var d = new Date();
			e.C = d.getTime();
			if(!jive.ext.y._sliders_on){
				// if animations are turned off
				// jump to the end
				e.slideTime = 0;
			}
			if (!that.moving) this._slide();
		}catch(ex){
			alert("error initializing slider:" + e.message);
		}
	}

	this._slide = function() {
		try{
			var now, s, t, newY;
			now = new Date();
			t = now.getTime() - e.C;
			if (e.stop) {
				that.moving = false;
			} else{

				if(t < e.slideTime){
					setTimeout("jive.ext.y._sliders.get('" + that.hash + "')._slide();", e.timeout);
					var s = Math.sin(e.B * t);
					for(var i=0;i<tickers.length; i++){
						tickers[i].tick(s);
					}
					that.moving = true;
				}else{
					for(var i=0;i<tickers.length;i++){
						tickers[i].stop();
					}
					that.moving = false;
				}
			}
		}catch(ex){
			alert("error sliding slider:" + e);
		}
	}



	/**
	 * add a ticker
	 */
	this.addTicker = function(t){
		tickers.push(t);
	}

}

jive.ext.y.EndOfSlideTicker = function(thunk){
	this.init = function(){
	}
	this.tick = function(s){
	}
	this.stop = function(){
//		alert("calling: " + thunk);
		thunk();
	}
}

jive.ext.y.SlideToTicker = function(e, x, y){

	this.init = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		e.xTarget = x;
		e.yTarget = y;
		e.yA = e.yTarget - jive.ext.x.xTop(e); // A = distance
		e.xA = e.xTarget - jive.ext.x.xLeft(e); // A = distance
		e.yD = jive.ext.x.xTop(e); // D = initial position
		e.xD = jive.ext.x.xLeft(e); // D = initial position
	}
	this.tick = function(s){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		var newX = Math.round(e.xA * s + e.xD);
		var newY = Math.round(e.yA * s + e.yD);
		jive.ext.x.xMoveTo(e, newX, newY);
	}
	this.stop = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		jive.ext.x.xMoveTo(e, e.xTarget, e.yTarget);
	}
}


jive.ext.y.SlideHeightToTicker = function(e, h){

	this.init = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		e.xTargetH = h;
		e.xAH = e.xTargetH - jive.ext.x.xHeight(e); // A = distance
		e.xDH = jive.ext.x.xHeight(e); // D = initial position
	}
	this.tick = function(s){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		var newH = Math.round(e.xAH * s + e.xDH);
		jive.ext.x.xHeight(e, newH);
	}
	this.stop = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		jive.ext.x.xHeight(e, e.xTargetH);
	}
}

jive.ext.y.SlideWidthToTicker = function(e, w){

	this.init = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		e.xTargetW = w;
		e.xAW = e.xTargetW - jive.ext.x.xWidth(e); // A = distance
		e.xDW = jive.ext.x.xWidth(e); // D = initial position
	}
	this.tick = function(s){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		var newW = Math.round(e.xAW * s + e.xDW);
		jive.ext.x.xWidth(e, newW);
	}
	this.stop = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		jive.ext.x.xWidth(e, e.xTargetW);
	}
}

jive.ext.y.SlideTopToTicker = function(e, t){

	this.init = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		e.xTargetT = t;
		e.xAT = e.xTargetT - jive.ext.x.xTop(e); // A = distance
		e.xDT = jive.ext.x.xTop(e); // D = initial position
	}
	this.tick = function(s){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		var newT = Math.round(e.xAT * s + e.xDT);
		jive.ext.x.xTop(e, newT);
	}
	this.stop = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		jive.ext.x.xTop(e, e.xTargetT);
	}
}

jive.ext.y.SlideBottomToTicker = function(e, t){
	
	this.init = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		e.xTargetT = t;
		e.xAT = e.xTargetT - jive.ext.y.yBottom(e); // A = distance
		e.xDT = jive.ext.y.yBottom(e); // D = initial position
	}
	this.tick = function(s){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		var newT = Math.round(e.xAT * s + e.xDT);
		jive.ext.y.yBottom(e, newT);
	}
	this.stop = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		jive.ext.y.yBottom(e, e.xTargetT);
	}
}

jive.ext.y.SlideOpacityToTicker = function(e, o){

	this.init = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		e.xTargetO = o;
		e.xAO = e.xTargetO - jive.ext.y.yOpacity(e); // A = distance
		e.xDO = jive.ext.y.yOpacity(e); // D = initial position
	}
	this.tick = function(s){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		var newO = Math.round(e.xAO * s + e.xDO);
		jive.ext.y.yOpacity(e, newO);
	}
	this.stop = function(){
		if (!(e=jive.ext.x.xGetElementById(e))) return;
		jive.ext.y.yOpacity(e, e.xTargetO);
	}
}



/* file:jscripts/tiny_mce/classes/tinymce.js */

var tinymce = {
	majorVersion : '3',
	minorVersion : '0.7',
	releaseDate : '2008-04-14',

	_init : function() {
		var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base;

		// Browser checks
		t.isOpera = w.opera && opera.buildNumber;
		t.isWebKit = /WebKit/.test(ua);
		t.isOldWebKit = t.isWebKit && !w.getSelection().getRangeAt;
		t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
		t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
		t.isGecko = !t.isWebKit && /Gecko/.test(ua);
		t.isMac = ua.indexOf('Mac') != -1;

		// TinyMCE .NET webcontrol might be setting the values for TinyMCE
		if (w.tinyMCEPreInit) {
			t.suffix = tinyMCEPreInit.suffix;
			t.baseURL = tinyMCEPreInit.base;
			return;
		}

		// Get suffix and base
		t.suffix = '';

		// If base element found, add that infront of baseURL
		nl = d.getElementsByTagName('base');
		for (i=0; i<nl.length; i++) {
			if (nl[i].href)
				base = nl[i].href;
		}

		function getBase(n) {
			if (n.src && /tiny_mce(|_dev|_src|_gzip|_jquery|_prototype).js/.test(n.src)) {
				if (/_(src|dev)\.js/g.test(n.src))
					t.suffix = '_src';

				t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));

				// If path to script is relative and a base href was found add that one infront
				if (base && t.baseURL.indexOf('://') == -1)
					t.baseURL = base + t.baseURL;

				return t.baseURL;
			}

			return null;
		};

		// Check document
		nl = d.getElementsByTagName('script');
		for (i=0; i<nl.length; i++) {
			if (getBase(nl[i]))
				return;
		}

		// Check head
		n = d.getElementsByTagName('head')[0];
		if (n) {
			nl = n.getElementsByTagName('script');
			for (i=0; i<nl.length; i++) {
				if (getBase(nl[i]))
					return;
			}
		}

		return;
	},

	is : function(o, t) {
		var n = typeof(o);

		if (!t)
			return n != 'undefined';

		if (t == 'array' && (o instanceof Array))
			return true;

		return n == t;
	},

	// #if !jquery

	each : function(o, cb, s) {
		var n, l;

		if (!o)
			return 0;

		s = s || o;

		if (typeof(o.length) != 'undefined') {
			// Indexed arrays, needed for Safari
			for (n=0, l = o.length; n<l; n++) {
				if (cb.call(s, o[n], n, o) === false)
					return 0;
			}
		} else {
			// Hashtables
			for (n in o) {
				if (o.hasOwnProperty(n)) {
					if (cb.call(s, o[n], n, o) === false)
						return 0;
				}
			}
		}

		return 1;
	},

	map : function(a, f) {
		var o = [];

		tinymce.each(a, function(v) {
			o.push(f(v));
		});

		return o;
	},

	grep : function(a, f) {
		var o = [];

		tinymce.each(a, function(v) {
			if (!f || f(v))
				o.push(v);
		});

		return o;
	},

	inArray : function(a, v) {
		var i, l;

		if (a) {
			for (i = 0, l = a.length; i < l; i++) {
				if (a[i] === v)
					return i;
			}
		}

		return -1;
	},

	extend : function(o, e) {
		var i, a = arguments;

		for (i=1; i<a.length; i++) {
			e = a[i];

			tinymce.each(e, function(v, n) {
				if (typeof(v) !== 'undefined')
					o[n] = v;
			});
		}

		return o;
	},

	trim : function(s) {
		return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
	},

	// #endif

	create : function(s, p) {
		var t = this, sp, ns, cn, scn, c, de = 0;

		// Parse : <prefix> <class>:<super class>
		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name

		// Create namespace for new class
		ns = t.createNS(s[3].replace(/\.\w+$/, ''));

		// Class already exists
		if (ns[cn])
			return;

		// Make pure static class
		if (s[2] == 'static') {
			ns[cn] = p;

			if (this.onCreate)
				this.onCreate(s[2], s[3], ns[cn]);

			return;
		}

		// Create default constructor
		if (!p[cn]) {
			p[cn] = function() {};
			de = 1;
		}

		// Add constructor and methods
		ns[cn] = p[cn];
		t.extend(ns[cn].prototype, p);

		// Extend
		if (s[5]) {
			sp = t.resolve(s[5]).prototype;
			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name

			// Extend constructor
			c = ns[cn];
			if (de) {
				// Add passthrough constructor
				ns[cn] = function() {
					return sp[scn].apply(this, arguments);
				};
			} else {
				// Add inherit constructor
				ns[cn] = function() {
					this.parent = sp[scn];
					return c.apply(this, arguments);
				};
			}
			ns[cn].prototype[cn] = ns[cn];

			// Add super methods
			t.each(sp, function(f, n) {
				ns[cn].prototype[n] = sp[n];
			});

			// Add overridden methods
			t.each(p, function(f, n) {
				// Extend methods if needed
				if (sp[n]) {
					ns[cn].prototype[n] = function() {
						this.parent = sp[n];
						return f.apply(this, arguments);
					};
				} else {
					if (n != cn)
						ns[cn].prototype[n] = f;
				}
			});
		}

		// Add static methods
		t.each(p['static'], function(f, n) {
			ns[cn][n] = f;
		});

		if (this.onCreate)
			this.onCreate(s[2], s[3], ns[cn].prototype);
	},

	walk : function(o, f, n, s) {
		s = s || this;

		if (o) {
			if (n)
				o = o[n];

			tinymce.each(o, function(o, i) {
				if (f.call(s, o, i, n) === false)
					return false;

				tinymce.walk(o, f, n, s);
			});
		}
	},

	createNS : function(n, o) {
		var i, v;

		o = o || window;

		n = n.split('.');
		for (i=0; i<n.length; i++) {
			v = n[i];

			if (!o[v])
				o[v] = {};

			o = o[v];
		}

		return o;
	},

	resolve : function(n, o) {
		var i, l;

		o = o || window;

		n = n.split('.');
		for (i=0, l = n.length; i<l; i++) {
			o = o[n[i]];

			if (!o)
				break;
		}

		return o;
	},

	addUnload : function(f, s) {
		var t = this, w = window;

		f = {func : f, scope : s || this};

		if (!t.unloads) {
			function unload() {
				var li = t.unloads, o, n;

				if (li) {
					// Call unload handlers
					for (n in li) {
						o = li[n];

						if (o && o.func)
							o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
					}

					// Detach unload function
					if (w.detachEvent) {
						w.detachEvent('onbeforeunload', fakeUnload);
						w.detachEvent('onunload', unload);
					} else if (w.removeEventListener)
						w.removeEventListener('unload', unload, false);

					// Destroy references
					t.unloads = o = li = w = unload = null;

					// Run garbarge collector on IE
					if (window.CollectGarbage)
						window.CollectGarbage();
				}
			};

			function fakeUnload() {
				var d = document;

				// Is there things still loading, then do some magic
				if (d.readyState == 'interactive') {
					function stop() {
						// Prevent memory leak
						d.detachEvent('onstop', stop);

						// Call unload handler
						unload();

						d = null;
					};

					// Fire unload when the currently loading page is stopped
					d.attachEvent('onstop', stop);

					// Remove onstop listener after a while to prevent the unload function
					// to execute if the user presses cancel in an onbeforeunload
					// confirm dialog and then presses the browser stop button
					window.setTimeout(function() {
						d.detachEvent('onstop', stop);
					}, 0);
				}
			};

			// Attach unload handler
			if (w.attachEvent) {
				w.attachEvent('onunload', unload);
				w.attachEvent('onbeforeunload', fakeUnload);
			} else if (w.addEventListener)
				w.addEventListener('unload', unload, false);

			// Setup initial unload handler array
			t.unloads = [f];
		} else
			t.unloads.push(f);

		return f;
	},

	removeUnload : function(f) {
		var u = this.unloads, r = null;

		tinymce.each(u, function(o, i) {
			if (o && o.func == f) {
				u.splice(i, 1);
				r = f;
				return false;
			}
		});

		return r;
	},

	explode : function(s, d) {
		return tinymce.map(s.split(d || ','), tinymce.trim);
	},

	_addVer : function(u, s) {
		return u + (u.indexOf('?') == -1 ? '?' : '&') + 'v=' + (tinymce.majorVersion + tinymce.minorVersion).replace(/[^0-9]/g, '');
	}

	};

// Required for GZip AJAX loading
window.tinymce = tinymce;

// Initialize the API
tinymce._init();

/* file:jscripts/tiny_mce/classes/adapter/jquery/adapter.js */


/* file:jscripts/tiny_mce/classes/adapter/prototype/adapter.js */


/* file:jscripts/tiny_mce/classes/util/Dispatcher.js */

tinymce.create('tinymce.util.Dispatcher', {
	scope : null,
	listeners : null,

	Dispatcher : function(s) {
		this.scope = s || this;
		this.listeners = [];
	},

	add : function(cb, s) {
		this.listeners.push({cb : cb, scope : s || this.scope});

		return cb;
	},

	addToTop : function(cb, s) {
		this.listeners.unshift({cb : cb, scope : s || this.scope});

		return cb;
	},

	remove : function(cb) {
		var l = this.listeners, o = null;

		tinymce.each(l, function(c, i) {
			if (cb == c.cb) {
				o = cb;
				l.splice(i, 1);
				return false;
			}
		});

		return o;
	},

	dispatch : function() {
		var s, a = arguments, i, li = this.listeners, c;

		// Needs to be a real loop since the listener count might change while looping
		// And this is also more efficient
		for (i = 0; i<li.length; i++) {
            try{
                c = li[i];
                s = c.cb.apply(c.scope, a);
            }catch(e){ }
			if (s === false)
				break;
		}

		return s;
	}

	});

/* file:jscripts/tiny_mce/classes/util/URI.js */

(function() {
	var each = tinymce.each;

	tinymce.create('tinymce.util.URI', {
		URI : function(u, s) {
			var t = this, o, a, b;

			// Default settings
			s = t.settings = s || {};

			// Strange app protocol or local anchor
			if (/^(mailto|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) {
				t.source = u;
				return;
			}

			// Absolute path with no host, fake host and protocol
			if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
				u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;

			// Relative path
			if (u.indexOf('://') === -1 && u.indexOf('//') !== 0)
				u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);

			// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
			u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
			u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
			each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
				var s = u[i];

				// Zope 3 workaround, they use @@something
				if (s)
					s = s.replace(/\(mce_at\)/g, '@@');

				t[v] = s;
			});

			if (b = s.base_uri) {
				if (!t.protocol)
					t.protocol = b.protocol;

				if (!t.userInfo)
					t.userInfo = b.userInfo;

				if (!t.port && t.host == 'mce_host')
					t.port = b.port;

				if (!t.host || t.host == 'mce_host')
					t.host = b.host;

				t.source = '';
			}

			//t.path = t.path || '/';
		},

		setPath : function(p) {
			var t = this;

			p = /^(.*?)\/?(\w+)?$/.exec(p);

			// Update path parts
			t.path = p[0];
			t.directory = p[1];
			t.file = p[2];

			// Rebuild source
			t.source = '';
			t.getURI();
		},

		toRelative : function(u) {
			var t = this, o;

			u = new tinymce.util.URI(u, {base_uri : t});

			// Not on same domain/port or protocol
			if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
				return u.getURI();

			o = t.toRelPath(t.path, u.path);

			// Add query
			if (u.query)
				o += '?' + u.query;

			// Add anchor
			if (u.anchor)
				o += '#' + u.anchor;

			return o;
		},

		toAbsolute : function(u, nh) {
			var u = new tinymce.util.URI(u, {base_uri : this});

			return u.getURI(this.host == u.host ? nh : 0);
		},

		toRelPath : function(base, path) {
			var items, bp = 0, out = '', i;

			// Split the paths
			base = base.substring(0, base.lastIndexOf('/'));
			base = base.split('/');
			items = path.split('/');

			if (base.length >= items.length) {
				for (i = 0; i < base.length; i++) {
					if (i >= items.length || base[i] != items[i]) {
						bp = i + 1;
						break;
					}
				}
			}

			if (base.length < items.length) {
				for (i = 0; i < items.length; i++) {
					if (i >= base.length || base[i] != items[i]) {
						bp = i + 1;
						break;
					}
				}
			}

			if (bp == 1)
				return path;

			for (i = 0; i < base.length - (bp - 1); i++)
				out += "../";

			for (i = bp - 1; i < items.length; i++) {
				if (i != bp - 1)
					out += "/" + items[i];
				else
					out += items[i];
			}

			return out;
		},

		toAbsPath : function(base, path) {
			var i, nb = 0, o = [];

			// Split paths
			base = base.split('/');
			path = path.split('/');

			// Remove empty chunks
			each(base, function(k) {
				if (k)
					o.push(k);
			});

			base = o;

			// Merge relURLParts chunks
			for (i = path.length - 1, o = []; i >= 0; i--) {
				// Ignore empty or .
				if (path[i].length == 0 || path[i] == ".")
					continue;

				// Is parent
				if (path[i] == '..') {
					nb++;
					continue;
				}

				// Move up
				if (nb > 0) {
					nb--;
					continue;
				}

				o.push(path[i]);
			}

			i = base.length - nb;

			// If /a/b/c or /
			if (i <= 0)
				return '/' + o.reverse().join('/');

			return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/');
		},

		getURI : function(nh) {
			var s, t = this;

			// Rebuild source
			if (!t.source || nh) {
				s = '';

				if (!nh) {
					if (t.protocol)
						s += t.protocol + '://';

					if (t.userInfo)
						s += t.userInfo + '@';

					if (t.host)
						s += t.host;

					if (t.port)
						s += ':' + t.port;
				}

				if (t.path)
					s += t.path;

				if (t.query)
					s += '?' + t.query;

				if (t.anchor)
					s += '#' + t.anchor;

				t.source = s;
			}

			return t.source;
		}

		});
})();

/* file:jscripts/tiny_mce/classes/util/Cookie.js */

(function() {
	var each = tinymce.each;

	tinymce.create('static tinymce.util.Cookie', {
		getHash : function(n) {
			var v = this.get(n), h;

			if (v) {
				each(v.split('&'), function(v) {
					v = v.split('=');
					h = h || {};
					h[unescape(v[0])] = unescape(v[1]);
				});
			}

			return h;
		},

		setHash : function(n, v, e, p, d, s) {
			var o = '';

			each(v, function(v, k) {
				o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
			});

			this.set(n, o, e, p, d, s);
		},

		get : function(n) {
			var c = document.cookie, e, p = n + "=", b;

			// Strict mode
			if (!c)
				return;

			b = c.indexOf("; " + p);

			if (b == -1) {
				b = c.indexOf(p);

				if (b != 0)
					return null;
			} else
				b += 2;

			e = c.indexOf(";", b);

			if (e == -1)
				e = c.length;

			return unescape(c.substring(b + p.length, e));
		},

		set : function(n, v, e, p, d, s) {
			document.cookie = n + "=" + escape(v) +
				((e) ? "; expires=" + e.toGMTString() : "") +
				((p) ? "; path=" + escape(p) : "") +
				((d) ? "; domain=" + d : "") +
				((s) ? "; secure" : "");
		},

		remove : function(n, p) {
			var d = new Date();

			d.setTime(d.getTime() - 1000);

			this.set(n, '', d, p, d);
		}

		});
})();

/* file:jscripts/tiny_mce/classes/util/JSON.js */

tinymce.create('static tinymce.util.JSON', {
	serialize : function(o) {
		var i, v, s = tinymce.util.JSON.serialize, t;

		if (o == null)
			return 'null';

		t = typeof o;

		if (t == 'string') {
			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';

			return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'])/g, function(a, b) {
			// "
				i = v.indexOf(b);

				if (i + 1)
					return '\\' + v.charAt(i + 1);

				a = b.charCodeAt().toString(16);

				return '\\u' + '0000'.substring(a.length) + a;
			}) + '"';
		}

		if (t == 'object') {
			if (o instanceof Array) {
					for (i=0, v = '['; i<o.length; i++)
						v += (i > 0 ? ',' : '') + s(o[i]);

					return v + ']';
				}

				v = '{';

				for (i in o)
					v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';

				return v + '}';
		}

		return '' + o;
	},

	parse : function(s) {
		try {
			return eval('(' + s + ')');
		} catch (ex) {
			// Ignore
		}
	}

	});

/* file:jscripts/tiny_mce/classes/util/XHR.js */

tinymce.create('static tinymce.util.XHR', {
	send : function(o) {
		var x, t, w = window, c = 0;

		// Default settings
		o.scope = o.scope || this;
		o.success_scope = o.success_scope || o.scope;
		o.error_scope = o.error_scope || o.scope;
		o.async = o.async === false ? false : true;
		o.data = o.data || '';

		function get(s) {
			x = 0;

			try {
				x = new ActiveXObject(s);
			} catch (ex) {
			}

			return x;
		};

		x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');

		if (x) {
			if (x.overrideMimeType)
				x.overrideMimeType(o.content_type);

			x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);

			if (o.content_type)
				x.setRequestHeader('Content-Type', o.content_type);

			x.send(o.data);

			// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
			t = w.setInterval(function() {
				if (x.readyState == 4 || c++ > 10000) {
					w.clearInterval(t);

					if (o.success && c < 10000 && x.status == 200)
						o.success.call(o.success_scope, '' + x.responseText, x, o);
					else if (o.error)
						o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);

					x = null;
				}
			}, 10);
		}

		}
});

/* file:jscripts/tiny_mce/classes/util/JSONRequest.js */

(function() {
	var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;

	tinymce.create('tinymce.util.JSONRequest', {
		JSONRequest : function(s) {
			this.settings = extend({
			}, s);
			this.count = 0;
		},

		send : function(o) {
			var ecb = o.error, scb = o.success;

			o = extend(this.settings, o);

			o.success = function(c, x) {
				c = JSON.parse(c);

				if (typeof(c) == 'undefined') {
					c = {
						error : 'JSON Parse error.'
					};
				}

				if (c.error)
					ecb.call(o.error_scope || o.scope, c.error, x);
				else
					scb.call(o.success_scope || o.scope, c.result);
			};

			o.error = function(ty, x) {
				ecb.call(o.error_scope || o.scope, ty, x);
			};

			o.data = JSON.serialize({
				id : o.id || 'c' + (this.count++),
				method : o.method,
				params : o.params
			});

			// JSON content type for Ruby on rails. Bug: #1883287
			o.content_type = 'application/json';

			XHR.send(o);
		},

		'static' : {
			sendRPC : function(o) {
				return new tinymce.util.JSONRequest().send(o);
			}
		}

		});
}());
/* file:jscripts/tiny_mce/classes/dom/DOMUtils.js */

(function() {
	// Shorten names
	var each = tinymce.each, is = tinymce.is;
	var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;

	tinymce.create('tinymce.dom.DOMUtils', {
		doc : null,
		root : null,
		files : null,
		listeners : {},
		pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
		cache : {},
		idPattern : /^#[\w]+$/,
		elmPattern : /^[\w_*]+$/,
		elmClassPattern : /^([\w_]*)\.([\w_]+)$/,

		DOMUtils : function(d, s) {
			var t = this;

			t.doc = d;
			t.win = window;
			t.files = {};
			t.cssFlicker = false;
			t.counter = 0;
			t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat";
			t.stdMode = d.documentMode === 8;

			this.settings = s = tinymce.extend({
				keep_values : false,
				hex_colors : 1,
				process_html : 1
			}, s);

			// Fix IE6SP2 flicker and check it failed for pre SP2
			if (tinymce.isIE6) {
				try {
					d.execCommand('BackgroundImageCache', false, true);
				} catch (e) {
					t.cssFlicker = true;
				}
			}

			tinymce.addUnload(t.destroy, t);
		},

		getRoot : function() {
			var t = this, s = t.settings;

			return (s && t.get(s.root_element)) || t.doc.body;
		},

		getViewPort : function(w) {
			var d, b;

			w = !w ? this.win : w;
			d = w.document;
			b = this.boxModel ? d.documentElement : d.body;

			// Returns viewport size excluding scrollbars
			return {
				x : w.pageXOffset || b.scrollLeft,
				y : w.pageYOffset || b.scrollTop,
				w : w.innerWidth || b.clientWidth,
				h : w.innerHeight || b.clientHeight
			};
		},

		getRect : function(e) {
			var p, t = this, w, h;

			e = t.get(e);
			p = t.getPos(e);
			w = t.getStyle(e, 'width');
			h = t.getStyle(e, 'height');

			// Non pixel value, then force offset/clientWidth
			if (w.indexOf('px') === -1)
				w = 0;

			// Non pixel value, then force offset/clientWidth
			if (h.indexOf('px') === -1)
				h = 0;

			return {
				x : p.x,
				y : p.y,
				w : parseInt(w) || e.offsetWidth || e.clientWidth,
				h : parseInt(h) || e.offsetHeight || e.clientHeight
			};
		},

		getParent : function(n, f, r) {
			var na, se = this.settings;

			n = this.get(n);

			if (se.strict_root)
				r = r || this.getRoot();

			// Wrap node name as func
			if (is(f, 'string')) {
				na = f.toUpperCase();

				f = function(n) {
					var s = false;

					// Any element
					if (n.nodeType == 1 && na === '*') {
						s = true;
						return false;
					}

					each(na.split(','), function(v) {
						if (n.nodeType == 1 && ((se.strict && n.nodeName.toUpperCase() == v) || n.nodeName == v)) {
							s = true;
							return false; // Break loop
						}
					});

					return s;
				};
			}

			while (n) {
				if (n == r)
					return null;

				if (f(n))
					return n;

				n = n.parentNode;
			}

			return null;
		},

		get : function(e) {
			var n;

			if (this.doc && typeof(e) == 'string') {
				n = e;
				try{
					e = this.doc.getElementById(e);

					// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
					if (e && e.id !== n)
						return this.doc.getElementsByName(n)[1];
				}catch(ex){ }
			}

			return e;
		},

		// #if !jquery

		select : function(pa, s) {
			var t = this, cs, c, pl, o = [], x, i, l, n;

			s = t.get(s) || t.doc;

			// Look for native support and use that if it's found
			if (s.querySelectorAll) {
				// Element scope then use temp id
				// We need to do this to be compatible with other implementations
				// See bug report: http://bugs.webkit.org/show_bug.cgi?id=17461
				if (s != t.doc) {
					i = s.id;
					s.id = '_mc_tmp';
					pa = '#_mc_tmp ' + pa;
				}

				// Select elements
				l = tinymce.grep(s.querySelectorAll(pa));

				// Restore old id
				s.id = i;

				return l;
			}

			if (t.settings.strict) {
				function get(s, n) {
					return s.getElementsByTagName(n.toLowerCase());
				};
			} else {
				function get(s, n) {
					return s.getElementsByTagName(n);
				};
			}

			// Simple element pattern. For example: "p" or "*"
			if (t.elmPattern.test(pa)) {
				x = get(s, pa);

				for (i = 0, l = x.length; i<l; i++)
					o.push(x[i]);

				return o;
			}

			// Simple class pattern. For example: "p.class" or ".class"
			if (t.elmClassPattern.test(pa)) {
				pl = t.elmClassPattern.exec(pa);
				x = get(s, pl[1] || '*');
				c = ' ' + pl[2] + ' ';

				for (i = 0, l = x.length; i<l; i++) {
					n = x[i];

					if (n.className && (' ' + n.className + ' ').indexOf(c) !== -1)
						o.push(n);
				}

				return o;
			}

			function collect(n) {
				if (!n.mce_save) {
					n.mce_save = 1;
					o.push(n);
				}
			};

			function collectIE(n) {
				if (!n.getAttribute('mce_save')) {
					n.setAttribute('mce_save', '1');
					o.push(n);
				}
			};

			function find(n, f, r) {
				var i, l, nl = get(r, n);

				for (i = 0, l = nl.length; i < l; i++)
					f(nl[i]);
			};

			each(pa.split(','), function(v, i) {
				v = tinymce.trim(v);

				// Simple element pattern, most common in TinyMCE
				if (t.elmPattern.test(v)) {
					each(get(s, v), function(n) {
						collect(n);
					});

					return;
				}

				// Simple element pattern with class, fairly common in TinyMCE
				if (t.elmClassPattern.test(v)) {
					x = t.elmClassPattern.exec(v);

					each(get(s, x[1]), function(n) {
						if (t.hasClass(n, x[2]))
							collect(n);
					});

					return;
				}

				if (!(cs = t.cache[pa])) {
					cs = 'x=(function(cf, s) {';
					pl = v.split(' ');

					each(pl, function(v) {
						var p = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@([\w\\]+)([\^\$\*!]?=)([\w\\]+)\])?(?:\:([\w\\]+))?/i.exec(v);

						// Find elements
						p[1] = p[1] || '*';
						cs += 'find("' + p[1] + '", function(n) {';

						// Check id
						if (p[2])
							cs += 'if (n.id !== "' + p[2] + '") return;';

						// Check classes
						if (p[3]) {
							cs += 'var c = " " + n.className + " ";';
							cs += 'if (';
							c = '';
							each(p[3].split('.'), function(v) {
								if (v)
									c += (c ? '||' : '') + 'c.indexOf(" ' + v + ' ") === -1';
							});
							cs += c + ') return;';
						}
					});

					cs += 'cf(n);';

					for (i = pl.length - 1; i >= 0; i--)
						cs += '}, ' + (i ? 'n' : 's') + ');';

					cs += '})';

					// Compile CSS pattern function
					t.cache[pa] = cs = eval(cs);
				}

				// Run selector function
				cs(isIE ? collectIE : collect, s);
			});

			// Cleanup
			each(o, function(n) {
				if (isIE)
					n.removeAttribute('mce_save');
				else
					delete n.mce_save;
			});

			return o;
		},

		// #endif

		add : function(p, n, a, h, c) {
			var t = this;

			return this.run(p, function(p) {
				var e, k;

				e = is(n, 'string') ? t.doc.createElement(n) : n;

				if (a) {
					for (k in a) {
						if (a.hasOwnProperty(k) && !is(a[k], 'object'))
							t.setAttrib(e, k, '' + a[k]);
					}

					if (a.style && !is(a.style, 'string')) {
						each(a.style, function(v, n) {
							t.setStyle(e, n, v);
						});
					}
				}

				if (h) {
					if (h.nodeType)
						e.appendChild(h);
					else
						t.setHTML(e, h);
				}

				return !c ? p.appendChild(e) : e;
			});
		},

		create : function(n, a, h) {
			return this.add(this.doc.createElement(n), n, a, h, 1);
		},

		createHTML : function(n, a, h) {
			var o = '', t = this, k;

			o += '<' + n;

			for (k in a) {
				if (a.hasOwnProperty(k))
					o += ' ' + k + '="' + t.encode(a[k]) + '"';
			}

			if (tinymce.is(h))
				return o + '>' + h + '</' + n + '>';

			return o + ' />';
		},

		remove : function(n, k) {
			return this.run(n, function(n) {
				var p, g;

				p = n.parentNode;

				if (!p)
					return null;

				if (k) {
					each (n.childNodes, function(c) {
						p.insertBefore(c.cloneNode(true), n);
					});
				}

				// Fix IE psuedo leak
		/*		if (isIE) {
					p = n.cloneNode(true);
					n.outerHTML = '';

					return p;
				}*/

				return p.removeChild(n);
			});
		},

		// #if !jquery

		setStyle : function(n, na, v) {
			var t = this;

			return t.run(n, function(e) {
				var s, i;

				s = e.style;

				// Camelcase it, if needed
				na = na.replace(/-(\D)/g, function(a, b){
					return b.toUpperCase();
				});

				// Default px suffix on these
				if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
					v += 'px';

				switch (na) {
					case 'opacity':
						// IE specific opacity
						if (isIE) {
							s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";

							if (!n.currentStyle || !n.currentStyle.hasLayout)
								s.display = 'inline-block';
						}

						// Fix for older browsers
						s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
						break;

					case 'float':
						isIE ? s.styleFloat = v : s.cssFloat = v;
						break;

					default:
						s[na] = v || '';
				}

				// Force update of the style data
				if (t.settings.update_styles)
					t.setAttrib(e, 'mce_style');
			});
		},

		getStyle : function(n, na, c) {
			n = this.get(n);

			if (!n)
				return false;

			// Gecko
			if (this.doc.defaultView && c) {
				// Remove camelcase
				na = na.replace(/[A-Z]/g, function(a){
					return '-' + a;
				});

				try {
					return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
				} catch (ex) {
					// Old safari might fail
					return null;
				}
			}

			// Camelcase it, if needed
			na = na.replace(/-(\D)/g, function(a, b){
				return b.toUpperCase();
			});

			if (na == 'float')
				na = isIE ? 'styleFloat' : 'cssFloat';

			// IE & Opera
			if (n.currentStyle && c)
				return n.currentStyle[na];

			return n.style[na];
		},

		setStyles : function(e, o) {
			var t = this, s = t.settings, ol;

			ol = s.update_styles;
			s.update_styles = 0;

			each(o, function(v, n) {
				t.setStyle(e, n, v);
			});

			// Update style info
			s.update_styles = ol;
			if (s.update_styles)
				t.setAttrib(e, s.cssText);
		},

		setAttrib : function(e, n, v) {
			var t = this;

			// Strict XML mode
			if (t.settings.strict)
				n = n.toLowerCase();

			return this.run(e, function(e) {
				var s = t.settings;

				switch (n) {
					case "style":
						// No mce_style for elements with these since they might get resized by the user
						if (s.keep_values && /^(| )(top|left|bottom|right|width|height)/i.test(v)) {
							if (v)
								e.setAttribute('mce_style', v, 2);
							else
								e.removeAttribute('mce_style', 2);
						}

						e.style.cssText = v;
						break;

					case "class":
						e.className = v || ''; // Fix IE null bug
						break;

					case "src":
					case "href":
						if (s.keep_values) {
							if (s.url_converter)
								v = s.url_converter.call(s.url_converter_scope || t, v, n, e);

							t.setAttrib(e, 'mce_' + n, v, 2);
						}

						break;
				}

				if (is(v) && v !== null && v.length !== 0)
					e.setAttribute(n, '' + v, 2);
				else
					e.removeAttribute(n, 2);
			});
		},

		setAttribs : function(e, o) {
			var t = this;

			return this.run(e, function(e) {
				each(o, function(v, n) {
					t.setAttrib(e, n, v);
				});
			});
		},

		// #endif

		getAttrib : function(e, n, dv) {
			var v, t = this;

			e = t.get(e);

			if (!e || e.nodeType !== 1)
				return false;

			if (!is(dv))
				dv = "";

			// Try the mce variant for these
			if (/^(src|href|style|coords)$/.test(n)) {
				v = e.getAttribute("mce_" + n);

				if (v)
					return v;
			}

			v = e.getAttribute(n, 2);

			if (!v) {
				switch (n) {
					case 'class':
						v = e.className;
						break;

					default:
						// Fix for IE crash Bug: #1884376 probably due to invalid DOM structure
						if (isIE && n === 'name' && e.nodeName === 'A') {
							v = e.name;
							break;
						}

						v = e.attributes[n];
						v = v && is(v.nodeValue) ? v.nodeValue : v;
				}
			}

			switch (n) {
				case 'style':
					v = v || e.style.cssText;

					if (v) {
						v = t.serializeStyle(t.parseStyle(v));

						if (t.settings.keep_values && /^(| )(top|left|bottom|right|width|height)/i.test(v))
							e.setAttribute('mce_style', v);
					}

					break;
			}

			// Remove Apple and WebKit stuff
			if (isWebKit && n === "class" && v)
				v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');

			// Handle IE issues
			if (isIE) {
				switch (n) {
					case 'rowspan':
					case 'colspan':
						// IE returns 1 as default value
						if (v === 1)
							v = '';

						break;

					case 'size':
						// IE returns +0 as default value for size
						if (v === '+0')
							v = '';

						break;

					case 'hspace':
						// IE returns -1 as default value
						if (v === -1)
							v = '';

						break;

					case 'tabindex':
						// IE returns 32768 as default value
						if (v === 32768)
							v = '';

						break;

					case 'shape':
						v = v.toLowerCase();
						break;

					default:
						// IE has odd anonymous function for event attributes
						if (n.indexOf('on') === 0 && v)
							v = ('' + v).replace(/^function\s+anonymous\(\)\s+\{\s+(.*)\s+\}$/, '$1');
				}
			}

			return (v && v != '') ? '' + v : dv;
		},

		getPos : function(n) {
			var t = this, x = 0, y = 0, e, d = t.doc, r;

			n = t.get(n);

			// Use getBoundingClientRect on IE, Opera has it but it's not perfect
			if (n && isIE) {
				n = n.getBoundingClientRect();
				e = t.boxModel ? d.documentElement : d.body;
				x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
				x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
				n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset

				return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
			}

			r = n;
			while (r) {
				x += r.offsetLeft || 0;
				y += r.offsetTop || 0;
				r = r.offsetParent;
			}

			r = n;
			while (r) {
				// Opera 9.25 bug fix, fixed in 9.50
				if (!/^table-row|inline.*/i.test(t.getStyle(r, "display", 1))) {
					x -= r.scrollLeft || 0;
					y -= r.scrollTop || 0;
				}

				r = r.parentNode;

				if (r == d.body)
					break;
			}

			return {x : x, y : y};
		},

		parseStyle : function(st) {
			var t = this, s = t.settings, o = {};

			if (!st)
				return o;

			function compress(p, s, ot) {
				var t, r, b, l;

				// Get values and check it it needs compressing
				t = o[p + '-top' + s];
				if (!t)
					return;

				r = o[p + '-right' + s];
				if (t != r)
					return;

				b = o[p + '-bottom' + s];
				if (r != b)
					return;

				l = o[p + '-left' + s];
				if (b != l)
					return;

				// Compress
				o[ot] = l;
				delete o[p + '-top' + s];
				delete o[p + '-right' + s];
				delete o[p + '-bottom' + s];
				delete o[p + '-left' + s];
			};

			function compress2(ta, a, b, c) {
				var t;

				t = o[a];
				if (!t)
					return;

				t = o[b];
				if (!t)
					return;

				t = o[c];
				if (!t)
					return;

				// Compress
				o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
				delete o[a];
				delete o[b];
				delete o[c];
			};

			each(st.split(';'), function(v) {
				var sv, ur = [];

				if (v) {
					v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
					v = v.split(':');
					sv = tinymce.trim(v[1]);
					sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});

					sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {
						return t.toHex(v);
					});

					if (s.url_converter) {
						sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
							return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null)) + ')';
						});
					}

					o[tinymce.trim(v[0]).toLowerCase()] = sv;
				}
			});

			compress("border", "", "border");
			compress("border", "-width", "border-width");
			compress("border", "-color", "border-color");
			compress("border", "-style", "border-style");
			compress("padding", "", "padding");
			compress("margin", "", "margin");
			compress2('border', 'border-width', 'border-style', 'border-color');

			if (isIE) {
				// Remove pointless border
				if (o.border == 'medium none')
					o.border = '';
			}

			return o;
		},

		serializeStyle : function(o) {
			var s = '';

			each(o, function(v, k) {
				if (k && v) {
					switch (k) {
						case 'color':
						case 'background-color':
							v = v.toLowerCase();
							break;
					}

					s += (s ? ' ' : '') + k + ': ' + v + ';';
				}
			});

			return s;
		},

		loadCSS : function(u) {
			var t = this, d = t.doc;

			if (!u)
				u = '';

			each(u.split(','), function(u) {
				if (t.files[u])
					return;

				t.files[u] = true;
				t.add(t.select('head')[0], 'link', {rel : 'stylesheet', href : tinymce._addVer(u)});
			});
		},

		// #if !jquery

		addClass : function(e, c) {
			return this.run(e, function(e) {
				var o;

				if (!c)
					return 0;

				if (this.hasClass(e, c))
					return e.className;

				o = this.removeClass(e, c);

				return e.className = (o != '' ? (o + ' ') : '') + c;
			});
		},

		removeClass : function(e, c) {
			var t = this, re;

			return t.run(e, function(e) {
				var v;

				if (t.hasClass(e, c)) {
					if (!re)
						re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");

					v = e.className.replace(re, ' ');

					return e.className = tinymce.trim(v != ' ' ? v : '');
				}

				return e.className;
			});
		},

		hasClass : function(n, c) {
			n = this.get(n);

			if (!n || !c)
				return false;

			return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
		},

		show : function(e) {
			return this.setStyle(e, 'display', 'block');
		},

		hide : function(e) {
			return this.setStyle(e, 'display', 'none');
		},

		isHidden : function(e) {
			e = this.get(e);

			return e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
		},

		// #endif

		uniqueId : function(p) {
			do{
                id = (!p ? 'mce_' : p) + (this.counter++);
            }while(this.doc.getElementById(id));
            return id;
        },

		setHTML : function(e, h) {
			var t = this;

			return this.run(e, function(e) {
				var x, i, nl, n, p, x;

				h = t.processHTML(h);

				if (isIE) {
					function set() {
//						try {
//							// IE will remove comments from the beginning
//							// unless you padd the contents with something
//							e.innerHTML = '<br />' + h;
//							e.removeChild(e.firstChild);
//						} catch (ex) {
							// IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p
							// This seems to fix this problem

							// Remove all child nodes
							while (e.childNodes.length > 0)
								e.removeChild(e.childNodes[0]);

							// Create new div with HTML contents and a BR infront to keep comments
							x = t.create('div');
							x.innerHTML = '<br />' + h;

                            while(x.childNodes.length > 1 && e.nodeType == 1 && e.nodeName.toLowerCase() != "input"){
                                // Skip br element
                                e.appendChild(x.childNodes[1]);
                            }
//						}
					};

					// IE has a serious bug when it comes to paragraphs it can produce an invalid
					// DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
					// It seems to be that IE doesn't like a root block element placed inside another root block element
					if (t.settings.fix_ie_paragraphs)
						h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true">&nbsp;</p>');

					set();

					if (t.settings.fix_ie_paragraphs) {
						// Check for odd paragraphs this is a sign of a broken DOM
						nl = e.getElementsByTagName("p");
						for (i = nl.length - 1, x = 0; i >= 0; i--) {
							n = nl[i];

							if (!n.hasChildNodes()) {
								if (!n.mce_keep) {
									x = 1; // Is broken
									break;
								}

								n.removeAttribute('mce_keep');
							}
						}
					}

					// Time to fix the madness IE left us
					if (x) {
						// So if we replace the p elements with divs and mark them and then replace them back to paragraphs
						// after we use innerHTML we can fix the DOM tree
						h = h.replace(/<p([^>]+)>|<p>/g, '<div$1 mce_tmp="1">');
						h = h.replace(/<\/p>/g, '</div>');

						// Set the new HTML with DIVs
						set();

						// Replace all DIV elements with he mce_tmp attibute back to paragraphs
						// This is needed since IE has a annoying bug see above for details
						// This is a slow process but it has to be done. :(
						if (t.settings.fix_ie_paragraphs) {
							nl = e.getElementsByTagName("DIV");
							for (i = nl.length - 1; i >= 0; i--) {
								n = nl[i];

								// Is it a temp div
								if (n.mce_tmp) {
									// Create new paragraph
									p = t.doc.createElement('p');

									// Copy all attributes
									n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {
										var v;

										if (b !== 'mce_tmp') {
											v = n.getAttribute(b);

											if (!v && b === 'class')
												v = n.className;

											p.setAttribute(b, v);
										}
									});

									// Append all children to new paragraph
									for (x = 0; x<n.childNodes.length; x++)
										p.appendChild(n.childNodes[x].cloneNode(true));

									// Replace div with new paragraph
									n.swapNode(p);
								}
							}
						}
					}
				} else
					e.innerHTML = h;

				return h;
			});
		},

		processHTML : function(h) {
			var t = this, s = t.settings;

			if (!s.process_html)
				return h;

			// Convert strong and em to b and i in FF since it can't handle them
			if (tinymce.isGecko) {
				h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');
				h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');
			} else if (isIE)
				h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos

			// Fix some issues
			h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open

			// Store away src and href in mce_src and mce_href since browsers mess them up
			if (s.keep_values) {
				// Wrap scripts in comments for serialization purposes
				if (h.indexOf('<script') !== -1) {
					h = h.replace(/<script>/g, '<script type="text/javascript">');
					h = h.replace(/<script(|[^>]+)>(\s*<!--|\/\/\s*<\[CDATA\[)?[\r\n]*/g, '<mce:script$1><!--\n');
					h = h.replace(/\s*(\/\/\s*-->|\/\/\s*]]>)?<\/script>/g, '\n// --></mce:script>');
					h = h.replace(/<mce:script(|[^>]+)><!--\n\/\/ --><\/mce:script>/g, '<mce:script$1></mce:script>');
				}

				// Process all tags with src, href or style
				h = h.replace(/<([\w:]+) [^>]*(src|href|style|coords)[^>]*>/gi, function(a, n) {
					function handle(m, b, c) {
						var u = c;

						// Tag already got a mce_ version
						if (a.indexOf('mce_' + b) != -1)
							return m;

						if (b == 'style') {
							// Why did I need this one?
							//if (isIE)
							//	u = t.serializeStyle(t.parseStyle(u));

							// No mce_style for elements with these since they might get resized by the user
							if (/^(| )(top|left|bottom|right|width|height)/i.test(c))
								return m;

							if (s.hex_colors) {
								u = u.replace(/rgb\([^\)]+\)/g, function(v) {
									return t.toHex(v);
								});
							}

							if (s.url_converter) {
								u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) {
									return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')';
								});
							}
						} else if (b != 'coords') {
							if (s.url_converter)
								u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));
						}

						return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';
					};

					a = a.replace(/ (src|href|style|coords)=[\"]([^\"]+)[\"]/gi, handle); // W3C
					a = a.replace(/ (src|href|style|coords)=[\']([^\']+)[\']/gi, handle); // W3C

					return a.replace(/ (src|href|style|coords)=([^\s\"\'>]+)/gi, handle); // IE
				});
			}

			return h;
		},

		getOuterHTML : function(e) {
			var d;

			e = this.get(e);

			if (!e)
				return null;

			if (isIE)
				return e.outerHTML;

			d = (e.ownerDocument || this.doc).createElement("body");
			d.appendChild(e.cloneNode(true));

			return d.innerHTML;
		},

		setOuterHTML : function(e, h, d) {
			var t = this;

			return this.run(e, function(e) {
				var n, tp;

				e = t.get(e);
				d = d || e.ownerDocument || t.doc;

				if (isIE && e.nodeType == 1)
					e.outerHTML = h;
				else {
					tp = d.createElement("body");
					tp.innerHTML = h;

					n = tp.lastChild;
					while (n) {
						t.insertAfter(n.cloneNode(true), e);
						n = n.previousSibling;
					}

					t.remove(e);
				}
			});
		},

		decode : function(s) {
			var e;

			// Look for entities to decode
			if (/&[^;]+;/.test(s)) {
				// Decode the entities using a div element not super efficient but less code
				e = this.doc.createElement("div");
				e.innerHTML = s;

				return !e.firstChild ? s : e.firstChild.nodeValue;
			}

			return s;
		},

		encode : function(s) {
			return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {
				switch (c) {
					case '&':
						return '&amp;';

					case '"':
						return '&quot;';

					case '<':
						return '&lt;';

					case '>':
						return '&gt;';
				}

				return c;
			}) : s;
		},

		// #if !jquery

		insertAfter : function(n, r) {
			var t = this;

			r = t.get(r);

			return this.run(n, function(n) {
				var p, ns;

				p = r.parentNode;
				ns = r.nextSibling;

				if (ns)
					p.insertBefore(n, ns);
				else
					p.appendChild(n);

				return n;
			});
		},

		// #endif

		isBlock : function(n) {
			if (n.nodeType && n.nodeType !== 1)
				return false;

			n = n.nodeName || n;

			return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|TH|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);
		},

		// #if !jquery

		replace : function(n, o, k) {
			if (is(o, 'array'))
				n = n.cloneNode(true);

			return this.run(o, function(o) {
				if (k) {
					each(o.childNodes, function(c) {
						n.appendChild(c.cloneNode(true));
					});
				}

				// Fix IE psuedo leak for elements since replacing elements if fairly common
				// Will break parentNode for some unknown reason
	/*			if (isIE && o.nodeType === 1) {
					o.parentNode.insertBefore(n, o);
					o.outerHTML = '';
					return n;
				}*/

				return o.parentNode.replaceChild(n, o);
			});
		},

		// #endif

		toHex : function(s) {
			var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);

			function hex(s) {
				s = parseInt(s).toString(16);

				return s.length > 1 ? s : '0' + s; // 0 -> 00
			};

			if (c) {
				s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);

				return s;
			}

			return s;
		},

		getClasses : function() {
			var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;

			if (t.classes)
				return t.classes;

			function addClasses(s) {
				// IE style imports
				each(s.imports, function(r) {
					addClasses(r);
				});

				each(s.cssRules || s.rules, function(r) {
					// Real type or fake it on IE
					switch (r.type || 1) {
						// Rule
						case 1:
							if (r.selectorText) {
								each(r.selectorText.split(','), function(v) {
									v = v.replace(/^\s*|\s*$|^\s\./g, "");

									// Is internal or it doesn\'t contain a class
									if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
										return;

									// Remove everything but class name
									ov = v;
									v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');

									// Filter classes
									if (f && !(v = f(v, ov)))
										return;

									if (!lo[v]) {
										cl.push({'class' : v});
										lo[v] = 1;
									}
								});
							}
							break;

						// Import
						case 3:
							addClasses(r.styleSheet);
							break;
					}
				});
			};

			try {
				each(t.doc.styleSheets, addClasses);
			} catch (ex) {
				// Ignore
			}

			if (cl.length > 0)
				t.classes = cl;

			return cl;
		},

		run : function(e, f, s) {
			var t = this, o;

			if (t.doc && typeof(e) === 'string')
				e = t.doc.getElementById(e);

			if (!e)
				return false;

			s = s || this;
			if (!e.nodeType && (e.length || e.length === 0)) {
				o = [];

				each(e, function(e, i) {
					if (e) {
						if (typeof(e) == 'string')
							e = t.doc.getElementById(e);

						o.push(f.call(s, e, i));
					}
				});

				return o;
			}

			return f.call(s, e);
		},

		getAttribs : function(n) {
			var o;

			n = this.get(n);

			if (!n)
				return [];

			if (isIE) {
				o = [];

				// Object will throw exception in IE
				if (n.nodeName == 'OBJECT')
					return n.attributes;

				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
				n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) {
					o.push({specified : 1, nodeName : b});
				});

				return o;
			}

			return n.attributes;
		},

		destroy : function(s) {
			var t = this;

			t.win = t.doc = t.root = null;

			// Manual destroy then remove unload handler
			if (!s)
				tinymce.removeUnload(t.destroy);
		}

		/*
		walk : function(n, f, s) {
			var d = this.doc, w;

			if (d.createTreeWalker) {
				w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);

				while ((n = w.nextNode()) != null)
					f.call(s || this, n);
			} else
				tinymce.walk(n, f, 'childNodes', s);
		}
		*/

		/*
		toRGB : function(s) {
			var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);

			if (c) {
				// #FFF -> #FFFFFF
				if (!is(c[3]))
					c[3] = c[2] = c[1];

				return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
			}

			return s;
		}
		*/

		});

	// Setup page DOM
	tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
})();

/* file:jscripts/tiny_mce/classes/dom/Event.js */

(function() {
	// Shorten names
	var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;

	tinymce.create('static tinymce.dom.Event', {
		inits : [],
		events : [],

		// #if !jquery

		add : function(o, n, f, s) {
			var cb, t = this, el = t.events, r;

			// Handle array
			if (o && o instanceof Array) {
				r = [];

				each(o, function(o) {
					o = DOM.get(o);
					r.push(t.add(o, n, f, s));
				});

				return r;
			}

			o = DOM.get(o);

			if (!o)
				return;

			// Setup event callback
			cb = function(e) {
				e = e || window.event;

				// Patch in target in IE it's W3C valid
				if (e && !e.target && isIE)
					e.target = e.srcElement;

				if (!s)
					return f(e);

				return f.call(s, e);
			};

			if (n == 'unload') {
				tinymce.unloads.unshift({func : cb});
				return cb;
			}

			if (n == 'init') {
				if (t.domLoaded)
					cb();
				else
					t.inits.push(cb);

				return cb;
			}

			// Store away listener reference
			el.push({
				obj : o,
				name : n,
				func : f,
				cfunc : cb,
				scope : s
			});

			t._add(o, n, cb);

			return f;
		},

		remove : function(o, n, f) {
			var t = this, a = t.events, s = false, r;

			// Handle array
			if (o && o instanceof Array) {
				r = [];

				each(o, function(o) {
					o = DOM.get(o);
					r.push(t.remove(o, n, f));
				});

				return r;
			}

			o = DOM.get(o);

			each(a, function(e, i) {
				if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
					a.splice(i, 1);
					t._remove(o, n, e.cfunc);
					s = true;
					return false;
				}
			});

			return s;
		},

		clear : function(o) {
			var t = this, a = t.events, i, e;

			if (o) {
				o = DOM.get(o);

				for (i = a.length - 1; i >= 0; i--) {
					e = a[i];

					if (e.obj === o) {
						t._remove(e.obj, e.name, e.cfunc);
						e.obj = e.cfunc = null;
						a.splice(i, 1);
					}
				}
			}
		},

		// #endif

		cancel : function(e) {
			if (!e)
				return false;

			this.stop(e);
			return this.prevent(e);
		},

		stop : function(e) {
			if (e.stopPropagation)
				e.stopPropagation();
			else
				e.cancelBubble = true;

			return false;
		},

		prevent : function(e) {
			if (e.preventDefault)
				e.preventDefault();
			else
				e.returnValue = false;

			return false;
		},

		_unload : function() {
			var t = Event;

			each(t.events, function(e, i) {
				t._remove(e.obj, e.name, e.cfunc);
				e.obj = e.cfunc = null;
			});

			t.events = [];
			t = null;
		},

		_add : function(o, n, f) {
			if (o.attachEvent)
				o.attachEvent('on' + n, f);
			else if (o.addEventListener)
				o.addEventListener(n, f, false);
			else
				o['on' + n] = f;
		},

		_remove : function(o, n, f) {
			if (o) {
				try {
					if (o.detachEvent)
						o.detachEvent('on' + n, f);
					else if (o.removeEventListener)
						o.removeEventListener(n, f, false);
					else
						o['on' + n] = null;
				} catch (ex) {
					// Might fail with permission denined on IE so we just ignore that
				}
			}
		},

		_pageInit : function() {
			var e = Event;

			e._remove(window, 'DOMContentLoaded', e._pageInit);
			e.domLoaded = true;

			each(e.inits, function(c) {
				c();
			});

			e.inits = [];
		},

		_wait : function() {
			var t;

			// No need since the document is already loaded
			if (window.tinyMCE_GZ && tinyMCE_GZ.loaded)
				return;

			if (isIE && document.location.protocol != 'https:') {
				// Fake DOMContentLoaded on IE
				document.write('<script id=__ie_onload defer src=\'javascript:""\';><\/script>');
				DOM.get("__ie_onload").onreadystatechange = function() {
					if (this.readyState == "complete") {
						Event._pageInit();
						DOM.get("__ie_onload").onreadystatechange = null; // Prevent leak
					}
				};
			} else {
				Event._add(window, 'DOMContentLoaded', Event._pageInit, Event);

				if (isIE || isWebKit) {
					t = setInterval(function() {
						if (/loaded|complete/.test(document.readyState)) {
							clearInterval(t);
							Event._pageInit();
						}
					}, 10);
				}
			}
		}

		});

	// Shorten name
	Event = tinymce.dom.Event;

	// Dispatch DOM content loaded event for IE and Safari
	Event._wait();
	tinymce.addUnload(Event._unload);
})();

/* file:jscripts/tiny_mce/classes/dom/Element.js */

(function() {
	var each = tinymce.each;

	tinymce.create('tinymce.dom.Element', {
		Element : function(id, s) {
			var t = this, dom, el;

			s = s || {};
			t.id = id;
			t.dom = dom = s.dom || tinymce.DOM;
			t.settings = s;

			// Only IE leaks DOM references, this is a lot faster
			if (!tinymce.isIE)
				el = t.dom.get(t.id);

			each([
				'getPos',
				'getRect',
				'getParent',
				'add',
				'setStyle',
				'getStyle',
				'setStyles',
				'setAttrib',
				'setAttribs',
				'getAttrib',
				'addClass',
				'removeClass',
				'hasClass',
				'getOuterHTML',
				'setOuterHTML',
				'remove',
				'show',
				'hide',
				'isHidden',
				'setHTML',
				'get'
			], function(k) {
				t[k] = function() {
					var a = arguments, o;

					// Opera fails
					if (tinymce.isOpera) {
						a = [id];

						each(arguments, function(v) {
							a.push(v);
						});
					} else
						Array.prototype.unshift.call(a, el || id);

					o = dom[k].apply(dom, a);
					t.update(k);

					return o;
				};
			});
		},

		on : function(n, f, s) {
			return tinymce.dom.Event.add(this.id, n, f, s);
		},

		getXY : function() {
			return {
				x : parseInt(this.getStyle('left')),
				y : parseInt(this.getStyle('top'))
			};
		},

		getSize : function() {
			var n = this.dom.get(this.id);
            if(!n){
                n = tinyMCE.activeEditor.getContainer();
            }

            return {
				w : parseInt(this.getStyle('width') || n.clientWidth),
				h : parseInt(this.getStyle('height') || n.clientHeight)
			};
		},

		moveTo : function(x, y) {
			this.setStyles({left : x, top : y});
		},

		moveBy : function(x, y) {
			var p = this.getXY();

			this.moveTo(p.x + x, p.y + y);
		},

		resizeTo : function(w, h) {
			this.setStyles({width : w, height : h});
		},

		resizeBy : function(w, h) {
			var s = this.getSize();

			this.resizeTo(s.w + w, s.h + h);
		},

		update : function(k) {
			var t = this, b, dom = t.dom;

			if (tinymce.isIE6 && t.settings.blocker) {
				k = k || '';

				// Ignore getters
				if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
					return;

				// Remove blocker on remove
				if (k == 'remove') {
					dom.remove(t.blocker);
					return;
				}

				if (!t.blocker) {
					t.blocker = dom.uniqueId();
					b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});
					dom.setStyle(b, 'opacity', 0);
				} else
					b = dom.get(t.blocker);

				dom.setStyle(b, 'left', t.getStyle('left', 1));
				dom.setStyle(b, 'top', t.getStyle('top', 1));
				dom.setStyle(b, 'width', t.getStyle('width', 1));
				dom.setStyle(b, 'height', t.getStyle('height', 1));
				dom.setStyle(b, 'display', t.getStyle('display', 1));
				dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1);
			}
		}

		});
})();

/* file:jscripts/tiny_mce/classes/dom/Selection.js */

(function() {
	// Shorten names
	var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;

	tinymce.create('tinymce.dom.Selection', {
		Selection : function(dom, win, serializer) {
			var t = this;

			t.dom = dom;
			t.win = win;
			t.serializer = serializer;

			// Prevent leaks
			tinymce.addUnload(t.destroy, t);
		},

		getContent : function(s) {
			var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;

			s = s || {};
			wb = wa = '';
			s.get = true;
			s.format = s.format || 'html';

			if (s.format == 'text')
				return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));

			if (r.cloneContents) {
				n = r.cloneContents();

				if (n)
					e.appendChild(n);
			} else if (is(r.item) || is(r.htmlText))
				e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
			else
				e.innerHTML = r.toString();

			// Keep whitespace before and after
			if (/^\s/.test(e.innerHTML))
				wb = ' ';

			if (/\s+$/.test(e.innerHTML))
				wa = ' ';

			s.getInner = true;

			return t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;
		},

		setContent : function(h, s) {
			var t = this, r = t.getRng(), d;

			s = s || {format : 'html'};
			s.set = true;
			h = t.dom.processHTML(h);

			if (r.insertNode) {
				d = t.win.document;

				// Gecko has a bug where if you insert &nbsp; using InsertHTML it will insert a space instead
				// So we simply check if the input is HTML or text and then insert text using the insertNode method
				if (tinymce.isGecko && h.indexOf('<') == -1) {
					r.deleteContents();
					r.insertNode(t.getRng().createContextualFragment(h + '<span id="__caret">_</span>'));
					t.select(t.dom.get('__caret'));
					t.getRng().deleteContents();
					return;
				}

				// Use insert HTML if it exists (places cursor after content)
				try {
					// This might fail with an exception see bug #1893736
					if (d.queryCommandEnabled('InsertHTML'))
						return d.execCommand('InsertHTML', false, h);
				} catch (ex) {
					// Use old school method
					r.deleteContents();
					r.insertNode(t.getRng().createContextualFragment(h));
				}
			} else {
				if (r.item)
					r.item(0).outerHTML = h;
				else
					r.pasteHTML(h);
			}
		},

		getStart : function() {
			var t = this, r = t.getRng(), e;

			if (isIE) {
				if (r.item)
					return r.item(0);

				r = r.duplicate();
				r.collapse(1);
				e = r.parentElement();

				if (e.nodeName == 'BODY')
					return e.firstChild;

				return e;
			} else {
				e = r.startContainer;

				if (e.nodeName == 'BODY')
					return e.firstChild;

				return t.dom.getParent(e, function(n) {return n.nodeType == 1;});
			}
		},

		getEnd : function() {
			var t = this, r = t.getRng(), e;

			if (isIE) {
				if (r.item)
					return r.item(0);

				r = r.duplicate();
				r.collapse(0);
				e = r.parentElement();

				if (e.nodeName == 'BODY')
					return e.lastChild;

				return e;
			} else {
				e = r.endContainer;

				if (e.nodeName == 'BODY')
					return e.lastChild;

				return t.dom.getParent(e, function(n) {return n.nodeType == 1;});
			}
		},

		getBookmark : function(si) {
			var t = this, r = t.getRng(), tr, sx, sy, vp = t.dom.getViewPort(t.win), e, sp, bp, le, c = -0xFFFFFF, s, ro = t.dom.getRoot(), wb = 0, wa = 0, nv;
			sx = vp.x;
			sy = vp.y;

			// Simple bookmark fast but not as persistent
			if (si == 'simple')
				return {rng : r, scrollX : sx, scrollY : sy};

			// Handle IE
			if (isIE) {
				// Control selection
				if (r.item) {
					e = r.item(0);

					each(t.dom.select(e.nodeName), function(n, i) {
						if (e == n) {
							sp = i;
							return false;
						}
					});

					return {
						tag : e.nodeName,
						index : sp,
						scrollX : sx,
						scrollY : sy
					};
				}

				// Text selection
				tr = t.dom.doc.body.createTextRange();
				tr.moveToElementText(ro);
				tr.collapse(true);
				bp = Math.abs(tr.move('character', c));

				tr = r.duplicate();
				tr.collapse(true);
				sp = Math.abs(tr.move('character', c));

				tr = r.duplicate();
				tr.collapse(false);
				le = Math.abs(tr.move('character', c)) - sp;

				return {
					start : sp - bp,
					length : le,
					scrollX : sx,
					scrollY : sy
				};
			}

			// Handle W3C
			e = t.getNode();
			s = t.getSel();

			if (!s)
				return null;

			// Image selection
			if (e && e.nodeName == 'IMG') {
				return {
					scrollX : sx,
					scrollY : sy
				};
			}

			// Text selection

			function getPos(r, sn, en) {
				var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};

				while ((n = w.nextNode()) != null) {
					if (n == sn)
						d.start = p;

					if (n == en) {
						d.end = p;
						return d;
					}

					p += tinymce.trim(n.nodeValue || '').length;
				}

				return null;
			};

			// Caret or selection
			if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
				e = getPos(ro, s.anchorNode, s.focusNode);

				if (!e)
					return {scrollX : sx, scrollY : sy};

				// Count whitespace before
				(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});

				return {
					start : Math.max(e.start + s.anchorOffset - wb, 0),
					end : Math.max(e.end + s.focusOffset - wb, 0),
					scrollX : sx,
					scrollY : sy,
					beg : s.anchorOffset - wb == 0
				};
			} else {
				e = getPos(ro, r.startContainer, r.endContainer);

				// Count whitespace before start and end container
				(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
				(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});

				if (!e)
					return {scrollX : sx, scrollY : sy};

				return {
					start : Math.max(e.start + r.startOffset - wb, 0),
					end : Math.max(e.end + r.endOffset - wa, 0),
					scrollX : sx,
					scrollY : sy,
					beg : r.startOffset - wb == 0
				};
			}
		},

		moveToBookmark : function(b) {
			var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv;

			function getPos(r, sp, ep) {
				var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb;

				while ((n = w.nextNode()) != null) {
					wa = wb = 0;

					nv = n.nodeValue || '';
					nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});
					nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});

					nvl = tinymce.trim(nv).length;
					p += nvl;

					if (p >= sp && !d.startNode) {
						o = sp - (p - nvl);

						// Fix for odd quirk in FF
						if (b.beg && o >= nvl)
							continue;

						d.startNode = n;
						d.startOffset = o + wb;
					}

					if (p >= ep) {
						d.endNode = n;
						d.endOffset = ep - (p - nvl) + wb;
						return d;
					}
				}

				return null;
			};

			if (!b)
				return false;

			t.win.scrollTo(b.scrollX, b.scrollY);

			// Handle explorer
			if (isIE) {
				// Handle simple
				if (r = b.rng) {
					try {
						r.select();
					} catch (ex) {
						// Ignore
					}

					return true;
				}

				t.win.focus();

				// Handle control bookmark
				if (b.tag) {
					r = ro.createControlRange();

					each(t.dom.select(b.tag), function(n, i) {
						if (i == b.index)
							r.addElement(n);
					});
				} else {
					// Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
					try {
						// Incorrect bookmark
						if (b.start < 0)
							return true;

						r = s.createRange();
						r.moveToElementText(ro);
						r.collapse(true);
						r.moveStart('character', b.start);
						r.moveEnd('character', b.length);
					} catch (ex2) {
						return true;
					}
				}

				try {
					r.select();
				} catch (ex) {
					// Needed for some odd IE bug #1843306
				}

				return true;
			}

			// Handle W3C
			if (!s)
				return false;

			// Handle simple
			if (b.rng) {
				s.removeAllRanges();
				s.addRange(b.rng);
			} else {
				if (is(b.start) && is(b.end)) {
					try {
						sd = getPos(ro, b.start, b.end);

						if (sd) {
							r = t.dom.doc.createRange();
							r.setStart(sd.startNode, sd.startOffset);
							r.setEnd(sd.endNode, sd.endOffset);
							s.removeAllRanges();
							s.addRange(r);
						}

						if (!tinymce.isOpera)
							t.win.focus();
					} catch (ex) {
						// Ignore
					}
				}
			}
		},

		select : function(n, c) {
			var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;

			function first(n) {
				return n ? d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() : null;
			};

			function last(n) {
				var c, o, w;

				if (!n)
					return null;

				w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
				while (c = w.nextNode())
					o = c;

				return o;
			};

			if (isIE) {
				try {
					b = d.body;

					if (/^(IMG|TABLE)$/.test(n.nodeName)) {
						r = b.createControlRange();
						r.addElement(n);
					} else {
						r = b.createTextRange();
						r.moveToElementText(n);
					}

					r.select();
				} catch (ex) {
					// Throws illigal agrument in IE some times
				}
			} else {
				if (c) {
					fn = first(n);
					ln = last(n);

					if (fn && ln) {
						//console.debug(fn, ln);
						r = d.createRange();
						r.setStart(fn, 0);
						r.setEnd(ln, ln.nodeValue.length);
					} else
						r.selectNode(n);
				} else
					r.selectNode(n);

				t.setRng(r);
			}

			return n;
		},

		isCollapsed : function() {
			var t = this, r = t.getRng(), s = t.getSel();

			if (!r || r.item)
				return false;

			return !s || r.boundingWidth == 0 || s.isCollapsed;
		},

		collapse : function(b) {
			var t = this, r = t.getRng(), n;

			// Control range on IE
			if (r.item) {
				n = r.item(0);
				r = this.win.document.body.createTextRange();
				r.moveToElementText(n);
			}

			r.collapse(!!b);
			t.setRng(r);
		},

		getSel : function() {
			var t = this, w = this.win;

			return w.getSelection ? w.getSelection() : w.document.selection;
		},

		getRng : function() {
			var t = this, s = t.getSel(), r;

			try {
				if (s)
					r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());
			} catch (ex) {
				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
			}

			// No range found then create an empty one
			// This can occur when the editor is placed in a hidden container element on Gecko
			// Or on IE when there was an exception
			if (!r)
				r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();

			return r;
		},

		setRng : function(r) {
			var s;

			if (!isIE) {
				s = this.getSel();

				if (s) {
					s.removeAllRanges();
					s.addRange(r);
				}
			} else {
				try {
					r.select();
				} catch (ex) {
					// Needed for some odd IE bug #1843306
				}
			}
		},

		setNode : function(n) {
			var t = this;

			t.setContent(t.dom.getOuterHTML(n));

			return n;
		},

		getNode : function() {
			var t = this, r = t.getRng(), s = t.getSel(), e;

			if (!isIE) {
				// Range maybe lost after the editor is made visible again
				if (!r)
					return t.dom.getRoot();

				e = r.commonAncestorContainer;

				// Handle selection a image or other control like element such as anchors
				if (!r.collapsed) {
					if (r.startContainer == r.endContainer || (tinymce.isWebKit && r.startContainer == r.endContainer.parentNode)) {
						if (r.startOffset - r.endOffset < 2 || tinymce.isWebKit) {
							if (r.startContainer.hasChildNodes())
								e = r.startContainer.childNodes[r.startOffset];
						}
					}
				}

				return t.dom.getParent(e, function(n) {
					return n.nodeType == 1;
				});
			}

			return r.item ? r.item(0) : r.parentElement();
		},

		destroy : function(s) {
			var t = this;

			t.win = null;

			// Manual destroy then remove unload handler
			if (!s)
				tinymce.removeUnload(t.destroy);
		}

		});
})();

/* file:jscripts/tiny_mce/classes/dom/XMLWriter.js */

(function() {
	tinymce.create('tinymce.dom.XMLWriter', {
		node : null,

		XMLWriter : function(s) {
			// Get XML document
			function getXML() {
				var i = document.implementation;

				if (!i || !i.createDocument) {
					// Try IE objects
					try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}
					try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}
				} else
					return i.createDocument('', '', null);
			};

			this.doc = getXML();

			// Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers
			this.valid = tinymce.isOpera || tinymce.isWebKit;

			this.reset();
		},

		reset : function() {
			var t = this, d = t.doc;

			if (d.firstChild)
				d.removeChild(d.firstChild);

			t.node = d.appendChild(d.createElement("html"));
		},

		writeStartElement : function(n) {
			var t = this;

			t.node = t.node.appendChild(t.doc.createElement(n));
		},

		writeAttribute : function(n, v) {
			if (this.valid)
				v = v.replace(/>/g, '%MCGT%');

			this.node.setAttribute(n, v);
		},

		writeEndElement : function() {
			this.node = this.node.parentNode;
		},

		writeFullEndElement : function() {
			var t = this, n = t.node;

			n.appendChild(t.doc.createTextNode(""));
			t.node = n.parentNode;
		},

		writeText : function(v) {
			if (this.valid)
				v = v.replace(/>/g, '%MCGT%');

			this.node.appendChild(this.doc.createTextNode(v));
		},

		writeCDATA : function(v) {
			this.node.appendChild(this.doc.createCDATA(v));
		},

		writeComment : function(v) {
			this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));
		},

		getContent : function() {
			var h;

			h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);
			h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
			h = h.replace(/ ?\/>/g, ' />');

			if (this.valid)
				h = h.replace(/\%MCGT%/g, '&gt;');

			return h;
		}

		});
})();

/* file:jscripts/tiny_mce/classes/dom/StringWriter.js */

(function() {
	tinymce.create('tinymce.dom.StringWriter', {
		str : null,
		tags : null,
		count : 0,
		settings : null,
		indent : null,

		StringWriter : function(s) {
			this.settings = tinymce.extend({
				indent_char : ' ',
				indentation : 1
			}, s);

			this.reset();
		},

		reset : function() {
			this.indent = '';
			this.str = "";
			this.tags = [];
			this.count = 0;
		},

		writeStartElement : function(n) {
			this._writeAttributesEnd();
			this.writeRaw('<' + n);
			this.tags.push(n);
			this.inAttr = true;
			this.count++;
			this.elementCount = this.count;
		},

		writeAttribute : function(n, v) {
			var t = this;

			t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
		},

		writeEndElement : function() {
			var n;

			if (this.tags.length > 0) {
				n = this.tags.pop();

				if (this._writeAttributesEnd(1))
					this.writeRaw('</' + n + '>');

				if (this.settings.indentation > 0)
					this.writeRaw('\n');
			}
		},

		writeFullEndElement : function() {
			if (this.tags.length > 0) {
				this._writeAttributesEnd();
				this.writeRaw('</' + this.tags.pop() + '>');

				if (this.settings.indentation > 0)
					this.writeRaw('\n');
			}
		},

		writeText : function(v) {
			this._writeAttributesEnd();
			this.writeRaw(this.encode(v));
			this.count++;
		},

		writeCDATA : function(v) {
			this._writeAttributesEnd();
			this.writeRaw('<![CDATA[' + v + ']]>');
			this.count++;
		},

		writeComment : function(v) {
			this._writeAttributesEnd();
			this.writeRaw('<!-- ' + v + '-->');
			this.count++;
		},

		writeRaw : function(v) {
			this.str += v;
		},

		encode : function(s) {
			return s.replace(/[<>&"]/g, function(v) {
				switch (v) {
					case '<':
						return '&lt;';

					case '>':
						return '&gt;';

					case '&':
						return '&amp;';

					case '"':
						return '&quot;';
				}

				return v;
			});
		},

		getContent : function() {
			return this.str;
		},

		_writeAttributesEnd : function(s) {
			if (!this.inAttr)
				return;

			this.inAttr = false;

			if (s && this.elementCount == this.count) {
				this.writeRaw(' />');
				return false;
			}

			this.writeRaw('>');

			return true;
		}

		});
})();

/* file:jscripts/tiny_mce/classes/dom/Serializer.js */

(function() {
	// Shorten names
	var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;

	// Returns only attribites that have values not all attributes in IE
	function getIEAtts(n) {
		var o = [];

		// Object will throw exception in IE
		if (n.nodeName == 'OBJECT')
			return n.attributes;

		n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) {
			o.push({specified : 1, nodeName : b});
		});

		return o;
	};

	function wildcardToRE(s) {
		return s.replace(/([?+*])/g, '.$1');
	};

	tinymce.create('tinymce.dom.Serializer', {
		Serializer : function(s) {
			var t = this;

			t.key = 0;
			t.onPreProcess = new Dispatcher(t);
			t.onPostProcess = new Dispatcher(t);

			if (tinymce.relaxedDomain && tinymce.isGecko) {
				// Gecko has a bug where we can't create a new XML document if domain relaxing is used
				t.writer = new tinymce.dom.StringWriter();
			} else {
				try {
					t.writer = new tinymce.dom.XMLWriter();
				} catch (ex) {
					// IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter
					t.writer = new tinymce.dom.StringWriter();
				}
			}

			// Default settings
			t.settings = s = extend({
				dom : tinymce.DOM,
				valid_nodes : 0,
				node_filter : 0,
				attr_filter : 0,
				invalid_attrs : /^(mce_|_moz_)/,
				closed : /(br|hr|input|meta|img|link|param)/,
				entity_encoding : 'named',
				entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro',
				valid_elements : '*[*]',
				extended_valid_elements : 0,
				valid_child_elements : 0,
				invalid_elements : 0,
				fix_table_elements : 0,
				fix_list_elements : true,
				fix_content_duplication : true,
				convert_fonts_to_spans : false,
				font_size_classes : 0,
				font_size_style_values : 0,
				apply_source_formatting : 0,
				indent_mode : 'simple',
				indent_char : '\t',
				indent_levels : 1,
				remove_linebreaks : 1
			}, s);

			t.dom = s.dom;

			if (s.fix_list_elements) {
				t.onPreProcess.add(function(se, o) {
					var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;

					function prevNode(e, n) {
						var a = n.split(','), i;

						while ((e = e.previousSibling) != null) {
							for (i=0; i<a.length; i++) {
								if (e.nodeName == a[i])
									return e;
							}
						}

						return null;
					};

					for (x=0; x<a.length; x++) {
						nl = t.dom.select(a[x], o.node);

						for (i=0; i<nl.length; i++) {
							n = nl[i];
							p = n.parentNode;

							if (r.test(p.nodeName)) {
								np = prevNode(n, 'LI');

								if (!np) {
									np = t.dom.create('li');
									np.innerHTML = '&nbsp;';
									np.appendChild(n);
									p.insertBefore(np, p.firstChild);
								} else
									np.appendChild(n);
							}
						}
					}
				});
			}

			if (s.fix_table_elements) {
				t.onPreProcess.add(function(se, o) {
					each(t.dom.select('table', o.node), function(e) {
						var pa = t.dom.getParent(e, 'H1,H2,H3,H4,H5,H6,P'), pa2, n, tm, pl = [], i, ns;

						if (pa) {
							pa2 = pa.cloneNode(false);

							pl.push(e);
							for (n = e; n = n.parentNode;) {
								pl.push(n);

								if (n == pa)
									break;
							}

							tm = pa2;
							for (i = pl.length - 1; i >= 0; i--) {
								if (i == pl.length - 1) {
									while (ns = pl[i - 1].nextSibling)
										tm.appendChild(ns.parentNode.removeChild(ns));
								} else {
									n = pl[i].cloneNode(false);

									if (i != 0) {
										while (ns = pl[i - 1].nextSibling)
											n.appendChild(ns.parentNode.removeChild(ns));
									}

									tm = tm.appendChild(n);
								}
							}

							e = t.dom.insertAfter(e.parentNode.removeChild(e), pa);
							t.dom.insertAfter(e, pa);
							t.dom.insertAfter(pa2, e);
						}
					});
				});
			}
		},

		setEntities : function(s) {
			var t = this, a, i, l = {}, re = '', v;

			// No need to setup more than once
			if (t.entityLookup)
				return;

			// Build regex and lookup array
			a = s.split(',');
			for (i = 0; i < a.length; i += 2) {
				v = a[i];

				// Don't add default &amp; &quot; etc.
				if (v == 34 || v == 38 || v == 60 || v == 62)
					continue;

				l[String.fromCharCode(a[i])] = a[i + 1];

				v = parseInt(a[i]).toString(16);
				re += '\\u' + '0000'.substring(v.length) + v;
			}

			if (!re) {
				t.settings.entity_encoding = 'raw';
				return;
			}

			t.entitiesRE = new RegExp('[' + re + ']', 'g');
			t.entityLookup = l;
		},

		setValidChildRules : function(s) {
			this.childRules = null;
			this.addValidChildRules(s);
		},

		addValidChildRules : function(s) {
			var t = this, inst, intr, bloc;

			if (!s)
				return;

			inst = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment';
			intr = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment';
			bloc = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP';

			each(s.split(','), function(s) {
				var p = s.split(/\[|\]/), re;

				s = '';
				each(p[1].split('|'), function(v) {
					if (s)
						s += '|';

					switch (v) {
						case '%itrans':
							v = intr;
							break;

						case '%itrans_na':
							v = intr.substring(2);
							break;

						case '%istrict':
							v = inst;
							break;

						case '%istrict_na':
							v = inst.substring(2);
							break;

						case '%btrans':
							v = bloc;
							break;

						case '%bstrict':
							v = bloc;
							break;
					}

					s += v;
				});
				re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');

				each(p[0].split('/'), function(s) {
					t.childRules = t.childRules || {};
					t.childRules[s] = re;
				});
			});

			// Build regex
			s = '';
			each(t.childRules, function(v, k) {
				if (s)
					s += '|';

				s += k;
			});

			t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');

			/*console.debug(t.parentElementsRE.toString());
			each(t.childRules, function(v) {
				console.debug(v.toString());
			});*/
		},

		setRules : function(s) {
			var t = this;

			t._setup();
			t.rules = {};
			t.wildRules = [];
			t.validElements = {};

			return t.addRules(s);
		},

		addRules : function(s) {
			var t = this, dr;

			if (!s)
				return;

			t._setup();

			each(s.split(','), function(s) {
				var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];

				// Extend with default rules
				if (dr)
					at = tinymce.extend([], dr.attribs);

				// Parse attributes
				if (p.length > 1) {
					each(p[1].split('|'), function(s) {
						var ar = {}, i;

						at = at || [];

						// Parse attribute rule
						s = s.replace(/::/g, '~');
						s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);
						s[2] = s[2].replace(/~/g, ':');

						// Add required attributes
						if (s[1] == '!') {
							ra = ra || [];
							ra.push(s[2]);
						}

						// Remove inherited attributes
						if (s[1] == '-') {
							for (i = 0; i <at.length; i++) {
								if (at[i].name == s[2]) {
									at.splice(i, 1);
									return;
								}
							}
						}

						switch (s[3]) {
							// Add default attrib values
							case '=':
								ar.defaultVal = s[4] || '';
								break;

							// Add forced attrib values
							case ':':
								ar.forcedVal = s[4];
								break;

							// Add validation values
							case '<':
								ar.validVals = s[4].split('?');
								break;
						}

						if (/[*.?]/.test(s[2])) {
							wat = wat || [];
							ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
							wat.push(ar);
						} else {
							ar.name = s[2];
							at.push(ar);
						}

						va.push(s[2]);
					});
				}

				// Handle element names
				each(tn, function(s, i) {
					var pr = s.charAt(0), x = 1, ru = {};

					// Extend with default rule data
					if (dr) {
						if (dr.noEmpty)
							ru.noEmpty = dr.noEmpty;

						if (dr.fullEnd)
							ru.fullEnd = dr.fullEnd;

						if (dr.padd)
							ru.padd = dr.padd;
					}

					// Handle prefixes
					switch (pr) {
						case '-':
							ru.noEmpty = true;
							break;

						case '+':
							ru.fullEnd = true;
							break;

						case '#':
							ru.padd = true;
							break;

						default:
							x = 0;
					}

					tn[i] = s = s.substring(x);
					t.validElements[s] = 1;

					// Add element name or element regex
					if (/[*.?]/.test(tn[0])) {
						ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');
						t.wildRules = t.wildRules || {};
						t.wildRules.push(ru);
					} else {
						ru.name = tn[0];

						// Store away default rule
						if (tn[0] == '@')
							dr = ru;

						t.rules[s] = ru;
					}

					ru.attribs = at;

					if (ra)
						ru.requiredAttribs = ra;

					if (wat) {
						// Build valid attributes regexp
						s = '';
						each(va, function(v) {
							if (s)
								s += '|';

							s += '(' + wildcardToRE(v) + ')';
						});
						ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
						ru.wildAttribs = wat;
					}
				});
			});

			// Build valid elements regexp
			s = '';
			each(t.validElements, function(v, k) {
				if (s)
					s += '|';

				if (k != '@')
					s += k;
			});
			t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');

			//console.debug(t.validElementsRE.toString());
			//console.dir(t.rules);
			//console.dir(t.wildRules);
		},

		findRule : function(n) {
			var t = this, rl = t.rules, i, r;

			t._setup();

			// Exact match
			r = rl[n];
			if (r)
				return r;

			// Try wildcards
			rl = t.wildRules;
			for (i = 0; i < rl.length; i++) {
				if (rl[i].nameRE.test(n))
					return rl[i];
			}

			return null;
		},

		findAttribRule : function(ru, n) {
			var i, wa = ru.wildAttribs;

			for (i = 0; i < wa.length; i++) {
				if (wa[i].nameRE.test(n))
					return wa[i];
			}

			return null;
		},

		serialize : function(n, o) {
			var h, t = this;

			t._setup();
			o = o || {};
			o.format = o.format || 'html';
			t.processObj = o;
			n = n.cloneNode(true);
			t.key = '' + (parseInt(t.key) + 1);

			// Pre process
			if (!o.no_events) {
				o.node = n;
				t.onPreProcess.dispatch(t, o);
			}

			// Serialize HTML DOM into a string
			t.writer.reset();
			t._serializeNode(n, o.getInner);

			// Post process
			o.content = t.writer.getContent();

			if (!o.no_events)
				t.onPostProcess.dispatch(t, o);

			t._postProcess(o);
			o.node = null;

			return tinymce.trim(o.content);
		},

		// Internal functions

		_postProcess : function(o) {
			var t = this, s = t.settings, h = o.content, sc = [], p;

			if (o.format == 'html') {
				// Protect some elements
				p = t._protect({
					content : h,
					patterns : [
						{pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
						{pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
						{pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1}
					]
				});

				h = p.content;

				// Entity encode
				if (s.entity_encoding !== 'raw')
					h = t._encode(h);

				// Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor
/*				if (o.set)
					h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
				else
					h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/

				// Since Gecko and Safari keeps whitespace in the DOM we need to
				// remove it inorder to match other browsers. But I think Gecko and Safari is right.
				// This process is only done when getting contents out from the editor.
				if (!o.set) {
					// We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char
					h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');

					if (s.remove_linebreaks) {
						h = h.replace(/\r?\n|\r/g, ' ');
						h = h.replace(/(<[^>]+>)\s+/g, '$1 ');
						h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');
						h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>'); // Trim block start
						h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>'); // Trim block start
						h = h.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '</$1>'); // Trim block end
					}

					// Simple indentation
					if (s.apply_source_formatting && s.indent_mode == 'simple') {
						// Add line breaks before and after block elements
						h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');
						h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');
						h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');
						h = h.replace(/\n\n/g, '\n');
					}
				}

				h = t._unprotect(h, p);

				// Restore the \u00a0 character if raw mode is enabled
				if (s.entity_encoding == 'raw')
					h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');
			}

			o.content = h;
		},

		_serializeNode : function(n, inn) {
			var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv;

			if (!s.node_filter || s.node_filter(n)) {
				switch (n.nodeType) {
					case 1: // Element
						if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
							return;

						iv = false;
						hc = n.hasChildNodes();
						nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();

						// Add correct prefix on IE
						if (isIE) {
							if (n.scopeName !== 'HTML' && n.scopeName !== 'html')
								nn = n.scopeName + ':' + nn;
						}

						// Remove mce prefix on IE needed for the abbr element
						if (nn.indexOf('mce:') === 0)
							nn = nn.substring(4);

						// Check if valid
						if (!t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
							iv = true;
							break;
						}

						if (isIE) {
							// Fix IE content duplication (DOM can have multiple copies of the same node)
							if (s.fix_content_duplication) {
								if (n.mce_serialized == t.key)
									return;

								n.mce_serialized = t.key;
							}

							// IE sometimes adds a / infront of the node name
							if (nn.charAt(0) == '/')
								nn = nn.substring(1);
						} else if (isGecko) {
							// Ignore br elements
							if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')
								return;
						}

						// Check if valid child
						if (t.childRules) {
							if (t.parentElementsRE.test(t.elementName)) {
								if (!t.childRules[t.elementName].test(nn)) {
									iv = true;
									break;
								}
							}

							t.elementName = nn;
						}

						ru = t.findRule(nn);
						nn = ru.name || nn;

						// Skip empty nodes or empty node name in IE
						if ((!hc && ru.noEmpty) || (isIE && !nn)) {
							iv = true;
							break;
						}

						// Check required
						if (ru.requiredAttribs) {
							a = ru.requiredAttribs;

							for (i = a.length - 1; i >= 0; i--) {
								if (this.dom.getAttrib(n, a[i]) !== '')
									break;
							}

							// None of the required was there
							if (i == -1) {
								iv = true;
								break;
							}
						}

						w.writeStartElement(nn);

						// Add ordered attributes
						if (ru.attribs) {
							for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
								a = at[i];
								v = t._getAttrib(n, a);

								if (v !== null)
									w.writeAttribute(a.name, v);
							}
						}

						// Add wild attributes
						if (ru.validAttribsRE) {
							at = isIE ? getIEAtts(n) : n.attributes;
							for (i=at.length-1; i>-1; i--) {
								no = at[i];

								if (no.specified) {
									a = no.nodeName.toLowerCase();

									if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
										continue;

									ar = t.findAttribRule(ru, a);
                                    if(ar !== null){
                                        v = t._getAttrib(n, ar, a);

                                        if (v !== null)
                                            w.writeAttribute(a, v);
                                    }
                                }
							}
						}

						// Padd empty nodes with a &nbsp;
						if (!hc && ru.padd)
							w.writeText('\u00a0');

						break;

					case 3: // Text
						// Check if valid child
						if (t.childRules && t.parentElementsRE.test(t.elementName)) {
							if (!t.childRules[t.elementName].test(n.nodeName))
								return;
						}

						return w.writeText(n.nodeValue);

					case 4: // CDATA
						return w.writeCDATA(n.nodeValue);

					case 8: // Comment
						return w.writeComment(n.nodeValue);
				}
			} else if (n.nodeType == 1)
				hc = n.hasChildNodes();

			if (hc) {
				cn = n.firstChild;

				while (cn) {
					t._serializeNode(cn);
					t.elementName = nn;
					cn = cn.nextSibling;
				}
			}

			// Write element end
			if (!iv) {
				if (hc || !s.closed.test(nn))
					w.writeFullEndElement();
				else
					w.writeEndElement();
			}
		},

		_protect : function(o) {
			var t = this;

			o.items = o.items || [];

			function enc(s) {
				return s.replace(/[\r\n\\]/g, function(c) {
					if (c === '\n')
						return '\\n';
					else if (c === '\\')
						return '\\\\';

					return '\\r';
				});
			};

			function dec(s) {
				return s.replace(/\\[\\rn]/g, function(c) {
					if (c === '\\n')
						return '\n';
					else if (c === '\\\\')
						return '\\';

					return '\r';
				});
			};

			each(o.patterns, function(p) {
				o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
					b = dec(b);

					if (p.encode)
						b = t._encode(b);

					o.items.push(b);
					return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
				}));
			});

			return o;
		},

		_unprotect : function(h, o) {
			h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
				return o.items[parseInt(b)];
			});

			o.items = [];

			return h;
		},

		_encode : function(h) {
			var t = this, s = t.settings, l;

			// Entity encode
			if (s.entity_encoding !== 'raw') {
				if (s.entity_encoding.indexOf('named') != -1) {
					t.setEntities(s.entities);
					l = t.entityLookup;

					h = h.replace(t.entitiesRE, function(a) {
						var v;

						if (v = l[a])
							a = '&' + v + ';';

						return a;
					});
				}

				if (s.entity_encoding.indexOf('numeric') != -1) {
					h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
						return '&#' + a.charCodeAt(0) + ';';
					});
				}
			}

			return h;
		},

		_setup : function() {
			var t = this, s = this.settings;

			if (t.done)
				return;

			t.done = 1;

			t.setRules(s.valid_elements);
			t.addRules(s.extended_valid_elements);
			t.addValidChildRules(s.valid_child_elements);

			if (s.invalid_elements)
				t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(',', '|').toLowerCase()) + ')$');

			if (s.attrib_value_filter)
				t.attribValueFilter = s.attribValueFilter;
		},

		_getAttrib : function(n, a, na) {
			var i, v;

			na = na || a.name;

			if (a.forcedVal && (v = a.forcedVal)) {
				if (v === '{$uid}')
					return this.dom.uniqueId();

				return v;
			}

			v = this.dom.getAttrib(n, na);

			switch (na) {
				case 'rowspan':
				case 'colspan':
					// Whats the point? Remove usless attribute value
					if (v == '1')
						v = '';

					break;
			}

			if (this.attribValueFilter)
				v = this.attribValueFilter(na, v, n);

			if (a.validVals) {
				for (i = a.validVals.length - 1; i >= 0; i--) {
					if (v == a.validVals[i])
						break;
				}

				if (i == -1)
					return null;
			}

			if (v === '' && typeof(a.defaultVal) != 'undefined') {
				v = a.defaultVal;

				if (v === '{$uid}')
					return this.dom.uniqueId();

				return v;
			} else {
				// Remove internal mceItemXX classes when content is extracted from editor
				if (na == 'class' && this.processObj.get)
					v = v.replace(/\s?mceItem\w+\s?/g, '');
			}

			if (v === '')
				return null;


			return v;
		}

		});
})();

/* file:jscripts/tiny_mce/classes/dom/ScriptLoader.js */

(function() {
	var each = tinymce.each;

	tinymce.create('tinymce.dom.ScriptLoader', {
		ScriptLoader : function(s) {
			this.settings = s || {};
			this.queue = [];
			this.lookup = {};
		},

		isDone : function(u) {
			return this.lookup[u] ? this.lookup[u].state == 2 : 0;
		},

		markDone : function(u) {
			this.lookup[u] = {state : 2, url : u};
		},

		add : function(u, cb, s, pr) {
			var t = this, lo = t.lookup, o;

			if (o = lo[u]) {
				// Is loaded fire callback
				if (cb && o.state == 2)
					cb.call(s || this);

				return o;
			}

			o = {state : 0, url : u, func : cb, scope : s || this};

			if (pr)
				t.queue.unshift(o);
			else
				t.queue.push(o);

			lo[u] = o;

			return o;
		},

		load : function(u, cb, s) {
			var t = this, o;

			if (o = t.lookup[u]) {
				// Is loaded fire callback
				if (cb && o.state == 2)
					cb.call(s || t);

				return o;
			}

			function loadScript(u) {
				if (tinymce.dom.Event.domLoaded || t.settings.strict_mode) {
					tinymce.util.XHR.send({
						url : tinymce._addVer(u),
						error : t.settings.error,
						async : false,
						success : function(co) {
							t.eval(co);
						}
					});
				} else
					document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');
			};

            if (!tinymce.is(u, 'string')) {
				each(u, function(u) {
					loadScript(u);
				});

				if (cb)
					cb.call(s || t);
			} else {
				loadScript(u);

				if (cb)
					cb.call(s || t);
			}
		},

		loadQueue : function(cb, s) {
			var t = this;

			if (!t.queueLoading) {
				t.queueLoading = 1;
				t.queueCallbacks = [];

				t.loadScripts(t.queue, function() {
					t.queueLoading = 0;

					if (cb)
						cb.call(s || t);

					each(t.queueCallbacks, function(o) {
						o.func.call(o.scope);
					});
				});
			} else if (cb)
				t.queueCallbacks.push({func : cb, scope : s || t});
		},

		eval : function(co) {
			var w = window;

			// Evaluate script
			if (!w.execScript) {
				try {
					eval.call(w, co);
				} catch (ex) {
					eval(co, w); // Firefox 3.0a8
				}
			} else
				w.execScript(co); // IE
		},

		loadScripts : function(sc, cb, s) {
			var t = this, lo = t.lookup;

			function done(o) {
				o.state = 2; // Has been loaded

				// Run callback
				if (o.func)
					o.func.call(o.scope || t);
			};

			function allDone() {
				var l;

				// Check if all files are loaded
				l = sc.length;
				each(sc, function(o) {
					o = lo[o.url];

					if (o.state === 2) {// It has finished loading
						done(o);
						l--;
					} else
						load(o);
				});

				// They are all loaded
				if (l === 0 && cb) {
					cb.call(s || t);
					cb = 0;
				}
			};

			function load(o) {
				if (o.state > 0)
					return;

				o.state = 1; // Is loading

				tinymce.util.XHR.send({
					url : o.url,
					error : t.settings.error,
					success : function(co) {
						t.eval(co);
						done(o);
						allDone();
					}
				});
			};

			each(sc, function(o) {
				var u = o.url;

				// Add to queue if needed
				if (!lo[u]) {
					lo[u] = o;
					t.queue.push(o);
				} else
					o = lo[u];

				// Is already loading or has been loaded
				if (o.state > 0)
					return;

				if (!tinymce.dom.Event.domLoaded && !t.settings.strict_mode) {
					var ix, ol = '';

					// Add onload events
					if (cb || o.func) {
						o.state = 1; // Is loading

						ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
							done(o);
							allDone();
						});

						if (tinymce.isIE)
							ol = ' onreadystatechange="';
						else
							ol = ' onload="';

						ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';
					}

					document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');

					if (!o.func)
						done(o);
				} else
					load(o);
			});

			allDone();
		},

		// Static methods
		'static' : {
			_addOnLoad : function(f) {
				var t = this;

				t._funcs = t._funcs || [];
				t._funcs.push(f);

				return t._funcs.length - 1;
			},

			_onLoad : function(e, u, ix) {
				if (!tinymce.isIE || e.readyState == 'complete')
					this._funcs[ix].call(this);
			}
		}

		});

	// Global script loader
	tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
})();

/* file:jscripts/tiny_mce/classes/ui/Control.js */

(function() {
	// Shorten class names
	var DOM = tinymce.DOM, is = tinymce.is;

	tinymce.create('tinymce.ui.Control', {
		Control : function(id, s) {
			this.id = id;
			this.settings = s = s || {};
			this.rendered = false;
			this.onRender = new tinymce.util.Dispatcher(this);
			this.classPrefix = '';
			this.scope = s.scope || this;
			this.disabled = 0;
			this.active = 0;
		},

		setDisabled : function(s) {
			var e;

			if (s != this.disabled) {
				e = DOM.get(this.id);

				// Add accessibility title for unavailable actions
				if (e && this.settings.unavailable_prefix) {
					if (s) {
						this.prevTitle = e.title;
						e.title = this.settings.unavailable_prefix + ": " + e.title;
					} else
						e.title = this.prevTitle;
				}

				this.setState('Disabled', s);
				this.setState('Enabled', !s);
				this.disabled = s;
			}
		},

		isDisabled : function() {
			return this.disabled;
		},

		setActive : function(s) {
			if (s != this.active) {
				this.setState('Active', s);
				this.active = s;
			}
		},

		isActive : function() {
			return this.active;
		},

		setState : function(c, s) {
			var n = DOM.get(this.id);

			c = this.classPrefix + c;

			if (s)
				DOM.addClass(n, c);
			else
				DOM.removeClass(n, c);
		},

		isRendered : function() {
			return this.rendered;
		},

		renderHTML : function() {
		},

		renderTo : function(n) {
			DOM.setHTML(n, this.renderHTML());
		},

		postRender : function() {
			var t = this, b;

			// Set pending states
			if (is(t.disabled)) {
				b = t.disabled;
				t.disabled = -1;
				t.setDisabled(b);
			}

			if (is(t.active)) {
				b = t.active;
				t.active = -1;
				t.setActive(b);
			}
		},

		remove : function() {
			DOM.remove(this.id);
			this.destroy();
		},

		destroy : function() {
			tinymce.dom.Event.clear(this.id);
		}

		});
})();
/* file:jscripts/tiny_mce/classes/ui/Container.js */

tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
	Container : function(id, s) {
		this.parent(id, s);
		this.controls = [];
		this.lookup = {};
	},

	add : function(c) {
		this.lookup[c.id] = c;
		this.controls.push(c);

		return c;
	},

	get : function(n) {
		return this.lookup[n];
	}

	});


/* file:jscripts/tiny_mce/classes/ui/Separator.js */

tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
	Separator : function(id, s) {
		this.parent(id, s);
		this.classPrefix = 'mceSeparator';
	},

	renderHTML : function() {
		return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});
	}

	});

/* file:jscripts/tiny_mce/classes/ui/MenuItem.js */

(function() {
	var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;

	tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {
		MenuItem : function(id, s) {
			this.parent(id, s);
			this.classPrefix = 'mceMenuItem';
		},

		setSelected : function(s) {
			this.setState('Selected', s);
			this.selected = s;
		},

		isSelected : function() {
			return this.selected;
		},

		postRender : function() {
			var t = this;

			t.parent();

			// Set pending state
			if (is(t.selected))
				t.setSelected(t.selected);
		}

		});
})();

/* file:jscripts/tiny_mce/classes/ui/Menu.js */

(function() {
	var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;

	tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {
		Menu : function(id, s) {
			var t = this;

			t.parent(id, s);
			t.items = {};
			t.collapsed = false;
			t.menuCount = 0;
			t.onAddItem = new tinymce.util.Dispatcher(this);
		},

		expand : function(d) {
			var t = this;

			if (d) {
				walk(t, function(o) {
					if (o.expand)
						o.expand();
				}, 'items', t);
			}

			t.collapsed = false;
		},

		collapse : function(d) {
			var t = this;

			if (d) {
				walk(t, function(o) {
					if (o.collapse)
						o.collapse();
				}, 'items', t);
			}

			t.collapsed = true;
		},

		isCollapsed : function() {
			return this.collapsed;
		},

		add : function(o) {
			if (!o.settings)
				o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);

			this.onAddItem.dispatch(this, o);

			return this.items[o.id] = o;
		},

		addSeparator : function() {
			return this.add({separator : true});
		},

		addMenu : function(o) {
			if (!o.collapse)
				o = this.createMenu(o);

			this.menuCount++;

			return this.add(o);
		},

		hasMenus : function() {
			return this.menuCount !== 0;
		},

		remove : function(o) {
			delete this.items[o.id];
		},

		removeAll : function() {
			var t = this;

			walk(t, function(o) {
				if (o.removeAll)
					o.removeAll();
				else
					o.remove();

				o.destroy();
			}, 'items', t);

			t.items = {};
		},

		createMenu : function(o) {
			var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);

			m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);

			return m;
		}

		});
})();
/* file:jscripts/tiny_mce/classes/ui/DropMenu.js */

(function() {
	var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;

	tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {
		DropMenu : function(id, s) {
			s = s || {};
			s.container = s.container || DOM.doc.body;
			s.offset_x = s.offset_x || 0;
			s.offset_y = s.offset_y || 0;
			s.vp_offset_x = s.vp_offset_x || 0;
			s.vp_offset_y = s.vp_offset_y || 0;

			if (is(s.icons) && !s.icons)
				s['class'] += ' mceNoIcons';

			this.parent(id, s);
			this.onShowMenu = new tinymce.util.Dispatcher(this);
			this.onHideMenu = new tinymce.util.Dispatcher(this);
			this.classPrefix = 'mceMenu';

			// Fix for odd IE bug: #1903622
			this.fixIE = tinymce.isIE && DOM.win.top != DOM.win;
		},

		createMenu : function(s) {
			var t = this, cs = t.settings, m;

			s.container = s.container || cs.container;
			s.parent = t;
			s.constrain = s.constrain || cs.constrain;
			s['class'] = s['class'] || cs['class'];

            s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
			s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
			m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);

			m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);

			return m;
		},

		update : function() {
			var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;

			tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
			th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;

			if (!DOM.boxModel)
				t.element.setStyles({width : tw + 2, height : th + 2});
			else
				t.element.setStyles({width : tw, height : th});

			if (s.max_width)
				DOM.setStyle(co, 'width', tw);

            if (s.max_height) {
				DOM.setStyle(co, 'height', th);

                // start edits
                // https://brewspace.jiveland.com/docs/DOC-10757
				if (tb.clientHeight < s.max_height)
					DOM.setStyle(co, 'overflow', 'hidden');
                // end edits
			}
		},

		showMenu : function(x, y, px) {
			var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;

			t.collapse(1);

			if (t.isMenuVisible)
				return;

			if (!t.rendered) {
				co = DOM.add(t.settings.container, t.renderNode());

				each(t.items, function(o) {
					o.postRender();
				});

				t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
			} else
				co = DOM.get('menu_' + t.id);

			// Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug
			if (!tinymce.isOpera)
				DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});

			DOM.show(co);
			t.update();

			x += s.offset_x || 0;
			y += s.offset_y || 0;
			vp.w -= 4;
			vp.h -= 4;

            // start new code
            // https://brewspace.jiveland.com/docs/DOC-10757
            w = co.clientWidth - ot;
            h = co.clientHeight - ot;

            if(x+w > vp.w){
                x = vp.w - w - 40;
            }
            // end new code

            // Move inside viewport if not submenu
			if (s.constrain) {
                w = co.clientWidth - ot;
                h = co.clientHeight - ot;
				mx = vp.x + vp.w;
				my = vp.y + vp.h;

				if ((x + s.vp_offset_x + w) > mx)
					x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);

				if ((y + s.vp_offset_y + h) > my)
					y = Math.max(0, (my - s.vp_offset_y) - h);
			}

			DOM.setStyles(co, {left : x , top : y});
			t.element.update();

			t.isMenuVisible = 1;
            Event.add(t.id + '_menu', 'mouseup', function(evt) {
                tinymce.dom.Event.cancel(evt);
            });

            Event.add(t.id + '_menu', t.fixIE ? 'click' : 'mousedown' , function(evt) {
                tinymce.dom.Event.cancel(evt);
            });
			t.mouseClickFunc = Event.add(co, t.fixIE ? 'mousedown' : 'click', function(evt) {
				var m;
                tinymce.dom.Event.cancel(evt);

				e = evt.target;

				if (e && (e = DOM.getParent(e, 'TR')) && !DOM.hasClass(e, cp + 'ItemSub')) {
					m = t.items[e.id];

					if (m.isDisabled())
						return;

					dm = t;

					// Wait a while to fix IE bug where it looses the selection if the user clicks on a menu
					// item when the editor is placed within an frame or iframe
					DOM.win.setTimeout(function() {
						while (dm) {
							if (dm.hideMenu)
								dm.hideMenu();

							dm = dm.settings.parent;
						}
					}, 0);

					if (m.settings.onclick)
						m.settings.onclick(e);

					return Event.cancel(e); // Cancel to fix onbeforeunload problem
				}
			});

			if (t.hasMenus()) {
				t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
					var m, r, mi;

					e = e.target;
					if (e && (e = DOM.getParent(e, 'TR'))) {
						m = t.items[e.id];

						if (t.lastMenu)
							t.lastMenu.collapse(1);

						if (m.isDisabled())
							return;

						if (e && DOM.hasClass(e, cp + 'ItemSub')) {
							//p = DOM.getPos(s.container);
							r = DOM.getRect(e);
							m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
							t.lastMenu = m;
							DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');
						}
					}
				});
			}

			t.onShowMenu.dispatch(t);

			if (s.keyboard_focus) {
				Event.add(co, 'keydown', t._keyHandler, t);
				DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link
			}
		},

		hideMenu : function(c) {
			var t = this, co = DOM.get('menu_' + t.id), e;

			if (!t.isMenuVisible)
				return;

			Event.remove(co, 'mouseover', t.mouseOverFunc);
			Event.remove(co, t.fixIE ? 'mousedown' : 'click', t.mouseClickFunc);
			Event.remove(co, 'keydown', t._keyHandler);
			DOM.hide(co);
			t.isMenuVisible = 0;

			if (!c)
				t.collapse(1);

			if (t.element)
				t.element.hide();

			if (e = DOM.get(t.id))
				DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');

			t.onHideMenu.dispatch(t);
		},

		add : function(o) {
			var t = this, co;

			o = t.parent(o);

			if (t.isRendered && (co = DOM.get('menu_' + t.id)))
				t._add(DOM.select('tbody', co)[0], o);

			return o;
		},

		collapse : function(d) {
			this.parent(d);
			this.hideMenu(1);
		},

		remove : function(o) {
			DOM.remove(o.id);
			this.destroy();

			return this.parent(o);
		},

		destroy : function() {
			var t = this, co = DOM.get('menu_' + t.id);

			Event.remove(co, 'mouseover', t.mouseOverFunc);
			Event.remove(co, 'click', t.mouseClickFunc);

			if (t.element)
				t.element.remove();

			DOM.remove(co);
		},

		renderNode : function() {
			var t = this, s = t.settings, n, tb, co, w;

			w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});
			co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});
			t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});

			if (s.menu_line)
				DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});

//			n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
			n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});
			tb = DOM.add(n, 'tbody');

			each(t.items, function(o) {
				t._add(tb, o);
			});

			t.rendered = true;

			return w;
		},

		// Internal functions

		_keyHandler : function(e) {
			// Accessibility feature
			if (e.keyCode == 27)
				this.hideMenu();
		},

		_add : function(tb, o) {
			var n, s = o.settings, a, ro, it, cp = this.classPrefix;

			if (s.separator) {
				ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});
				DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});

				if (n = ro.previousSibling)
					DOM.addClass(n, 'mceLast');

				return;
			}

			n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});
			n = it = DOM.add(n, 'td');
			n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});

			DOM.addClass(it, s['class']);
//			n = DOM.add(n, 'span', {'class' : 'item'});
			DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});
			n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);

			if (o.settings.style)
				DOM.setAttrib(n, 'style', o.settings.style);

			if (tb.childNodes.length == 1)
				DOM.addClass(ro, 'mceFirst');

			if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))
				DOM.addClass(ro, 'mceFirst');

			if (o.collapse)
				DOM.addClass(ro, cp + 'ItemSub');

			if (n = ro.previousSibling)
				DOM.removeClass(n, 'mceLast');

			DOM.addClass(ro, 'mceLast');
		}

		});
})();
/* file:jscripts/tiny_mce/classes/ui/Button.js */

(function() {
	var DOM = tinymce.DOM;

	tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
		Button : function(id, s) {
			this.parent(id, s);
			this.classPrefix = 'mceButton';
		},

		renderHTML : function() {
			var cp = this.classPrefix, s = this.settings, h, l;

			l = DOM.encode(s.label || '');
			h = '<a id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" title="' + DOM.encode(s.title) + '">';

			if (s.image)
				h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';
			else
				h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';

			return h;
		},

		postRender : function() {
			var t = this, s = t.settings;

			tinymce.dom.Event.add(t.id, 'click', function(e) {
				if (!t.isDisabled())
					return s.onclick.call(s.scope, e);
			});
		}

		});
})();

/* file:jscripts/tiny_mce/classes/ui/ListBox.js */

(function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;

	tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
		ListBox : function(id, s) {
			var t = this;

			t.parent(id, s);
			t.items = [];
			t.onChange = new Dispatcher(t);
			t.onPostRender = new Dispatcher(t);
			t.onAdd = new Dispatcher(t);
			t.onRenderMenu = new tinymce.util.Dispatcher(this);
			t.classPrefix = 'mceListBox';
		},

		select : function(v) {
			var t = this, e, fv;

			// Do we need to do something?
			if (v != t.selectedValue) {
				e = DOM.get(t.id + '_text');
				t.selectedValue = v;

				// Find item
				each(t.items, function(o) {
					if (o.value == v) {
						DOM.setHTML(e, DOM.encode(o.title));
						fv = 1;
						return false;
					}
				});

				// If no item was found then present title
				if (!fv) {
					DOM.setHTML(e, DOM.encode(t.settings.title));
					DOM.addClass(e, 'mceTitle');
					e = 0;
					return;
				} else
					DOM.removeClass(e, 'mceTitle');
			}

			e = 0;
		},

		add : function(n, v, o) {
			var t = this;

			o = o || {};
			o = tinymce.extend(o, {
				title : n,
				value : v
			});

			t.items.push(o);
			t.onAdd.dispatch(t, o);
		},

		getLength : function() {
			return this.items.length;
		},

		renderHTML : function() {
			var h = '', t = this, s = t.settings, cp = t.classPrefix;

			h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
			h += '<td>' + DOM.createHTML('a', {id : t.id + '_text', href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';
			h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';
			h += '</tr></tbody></table>';

			return h;
		},

		showMenu : function() {
			var t = this, p1, p2, e = DOM.get(this.id), m;

			if (t.isDisabled() || t.items.length == 0)
				return;

			if (t.menu && t.menu.isMenuVisible)
				return t.hideMenu();

			if (!t.isMenuRendered) {
				t.renderMenu();
				t.isMenuRendered = true;
			}

			p1 = DOM.getPos(this.settings.menu_container);
			p2 = DOM.getPos(e);

			m = t.menu;
			m.settings.offset_x = p2.x;
			m.settings.offset_y = p2.y;
			m.settings.keyboard_focus = t._focused;

			// Select in menu
			if (t.oldID)
				m.items[t.oldID].setSelected(0);

			each(t.items, function(o) {
				if (o.value === t.selectedValue) {
					m.items[o.id].setSelected(1);
					t.oldID = o.id;
				}
			});

			m.showMenu(0, e.clientHeight);

			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
			DOM.addClass(t.id, t.classPrefix + 'Selected');
		},

		hideMenu : function(e) {
			var t = this;

			// Prevent double toogles by canceling the mouse click event to the button
			if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
				return;

			if (!e || !DOM.getParent(e.target, function(n) {return DOM.hasClass(n, 'mceMenu');})) {
				DOM.removeClass(t.id, t.classPrefix + 'Selected');
				Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);

				if (t.menu)
					t.menu.hideMenu();
			}
		},

		renderMenu : function() {
			var t = this, m;

			m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
				menu_line : 1,
				'class' : t.classPrefix + 'Menu mceNoIcons',
				max_width : 150,
				max_height : 150
			});

			m.onHideMenu.add(t.hideMenu, t);

			m.add({
				title : t.settings.title,
				'class' : 'mceMenuItemTitle'
			}).setDisabled(1);

			each(t.items, function(o) {
				o.id = DOM.uniqueId();
				o.onclick = function() {
					if (t.settings.onselect(o.value) !== false)
						t.select(o.value); // Must be runned after
				};

				m.add(o);
			});

			t.onRenderMenu.dispatch(t, m);
			t.menu = m;
		},

		postRender : function() {
			var t = this, cp = t.classPrefix;

			Event.add(t.id, 'click', t.showMenu, t);
			Event.add(t.id + '_text', 'focus', function() {t._focused = 1;});
			Event.add(t.id + '_text', 'blur', function() {t._focused = 0;});

			// Old IE doesn't have hover on all elements
			if (tinymce.isIE6 || !DOM.boxModel) {
				Event.add(t.id, 'mouseover', function() {
					if (!DOM.hasClass(t.id, cp + 'Disabled'))
						DOM.addClass(t.id, cp + 'Hover');
				});

				Event.add(t.id, 'mouseout', function() {
					if (!DOM.hasClass(t.id, cp + 'Disabled'))
						DOM.removeClass(t.id, cp + 'Hover');
				});
			}

			t.onPostRender.dispatch(t, DOM.get(t.id));
		},

		destroy : function() {
			this.parent();

			Event.clear(this.id + '_text');
		}

		});
})();
/* file:jscripts/tiny_mce/classes/ui/NativeListBox.js */

(function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;

	tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
		NativeListBox : function(id, s) {
			this.parent(id, s);
			this.classPrefix = 'mceNativeListBox';
		},

		setDisabled : function(s) {
			DOM.get(this.id).disabled = s;
		},

		isDisabled : function() {
			return DOM.get(this.id).disabled;
		},

		select : function(v) {
			var e = DOM.get(this.id), ol = e.options;

			v = '' + (v || '');

			e.selectedIndex = 0;
			each(ol, function(o, i) {
				if (o.value == v) {
					e.selectedIndex = i;
					return false;
				}
			});
		},

		add : function(n, v, a) {
			var o, t = this;

			a = a || {};
			a.value = v;

			if (t.isRendered())
				DOM.add(DOM.get(this.id), 'option', a, n);

			o = {
				title : n,
				value : v,
				attribs : a
			};

			t.items.push(o);
			t.onAdd.dispatch(t, o);
		},

		getLength : function() {
			return DOM.get(this.id).options.length - 1;
		},

		renderHTML : function() {
			var h, t = this;

			h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');

			each(t.items, function(it) {
				h += DOM.createHTML('option', {value : it.value}, it.title);
			});

			h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);

			return h;
		},

		postRender : function() {
			var t = this, ch;

			t.rendered = true;

			function onChange(e) {
				var v = e.target.options[e.target.selectedIndex].value;

				t.onChange.dispatch(t, v);

				if (t.settings.onselect)
					t.settings.onselect(v);
			};

			Event.add(t.id, 'change', onChange);

			// Accessibility keyhandler
			Event.add(t.id, 'keydown', function(e) {
				var bf;

				Event.remove(t.id, 'change', ch);

				bf = Event.add(t.id, 'blur', function() {
					Event.add(t.id, 'change', onChange);
					Event.remove(t.id, 'blur', bf);
				});

				if (e.keyCode == 13 || e.keyCode == 32) {
					onChange(e);
					return Event.cancel(e);
				}
			});

			t.onPostRender.dispatch(t, DOM.get(t.id));
		}

		});
})();
/* file:jscripts/tiny_mce/classes/ui/MenuButton.js */

(function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;

	tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
		MenuButton : function(id, s) {
			this.parent(id, s);
			this.onRenderMenu = new tinymce.util.Dispatcher(this);
			s.menu_container = s.menu_container || DOM.doc.body;
		},

		showMenu : function() {
			var t = this, p1, p2, e = DOM.get(t.id), m;

			if (t.isDisabled())
				return;

			if (!t.isMenuRendered) {
				t.renderMenu();
				t.isMenuRendered = true;
			}

			if (t.isMenuVisible)
				return t.hideMenu();

			p1 = DOM.getPos(t.settings.menu_container);
			p2 = DOM.getPos(e);

			m = t.menu;
			m.settings.offset_x = p2.x;
			m.settings.offset_y = p2.y;
			m.settings.vp_offset_x = p2.x;
			m.settings.vp_offset_y = p2.y;
			m.settings.keyboard_focus = t._focused;
			m.showMenu(0, e.clientHeight);

			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
			t.setState('Selected', 1);

			t.isMenuVisible = 1;
		},

		renderMenu : function() {
			var t = this, m;

			m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
				menu_line : 1,
				'class' : this.classPrefix + 'Menu',
				icons : t.settings.icons
			});

			m.onHideMenu.add(t.hideMenu, t);

			t.onRenderMenu.dispatch(t, m);
			t.menu = m;
		},

		hideMenu : function(e) {
			var t = this;

			// Prevent double toogles by canceling the mouse click event to the button
			if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))
				return;

			if (!e || !DOM.getParent(e.target, function(n) {return DOM.hasClass(n, 'mceMenu');})) {
				t.setState('Selected', 0);
				Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
				if (t.menu)
					t.menu.hideMenu();
			}

			t.isMenuVisible = 0;
		},

		postRender : function() {
			var t = this, s = t.settings;

			Event.add(t.id, 'click', function() {
				if (!t.isDisabled()) {
					if (s.onclick)
						s.onclick(t.value);

					t.showMenu();
				}
			});
		}

		});
})();

/* file:jscripts/tiny_mce/classes/ui/SplitButton.js */

(function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;

	tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
		SplitButton : function(id, s) {
			this.parent(id, s);
			this.classPrefix = 'mceSplitButton';
		},

		renderHTML : function() {
			var h, t = this, s = t.settings, h1;

			h = '<tbody><tr>';

			if (s.image)
				h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});
			else
				h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');

			h += '<td>' + DOM.createHTML('a', {id : t.id + '_action', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';

			h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});
			h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';

			h += '</tr></tbody>';

			return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);
		},

		postRender : function() {
			var t = this, s = t.settings;

			if (s.onclick) {
				Event.add(t.id + '_action', 'click', function() {
					if (!t.isDisabled())
						s.onclick(t.value);
				});
			}

			Event.add(t.id + '_open', 'click', t.showMenu, t);
			Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});
			Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});

			// Old IE doesn't have hover on all elements
			if (tinymce.isIE6 || !DOM.boxModel) {
				Event.add(t.id, 'mouseover', function() {
					if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
						DOM.addClass(t.id, 'mceSplitButtonHover');
				});

				Event.add(t.id, 'mouseout', function() {
					if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
						DOM.removeClass(t.id, 'mceSplitButtonHover');
				});
			}
		},

		destroy : function() {
			this.parent();

			Event.clear(this.id + '_action');
			Event.clear(this.id + '_open');
		}

		});
})();

/* file:jscripts/tiny_mce/classes/ui/ColorSplitButton.js */

(function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;

	tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
		ColorSplitButton : function(id, s) {
			var t = this;

			t.parent(id, s);

			t.settings = s = tinymce.extend({
				colors : '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF',
				grid_width : 8,
				default_color : '#888888'
			}, t.settings);

			t.value = s.default_color;
		},

		showMenu : function() {
			var t = this, r, p, e, p2;

			if (t.isDisabled())
				return;

			if (!t.isMenuRendered) {
				t.renderMenu();
				t.isMenuRendered = true;
			}

			if (t.isMenuVisible)
				return t.hideMenu();

			e = DOM.get(t.id);
			DOM.show(t.id + '_menu');
			DOM.addClass(e, 'mceSplitButtonSelected');
			p2 = DOM.getPos(e);
			DOM.setStyles(t.id + '_menu', {
				left : p2.x,
				top : p2.y + e.clientHeight,
				zIndex : 200000
			});
			e = 0;

			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);

			if (t._focused) {
				t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
					if (e.keyCode == 27)
						t.hideMenu();
				});

				DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
			}

			t.isMenuVisible = 1;
		},

		hideMenu : function(e) {
			var t = this;

			// Prevent double toogles by canceling the mouse click event to the button
			if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
				return;

			if (!e || !DOM.getParent(e.target, function(n) {return DOM.hasClass(n, 'mceSplitButtonMenu');})) {
				DOM.removeClass(t.id, 'mceSplitButtonSelected');
				Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
				Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
				DOM.hide(t.id + '_menu');
			}

			t.isMenuVisible = 0;
		},

		renderMenu : function() {
			var t = this, m, i = 0, s = t.settings, n, tb, tr, w;

			w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
			m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
			DOM.add(m, 'span', {'class' : 'mceMenuLine'});

			n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});
			tb = DOM.add(n, 'tbody');

			// Generate color grid
			i = 0;
			each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
				c = c.replace(/^#/, '');

				if (!i--) {
					tr = DOM.add(tb, 'tr');
					i = s.grid_width - 1;
				}

				n = DOM.add(tr, 'td');

				n = DOM.add(n, 'a', {
					href : 'javascript:;',
					style : {
						backgroundColor : '#' + c
					},
					mce_color : '#' + c
				});
			});

			if (s.more_colors_func) {
				n = DOM.add(tb, 'tr');
				n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});
				n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);

				Event.add(n, 'click', function(e) {
					s.more_colors_func.call(s.more_colors_scope || this);
					return Event.cancel(e); // Cancel to fix onbeforeunload problem
				});
			}

			DOM.addClass(m, 'mceColorSplitMenu');

            Event.add(t.id + '_menu', 'mouseup', function(evt) {
                tinymce.dom.Event.cancel(evt);
            });
            Event.add(t.id + '_menu', 'mousedown', function(evt) {
                tinymce.dom.Event.cancel(evt);
            });
            Event.add(t.id + '_menu', 'click', function(evt) {
				var c;
                tinymce.dom.Event.cancel(evt);

                e = evt.target;

				if (e.nodeName == 'A' && (c = e.getAttribute('mce_color'))){
                    t.setColor(c);
                }
			});

			return w;
		},

		setColor : function(c) {
			var t = this;

			DOM.setStyle(t.id + '_preview', 'backgroundColor', c);

			t.value = c;
			t.hideMenu();
			t.settings.onselect(c);
		},

		postRender : function() {
			var t = this, id = t.id;

			t.parent();
			DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
		},

		destroy : function() {
			this.parent();

			Event.clear(this.id + '_menu');
			Event.clear(this.id + '_more');
			DOM.remove(this.id + '_menu');
		}

		});
})();

/* file:jscripts/tiny_mce/classes/ui/Toolbar.js */

tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
	renderHTML : function() {
		var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;

		cl = t.controls;
		for (i=0; i<cl.length; i++) {
			// Get current control, prev control, next control and if the control is a list box or not
			co = cl[i];
			pr = cl[i - 1];
			nx = cl[i + 1];

			// Add toolbar start
			if (i === 0) {
				c = 'mceToolbarStart';

				if (co.Button)
					c += ' mceToolbarStartButton';
				else if (co.SplitButton)
					c += ' mceToolbarStartSplitButton';
				else if (co.ListBox)
					c += ' mceToolbarStartListBox';

				h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
			}

			// Add toolbar end before list box and after the previous button
			// This is to fix the o2k7 editor skins
			if (pr && co.ListBox) {
				if (pr.Button || pr.SplitButton)
					h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));
			}

			// Render control HTML

			// IE 8 quick fix, needed to propertly generate a hit area for anchors
			if (dom.stdMode)
				h += '<td style="position: relative">' + co.renderHTML() + '</td>';
			else
				h += '<td>' + co.renderHTML() + '</td>';

			// Add toolbar start after list box and before the next button
			// This is to fix the o2k7 editor skins
			if (nx && co.ListBox) {
				if (nx.Button || nx.SplitButton)
					h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));
			}
		}

		c = 'mceToolbarEnd';

		if (co.Button)
			c += ' mceToolbarEndButton';
		else if (co.SplitButton)
			c += ' mceToolbarEndSplitButton';
		else if (co.ListBox)
			c += ' mceToolbarEndListBox';

		h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));

		return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>');
	}

	});

/* file:jscripts/tiny_mce/classes/AddOnManager.js */

(function() {
	var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;

	tinymce.create('tinymce.AddOnManager', {
		items : [],
		urls : {},
		lookup : {},
		onAdd : new Dispatcher(this),

		get : function(n) {
			return this.lookup[n];
		},

		requireLangPack : function(n) {
			var u, s;

			if (tinymce.EditorManager.settings) {
				u = this.urls[n] + '/langs/' + tinymce.EditorManager.settings.language + '.js';
				s = tinymce.EditorManager.settings;

				if (s) {
					if (!tinymce.dom.Event.domLoaded && !s.strict_mode)
						tinymce.ScriptLoader.load(u);
					else
						tinymce.ScriptLoader.add(u);
				}
			}
		},

		add : function(id, o) {
			this.items.push(o);
			this.lookup[id] = o;
			this.onAdd.dispatch(this, id, o);

			return o;
		},

		load : function(n, u, cb, s) {
			var t = this;

            // begin edits //
            // https://brewspace.jiveland.com/docs/DOC-10757 //
            if (t.lookup[n])
                return;
            // end edits //

            if (t.urls[n])
                return;

			if (u.indexOf('/') != 0 && u.indexOf('://') == -1)
				u = tinymce.baseURL + '/' +  u;

			t.urls[n] = u.substring(0, u.lastIndexOf('/'));
			tinymce.ScriptLoader.add(u, cb, s);
		}

		});

	// Create plugin and theme managers
	tinymce.PluginManager = new tinymce.AddOnManager();
	tinymce.ThemeManager = new tinymce.AddOnManager();
}());
/* file:jscripts/tiny_mce/classes/EditorManager.js */

(function() {
	// Shorten names
	var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;

	tinymce.create('static tinymce.EditorManager', {
		editors : {},
		i18n : {},
		activeEditor : null,

		preInit : function() {
			var t = this, lo = window.location;

			// Setup some URLs where the editor API is located and where the document is
			tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
			if (!/[\/\\]$/.test(tinymce.documentBaseURL))
				tinymce.documentBaseURL += '/';

			tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
			tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);

			// Setup document domain
			if (tinymce.EditorManager.baseURI.host != lo.hostname && lo.hostname)
				document.domain = tinymce.relaxedDomain = lo.hostname.replace(/.*\.(.+\..+)$/, '$1');

			// Add before unload listener
			// This was required since IE was leaking memory if you added and removed beforeunload listeners
			// with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
			t.onBeforeUnload = new tinymce.util.Dispatcher(t);

			// Must be on window or IE will leak if the editor is placed in frame or iframe
			Event.add(window, 'beforeunload', function(e) {
				t.onBeforeUnload.dispatch(t, e);
			});
		},

		init : function(s) {
			var t = this, pl, sl = tinymce.ScriptLoader, c, e;

			function execCallback(se, n, s) {
				var f = se[n];

				if (!f)
					return;

				if (tinymce.is(f, 'string')) {
					s = f.replace(/\.\w+$/, '');
					s = s ? tinymce.resolve(s) : 0;
					f = tinymce.resolve(f);
				}

				return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
			};

			s = extend({
				theme : "simple",
				language : "en",
				strict_loading_mode : document.contentType == 'application/xhtml+xml'
			}, s);

			t.settings = s;

			// If page not loaded and strict mode isn't enabled then load them
			if (!Event.domLoaded && !s.strict_loading_mode) {
				// Load language
				if (s.language)
					sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');

				// Load theme
				if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
					ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');

				// Load plugins
				if (s.plugins) {
					pl = explode(s.plugins);

					// Load compat2x first
					if (tinymce.inArray(pl, 'compat2x') != -1)
						PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');

					// Load rest if plugins
					each(pl, function(v) {
						if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {
							// Skip safari plugin for other browsers
							if (!tinymce.isWebKit && v == 'safari')
								return;

//							PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
						}
					});
				}

				sl.loadQueue();
			}

			// Legacy call
			Event.add(document, 'init', function() {
				var l, co;

				execCallback(s, 'onpageload');

				// Verify that it's a valid browser
				if (s.browsers) {
					l = false;

					each(explode(s.browsers), function(v) {
						switch (v) {
							case 'ie':
							case 'msie':
								if (tinymce.isIE)
									l = true;
								break;

							case 'gecko':
								if (tinymce.isGecko)
									l = true;
								break;

							case 'safari':
							case 'webkit':
								if (tinymce.isWebKit)
									l = true;
								break;

							case 'opera':
								if (tinymce.isOpera)
									l = true;

								break;
						}
					});

					// Not a valid one
					if (!l)
						return;
				}

				switch (s.mode) {
					case "exact":
						l = s.elements || '';

						if(l.length > 0) {
							each(explode(l), function(v) {
								if (DOM.get(v))
									new tinymce.Editor(v, s).render(1);
								else {
									c = 0;

									each(document.forms, function(f) {
										each(f.elements, function(e) {
											if (e.name === v) {
												v = 'mce_editor_' + c;
												DOM.setAttrib(e, 'id', v);
												new tinymce.Editor(v, s).render(1);
											}
										});
									});
								}
							});
						}
						break;

					case "textareas":
					case "specific_textareas":
						function hasClass(n, c) {
							return new RegExp('\\b' + c + '\\b', 'g').test(n.className);
						};

						each(DOM.select('textarea'), function(v) {
							if (s.editor_deselector && hasClass(v, s.editor_deselector))
								return;

							if (!s.editor_selector || hasClass(v, s.editor_selector)) {
								// Can we use the name
								e = DOM.get(v.name);
								if (!v.id && !e)
									v.id = v.name;

								// Generate unique name if missing or already exists
								if (!v.id || t.get(v.id))
									v.id = DOM.uniqueId();

								new tinymce.Editor(v.id, s).render(1);
							}
						});
						break;
				}

				// Call onInit when all editors are initialized
				if (s.oninit) {
					l = co = 0;

					each (t.editors, function(ed) {
						co++;

						if (!ed.initialized) {
							// Wait for it
							ed.onInit.add(function() {
								l++;

								// All done
								if (l == co)
									execCallback(s, 'oninit');
							});
						} else
							l++;

						// All done
						if (l == co)
							execCallback(s, 'oninit');
					});
				}
			});
		},

		get : function(id) {
			return this.editors[id];
		},

		getInstanceById : function(id) {
			return this.get(id);
		},

		add : function(e) {
			this.editors[e.id] = e;
			this._setActive(e);

			return e;
		},

		remove : function(e) {
			var t = this;

			// Not in the collection
			if (!t.editors[e.id])
				return null;

			delete t.editors[e.id];

			// Select another editor since the active one was removed
			if (t.activeEditor == e) {
				each(t.editors, function(e) {
					t._setActive(e);
					return false; // Break
				});
			}

			e.destroy();

			return e;
		},

		execCommand : function(c, u, v) {
			var t = this, ed = t.get(v), w;

			// Manager commands
			switch (c) {
				case "mceFocus":
					ed.focus();
					return true;

				case "mceAddEditor":
				case "mceAddControl":
					if (!t.get(v))
						new tinymce.Editor(v, t.settings).render();

					return true;

				case "mceAddFrameControl":
					w = v.window;

					// Add tinyMCE global instance and tinymce namespace to specified window
					w.tinyMCE = tinyMCE;
					w.tinymce = tinymce;

					tinymce.DOM.doc = w.document;
					tinymce.DOM.win = w;

					ed = new tinymce.Editor(v.element_id, v);
					ed.render();

					// Fix IE memory leaks
					if (tinymce.isIE) {
						function clr() {
							ed.destroy();
							w.detachEvent('onunload', clr);
							w = w.tinyMCE = w.tinymce = null; // IE leak
						};

						w.attachEvent('onunload', clr);
					}

					v.page_window = null;

					return true;

				case "mceRemoveEditor":
				case "mceRemoveControl":
					ed.remove();
					return true;

				case 'mceToggleEditor':
					if (!ed) {
						t.execCommand('mceAddControl', 0, v);
						return true;
					}

					if (ed.isHidden())
						ed.show();
					else
						ed.hide();

					return true;
			}

			// Run command on active editor
			if (t.activeEditor)
				return t.activeEditor.execCommand(c, u, v);

			return false;
		},

		execInstanceCommand : function(id, c, u, v) {
			var ed = this.get(id);

			if (ed)
				return ed.execCommand(c, u, v);

			return false;
		},

		triggerSave : function() {
			each(this.editors, function(e) {
				e.save();
			});
		},

		addI18n : function(p, o) {
			var lo, i18n = this.i18n;

			if (!tinymce.is(p, 'string')) {
				each(p, function(o, lc) {
					each(o, function(o, g) {
						each(o, function(o, k) {
							if (g === 'common')
								i18n[lc + '.' + k] = o;
							else
								i18n[lc + '.' + g + '.' + k] = o;
						});
					});
				});
			} else {
				each(o, function(o, k) {
					i18n[p + '.' + k] = o;
				});
			}
		},

		// Private methods

		_setActive : function(e) {
			this.selectedInstance = this.activeEditor = e;
		}

		});

	tinymce.EditorManager.preInit();
})();

// Short for editor manager window.tinyMCE is needed when TinyMCE gets loaded though a XHR call
var tinyMCE = window.tinyMCE = tinymce.EditorManager;

/* file:jscripts/tiny_mce/classes/Editor.js */

(function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;
	var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;
	var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;
	var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;

	tinymce.create('tinymce.Editor', {
		Editor : function(id, s) {
			var t = this;

			t.id = t.editorId = id;
			t.execCommands = {};
			t.queryStateCommands = {};
			t.queryValueCommands = {};
			t.plugins = {};

			// Add events to the editor
			each([
				'onPreInit',
				'onBeforeRenderUI',
				'onPostRender',
				'onInit',
				'onRemove',
				'onActivate',
				'onDeactivate',
				'onClick',
				'onEvent',
				'onMouseUp',
				'onMouseDown',
				'onDblClick',
				'onKeyDown',
				'onKeyUp',
				'onKeyPress',
				'onContextMenu',
				'onSubmit',
				'onReset',
				'onPaste',
				'onPreProcess',
				'onPostProcess',
				'onBeforeSetContent',
				'onBeforeGetContent',
				'onSetContent',
				'onGetContent',
				'onLoadContent',
				'onSaveContent',
				'onNodeChange',
				'onChange',
				'onBeforeExecCommand',
				'onExecCommand',
				'onUndo',
				'onRedo',
				'onVisualAid',
				'onSetProgressState'
			], function(e) {
				t[e] = new Dispatcher(t);
			});

			// Default editor config
			t.settings = s = extend({
				id : id,
				language : 'en',
				docs_language : 'en',
				theme : 'simple',
				skin : 'default',
				delta_width : 0,
				delta_height : 0,
				popup_css : '',
				plugins : '',
				document_base_url : tinymce.documentBaseURL,
				add_form_submit_trigger : 1,
				submit_patch : 1,
				add_unload_trigger : 1,
				convert_urls : 1,
				relative_urls : 1,
				remove_script_host : 1,
				table_inline_editing : 0,
				object_resizing : 1,
				cleanup : 1,
				accessibility_focus : 1,
				custom_shortcuts : 1,
				custom_undo_redo_keyboard_shortcuts : 1,
				custom_undo_redo_restore_selection : 1,
				custom_undo_redo : 1,
				doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
				visual_table_class : 'mceItemTable',
				visual : 1,
				inline_styles : true,
				convert_fonts_to_spans : true,
				font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
				apply_source_formatting : 1,
				directionality : 'ltr',
				forced_root_block : 'p',
				valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote,-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value|_value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',
				hidden_input : 1,
				padd_empty_editor : 1,
				render_ui : 1,
				init_theme : 1,
				force_p_newlines : 1,
				indentation : '30px'
			}, s);

			// Setup URIs
			t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
				base_uri : tinyMCE.baseURI
			});
			t.baseURI = EditorManager.baseURI;

			// Call setup
			t.execCallback('setup', t);
		},

		render : function(nst) {
			var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;

			// Page is not loaded yet, wait for it
			if (!Event.domLoaded) {
				Event.add(document, 'init', function() {
					t.render();
				});
				return;
			}

			// Force strict loading mode if render us called by user and not internally
			if (!nst) {
				s.strict_loading_mode = 1;
				tinyMCE.settings = s;
			}

			// Element not found, then skip initialization
			if (!t.getElement())
				return;

			if (s.strict_loading_mode) {
				sl.settings.strict_mode = s.strict_loading_mode;
				tinymce.DOM.settings.strict = 1;
			}

			// Add hidden input for non input elements inside form elements
			if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
				DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);

			t.windowManager = new tinymce.WindowManager(t);

			if (s.encoding == 'xml') {
				t.onGetContent.add(function(ed, o) {
					if (o.get)
						o.content = DOM.encode(o.content);
				});
			}

			if (s.add_form_submit_trigger) {
				t.onSubmit.addToTop(function() {
					if (t.initialized) {
						t.save();
						t.isNotDirty = 1;
					}
				});
			}

			if (s.add_unload_trigger) {
				t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
					if (t.initialized && !t.destroyed && !t.isHidden())
						t.save({format : 'raw', no_events : true});
				});
			}

			tinymce.addUnload(t.destroy, t);

			if (s.submit_patch) {
				t.onBeforeRenderUI.add(function() {
					var n = t.getElement().form;

					if (!n)
						return;

					// Already patched
					if (n._mceOldSubmit)
						return;

					// Check page uses id="submit" or name="submit" for it's submit button
					if (!n.submit.nodeType && !n.submit.length) {
						t.formElement = n;
						n._mceOldSubmit = n.submit;
						n.submit = function() {
							// Save all instances
							EditorManager.triggerSave();
							t.isNotDirty = 1;

							return this._mceOldSubmit(this);
						};
					}

					n = null;
				});
			}

			// Load scripts
			function loadScripts() {
				if (s.language)
					sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');

				if (s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
					ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');

				each(explode(s.plugins), function(p) {
					if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {
						// Skip safari plugin for other browsers
						if (!isWebKit && p == 'safari')
							return;

						PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
					}
				});

				// Init when que is loaded
				sl.loadQueue(function() {
					if (s.ask) {
						function ask() {
							t.windowManager.confirm(t.getLang('edit_confirm'), function(s) {
								if (s)
									t.init();
								else
									Event.remove(t.id, 'focus', ask);
							});
						};

						Event.add(t.id, 'focus', ask);
						return;
					}

					if (!t.removed)
						t.init();
				});
			};

			// Load compat2x first
			if (s.plugins.indexOf('compat2x') != -1) {
				PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
				sl.loadQueue(loadScripts);
			} else
				loadScripts();
		},

		init : function() {
			var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;

			EditorManager.add(t);

			// Create theme
			s.theme = s.theme.replace(/-/, '');
			o = ThemeManager.get(s.theme);
			t.theme = new o();

			if (t.theme.init && s.init_theme)
				t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));

			// Create all plugins
			each(explode(s.plugins.replace(/\-/g, '')), function(p) {
				var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;

				if (c) {
					po = new c(t, u);

					t.plugins[p] = po;

					if (po.init)
						po.init(t, u);
				}
			});

			// Setup popup CSS path(s)
			if (s.popup_css)
				s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
			else
				s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");

			if (s.popup_css_add)
				s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);

			// Setup control factory
			t.controlManager = new tinymce.ControlManager(t);
			t.undoManager = new tinymce.UndoManager(t);

			// Pass through
			t.undoManager.onAdd.add(function(um, l) {
				return t.onChange.dispatch(t, l, um);
			});

			t.undoManager.onUndo.add(function(um, l) {
				return t.onUndo.dispatch(t, l, um);
			});

			t.undoManager.onRedo.add(function(um, l) {
				return t.onRedo.dispatch(t, l, um);
			});

			if (s.custom_undo_redo) {
				t.onExecCommand.add(function(ed, cmd, ui, val, a) {
					if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
						t.undoManager.add();
				});
			}

			t.onExecCommand.add(function(ed, c) {
				// Don't refresh the select lists until caret move
				if (!/^(FontName|FontSize)$/.test(c))
					t.nodeChanged();
			});

			// Remove ghost selections on images and tables in Gecko
			if (isGecko) {
				function repaint(a, o) {
					if (!o || !o.initial)
						t.execCommand('mceRepaint');
				};

				t.onUndo.add(repaint);
				t.onRedo.add(repaint);
				t.onSetContent.add(repaint);
			}

			// Enables users to override the control factory
			t.onBeforeRenderUI.dispatch(t, t.controlManager);

			// Measure box
			if (s.render_ui) {
				w = s.width || e.style.width || e.clientWidth;
				h = s.height || e.style.height || e.clientHeight;
				t.orgDisplay = e.style.display;
				re = /^[0-9\.]+(|px)$/i;

				if (re.test('' + w))
					w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);

				if (re.test('' + h))
					h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);

				// Render UI
				o = t.theme.renderUI({
					targetNode : e,
					width : w,
					height : h,
					deltaWidth : s.delta_width,
					deltaHeight : s.delta_height
				});

				t.editorContainer = o.editorContainer;
			}


			// Resize editor
			DOM.setStyles(o.sizeContainer || o.editorContainer, {
				width : w,
				height : h
			});

			h = (o.iframeHeight || h) + ((h + '').indexOf('%') == -1 ? (o.deltaHeight || 0) : '');
			if (h < 100)
				h = 100;

			t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '"></base>';
			t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';

			if (tinymce.relaxedDomain)
				t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';

			bi = s.body_id || 'tinymce';
			if (bi.indexOf('=') != -1) {
				bi = t.getParam('body_id', '', 'hash');
				bi = bi[t.id] || bi;
			}

			bc = s.body_class || '';
			if (bc.indexOf('=') != -1) {
				bc = t.getParam('body_class', '', 'hash');
				bc = bc[t.id] || '';
			}

			t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';

			// Domain relaxing enabled, then set document domain
			if (tinymce.relaxedDomain) {
				// We need to write the contents here in IE since multiple writes messes up refresh button and back button
				if (isIE)
					u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';
				else if (tinymce.isOpera)
					u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';
			}

			// Create iframe
			n = DOM.add(o.iframeContainer, 'iframe', {
				id : t.id + "_ifr",
				src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
				frameBorder : '0',
				style : {
					width : '100%',
					height : h
				}
			});

			t.contentAreaContainer = o.iframeContainer;
			DOM.get(o.editorContainer).style.display = t.orgDisplay;
			DOM.get(t.id).style.display = 'none';

			// Safari 2.x requires us to wait for the load event and load a real HTML doc
			if (tinymce.isOldWebKit) {
				Event.add(n, 'load', t.setupIframe, t);
				n.src = tinymce.baseURL + '/plugins/safari/blank.htm';
			} else {
				if (!isIE || !tinymce.relaxedDomain)
					t.setupIframe();

				e = n = o = null; // Cleanup
			}
		},

		setupIframe : function() {
			var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;

			// Setup iframe body
			if (!isIE || !tinymce.relaxedDomain) {
				d.open();
				d.write(t.iframeHTML);
				d.close();
			}

			// Design mode needs to be added here Ctrl+A will fail otherwise
			if (!isIE) {
				try {
					d.designMode = 'On';
				} catch (ex) {
					// Will fail on Gecko if the editor is placed in an hidden container element
					// The design mode will be set ones the editor is focused
				}
			}

			// IE needs to use contentEditable or it will display non secure items for HTTPS
			if (isIE) {
				// It will not steal focus if we hide it while setting contentEditable
				b = t.getBody();
				DOM.hide(b);
				b.contentEditable = true;
				DOM.show(b);
			}

			// Setup objects
			t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), {
				keep_values : true,
				url_converter : t.convertURL,
				url_converter_scope : t,
				hex_colors : s.force_hex_style_colors,
				class_filter : s.class_filter,
				update_styles : 1,
				fix_ie_paragraphs : 1
			});

			t.serializer = new tinymce.dom.Serializer({
				entity_encoding : s.entity_encoding,
				entities : s.entities,
				valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,
				extended_valid_elements : s.extended_valid_elements,
				valid_child_elements : s.valid_child_elements,
				invalid_elements : s.invalid_elements,
				fix_table_elements : s.fix_table_elements,
				fix_list_elements : s.fix_list_elements,
				fix_content_duplication : s.fix_content_duplication,
				convert_fonts_to_spans : s.convert_fonts_to_spans,
				font_size_classes  : s.font_size_classes,
				font_size_style_values : s.font_size_style_values,
				apply_source_formatting : s.apply_source_formatting,
				remove_linebreaks : s.remove_linebreaks,
				dom : t.dom
			});

			t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
			t.forceBlocks = new tinymce.ForceBlocks(t, {
				forced_root_block : s.forced_root_block
			});
			t.editorCommands = new tinymce.EditorCommands(t);

			// Pass through
			t.serializer.onPreProcess.add(function(se, o) {
				return t.onPreProcess.dispatch(t, o, se);
			});

			t.serializer.onPostProcess.add(function(se, o) {
				return t.onPostProcess.dispatch(t, o, se);
			});

			t.onPreInit.dispatch(t);

			if (!s.gecko_spellcheck)
				t.getBody().spellcheck = 0;

			t._addEvents();

			t.controlManager.onPostRender.dispatch(t, t.controlManager);
			t.onPostRender.dispatch(t);

			if (s.directionality)
				t.getBody().dir = s.directionality;

			if (s.nowrap)
				t.getBody().style.whiteSpace = "nowrap";

			if (s.auto_resize)
				t.onNodeChange.add(t.resizeToContent, t);

			if (s.custom_elements) {
				function handleCustom(ed, o) {
					each(explode(s.custom_elements), function(v) {
						var n;

						if (v.indexOf('~') === 0) {
							v = v.substring(1);
							n = 'span';
						} else
							n = 'div';

						o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');
						o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');
					});
				};

				t.onBeforeSetContent.add(handleCustom);
				t.onPostProcess.add(function(ed, o) {
					if (o.set)
						handleCustom(ed, o)
				});
			}

			if (s.handle_node_change_callback) {
				t.onNodeChange.add(function(ed, cm, n) {
					t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
				});
			}

			if (s.save_callback) {
				t.onSaveContent.add(function(ed, o) {
					var h = t.execCallback('save_callback', t.id, o.content, t.getBody());

					if (h)
						o.content = h;
				});
			}

			if (s.onchange_callback) {
				t.onChange.add(function(ed, l) {
					t.execCallback('onchange_callback', t, l);
				});
			}

			if (s.convert_newlines_to_brs) {
				t.onBeforeSetContent.add(function(ed, o) {
					if (o.initial)
						o.content = o.content.replace(/\r?\n/g, '<br />');
				});
			}

			if (s.fix_nesting && isIE) {
				t.onBeforeSetContent.add(function(ed, o) {
					o.content = t._fixNesting(o.content);
				});
			}

			if (s.preformatted) {
				t.onPostProcess.add(function(ed, o) {
					o.content = o.content.replace(/^\s*<pre.*?>/, '');
					o.content = o.content.replace(/<\/pre>\s*$/, '');

					if (o.set)
						o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
				});
			}

			if (s.verify_css_classes) {
				t.serializer.attribValueFilter = function(n, v) {
					var s, cl;

					if (n == 'class') {
						// Build regexp for classes
						if (!t.classesRE) {
							cl = t.dom.getClasses();

							if (cl.length > 0) {
								s = '';

								each (cl, function(o) {
									s += (s ? '|' : '') + o['class'];
								});

								t.classesRE = new RegExp('(' + s + ')', 'gi');
							}
						}

						return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
					}

					return v;
				};
			}

			if (s.convert_fonts_to_spans)
				t._convertFonts();

			if (s.inline_styles)
				t._convertInlineElements();

			if (s.cleanup_callback) {
				t.onBeforeSetContent.add(function(ed, o) {
					o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
				});

				t.onPreProcess.add(function(ed, o) {
					if (o.set)
						t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);

					if (o.get)
						t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
				});

				t.onPostProcess.add(function(ed, o) {
					if (o.set)
						o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);

					if (o.get)
						o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
				});
			}

			if (s.save_callback) {
				t.onGetContent.add(function(ed, o) {
					if (o.save)
						o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
				});
			}

			if (s.handle_event_callback) {
				t.onEvent.add(function(ed, e, o) {
					if (t.execCallback('handle_event_callback', e, ed, o) === false)
						Event.cancel(e);
				});
			}

			t.onSetContent.add(function() {
				// Safari needs some time, it will crash the browser when a link is created otherwise
				// I think this crash issue is resolved in the latest 3.0.4
				//window.setTimeout(function() {
					t.addVisual(t.getBody());
				//}, 1);
			});

			// Remove empty contents
			if (s.padd_empty_editor) {
				t.onPostProcess.add(function(ed, o) {
					o.content = o.content.replace(/^<p>(&nbsp;|#160;|\s|\u00a0)<\/p>$/, '');
				});
			}

			if (isGecko) {
				try {
					// Design mode must be set here once again to fix a bug where
					// Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
					d.designMode = 'Off';
					d.designMode = 'On';
				} catch (ex) {
					// Will fail on Gecko if the editor is placed in an hidden container element
					// The design mode will be set ones the editor is focused
				}
			}

			// A small timeout was needed since firefox will remove. Bug: #1838304
			setTimeout(function () {
				if (t.removed)
					return;

				t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
				t.startContent = t.getContent({format : 'raw'});
				t.undoManager.add({initial : true});
				t.initialized = true;

				t.onInit.dispatch(t);
				t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
				t.execCallback('init_instance_callback', t);
				t.focus(true);
				t.nodeChanged({initial : 1});

				// Load specified content CSS last
				if (s.content_css) {
					tinymce.each(explode(s.content_css), function(u) {
						t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
					});
				}

				// Handle auto focus
				if (s.auto_focus) {
					setTimeout(function () {
						var ed = EditorManager.get(s.auto_focus);

						ed.selection.select(ed.getBody(), 1);
						ed.selection.collapse(1);
						ed.getWin().focus();
					}, 100);
				}
			}, 1);

			e = null;
		},


		focus : function(sf) {
			var oed, t = this;

			if (!sf) {
				t.getWin().focus();

							}

			if (EditorManager.activeEditor != t) {
				if ((oed = EditorManager.activeEditor) != null)
					oed.onDeactivate.dispatch(oed, t);

				t.onActivate.dispatch(t, oed);
			}

			EditorManager._setActive(t);
		},

		execCallback : function(n) {
			var t = this, f = t.settings[n], s;

			if (!f)
				return;

			// Look through lookup
			if (t.callbackLookup && (s = t.callbackLookup[n])) {
				f = s.func;
				s = s.scope;
			}

			if (is(f, 'string')) {
				s = f.replace(/\.\w+$/, '');
				s = s ? tinymce.resolve(s) : 0;
				f = tinymce.resolve(f);
				t.callbackLookup = t.callbackLookup || {};
				t.callbackLookup[n] = {func : f, scope : s};
			}

			return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
		},

		translate : function(s) {
			var c = this.settings.language, i18n = EditorManager.i18n;

			if (!s)
				return '';

			return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
				return i18n[c + '.' + b] || '{#' + b + '}';
			});
		},

		getLang : function(n, dv) {
			return EditorManager.i18n[this.settings.language + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
		},

		getParam : function(n, dv, ty) {
			var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;

			if (ty === 'hash') {
				o = {};

				if (is(v, 'string')) {
					each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
						v = v.split('=');

						if (v.length > 1)
							o[tr(v[0])] = tr(v[1]);
						else
							o[tr(v[0])] = tr(v);
					});
				} else
					o = v;

				return o;
			}

			return v;
		},

		nodeChanged : function(o) {
			var t = this, s = t.selection, n = s.getNode() || t.getBody();

			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
			if (t.initialized) {
				t.onNodeChange.dispatch(
					t,
					o ? o.controlManager || t.controlManager : t.controlManager,
					isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state
					s.isCollapsed(),
					o
				);
			}
		},

		addButton : function(n, s) {
			var t = this;

			t.buttons = t.buttons || {};
			t.buttons[n] = s;
		},

		addCommand : function(n, f, s) {
			this.execCommands[n] = {func : f, scope : s || this};
		},

		addQueryStateHandler : function(n, f, s) {
			this.queryStateCommands[n] = {func : f, scope : s || this};
		},

		addQueryValueHandler : function(n, f, s) {
			this.queryValueCommands[n] = {func : f, scope : s || this};
		},

		addShortcut : function(pa, desc, cmd_func, sc) {
			var t = this, c;

			if (!t.settings.custom_shortcuts)
				return false;

			t.shortcuts = t.shortcuts || {};

			if (is(cmd_func, 'string')) {
				c = cmd_func;

				cmd_func = function() {
					t.execCommand(c, false, null);
				};
			}

			if (is(cmd_func, 'object')) {
				c = cmd_func;

				cmd_func = function() {
					t.execCommand(c[0], c[1], c[2]);
				};
			}

			each(explode(pa), function(pa) {
				var o = {
					func : cmd_func,
					scope : sc || this,
					desc : desc,
					alt : false,
					ctrl : false,
					shift : false
				};

				each(explode(pa, '+'), function(v) {
					switch (v) {
						case 'alt':
						case 'ctrl':
						case 'shift':
							o[v] = true;
							break;

						default:
							o.charCode = v.charCodeAt(0);
							o.keyCode = v.toUpperCase().charCodeAt(0);
					}
				});

				t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
			});

			return true;
		},

		execCommand : function(cmd, ui, val, a) {
			var t = this, s = 0, o, st;

			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
				t.focus();

			o = {};
			t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
			if (o.terminate)
				return false;

			// Comamnd callback
			if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
				t.onExecCommand.dispatch(t, cmd, ui, val, a);
				return true;
			}

			// Registred commands
			if (o = t.execCommands[cmd]) {
				st = o.func.call(o.scope, ui, val);

				// Fall through on true
				if (st !== true) {
					t.onExecCommand.dispatch(t, cmd, ui, val, a);
					return st;
				}
			}

			// Plugin commands
			each(t.plugins, function(p) {
				if (p.execCommand && p.execCommand(cmd, ui, val)) {
					t.onExecCommand.dispatch(t, cmd, ui, val, a);
					s = 1;
					return false;
				}
			});

			if (s)
				return true;

			// Theme commands
			if (t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
				t.onExecCommand.dispatch(t, cmd, ui, val, a);
				return true;
			}

			// Editor commands
			if (t.editorCommands.execCommand(cmd, ui, val)) {
				t.onExecCommand.dispatch(t, cmd, ui, val, a);
				return true;
			}

			// Browser commands
			t.getDoc().execCommand(cmd, ui, val);
			t.onExecCommand.dispatch(t, cmd, ui, val, a);
		},

		queryCommandState : function(c) {
			var t = this, o, s;

			// Is hidden then return undefined
			if (t._isHidden())
				return;

			// Registred commands
			if (o = t.queryStateCommands[c]) {
				s = o.func.call(o.scope);

				// Fall though on true
				if (s !== true)
					return s;
			}

			// Registred commands
			o = t.editorCommands.queryCommandState(c);
			if (o !== -1)
				return o;

			// Browser commands
			try {
				return this.getDoc().queryCommandState(c);
			} catch (ex) {
				// Fails sometimes see bug: 1896577
			}
		},

		queryCommandValue : function(c) {
			var t = this, o, s;

			// Is hidden then return undefined
			if (t._isHidden())
				return;

			// Registred commands
			if (o = t.queryValueCommands[c]) {
				s = o.func.call(o.scope);

				// Fall though on true
				if (s !== true)
					return s;
			}

			// Registred commands
			o = t.editorCommands.queryCommandValue(c);
			if (is(o))
				return o;

			// Browser commands
			try {
				return this.getDoc().queryCommandValue(c);
			} catch (ex) {
				// Fails sometimes see bug: 1896577
			}
		},

		show : function() {
			var t = this;

			DOM.show(t.getContainer());
			DOM.hide(t.id);
			t.load();
		},

		hide : function() {
			var t = this, d = t.getDoc();

			// Fixed bug where IE has a blinking cursor left from the editor
			if (isIE && d)
				d.execCommand('SelectAll');

			// We must save before we hide so Safari doesn't crash
			t.save();
			DOM.hide(t.getContainer());
			DOM.setStyle(t.id, 'display', t.orgDisplay);
		},

		isHidden : function() {
			return !DOM.isHidden(this.id);
		},

		setProgressState : function(b, ti, o) {
			this.onSetProgressState.dispatch(this, b, ti, o);

			return b;
		},

		resizeToContent : function() {
			var t = this;

			DOM.setStyle(t.id + "_ifr", 'height', t.getBody().scrollHeight);
		},

		load : function(o) {
			var t = this, e = t.getElement(), h;

			o = o || {};
			o.load = true;

			h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
			o.element = e;

			if (!o.no_events)
				t.onLoadContent.dispatch(t, o);

			o.element = e = null;

			return h;
		},

		save : function(o) {
			var t = this, e = t.getElement(), h, f;

			if (!t.initialized)
				return;

			o = o || {};
			o.save = true;

			o.element = e;
			h = o.content = t.getContent(o);

			if (!o.no_events)
				t.onSaveContent.dispatch(t, o);

			h = o.content;

			if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
				e.innerHTML = h;

				// Update hidden form element
				if (f = DOM.getParent(t.id, 'form')) {
					each(f.elements, function(e) {
						if (e.name == t.id) {
							e.value = h;
							return false;
						}
					});
				}
			} else
				e.value = h;

			o.element = e = null;

			return h;
		},

		setContent : function(h, o) {
			var t = this;

			o = o || {};
			o.format = o.format || 'html';
			o.set = true;
			o.content = h;

			if (!o.no_events)
				t.onBeforeSetContent.dispatch(t, o);

			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
			// It will also be impossible to place the caret in the editor unless there is a BR element present
			if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
				o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />', 1);
				o.format = 'raw';
			}

			o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));

			if (o.format != 'raw' && t.settings.cleanup) {
				o.getInner = true;
				o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
			}

			if (!o.no_events)
				t.onSetContent.dispatch(t, o);

			return o.content;
		},

		getContent : function(o) {
			var t = this, h;

			o = o || {};
			o.format = o.format || 'html';
			o.get = true;

			if (!o.no_events)
				t.onBeforeGetContent.dispatch(t, o);

			if (o.format != 'raw' && t.settings.cleanup) {
				o.getInner = true;
				h = t.serializer.serialize(t.getBody(), o);
			} else
				h = t.getBody().innerHTML;

			h = h.replace(/^\s*|\s*$/g, '');
			o = {content : h};
			t.onGetContent.dispatch(t, o);

			return o.content;
		},

		isDirty : function() {
			var t = this;

			return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
		},

		getContainer : function() {
			var t = this;

			if (!t.container)
				t.container = DOM.get(t.editorContainer || t.id + '_parent');

			return t.container;
		},

		getContentAreaContainer : function() {
			return this.contentAreaContainer;
		},

		getElement : function() {
			return DOM.get(this.settings.content_element || this.id);
		},

		getWin : function() {
			var t = this, e;

			if (!t.contentWindow) {
				e = DOM.get(t.id + "_ifr");

				if (e)
					t.contentWindow = e.contentWindow;
			}

			return t.contentWindow;
		},

		getDoc : function() {
			var t = this, w;

			if (!t.contentDocument) {
				w = t.getWin();

				if (w)
					t.contentDocument = w.document;
			}

			return t.contentDocument;
		},

		getBody : function() {
			return this.bodyElement || this.getDoc().body;
		},

		convertURL : function(u, n, e) {
			var t = this, s = t.settings;

			// Use callback instead
			if (s.urlconverter_callback)
				return t.execCallback('urlconverter_callback', u, e, true, n);

			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
			if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
				return u;

			// Convert to relative
			if (s.relative_urls)
				return t.documentBaseURI.toRelative(u);

			// Convert to absolute
			u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);

			return u;
		},

		addVisual : function(e) {
			var t = this, s = t.settings;

			e = e || t.getBody();

			if (!is(t.hasVisual))
				t.hasVisual = s.visual;

			each(t.dom.select('table,a', e), function(e) {
				var v;

				switch (e.nodeName) {
					case 'TABLE':
						v = t.dom.getAttrib(e, 'border');

						if (!v || v == '0') {
							if (t.hasVisual)
								t.dom.addClass(e, s.visual_table_class);
							else
								t.dom.removeClass(e, s.visual_table_class);
						}

						return;

					case 'A':
						v = t.dom.getAttrib(e, 'name');

						if (v) {
							if (t.hasVisual)
								t.dom.addClass(e, 'mceItemAnchor');
							else
								t.dom.removeClass(e, 'mceItemAnchor');
						}

						return;
				}
			});

			t.onVisualAid.dispatch(t, e, t.hasVisual);
		},

		remove : function() {
			var t = this, e = t.getContainer();

			t.removed = 1; // Cancels post remove event execution
			t.hide();

			t.execCallback('remove_instance_callback', t);
			t.onRemove.dispatch(t);

			// Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
			t.onExecCommand.listeners = [];

			EditorManager.remove(t);
			DOM.remove(e);
		},

		destroy : function(s) {
			var t = this;

			// One time is enough
			if (t.destroyed)
				return;

			if (!s) {
				tinymce.removeUnload(t.destroy);
				tinyMCE.onBeforeUnload.remove(t._beforeUnload);

				// Manual destroy
				if (t.theme.destroy)
					t.theme.destroy();

				// Destroy controls, selection and dom
				t.controlManager.destroy();
				t.selection.destroy();
				t.dom.destroy();

				// Remove all events

				// Don't clear the window or document if content editable
				// is enabled since other instances might still be present
				if (!t.settings.content_editable) {
					Event.clear(t.getWin());
					Event.clear(t.getDoc());
				}

				Event.clear(t.getBody());
				Event.clear(t.formElement);
			}

			if (t.formElement) {
				t.formElement.submit = t.formElement._mceOldSubmit;
				t.formElement._mceOldSubmit = null;
			}

			t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;

			if (t.selection)
				t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;

			t.destroyed = 1;
		},

		// Internal functions

		_addEvents : function() {
			// 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
			var t = this, i, s = t.settings, lo = {
				mouseup : 'onMouseUp',
				mousedown : 'onMouseDown',
				click : 'onClick',
				keyup : 'onKeyUp',
				keydown : 'onKeyDown',
				keypress : 'onKeyPress',
				submit : 'onSubmit',
				reset : 'onReset',
				contextmenu : 'onContextMenu',
				dblclick : 'onDblClick',
				paste : 'onPaste' // Doesn't work in all browsers yet
			};

			function eventHandler(e, o) {
				var ty = e.type;

				// Don't fire events when it's removed
				if (t.removed)
					return;

				// Generic event handler
				if (t.onEvent.dispatch(t, e, o) !== false) {
					// Specific event handler
					t[lo[e.fakeType || e.type]].dispatch(t, e, o);
				}
			};

			// Add DOM events
			each(lo, function(v, k) {
				switch (k) {
					case 'contextmenu':
						if (tinymce.isOpera) {
							// Fake contextmenu on Opera
							Event.add(t.getDoc(), 'mousedown', function(e) {
								if (e.ctrlKey) {
									e.fakeType = 'contextmenu';
									eventHandler(e);
								}
							});
						} else
							Event.add(t.getDoc(), k, eventHandler);
						break;

					case 'paste':
						Event.add(t.getBody(), k, function(e) {
							var tx, h, el, r;

							// Get plain text data
							if (e.clipboardData)
								tx = e.clipboardData.getData('text/plain');
							else if (tinymce.isIE)
								tx = t.getWin().clipboardData.getData('Text');

							// Get HTML data
							/*if (tinymce.isIE) {
								el = DOM.add(DOM.doc.body, 'div', {style : 'visibility:hidden;overflow:hidden;position:absolute;width:1px;height:1px'});
								r = DOM.doc.body.createTextRange();
								r.moveToElementText(el);
								r.execCommand('Paste');
								h = el.innerHTML;
								DOM.remove(el);
							}*/

							eventHandler(e, {text : tx, html : h});
						});
						break;

					case 'submit':
					case 'reset':
						Event.add(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
						break;

					default:
						Event.add(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
				}
			});

			Event.add(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
				t.focus(true);
			});


			// Fixes bug where a specified document_base_uri could result in broken images
			// This will also fix drag drop of images in Gecko
			if (tinymce.isGecko) {
				// Convert all images to absolute URLs
/*				t.onSetContent.add(function(ed, o) {
					each(ed.dom.select('img'), function(e) {
						var v;

						if (v = e.getAttribute('mce_src'))
							e.src = t.documentBaseURI.toAbsolute(v);
					})
				});*/

				Event.add(t.getDoc(), 'DOMNodeInserted', function(e) {
					var v;

					e = e.target;

					if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src')))
						e.src = t.documentBaseURI.toAbsolute(v);
				});
			}

			// Set various midas options in Gecko
			if (isGecko) {
				function setOpts() {
					var t = this, d = t.getDoc(), s = t.settings;

					if (isGecko) {
						if (t._isHidden()) {
							try {
								if (!s.content_editable)
									d.designMode = 'On';
							} catch (ex) {
								// Fails if it's hidden
							}
						}

						try {
							// Try new Gecko method
							d.execCommand("styleWithCSS", 0, false);
						} catch (ex) {
							// Use old method
							if (!t._isHidden())
								d.execCommand("useCSS", 0, true);
						}

						if (!s.table_inline_editing)
							try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}

						if (!s.object_resizing)
							try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
					}
				};

				t.onBeforeExecCommand.add(setOpts);
				t.onMouseDown.add(setOpts);
			}

			// Add node change handlers
			t.onMouseUp.add(t.nodeChanged);
			t.onClick.add(t.nodeChanged);
			t.onKeyUp.add(function(ed, e) {
				if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.keyCode == 46 || e.keyCode == 8 || e.ctrlKey)
					t.nodeChanged();
			});

			// Add reset handler
			t.onReset.add(function() {
				t.setContent(t.startContent, {format : 'raw'});
			});

			if (t.getParam('tab_focus')) {
				function tabCancel(ed, e) {
					if (e.keyCode === 9)
						return Event.cancel(e);
				};

				function tabHandler(ed, e) {
					var x, i, f, el, v;

					function find(d) {
						f = DOM.getParent(ed.id, 'form');
						el = f.elements;

						if (f) {
							each(el, function(e, i) {
								if (e.id == ed.id) {
									x = i;
									return false;
								}
							});

							if (d > 0) {
								for (i = x + 1; i < el.length; i++) {
									if (el[i].type != 'hidden')
										return el[i];
								}
							} else {
								for (i = x - 1; i >= 0; i--) {
									if (el[i].type != 'hidden')
										return el[i];
								}
							}
						}

						return null;
					};

					if (e.keyCode === 9) {
						v = explode(ed.getParam('tab_focus'));

						if (v.length == 1) {
							v[1] = v[0];
							v[0] = ':prev';
						}

						// Find element to focus
						if (e.shiftKey) {
							if (v[0] == ':prev')
								el = find(-1);
							else
								el = DOM.get(v[0]);
						} else {
							if (v[1] == ':next')
								el = find(1);
							else
								el = DOM.get(v[1]);
						}

						if (el) {
							if (ed = EditorManager.get(el.id || el.name))
								ed.focus();
							else
								window.setTimeout(function() {window.focus();el.focus();}, 10);

							return Event.cancel(e);
						}
					}
				};

				t.onKeyUp.add(tabCancel);

				if (isGecko) {
					t.onKeyPress.add(tabHandler);
					t.onKeyDown.add(tabCancel);
				} else
					t.onKeyDown.add(tabHandler);
			}

			// Add shortcuts
			if (s.custom_shortcuts) {
				if (s.custom_undo_redo_keyboard_shortcuts) {
					t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
					t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
				}

				// Add default shortcuts for gecko
				if (isGecko) {
					t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
					t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
					t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
				}

				// BlockFormat shortcuts keys
				for (i=1; i<=6; i++)
					t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']);

				t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
				t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
				t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);

				function find(e) {
					var v = null;

					if (!e.altKey && !e.ctrlKey && !e.metaKey)
						return v;

					each(t.shortcuts, function(o) {
						if (o.ctrl != e.ctrlKey && (!tinymce.isMac || o.ctrl == e.metaKey))
							return;

						if (o.alt != e.altKey)
							return;

						if (o.shift != e.shiftKey)
							return;

						if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
							v = o;
							return false;
						}
					});

					return v;
				};

				t.onKeyUp.add(function(ed, e) {
					var o = find(e);

					if (o)
						return Event.cancel(e);
				});

				t.onKeyPress.add(function(ed, e) {
					var o = find(e);

					if (o)
						return Event.cancel(e);
				});

				t.onKeyDown.add(function(ed, e) {
					var o = find(e);

					if (o) {
						o.func.call(o.scope);
						return Event.cancel(e);
					}
				});
			}

			if (tinymce.isIE) {
				// Fix so resize will only update the width and height attributes not the styles of an image
				// It will also block mceItemNoResize items
				Event.add(t.getDoc(), 'controlselect', function(e) {
					var re = t.resizeInfo, cb;

					e = e.target;

					// Don't do this action for non image elements
					if (e.nodeName !== 'IMG')
						return;

					if (re)
						Event.remove(re.node, re.ev, re.cb);

					if (!t.dom.hasClass(e, 'mceItemNoResize')) {
						ev = 'resizeend';
						cb = Event.add(e, ev, function(e) {
							var v;

							e = e.target;

							if (v = t.dom.getStyle(e, 'width')) {
								t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
								t.dom.setStyle(e, 'width', '');
							}

							if (v = t.dom.getStyle(e, 'height')) {
								t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
								t.dom.setStyle(e, 'height', '');
							}
						});
					} else {
						ev = 'resizestart';
						cb = Event.add(e, 'resizestart', Event.cancel, Event);
					}

					re = t.resizeInfo = {
						node : e,
						ev : ev,
						cb : cb
					};
				});

				t.onKeyDown.add(function(ed, e) {
					switch (e.keyCode) {
						case 8:
							// Fix IE control + backspace browser bug
							if (t.selection.getRng().item) {
								t.selection.getRng().item(0).removeNode();
								return Event.cancel(e);
							}
					}
				});
			}

			if (tinymce.isOpera) {
				t.onClick.add(function(ed, e) {
					Event.prevent(e);
				});
			}

			// Add custom undo/redo handlers
			if (s.custom_undo_redo) {
				function addUndo() {
					t.undoManager.typing = 0;
					t.undoManager.add();
				};

				// Add undo level on editor blur
				if (tinymce.isIE) {
					Event.add(t.getWin(), 'blur', function(e) {
						var n;

						// Check added for fullscreen bug
						if (t.selection) {
							n = t.selection.getNode();

							// Add undo level is selection was lost to another document
							if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc())
								addUndo();
						}
					});
				} else {
					Event.add(t.getDoc(), 'blur', function() {
						if (t.selection && !t.removed)
							addUndo();
					});
				}

				t.onMouseDown.add(addUndo);

				t.onKeyUp.add(function(ed, e) {
					if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) {
						t.undoManager.typing = 0;
						t.undoManager.add();
					}
				});

				t.onKeyDown.add(function(ed, e) {
					// Is caracter positon keys
					if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
						if (t.undoManager.typing) {
							t.undoManager.add();
							t.undoManager.typing = 0;
						}

						return;
					}

					if (!t.undoManager.typing) {
						t.undoManager.add();
						t.undoManager.typing = 1;
					}
				});
			}
		},

		_convertInlineElements : function() {
			var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp;

			function convert(ed, o) {
				if (!s.inline_styles)
					return;

				if (o.get) {
					each(t.dom.select('table,u,strike', o.node), function(n) {
						switch (n.nodeName) {
							case 'TABLE':
								if (v = dom.getAttrib(n, 'height')) {
									dom.setStyle(n, 'height', v);
									dom.setAttrib(n, 'height', '');
								}
								break;

							case 'U':
							case 'STRIKE':
								//sp = dom.create('span', {style : dom.getAttrib(n, 'style')});
								n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through';
								dom.setAttrib(n, 'mce_style', '');
								dom.setAttrib(n, 'mce_name', 'span');
								break;
						}
					});
				} else if (o.set) {
					each(t.dom.select('table,span', o.node).reverse(), function(n) {
						if (n.nodeName == 'TABLE') {
							if (v = dom.getStyle(n, 'height'))
								dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, ''));
						} else {
							// Convert spans to elements
							if (n.style.textDecoration == 'underline')
								na = 'u';
							else if (n.style.textDecoration == 'line-through')
								na = 'strike';
							else
								na = '';

							if (na) {
								n.style.textDecoration = '';
								dom.setAttrib(n, 'mce_style', '');

								e = dom.create(na, {
									style : dom.getAttrib(n, 'style')
								});

								dom.replace(e, n, 1);
							}
						}
					});
				}
			};

			t.onPreProcess.add(convert);

			if (!s.cleanup_on_startup) {
				t.onSetContent.add(function(ed, o) {
					if (o.initial)
						convert(t, {node : t.getBody(), set : 1});
				});
			}
		},

		_convertFonts : function() {
			var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl;

			// No need
			if (!s.inline_styles)
				return;

			// Font pt values and font size names
			fz = [8, 10, 12, 14, 18, 24, 36];
			fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];

			if (sl = s.font_size_style_values)
				sl = explode(sl);

			if (cl = s.font_size_classes)
				cl = explode(cl);

			function convertToFonts(no) {
				var n, f, nl, x, i, v, st;

				// Convert spans to fonts on non WebKit browsers
				if (tinymce.isWebKit || !s.inline_styles)
					return;

				nl = t.dom.select('span', no);
				for (x = nl.length - 1; x >= 0; x--) {
					n = nl[x];

					f = dom.create('font', {
						color : dom.toHex(dom.getStyle(n, 'color')),
						face : dom.getStyle(n, 'fontFamily'),
						style : dom.getAttrib(n, 'style'),
						'class' : dom.getAttrib(n, 'class')
					});

					// Clear color and font family
					st = f.style;
					if (st.color || st.fontFamily) {
						st.color = st.fontFamily = '';
						dom.setAttrib(f, 'mce_style', ''); // Remove cached style data
					}

					if (sl) {
						i = inArray(sl, dom.getStyle(n, 'fontSize'));

						if (i != -1) {
							dom.setAttrib(f, 'size', '' + (i + 1 || 1));
							f.style.fontSize = '';
						}
					} else if (cl) {
						i = inArray(cl, dom.getAttrib(n, 'class'));
						v = dom.getStyle(n, 'fontSize');

						if (i == -1 && v.indexOf('pt') > 0)
							i = inArray(fz, parseInt(v));

						if (i == -1)
							i = inArray(fzn, v);

						if (i != -1) {
							dom.setAttrib(f, 'size', '' + (i + 1 || 1));
							f.style.fontSize = '';
						}
					}

					if (f.color || f.face || f.size) {
						f.style.fontFamily = '';
						dom.setAttrib(f, 'mce_style', '');
						dom.replace(f, n, 1);
					}

					f = n = null;
				}
			};

			// Run on setup
			t.onSetContent.add(function(ed, o) {
				convertToFonts(ed.getBody());
			});

			// Run on cleanup
			t.onPreProcess.add(function(ed, o) {
				var n, sp, nl, x;

				// Keep unit tests happy
				if (!s.inline_styles)
					return;

				if (o.get) {
					nl = t.dom.select('font', o.node);
					for (x = nl.length - 1; x >= 0; x--) {
						n = nl[x];

						sp = dom.create('span', {
							style : dom.getAttrib(n, 'style'),
							'class' : dom.getAttrib(n, 'class')
						});

						dom.setStyles(sp, {
							fontFamily : dom.getAttrib(n, 'face'),
							color : dom.getAttrib(n, 'color'),
							backgroundColor : n.style.backgroundColor
						});

						if (n.size) {
							if (sl)
								dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);
							else
								dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]);
						}

						dom.setAttrib(sp, 'mce_style', '');
						dom.replace(sp, n, 1);
					}
				}
			});
		},

		_isHidden : function() {
			var s;

			if (!isGecko)
				return 0;

			// Weird, wheres that cursor selection?
			s = this.selection.getSel();
			return (!s || !s.rangeCount || s.rangeCount == 0);
		},

		// Fix for bug #1867292
		_fixNesting : function(s) {
			var d = [], i;

			s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {
				var e;

				// Handle end element
				if (b === '/') {
					if (!d.length)
						return '';

					if (c !== d[d.length - 1].tag) {
						for (i=d.length - 1; i>=0; i--) {
							if (d[i].tag === c) {
								d[i].close = 1;
								break;
							}
						}

						return '';
					} else {
						d.pop();

						if (d.length && d[d.length - 1].close) {
							a = a + '</' + d[d.length - 1].tag + '>';
							d.pop();
						}
					}
				} else {
					// Ignore these
					if (/^(br|hr|input|meta|img|link|param)$/i.test(c))
						return a;

					// Ignore closed ones
					if (/\/>$/.test(a))
						return a;

					d.push({tag : c}); // Push start element
				}

				return a;
			});

			// End all open tags
			for (i=d.length - 1; i>=0; i--)
				s += '</' + d[i].tag + '>';

			return s;
		}

		});
})();

/* file:jscripts/tiny_mce/classes/EditorCommands.js */

(function() {
	var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;

	tinymce.create('tinymce.EditorCommands', {
		EditorCommands : function(ed) {
			this.editor = ed;
		},

		execCommand : function(cmd, ui, val) {
			var t = this, ed = t.editor, f;

			switch (cmd) {
				case 'Cut':
				case 'Copy':
				case 'Paste':
					try {
						ed.getDoc().execCommand(cmd, ui, val);
					} catch (ex) {
						if (isGecko) {
							ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) {
								if (s)
									window.open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', 'mceExternal');
							});
						} else
							ed.windowManager.alert(ed.getLang('clipboard_no_support'));
					}

					return true;

				// Ignore these
				case 'mceResetDesignMode':
				case 'mceBeginUndoLevel':
					return true;

				// Ignore these
				case 'unlink':
					t.UnLink();
					return true;

				// Bundle these together
				case 'JustifyLeft':
				case 'JustifyCenter':
				case 'JustifyRight':
				case 'JustifyFull':
					t.mceJustify(cmd, cmd.substring(7).toLowerCase());
					return true;

				case 'mceEndUndoLevel':
				case 'mceAddUndoLevel':
					ed.undoManager.add();
					return true;

				default:
					f = this[cmd];

					if (f) {
						f.call(this, ui, val);
						return true;
					}
			}

			return false;
		},

		Indent : function() {
			var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;

			// Setup indent level
			iv = ed.settings.indentation;
			iu = /[a-z%]+$/i.exec(iv);
			iv = parseInt(iv);

			if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
				each(this._getSelectedBlocks(), function(e) {
					d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);
				});

				return;
			}

			ed.getDoc().execCommand('Indent', false, null);

			if (isIE) {
				d.getParent(s.getNode(), function(n) {
					if (n.nodeName == 'BLOCKQUOTE') {
						n.dir = n.style.cssText = '';
					}
				});
			}
		},

		Outdent : function() {
			var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;

			// Setup indent level
			iv = ed.settings.indentation;
			iu = /[a-z%]+$/i.exec(iv);
			iv = parseInt(iv);

			if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
				each(this._getSelectedBlocks(), function(e) {
					v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);
					d.setStyle(e, 'paddingLeft', v ? v + iu : '');
				});

				return;
			}

			ed.getDoc().execCommand('Outdent', false, null);
		},

		mceSetAttribute : function(u, v) {
			var ed = this.editor, d = ed.dom, e;

			if (e = d.getParent(ed.selection.getNode(), d.isBlock))
				d.setAttrib(e, v.name, v.value);
		},

		mceSetContent : function(u, v) {
			this.editor.setContent(v);
		},

		mceToggleVisualAid : function() {
			var ed = this.editor;

			ed.hasVisual = !ed.hasVisual;
			ed.addVisual();
		},

		mceReplaceContent : function(u, v) {
			var s = this.editor.selection;

			s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));
		},

		mceInsertLink : function(u, v) {
			var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'A');

			if (tinymce.is(v, 'string'))
				v = {href : v};

			function set(e) {
				each(v, function(v, k) {
					ed.dom.setAttrib(e, k, v);
				});
			};

			if (!e) {
				ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
				each(ed.dom.select('a'), function(e) {
					if (e.href == 'javascript:mctmp(0);')
						set(e);
				});
			} else {
				if (v.href)
					set(e);
				else
					ed.dom.remove(e, 1);
			}
		},

		UnLink : function() {
			var ed = this.editor, s = ed.selection;

			if (s.isCollapsed())
				s.select(s.getNode());

			ed.getDoc().execCommand('unlink', false, null);
			s.collapse(0);
		},

		FontName : function(u, v) {
			var t = this, ed = t.editor, s = ed.selection, e;

			if (!v) {
				if (s.isCollapsed())
					s.select(s.getNode());

				t.RemoveFormat();
			} else
				ed.getDoc().execCommand('FontName', false, v);
		},

		queryCommandValue : function(c) {
			var f = this['queryValue' + c];

			if (f)
				return f.call(this, c);

			return false;
		},

		queryCommandState : function(cmd) {
			var f;

			switch (cmd) {
				// Bundle these together
				case 'JustifyLeft':
				case 'JustifyCenter':
				case 'JustifyRight':
				case 'JustifyFull':
					return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());

				default:
					if (f = this['queryState' + cmd])
						return f.call(this, cmd);
			}

			return -1;
		},

		_queryState : function(c) {
			try {
				return this.editor.getDoc().queryCommandState(c);
			} catch (ex) {
				// Ignore exception
			}
		},

		_queryVal : function(c) {
			try {
				return this.editor.getDoc().queryCommandValue(c);
			} catch (ex) {
				// Ignore exception
			}
		},

		queryValueFontSize : function() {
			var ed = this.editor, v = 0, p;

			if (isOpera || isWebKit) {
				if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
					v = p.size;

				return v;
			}

			return this._queryVal('FontSize');
		},

		queryValueFontName : function() {
			var ed = this.editor, v = 0, p;

			if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
				v = p.face;

			if (!v)
				v = this._queryVal('FontName');

			return v;
		},

		mceJustify : function(c, v) {
			var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;

			if (ed.settings.inline_styles && this.queryStateJustify(c, v))
				rm = 1;

			bl = dom.getParent(n, ed.dom.isBlock);

			if (nn == 'IMG') {
				if (v == 'full')
					return;

				if (rm) {
					if (v == 'center')
						dom.setStyle(n.parentNode, 'textAlign', '');

					dom.setStyle(n, 'float', '');
					this.mceRepaint();
					return;
				}

				if (v == 'center') {
					// Do not change table elements
					if (/^(TD|TH)$/.test(bl.nodeName))
						bl = 0;

					if (!bl || bl.childNodes.length > 1) {
						nb = dom.create('p');
						nb.appendChild(n.cloneNode(false));

						if (bl)
							dom.insertAfter(nb, bl);
						else
							dom.insertAfter(nb, n);

						dom.remove(n);
						n = nb.firstChild;
						bl = nb;
					}

					dom.setStyle(bl, 'textAlign', v);
					dom.setStyle(n, 'float', '');
				} else {
					dom.setStyle(n, 'float', v);
					dom.setStyle(n.parentNode, 'textAlign', '');
				}

				this.mceRepaint();
				return;
			}

			// Handle the alignment outselfs, less quirks in all browsers
			if (ed.settings.inline_styles && ed.settings.forced_root_block) {
				if (rm)
					v = '';

				each(this._getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {
					dom.setAttrib(e, 'align', '');
					dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);
				});

				return;
			} else if (!rm)
				ed.getDoc().execCommand(c, false, null);

			if (ed.settings.inline_styles) {
				if (rm) {
					dom.getParent(ed.selection.getNode(), function(n) {
						if (n.style && n.style.textAlign)
							dom.setStyle(n, 'textAlign', '');
					});

					return;
				}

				each(dom.select('*'), function(n) {
					var v = n.align;

					if (v) {
						if (v == 'full')
							v = 'justify';

						dom.setStyle(n, 'textAlign', v);
						dom.setAttrib(n, 'align', '');
					}
				});
			}
		},

		mceSetCSSClass : function(u, v) {
			this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});
		},

		getSelectedElement : function() {
			var t = this, ed = t.editor, dom = ed.dom, se = ed.selection, r = se.getRng(), r1, r2, sc, ec, so, eo, e, sp, ep, re;

			if (se.isCollapsed() || r.item)
				return se.getNode();

			// Setup regexp
			re = ed.settings.merge_styles_invalid_parents;
			if (tinymce.is(re, 'string'))
				re = new RegExp(re, 'i');

			if (isIE) {
				r1 = r.duplicate();
				r1.collapse(true);
				sc = r1.parentElement();

				r2 = r.duplicate();
				r2.collapse(false);
				ec = r2.parentElement();

				if (sc != ec) {
					r1.move('character', 1);
					sc = r1.parentElement();
				}

				if (sc == ec) {
					r1 = r.duplicate();
					r1.moveToElementText(sc);

					if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)
						return re && re.test(sc.nodeName) ? null : sc;
				}
			} else {
				function getParent(n) {
					return dom.getParent(n, function(n) {return n.nodeType == 1;});
				};

				sc = r.startContainer;
				ec = r.endContainer;
				so = r.startOffset;
				eo = r.endOffset;

				if (!r.collapsed) {
					if (sc == ec) {
						if (so - eo < 2) {
							if (sc.hasChildNodes()) {
								sp = sc.childNodes[so];
								return re && re.test(sp.nodeName) ? null : sp;
							}
						}
					}
				}

				if (sc.nodeType != 3 || ec.nodeType != 3)
					return null;

				if (so == 0) {
					sp = getParent(sc);

					if (sp && sp.firstChild != sc)
						sp = null;
				}

				if (so == sc.nodeValue.length) {
					e = sc.nextSibling;

					if (e && e.nodeType == 1)
						sp = sc.nextSibling;
				}

				if (eo == 0) {
					e = ec.previousSibling;

					if (e && e.nodeType == 1)
						ep = e;
				}

				if (eo == ec.nodeValue.length) {
					ep = getParent(ec);

					if (ep && ep.lastChild != ec)
						ep = null;
				}

				// Same element
				if (sp == ep)
					return re && sp && re.test(sp.nodeName) ? null : sp;
			}

			return null;
		},

		InsertHorizontalRule : function() {
			// Fix for Gecko <hr size="1" /> issue and IE bug rep(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]");
			if (isGecko || isIE)
				this.editor.selection.setContent('<hr />');
			else
				this.editor.getDoc().execCommand('InsertHorizontalRule', false, '');
		},

		RemoveFormat : function() {
			var t = this, ed = t.editor, s = ed.selection, b;

			// Safari breaks tables
			if (isWebKit)
				s.setContent(s.getContent({format : 'raw'}).replace(/(<(span|b|i|strong|em|strike) [^>]+>|<(span|b|i|strong|em|strike)>|<\/(span|b|i|strong|em|strike)>|)/g, ''), {format : 'raw'});
			else
				ed.getDoc().execCommand('RemoveFormat', false, null);

			t.mceSetStyleInfo(0, {command : 'removeformat'});
			ed.addVisual();
		},

		mceSetStyleInfo : function(u, v) {
			var t = this, ed = t.editor, d = ed.getDoc(), dom = ed.dom, e, b, s = ed.selection, nn = v.wrapper || 'span', b = s.getBookmark(), re;

			function set(n, e) {
				if (n.nodeType == 1) {
					switch (v.command) {
						case 'setattrib':
							return dom.setAttrib(n, v.name, v.value);

						case 'setstyle':
							return dom.setStyle(n, v.name, v.value);

						case 'removeformat':
							return dom.setAttrib(n, 'class', '');
					}
				}
			};

			// Setup regexp
			re = ed.settings.merge_styles_invalid_parents;
			if (tinymce.is(re, 'string'))
				re = new RegExp(re, 'i');

			// Set style info on selected element
			if (e = t.getSelectedElement())
				set(e, 1);
			else {
				// Generate wrappers and set styles on them
				d.execCommand('FontName', false, '__');
				each(isWebKit ? dom.select('span') : dom.select('font'), function(n) {
					var sp, e;

					if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
						sp = dom.create(nn, {mce_new : '1'});

						set(sp);

						each (n.childNodes, function(n) {
							sp.appendChild(n.cloneNode(true));
						});

						dom.replace(sp, n);
					}
				});
			}

			// Remove wrappers inside new ones
			each(dom.select(nn).reverse(), function(n) {
				var p = n.parentNode;

				// Check if it's an old span in a new wrapper
				if (!dom.getAttrib(n, 'mce_new')) {
					// Find new wrapper
					p = dom.getParent(n, function(n) {
						return n.nodeType == 1 && dom.getAttrib(n, 'mce_new');
					});

					if (p)
						dom.remove(n, 1);
				}
			});

			// Merge wrappers with parent wrappers
			each(dom.select(nn).reverse(), function(n) {
				var p = n.parentNode;

				if (!p || !dom.getAttrib(n, 'mce_new'))
					return;

				// Has parent of the same type and only child
				if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)
					return dom.remove(p, 1);

				// Has parent that is more suitable to have the class and only child
				if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {
					set(p); // Set style info on parent instead
					dom.setAttrib(n, 'class', '');
				}
			});

			// Remove empty wrappers
			each(dom.select(nn).reverse(), function(n) {
				if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {
					if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))
						return dom.remove(n, 1);

					dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker
				}
			});

			s.moveToBookmark(b);
		},

		queryStateJustify : function(c, v) {
			var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;

			if (n && n.nodeName == 'IMG') {
				if (dom.getStyle(n, 'float') == v)
					return 1;

				return n.parentNode.style.textAlign == v;
			}

			n = dom.getParent(ed.selection.getStart(), function(n) {
				return n.nodeType == 1 && n.style.textAlign;
			});

			if (v == 'full')
				v = 'justify';

			if (ed.settings.inline_styles)
				return (n && n.style.textAlign == v);

			return this._queryState(c);
		},

		HiliteColor : function(ui, val) {
			var t = this, ed = t.editor, d = ed.getDoc();

			function set(s) {
				if (!isGecko)
					return;

				try {
					// Try new Gecko method
					d.execCommand("styleWithCSS", 0, s);
				} catch (ex) {
					// Use old
					d.execCommand("useCSS", 0, !s);
				}
			};

			if (isGecko || isOpera) {
				set(true);
				d.execCommand('hilitecolor', false, val);
				set(false);
			} else
				d.execCommand('BackColor', false, val);
		},

		Undo : function() {
			var ed = this.editor;

			if (ed.settings.custom_undo_redo) {
				ed.undoManager.undo();
				ed.nodeChanged();
			} else
				ed.getDoc().execCommand('Undo', false, null);
		},

		Redo : function() {
			var ed = this.editor;

			if (ed.settings.custom_undo_redo) {
				ed.undoManager.redo();
				ed.nodeChanged();
			} else
				ed.getDoc().execCommand('Redo', false, null);
		},

		FormatBlock : function(ui, val) {
			var t = this, ed = t.editor;

			val = ed.settings.forced_root_block ? (val || '<p>') : val;

			if (/^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(ed.selection.getNode().nodeName))
				t.mceRemoveNode();

			if (val.indexOf('<') == -1)
				val = '<' + val + '>';

			if (tinymce.isGecko)
				val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');

			ed.getDoc().execCommand('FormatBlock', false, val);
		},

		mceCleanup : function() {
			var ed = this.editor, s = ed.selection, b = s.getBookmark();
			ed.setContent(ed.getContent());
			s.moveToBookmark(b);
		},

		mceRemoveNode : function(ui, val) {
			var ed = this.editor, s = ed.selection, b, n = val || s.getNode();

			// Make sure that the body node isn't removed
			if (n == ed.getBody())
				return;

			b = s.getBookmark();
			ed.dom.remove(n, 1);
			s.moveToBookmark(b);
			ed.nodeChanged();
		},

		mceSelectNodeDepth : function(ui, val) {
			var ed = this.editor, s = ed.selection, c = 0;

			ed.dom.getParent(s.getNode(), function(n) {
				if (n.nodeType == 1 && c++ == val) {
					s.select(n);
					ed.nodeChanged();
					return false;
				}
			}, ed.getBody());
		},

		mceSelectNode : function(u, v) {
			this.editor.selection.select(v);
		},

		mceInsertContent : function(ui, val) {
			this.editor.selection.setContent(val);
		},

		mceInsertRawHTML : function(ui, val) {
			var ed = this.editor;

			ed.selection.setContent('tiny_mce_marker');
			ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));
		},

		mceRepaint : function() {
			var s, b, e = this.editor;

			if (tinymce.isGecko) {
				try {
					s = e.selection;
					b = s.getBookmark(true);

					if (s.getSel())
						s.getSel().selectAllChildren(e.getBody());

					s.collapse(true);
					s.moveToBookmark(b);
				} catch (ex) {
					// Ignore
				}
			}
		},

		queryStateUnderline : function() {
			var ed = this.editor, n = ed.selection.getNode();

			if (n && n.nodeName == 'A')
				return false;

			return this._queryState('Underline');
		},

		queryStateOutdent : function() {
			var ed = this.editor, n;

			if (ed.settings.inline_styles) {
				if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
					return true;

				if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
					return true;
			} else
				return !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE');

			return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList();
		},

		queryStateInsertUnorderedList : function() {
			return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');
		},

		queryStateInsertOrderedList : function() {
			return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');
		},

		queryStatemceBlockQuote : function() {
			return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});
		},

		mceBlockQuote : function() {
			var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;

			function getBQ(e) {
				return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
			};

			// Get start/end block
			sb = dom.getParent(s.getStart(), dom.isBlock);
			eb = dom.getParent(s.getEnd(), dom.isBlock);

			// Remove blockquote(s)
			if (bq = getBQ(sb)) {
				if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
					bm = s.getBookmark();

				// Move all elements after the end block into new bq
				if (getBQ(eb)) {
					bq2 = bq.cloneNode(false);

					while (n = eb.nextSibling)
						bq2.appendChild(n.parentNode.removeChild(n));
				}

				// Add new bq after
				if (bq2)
					dom.insertAfter(bq2, bq);

				// Move all selected blocks after the current bq
				nl = t._getSelectedBlocks(sb, eb);
				for (i = nl.length - 1; i >= 0; i--) {
					dom.insertAfter(nl[i], bq);
				}

				// Empty bq, then remove it
				if (/^\s*$/.test(bq.innerHTML))
					dom.remove(bq, 1); // Keep children so boomark restoration works correctly

				// Empty bq, then remote it
				if (bq2 && /^\s*$/.test(bq2.innerHTML))
					dom.remove(bq2, 1); // Keep children so boomark restoration works correctly

				if (!bm) {
					// Move caret inside empty block element
					if (!isIE) {
						r = ed.getDoc().createRange();
						r.setStart(sb, 0);
						r.setEnd(sb, 0);
						s.setRng(r);
					} else {
						s.select(sb);
						s.collapse(0);

						// IE misses the empty block some times element so we must move back the caret
						if (dom.getParent(s.getStart(), dom.isBlock) != sb) {
							r = s.getRng();
							r.move('character', -1);
							r.select();
						}
					}
				} else
					t.editor.selection.moveToBookmark(bm);

				return;
			}

			// Since IE can start with a totally empty document we need to add the first bq and paragraph
			if (isIE && !sb && !eb) {
				t.editor.getDoc().execCommand('Indent');
				n = getBQ(s.getNode());
				n.style.margin = n.dir = ''; // IE adds margin and dir to bq
				return;
			}

			if (!sb || !eb)
				return;

			// If empty paragraph node then do not use bookmark
			if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
				bm = s.getBookmark();

			// Move selected block elements into a bq
			each(t._getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {
				// Found existing BQ add to this one
				if (e.nodeName == 'BLOCKQUOTE' && !bq) {
					bq = e;
					return;
				}

				// No BQ found, create one
				if (!bq) {
					bq = dom.create('blockquote');
					e.parentNode.insertBefore(bq, e);
				}

				// Add children from existing BQ
				if (e.nodeName == 'BLOCKQUOTE' && bq) {
					n = e.firstChild;

					while (n) {
						bq.appendChild(n.cloneNode(true));
						n = n.nextSibling;
					}

					dom.remove(e);
					return;
				}

				// Add non BQ element to BQ
				bq.appendChild(dom.remove(e));
			});

			if (!bm) {
				// Move caret inside empty block element
				if (!isIE) {
					r = ed.getDoc().createRange();
					r.setStart(sb, 0);
					r.setEnd(sb, 0);
					s.setRng(r);
				} else {
					s.select(sb);
					s.collapse(1);
				}
			} else
				s.moveToBookmark(bm);
		},
/*
		_mceBlockQuote : function() {
			var t = this, s = t.editor.selection, b = s.getBookmark(), bq, dom = t.editor.dom;

			function findBQ(e) {
				return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
			};

			// Remove blockquote(s)
			if (findBQ(s.getStart())) {
				each(t._getSelectedBlocks(findBQ(s.getStart()), findBQ(s.getEnd())), function(e) {
					// Found BQ lets remove it
					if (e.nodeName == 'BLOCKQUOTE')
						dom.remove(e, 1);
				});

				t.editor.selection.moveToBookmark(b);
				return;
			}

			each(t._getSelectedBlocks(findBQ(s.getStart()), findBQ(s.getEnd())), function(e) {
				var n;

				// Found existing BQ add to this one
				if (e.nodeName == 'BLOCKQUOTE' && !bq) {
					bq = e;
					return;
				}

				// No BQ found, create one
				if (!bq) {
					bq = dom.create('blockquote');
					e.parentNode.insertBefore(bq, e);
				}

				// Add children from existing BQ
				if (e.nodeName == 'BLOCKQUOTE' && bq) {
					n = e.firstChild;

					while (n) {
						bq.appendChild(n.cloneNode(true));
						n = n.nextSibling;
					}

					dom.remove(e);

					return;
				}

				// Add non BQ element to BQ
				bq.appendChild(dom.remove(e));
			});

			t.editor.selection.moveToBookmark(b);
		},
*/
		_getSelectedBlocks : function(st, en) {
			var ed = this.editor, dom = ed.dom, s = ed.selection, sb, eb, n, bl = [];

			sb = dom.getParent(st || s.getStart(), dom.isBlock);
			eb = dom.getParent(en || s.getEnd(), dom.isBlock);

			if (sb)
				bl.push(sb);

			if (sb && eb && sb != eb) {
				n = sb;

				while ((n = n.nextSibling) && n != eb) {
					if (dom.isBlock(n))
						bl.push(n);
				}
			}

			if (eb && sb != eb)
				bl.push(eb);

			return bl;
		}
	});
})();


/* file:jscripts/tiny_mce/classes/UndoManager.js */

tinymce.create('tinymce.UndoManager', {
	index : 0,
	data : null,
	typing : 0,

	UndoManager : function(ed) {
		var t = this, Dispatcher = tinymce.util.Dispatcher;

		t.editor = ed;
		t.data = [];
		t.onAdd = new Dispatcher(this);
		t.onUndo = new Dispatcher(this);
		t.onRedo = new Dispatcher(this);
	},

	add : function(l) {
		var t = this, i, ed = t.editor, b, s = ed.settings, la;

		l = l || {};
		l.content = l.content || ed.getContent({format : 'raw', no_events : 1});

		// Add undo level if needed
		l.content = l.content.replace(/^\s*|\s*$/g, '');
		la = t.data[t.index > 0 ? t.index - 1 : 0];
		if (!l.initial && la && l.content == la.content)
			return null;

		// Time to compress
		if (s.custom_undo_redo_levels) {
			if (t.data.length > s.custom_undo_redo_levels) {
				for (i = 0; i < t.data.length - 1; i++)
					t.data[i] = t.data[i + 1];

				t.data.length--;
				t.index = t.data.length;
			}
		}

		if (s.custom_undo_redo_restore_selection && !l.initial)
			l.bookmark = b = l.bookmark || ed.selection.getBookmark();

		if (t.index < t.data.length && t.data[t.index].initial)
			t.index++;

		// Only initial marked undo levels should be allowed as first item
		// This to workaround a bug with Firefox and the blur event
		if (t.data.length === 0 && !l.initial)
			return null;

		// Add level
		t.data.length = t.index + 1;
		t.data[t.index++] = l;

		if (l.initial)
			t.index = 0;

		// Set initial bookmark use first real undo level
		if (t.data.length == 2 && t.data[0].initial)
			t.data[0].bookmark = b;

		t.onAdd.dispatch(t, l);
		ed.isNotDirty = 0;

		//console.dir(t.data);

		return l;
	},

	undo : function() {
		var t = this, ed = t.editor, l = l, i;

		if (t.typing) {
			t.add();
			t.typing = 0;
		}

		if (t.index > 0) {
			// If undo on last index then take snapshot
			if (t.index == t.data.length && t.index > 1) {
				i = t.index;
				t.typing = 0;

				if (!t.add())
					t.index = i;

				--t.index;
			}

			l = t.data[--t.index];
			ed.setContent(l.content, {format : 'raw'});
			ed.selection.moveToBookmark(l.bookmark);

			t.onUndo.dispatch(t, l);
		}

		return l;
	},

	redo : function() {
		var t = this, ed = t.editor, l = null;

		if (t.index < t.data.length - 1) {
			l = t.data[++t.index];
			ed.setContent(l.content, {format : 'raw'});
			ed.selection.moveToBookmark(l.bookmark);

			t.onRedo.dispatch(t, l);
		}

		return l;
	},

	clear : function() {
		var t = this;

		t.data = [];
		t.index = 0;
		t.typing = 0;
		t.add({initial : true});
	},

	hasUndo : function() {
		return this.index != 0 || this.typing;
	},

	hasRedo : function() {
		return this.index < this.data.length - 1;
	}

	});
/* file:jscripts/tiny_mce/classes/ForceBlocks.js */

(function() {
	// Shorten names
	var Event, isIE, isGecko, isOpera, each, extend;

	Event = tinymce.dom.Event;
	isIE = tinymce.isIE;
	isGecko = tinymce.isGecko;
	isOpera = tinymce.isOpera;
	each = tinymce.each;
	extend = tinymce.extend;

	tinymce.create('tinymce.ForceBlocks', {
		ForceBlocks : function(ed) {
			var t = this, s = ed.settings, elm;

			t.editor = ed;
			t.dom = ed.dom;
			elm = (s.forced_root_block || 'p').toLowerCase();
			s.element = elm.toUpperCase();

			ed.onPreInit.add(t.setup, t);

			t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');
			t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');
			t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');
			t.reNbsp2BR2 = new RegExp('<p( )([^>]+)>(&nbsp;|&#160;)<\\\/p>|<p>(&nbsp;|&#160;)<\\\/p>'.replace(/p/g, elm), 'gi');
			t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
			t.reTrailBr = new RegExp('\\s*<br \\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');

			function padd(ed, o) {
				if (isOpera)
					o.content = o.content.replace(t.reOpera, '</' + elm + '>');

				o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');

				if (!isIE && !isOpera && o.set) {
					// Use &nbsp; instead of BR in padded paragraphs
					o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');
					o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');
				} else {
					o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');
					o.content = o.content.replace(t.reTrailBr, '</' + elm + '>');
				}
			};

			ed.onBeforeSetContent.add(padd);
			ed.onPostProcess.add(padd);

			if (s.forced_root_block) {
				ed.onInit.add(t.forceRoots, t);
				ed.onSetContent.add(t.forceRoots, t);
				ed.onBeforeGetContent.add(t.forceRoots, t);
			}
		},

		setup : function() {
			var t = this, ed = t.editor, s = ed.settings;

			// Force root blocks when typing and when getting output
			if (s.forced_root_block) {
				ed.onKeyUp.add(t.forceRoots, t);
				ed.onPreProcess.add(t.forceRoots, t);
			}

			if (s.force_br_newlines) {
				// Force IE to produce BRs on enter
				if (isIE) {
					ed.onKeyPress.add(function(ed, e) {
						var n, s = ed.selection;

						if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {
							s.setContent('<br id="__" /> ', {format : 'raw'});
							n = ed.dom.get('__');
							n.removeAttribute('id');
							s.select(n);
							s.collapse();
							return Event.cancel(e);
						}
					});
				}

				return;
			}

			if (!isIE && s.force_p_newlines) {
/*				ed.onPreProcess.add(function(ed, o) {
					each(ed.dom.select('br', o.node), function(n) {
						var p = n.parentNode;

						// Replace <p><br /></p> with <p>&nbsp;</p>
						if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
							p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
						}
					});
				});*/

				ed.onKeyPress.add(function(ed, e) {
					if (e.keyCode == 13 && !e.shiftKey) {
						if (!t.insertPara(e))
							Event.cancel(e);
					}
				});

				if (isGecko) {
					ed.onKeyDown.add(function(ed, e) {
						if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
							t.backspaceDelete(e, e.keyCode == 8);
					});
				}
			}

			function ren(rn, na) {
				var ne = ed.dom.create(na);

				each(rn.attributes, function(a) {
					if (a.specified && a.nodeValue)
						ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue);
				});

				each(rn.childNodes, function(n) {
					ne.appendChild(n.cloneNode(true));
				});

				rn.parentNode.replaceChild(ne, rn);

				return ne;
			};

			// Replaces IE:s auto generated paragraphs with the specified element name
			if (isIE && s.element != 'P') {
				ed.onKeyPress.add(function(ed, e) {
					t.lastElm = ed.selection.getNode().nodeName;
				});

				ed.onKeyUp.add(function(ed, e) {
					var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody();

					if (b.childNodes.length === 1 && n.nodeName == 'P') {
						n = ren(n, s.element);
						sel.select(n);
						sel.collapse();
						ed.nodeChanged();
					} else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
						bl = ed.dom.getParent(n, 'P');

						if (bl) {
							ren(bl, s.element);
							ed.nodeChanged();
						}
					}
				});
			}
		},

		find : function(n, t, s) {
			var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1;

			while (n = w.nextNode()) {
				c++;

				// Index by node
				if (t == 0 && n == s)
					return c;

				// Node by index
				if (t == 1 && c == s)
					return n;
			}

			return -1;
		},

		forceRoots : function(ed, e) {
			var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF;
			var nx, bl, bp, sp, le, nl = b.childNodes, i;

			// Fix for bug #1863847
			if (e && e.keyCode == 13)
				return true;

			// Wrap non blocks into blocks
			for (i = nl.length - 1; i >= 0; i--) {
				nx = nl[i];

				// Is text or non block element
				if (nx.nodeType == 3 || (!t.dom.isBlock(nx) && nx.nodeType != 8)) {
					if (!bl) {
						// Create new block but ignore whitespace
						if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {
							// Store selection
							if (si == -2 && r) {
								if (!isIE) {
									so = r.startOffset;
									eo = r.endOffset;
									si = t.find(b, 0, r.startContainer);
									ei = t.find(b, 0, r.endContainer);
								} else {
									tr = d.body.createTextRange();
									tr.moveToElementText(b);
									tr.collapse(1);
									bp = tr.move('character', c) * -1;

									tr = r.duplicate();
									tr.collapse(1);
									sp = tr.move('character', c) * -1;

									tr = r.duplicate();
									tr.collapse(0);
									le = (tr.move('character', c) * -1) - sp;

									si = sp - bp;
									ei = le;
								}
							}

							bl = ed.dom.create(ed.settings.forced_root_block);
							bl.appendChild(nx.cloneNode(1));
							nx.parentNode.replaceChild(bl, nx);
						}
					} else {
						if (bl.hasChildNodes())
							bl.insertBefore(nx, bl.firstChild);
						else
							bl.appendChild(nx);
					}
				} else
					bl = null; // Time to create new block
			}

			// Restore selection
			if (si != -2) {
				if (!isIE) {
					bl = d.getElementsByTagName(ed.settings.element)[0];
					r = d.createRange();

					// Select last location or generated block
					if (si != -1)
						r.setStart(t.find(b, 1, si), so);
					else
						r.setStart(bl, 0);

					// Select last location or generated block
					if (ei != -1)
						r.setEnd(t.find(b, 1, ei), eo);
					else
						r.setEnd(bl, 0);

					if (s) {
						s.removeAllRanges();
						s.addRange(r);
					}
				} else {
					try {
						r = s.createRange();
						r.moveToElementText(b);
						r.collapse(1);
						r.moveStart('character', si);
						r.moveEnd('character', ei);
						r.select();
					} catch (ex) {
						// Ignore
					}
				}
			}
		},

		getParentBlock : function(n) {
			var d = this.dom;

			return d.getParent(n, d.isBlock);
		},

		insertPara : function(e) {
			var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;
			var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch;

			function isEmpty(n) {
				n = n.innerHTML;
				n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
				n = n.replace(/<[^>]+>/g, ''); // Remove all tags

				return n.replace(/[ \t\r\n]+/g, '') == '';
			};

			// If root blocks are forced then use Operas default behavior since it's really good
// Removed due to bug: #1853816
//			if (se.forced_root_block && isOpera)
//				return true;

			// Setup before range
			rb = d.createRange();

			// If is before the first block element and in body, then move it into first block element
			rb.setStart(s.anchorNode, s.anchorOffset);
			rb.collapse(true);

			// Setup after range
			ra = d.createRange();

			// If is before the first block element and in body, then move it into first block element
			ra.setStart(s.focusNode, s.focusOffset);
			ra.collapse(true);

			// Setup start/end points
			dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
			sn = dir ? s.anchorNode : s.focusNode;
			so = dir ? s.anchorOffset : s.focusOffset;
			en = dir ? s.focusNode : s.anchorNode;
			eo = dir ? s.focusOffset : s.anchorOffset;

			// If selection is in empty table cell
			if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
				dom.remove(sn.firstChild); // Remove BR

				// Create two new block elements
				ed.dom.add(sn, se.element, null, '<br />');
				aft = ed.dom.add(sn, se.element, null, '<br />');

				// Move caret into the last one
				r = d.createRange();
				r.selectNodeContents(aft);
				r.collapse(1);
				ed.selection.setRng(r);

				return false;
			}

			// If the caret is in an invalid location in FF we need to move it into the first block
			if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
				sn = en = sn.firstChild;
				so = eo = 0;
				rb = d.createRange();
				rb.setStart(sn, 0);
				ra = d.createRange();
				ra.setStart(en, 0);
			}

			// Never use body as start or end node
			sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
			sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
			en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
			en = en.nodeName == "BODY" ? en.firstChild : en;

			// Get start and end blocks
			sb = t.getParentBlock(sn);
			eb = t.getParentBlock(en);
			bn = sb ? sb.nodeName : se.element; // Get block name to create

			// Return inside list use default browser behavior
			if (t.dom.getParent(sb, function(n) { return /OL|UL|PRE/.test(n.nodeName); }))
				return true;

			// If caption or absolute layers then always generate new blocks within
			if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|static/gi.test(sb.style.position))) {
				bn = se.element;
				sb = null;
			}

			// If caption or absolute layers then always generate new blocks within
			if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|static/gi.test(eb.style.position))) {
				bn = se.element;
				eb = null;
			}

			// Use P instead
			if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(sb.style.cssFloat))) {
				bn = se.element;
				sb = eb = null;
			}

			// Setup new before and after blocks
			bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
			aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);

			// Remove id from after clone
			aft.removeAttribute('id');

			// Is header and cursor is at the end, then force paragraph under
			if (/^(H[1-6])$/.test(bn) && sn.nodeValue && so == sn.nodeValue.length)
				aft = ed.dom.create(se.element);

			// Find start chop node
			n = sc = sn;
			do {
				if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
					break;

				sc = n;
			} while ((n = n.previousSibling ? n.previousSibling : n.parentNode));

			// Find end chop node
			n = ec = en;
			do {
				if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
					break;

				ec = n;
			} while ((n = n.nextSibling ? n.nextSibling : n.parentNode));

			// Place first chop part into before block element
			if (sc.nodeName == bn)
				rb.setStart(sc, 0);
			else
				rb.setStartBefore(sc);

			rb.setEnd(sn, so);
			bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari

			// Place secnd chop part within new block element
			try {
				ra.setEndAfter(ec);
			} catch(ex) {
				//console.debug(s.focusNode, s.focusOffset);
			}

			ra.setStart(en, eo);
			aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari

			// Create range around everything
			r = d.createRange();
			if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
				r.setStartBefore(sc.parentNode);
			} else {
				if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
					r.setStartBefore(rb.startContainer);
				else
					r.setStart(rb.startContainer, rb.startOffset);
			}

			if (!ec.nextSibling && ec.parentNode.nodeName == bn)
				r.setEndAfter(ec.parentNode);
			else
				r.setEnd(ra.endContainer, ra.endOffset);

			// Delete and replace it with new block elements
			r.deleteContents();

			if (isOpera)
				ed.getWin().scrollTo(0, vp.y);

			// Never wrap blocks in blocks
			if (bef.firstChild && bef.firstChild.nodeName == bn)
				bef.innerHTML = bef.firstChild.innerHTML;

			if (aft.firstChild && aft.firstChild.nodeName == bn)
				aft.innerHTML = aft.firstChild.innerHTML;

			// Padd empty blocks
			if (isEmpty(bef))
				bef.innerHTML = '<br />';

			if (isEmpty(aft))
				aft.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there

			// Opera needs this one backwards
			if (isOpera) {
				r.insertNode(bef);
				r.insertNode(aft);
			} else {
				r.insertNode(aft);
				r.insertNode(bef);
			}

			// Normalize
			aft.normalize();
			bef.normalize();

			// Move cursor and scroll into view
			r = d.createRange();
			r.selectNodeContents(aft);
			r.collapse(1);
			s.removeAllRanges();
			s.addRange(r);

			// scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
			y = ed.dom.getPos(aft).y;
			ch = aft.clientHeight;

			// Is element within viewport
			if (y < vp.y || y + ch > vp.y + vp.h) {
				ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + ch);
				//console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight));
			}

			return false;
		},

		backspaceDelete : function(e, bs) {
			var t = this, ed = t.editor, b = ed.getBody(), n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;

			// The caret sometimes gets stuck in Gecko if you delete empty paragraphs
			// This workaround removes the element by hand and moves the caret to the previous element
			if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
				if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
					// Find previous block element
					n = sc;
					while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;

					if (n) {
						if (sc != b.firstChild) {
							// Find last text node
							w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
							while (tn = w.nextNode())
								n = tn;

							// Place caret at the end of last text node
							r = ed.getDoc().createRange();
							r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
							r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
							se.setRng(r);

							// Remove the target container
							ed.dom.remove(sc);
						}

						return Event.cancel(e);
					}
				}
			}

			// Gecko generates BR elements here and there, we don't like those so lets remove them
			function handler(e) {
				e = e.target;

				// A new BR was created in a block element, remove it
				if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
					Event.remove(b, 'DOMNodeInserted', handler);

					// Only remove BR elements that got inserted in the middle of the text
					if (e.previousSibling || e.nextSibling)
						ed.dom.remove(e);
				}
			};

			// Listen for new nodes
			Event._add(b, 'DOMNodeInserted', handler);

			// Remove listener
			window.setTimeout(function() {
				Event._remove(b, 'DOMNodeInserted', handler);
			}, 1);
		}
	});
})();

/* file:jscripts/tiny_mce/classes/ControlManager.js */

(function() {
	// Shorten names
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;

	tinymce.create('tinymce.ControlManager', {
		ControlManager : function(ed, s) {
			var t = this, i;

			s = s || {};
			t.editor = ed;
			t.controls = {};
			t.onAdd = new tinymce.util.Dispatcher(t);
			t.onPostRender = new tinymce.util.Dispatcher(t);
			t.prefix = s.prefix || ed.id + '_';
			t._cls = {};

			t.onPostRender.add(function() {
				each(t.controls, function(c) {
					c.postRender();
				});
			});
		},

		get : function(id) {
			return this.controls[this.prefix + id] || this.controls[id];
		},

		setActive : function(id, s) {
			var c = null;

			if (c = this.get(id))
				c.setActive(s);

			return c;
		},

		setDisabled : function(id, s) {
			var c = null;

			if (c = this.get(id))
				c.setDisabled(s);

			return c;
		},

		add : function(c) {
			var t = this;

			if (c) {
				t.controls[c.id] = c;
				t.onAdd.dispatch(c, t);
			}

			return c;
		},

		createControl : function(n) {
			var c, t = this, ed = t.editor;

			each(ed.plugins, function(p) {
				if (p.createControl) {
					c = p.createControl(n, t);

					if (c)
						return false;
				}
			});

			switch (n) {
				case "|":
				case "separator":
					return t.createSeparator();
			}

			if (!c && ed.buttons && (c = ed.buttons[n]))
				return t.createButton(n, c);

			return t.add(c);
		},

		createDropMenu : function(id, s, cc) {
			var t = this, ed = t.editor, c, bm, v, cls;

			s = extend({
				'class' : 'mceDropDown',
				constrain : ed.settings.constrain_menus
			}, s);

			s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
			if (v = ed.getParam('skin_variant'))
				s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);

			id = t.prefix + id;
			cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
			c = t.controls[id] = new cls(id, s);
			c.onAddItem.add(function(c, o) {
				var s = o.settings;

				s.title = ed.getLang(s.title, s.title);

				if (!s.onclick) {
					s.onclick = function(v) {
						ed.execCommand(s.cmd, s.ui || false, s.value);
					};
				}
			});

			ed.onRemove.add(function() {
				c.destroy();
			});

			// Fix for bug #1897785, #1898007
			if (tinymce.isIE) {
				c.onShowMenu.add(function() {
					var s = ed.selection, n = s.getNode();

					if (n.nodeName == 'IMG')
						bm = s.getBookmark();
					else
						bm = 0;
				});

				c.onHideMenu.add(function() {
					if (bm)
						ed.selection.moveToBookmark(bm);
				});
			}

			return t.add(c);
		},

		createListBox : function(id, s, cc) {
			var t = this, ed = t.editor, cmd, c, cls;

			if (t.get(id))
				return null;

			s.title = ed.translate(s.title);
			s.scope = s.scope || ed;

			if (!s.onselect) {
				s.onselect = function(v) {
					ed.execCommand(s.cmd, s.ui || false, v || s.value);
				};
			}

			s = extend({
				title : s.title,
				'class' : 'mce_' + id,
				scope : s.scope,
				control_manager : t
			}, s);

			id = t.prefix + id;

			if (ed.settings.use_native_selects)
				c = new tinymce.ui.NativeListBox(id, s);
			else {
				cls = cc || t._cls.listbox || tinymce.ui.ListBox;
				c = new cls(id, s);
			}

			t.controls[id] = c;

			// Fix focus problem in Safari
			if (tinymce.isWebKit) {
				c.onPostRender.add(function(c, n) {
					// Store bookmark on mousedown
					Event.add(n, 'mousedown', function() {
						ed.bookmark = ed.selection.getBookmark('simple');
					});

					// Restore on focus, since it might be lost
					Event.add(n, 'focus', function() {
						ed.selection.moveToBookmark(ed.bookmark);
						ed.bookmark = null;
					});
				});
			}

			if (c.hideMenu)
				ed.onMouseDown.add(c.hideMenu, c);

			return t.add(c);
		},

		createButton : function(id, s, cc) {
			var t = this, ed = t.editor, o, c, cls;

			if (t.get(id))
				return null;

			s.title = ed.translate(s.title);
			s.label = ed.translate(s.label);
			s.scope = s.scope || ed;

			if (!s.onclick && !s.menu_button) {
				s.onclick = function() {
					ed.execCommand(s.cmd, s.ui || false, s.value);
				};
			}

			s = extend({
				title : s.title,
				'class' : 'mce_' + id,
				unavailable_prefix : ed.getLang('unavailable', ''),
				scope : s.scope,
				control_manager : t
			}, s);

			id = t.prefix + id;

			if (s.menu_button) {
				cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
				c = new cls(id, s);
				ed.onMouseDown.add(c.hideMenu, c);
			} else {
				cls = t._cls.button || tinymce.ui.Button;
				c = new cls(id, s);
			}

			return t.add(c);
		},

		createMenuButton : function(id, s) {
			s = s || {};
			s.menu_button = 1;

			return this.createButton(id, s);
		},

		createSplitButton : function(id, s, cc) {
			var t = this, ed = t.editor, cmd, c, cls;

			if (t.get(id))
				return null;

			s.title = ed.translate(s.title);
			s.scope = s.scope || ed;

			if (!s.onclick) {
				s.onclick = function(v) {
					ed.execCommand(s.cmd, s.ui || false, v || s.value);
				};
			}

			if (!s.onselect) {
				s.onselect = function(v) {
					ed.execCommand(s.cmd, s.ui || false, v || s.value);
				};
			}

			s = extend({
				title : s.title,
				'class' : 'mce_' + id,
				scope : s.scope,
				control_manager : t
			}, s);

			id = t.prefix + id;
			cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
			c = t.add(new cls(id, s));
			ed.onMouseDown.add(c.hideMenu, c);

			return c;
		},

		createColorSplitButton : function(id, s, cc) {
			var t = this, ed = t.editor, cmd, c, cls;

			if (t.get(id))
				return null;

			s.title = ed.translate(s.title);
			s.scope = s.scope || ed;

			if (!s.onclick) {
				s.onclick = function(v) {
					ed.execCommand(s.cmd, s.ui || false, v || s.value);
				};
			}

			if (!s.onselect) {
				s.onselect = function(v) {
					ed.execCommand(s.cmd, s.ui || false, v || s.value);
				};
			}

			s = extend({
				title : s.title,
				'class' : 'mce_' + id,
				'menu_class' : ed.getParam('skin') + 'Skin',
				scope : s.scope,
				more_colors_title : ed.getLang('more_colors')
			}, s);

			id = t.prefix + id;
			cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
			c = new cls(id, s);
			ed.onMouseDown.add(c.hideMenu, c);

			// Remove the menu element when the editor is removed
			ed.onRemove.add(function() {
				c.destroy();
			});

			return t.add(c);
		},

		createToolbar : function(id, s, cc) {
			var c, t = this, cls;

			id = t.prefix + id;
			cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
			c = new cls(id, s);

			if (t.get(id))
				return null;

			return t.add(c);
		},

		createSeparator : function(cc) {
			var cls = cc || this._cls.separator || tinymce.ui.Separator;

			return new cls();
		},

		setControlType : function(n, c) {
			return this._cls[n.toLowerCase()] = c;
		},

		destroy : function() {
			each(this.controls, function(c) {
				c.destroy();
			});

			this.controls = null;
		}

		});
})();

/* file:jscripts/tiny_mce/classes/WindowManager.js */

(function() {
	var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;

	tinymce.create('tinymce.WindowManager', {
		WindowManager : function(ed) {
			var t = this;

			t.editor = ed;
			t.onOpen = new Dispatcher(t);
			t.onClose = new Dispatcher(t);
			t.params = {};
			t.features = {};
		},

		open : function(s, p) {
			var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;

			// Default some options
			s = s || {};
			p = p || {};
			sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
			sh = isOpera ? vp.h : screen.height;
			s.name = s.name || 'mc_' + new Date().getTime();
			s.width = parseInt(s.width || 320);
			s.height = parseInt(s.height || 240);
			s.resizable = true;
			s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
			s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
			p.inline = false;
			p.mce_width = s.width;
			p.mce_height = s.height;
			p.mce_auto_focus = s.auto_focus;

			if (mo) {
				if (isIE) {
					s.center = true;
					s.help = false;
					s.dialogWidth = s.width + 'px';
					s.dialogHeight = s.height + 'px';
					s.scroll = s.scrollbars || false;
				} else
					s.modal = s.alwaysRaised = s.dialog = s.centerscreen = s.dependent = true;
			}

			// Build features string
			each(s, function(v, k) {
				if (tinymce.is(v, 'boolean'))
					v = v ? 'yes' : 'no';

				if (!/^(name|url)$/.test(k)) {
					if (isIE && mo)
						f += (f ? ';' : '') + k + ':' + v;
					else
						f += (f ? ',' : '') + k + '=' + v;
				}
			});

			t.features = s;
			t.params = p;
			t.onOpen.dispatch(t, s, p);

			u = s.url || s.file;
			if (tinymce.relaxedDomain)
				u += (u.indexOf('?') == -1 ? '?' : '&') + 'mce_rdomain=' + tinymce.relaxedDomain;

			u = tinymce._addVer(u);

			try {
				if (isIE && mo) {
					w = 1;
					window.showModalDialog(u, window, f);
				} else
					w = window.open(u, s.name, f);
			} catch (ex) {
				// Ignore
			}

			if (!w)
				alert(t.editor.getLang('popup_blocked'));
		},

		close : function(w) {
			w.close();
			this.onClose.dispatch(this);
		},

		createInstance : function(cl, a, b, c, d, e) {
			var f = tinymce.resolve(cl);

			return new f(a, b, c, d, e);
		},

		confirm : function(t, cb, s) {
			cb.call(s || this, confirm(this._decode(this.editor.getLang(t, t))));
		},

		alert : function(tx, cb, s) {
			var t = this;

			alert(t._decode(t.editor.getLang(tx, tx)));

			if (cb)
				cb.call(s || t);
		},

		// Internal functions

		_decode : function(s) {
			return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
		}

		});
}());

tinyMCE.addI18n({en:{
common:{
edit_confirm:"Do you want to use the WYSIWYG mode for this textarea?",
apply:"Apply",
insert:"Insert",
update:"Update",
cancel:"Cancel",
close:"Close",
browse:"Browse",
class_name:"Class",
not_set:"-- Not set --",
clipboard_msg:"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?",
clipboard_no_support:"Currently not supported by your browser, use keyboard shortcuts instead.",
popup_blocked:"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.",
invalid_data:"Error: Invalid values entered, these are marked in red.",
more_colors:"More colors"
},
contextmenu:{
align:"Alignment",
left:"Left",
center:"Center",
right:"Right",
full:"Full"
},
insertdatetime:{
date_fmt:"%Y-%m-%d",
time_fmt:"%H:%M:%S",
insertdate_desc:"Insert date",
inserttime_desc:"Insert time",
months_long:"January,February,March,April,May,June,July,August,September,October,November,December",
months_short:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
day_long:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday",
day_short:"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun"
},
print:{
print_desc:"Print"
},
preview:{
preview_desc:"Preview"
},
directionality:{
ltr_desc:"Direction left to right",
rtl_desc:"Direction right to left"
},
layer:{
insertlayer_desc:"Insert new layer",
forward_desc:"Move forward",
backward_desc:"Move backward",
absolute_desc:"Toggle absolute positioning",
content:"New layer..."
},
save:{
save_desc:"Save",
cancel_desc:"Cancel all changes"
},
nonbreaking:{
nonbreaking_desc:"Insert non-breaking space character"
},
iespell:{
iespell_desc:"Run spell checking",
download:"ieSpell not detected. Do you want to install it now?"
},
advhr:{
advhr_desc:"Horizontale rule"
},
emotions:{
emotions_desc:"Emotions"
},
searchreplace:{
search_desc:"Find",
replace_desc:"Find/Replace"
},
advimage:{
image_desc:"Insert/edit image"
},
advlink:{
link_desc:"Insert/edit link"
},
xhtmlxtras:{
cite_desc:"Citation",
abbr_desc:"Abbreviation",
acronym_desc:"Acronym",
del_desc:"Deletion",
ins_desc:"Insertion",
attribs_desc:"Insert/Edit Attributes"
},
style:{
desc:"Edit CSS Style"
},
paste:{
paste_text_desc:"Paste as Plain Text",
paste_word_desc:"Paste from Word",
selectall_desc:"Select All"
},
paste_dlg:{
text_title:"Use CTRL+V on your keyboard to paste the text into the window.",
text_linebreaks:"Keep linebreaks",
word_title:"Use CTRL+V on your keyboard to paste the text into the window."
},
table:{
desc:"Insert Table",
row_before_desc:"Insert row before",
row_after_desc:"Insert row after",
delete_row_desc:"Delete row",
col_before_desc:"Insert column before",
col_after_desc:"Insert column after",
delete_col_desc:"Remove column",
split_cells_desc:"Split merged table cells",
merge_cells_desc:"Merge table cells",
row_desc:"Table row properties",
cell_desc:"Table cell properties",
props_desc:"Table properties",
paste_row_before_desc:"Paste table row before",
paste_row_after_desc:"Paste table row after",
cut_row_desc:"Cut table row",
copy_row_desc:"Copy table row",
del:"Delete table",
row:"Row",
col:"Column",
cell:"Cell",
row_up:"Move Row Up",
row_down:"Move Row Down",
col_left:"Move Column Left",
col_right:"Move Column Right"
},
autosave:{
unload_msg:"The changes you made will be lost if you navigate away from this page."
},
fullscreen:{
desc:"Toggle fullscreen mode"
},
media:{
desc:"Insert / edit embedded media",
edit:"Edit embedded media"
},
fullpage:{
desc:"Document properties"
},
template:{
desc:"Insert predefined template content"
},
visualchars:{
desc:"Visual control characters on/off."
},
spellchecker:{
desc:"Toggle spellchecker",
menu:"Spellchecker settings",
ignore_word:"Ignore word",
ignore_words:"Ignore all",
langs:"Languages",
wait:"Please wait...",
sug:"Suggestions",
no_sug:"No suggestions",
no_mpell:"No misspellings found."
},
pagebreak:{
desc:"Insert page break."
}}});

(function() {

    tinymce.create('tinymce.plugins.JiveListsPlugin', {


        fixLists : function(ed){
            var n = ed.selection.getNode();
            n = ed.dom.getParent(n, "li");
            if(n != null && n.nodeType == 1 && n.nodeName.toLowerCase() == "li"){
                var l = ed.dom.getParent(n, "ol,ul");
                if(tinymce.isGecko){
                    for(var i=0;i<l.childNodes.length;i++){
                        var li = l.childNodes[i];
                        if(li.nodeType == 1 && li.nodeName.toLowerCase() == "li" && li.childNodes.length == 0){
                            // add an empty <br> to the li. empty li's are bad :(
                            li.appendChild(ed.plugins.jivemacros.createMozBR(ed.getDoc()));
                        }
                    }
                }

                if(l.parentNode.nodeName.toLowerCase() == "p"){
                    var p = l.parentNode;
                    p.parentNode.insertBefore(l, p);
                    p.parentNode.removeChild(p);
                    ed.selection.select(n).collapse(true);
                }
                var ps = l.previousSibling;
                if(ps != null && l.nodeName == ps.nodeName){
                    // merge the lists
                    var i=0;
                    while(ps.childNodes.length){
                        l.insertBefore(ps.childNodes[0], l.childNodes[i]);
                        i++;
                    }
                }
                var ps = l.nextSibling;
                if(ps != null && l.nodeName == ps.nodeName){
                    // merge the lists
                    while(ps.childNodes.length){
                        l.appendChild(ps.childNodes[0]);
                    }
                }
            }
        },

        execCommand : function(cmd, ui, val){
            var ed = tinyMCE.activeEditor;
            if(cmd.indexOf("mceOLListStyle") == 0){
                var index = parseInt(cmd.substr("mceOLListStyle".length));

                var list = ed.plugins.jivelists.ol_styles;
                var style = list[index];

                var n = tinyMCE.activeEditor.selection.getNode();
                var par = ed.dom.getParent(n, 'ol');

                par.style.listStyleType = style[1];

                return true;
            }
            if(cmd.indexOf("mceULListStyle") == 0){
                var index = parseInt(cmd.substr("mceOLListStyle".length));

                var list = ed.plugins.jivelists.ul_styles;
                var style = list[index];

                var n = tinyMCE.activeEditor.selection.getNode();
                var par = ed.dom.getParent(n, 'ul');

                par.style.listStyleType = style[1];

                return true;
            }

            function fixBlockQuote(ed){
                var n = ed.selection.getNode();
                n = ed.dom.getParent(n, "ol,ul");
                var quote = ed.dom.getParent(n, "blockquote");
                if(quote != null){
                    var newList = ed.getDoc().createElement(n.nodeName);
                    quote.parentNode.insertBefore(newList, quote);
                    while(quote.childNodes.length) newList.appendChild(quote.childNodes[0]);
                    quote.parentNode.removeChild(quote);
                    ed.selection.select(n);
                }
            }

            if(cmd.indexOf("mceOutdent") == 0){
                ed.execCommand("Outdent");
                this.fixLists(ed);
                return true;
            }
            if(cmd.indexOf("mceIndent") == 0){
                ed.execCommand("Indent");
                this.fixLists(ed);
                fixBlockQuote(ed);
                return true;
            }
            if(cmd.indexOf("mceInsertUnorderedList") == 0){
                ed.execCommand("InsertUnorderedList");
                this.fixLists(ed);
                return true;
            }
            if(cmd.indexOf("mceInsertOrderedList") == 0){
                ed.execCommand("InsertOrderedList");
                this.fixLists(ed);
                return true;
            }

            return false;
        },


        cleanList : function(ed, n){
            if(n.nodeType != 1) return;
            for(var i=0;i<n.childNodes.length;i++){
                var kid = n.childNodes[i];
                if(kid.nodeType != 1 && kid.nodeType != 3 ||
                        kid.nodeType == 1 && (kid.nodeName.toLowerCase() == "meta" || kid.nodeName.toLowerCase() == "style")){
                    n.removeChild(kid);
                    i--;
                }
                this.cleanList(ed, kid);
            }
        },


        init : function(ed, url){
            this.url = url;
            var t = this;
            this.index = 0;
            this.ol_styles = new Array();
            this.ol_styles.push(["default",""]);
            this.ol_styles.push(["none","none"]);
            this.ol_styles.push(["inherit","inherit"]);
            this.ol_styles.push(["d","decimal"]);
            this.ol_styles.push(["dz","decimal-leading-zero"]);
            this.ol_styles.push(["ur","upper-roman"]);
            this.ol_styles.push(["lr","lower-roman"]);
            this.ol_styles.push(["ua","upper-alpha"]);
            this.ol_styles.push(["la","lower-alpha"]);
            this.ol_styles.push(["lg","lower-greek"]);
            this.ol_styles.push(["ki","katakana-iroha"]);
            this.ol_styles.push(["k","katakana"]);
            this.ol_styles.push(["hii","hiragana-iroha"]);
            this.ol_styles.push(["hi","hiragana"]);
            this.ol_styles.push(["ci","cjk-ideographic"]);
            this.ol_styles.push(["g","georgian"]);
            this.ol_styles.push(["a","armenian"]);
            this.ol_styles.push(["he","hebrew"]);

            this.ul_styles = new Array();
            this.ul_styles.push(["default",""]);
            this.ul_styles.push(["none","none"]);
            this.ul_styles.push(["inherit","inherit"]);
            this.ul_styles.push(["s","square"]);
            this.ul_styles.push(["c","circle"]);
            this.ul_styles.push(["di","disc"]);


            ed.onNodeChange.add(function(ed){
                this.fixLists(ed);
                var node = ed.selection.getNode();
                var n = ed.dom.getParent(node, "ul,ol");
                if(n){
                    this.cleanList(ed, n);
                }

            }, this);

            ed.onInit.add(function() {
                if (ed && ed.plugins.contextmenu) {
                    ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {
                        var sm, se = ed.selection, el = se.getNode() || ed.getBody();

                        if (ed.dom.getParent(e, 'ol')){
                            var par = ed.dom.getParent(e, 'ol');
                            var last = ed.dom.getParent(e, function(x){
                                return $def(x.tagName) && x.tagName.toLowerCase() == "ol" && x != par;
                            });

                            m.addSeparator();
                            sm = m.addMenu({title : 'jivelists.list_style', max_height : "200", 'class' : 'mceDropDown defaultSkin mceMacroMenu mceListBoxMenu'});
                            var list = ed.plugins.jivelists.ol_styles;
                            for(var i=0;i<list.length;i++){
                                if(last != null || last == null && list[i][0] != "inherit"){
                                    var clz = "";
                                    if(par.style.listStyleType == list[i][1]){
                                        clz = "mceMenuItemSelected";
                                    }else{
                                        clz = "";
                                    }
                                    sm.add({title : 'jivelists.' + list[i][0], cmd : 'mceOLListStyle' + i, 'class' : clz});
                                }
                                if(i == 2){
                                    sm.addSeparator();
                                }
                            }

                        }else if(ed.dom.getParent(e, 'ul')){
                            var par = ed.dom.getParent(e, 'ul');
                            var last = ed.dom.getParent(e, function(x){
                                return $def(x.tagName) && x.tagName.toLowerCase() == "ul" && x != par;
                            });

                            m.addSeparator();
                            sm = m.addMenu({title : 'jivelists.list_style', max_height : "200", 'class' : 'mceDropDown defaultSkin mceMacroMenu mceListBoxMenu'});
                            var list = ed.plugins.jivelists.ul_styles;
                            for(var i=0;i<list.length;i++){
                                if(last != null || last == null && list[i][0] != "inherit"){
                                    var clz = "";
                                    if(par.style.listStyleType == list[i][1]){
                                        clz = "mceMenuItemSelected";
                                    }else{
                                        clz = "";
                                    }
                                    sm.add({title : 'jivelists.' + list[i][0], cmd : 'mceULListStyle' + i, 'class' : clz});
                                }
                                if(i == 2){
                                    sm.addSeparator();
                                }
                            }

                        }
                    });
                }
            });


            this.prepForEnter = false;
            this.prepForDel = false;

            ed.onKeyDown.addToTop(function(ed, e) {
                this.prepForEnter = false;
                this.prepForDel = false;
                if(e.keyCode == 8 && ed.selection.isCollapsed()){ // delete key
                    var li = ed.dom.getParent(ed.selection.getNode(), 'li');
                    var list = ed.dom.getParent(ed.selection.getNode(), 'ul,ol');
                    var sel = ed.selection;
                    var isFrontOfLi = sel.getBookmark().start == 0 || sel.getRng().startOffset == 0;
                    if(li != null && li.nodeType == 1 && li.previousSibling== null && tinymce.isGecko){            // first li in list
                        var selMe = li.childNodes.length ? li.childNodes[0] : null;
                        var b = sel.getBookmark();
                        if(isFrontOfLi){
                            this.prepForDel = true;
                            tinymce.dom.Event.cancel(e);
                            while(li.childNodes.length){
                                var kid = li.childNodes[0];
                                if(kid.nodeType == 1 && kid.nodeName.toLowerCase() == "br"){
                                    var p = ed.getDoc().createElement('P');
                                    p.appendChild(kid);
                                    kid = p;
                                }
                                list.parentNode.insertBefore(kid, list);
                            }
                            list.removeChild(li);
                            if(selMe){
                                ed.selection.moveToBookmark(b);
//                                ed.selection.select(selMe);
//                                ed.selection.collapse(true);
                            }
                        }
                    }else if(li != null && li.nodeType == 1 && isFrontOfLi){                                   // not first li
                        this.prepForDel = true;
                        tinymce.dom.Event.cancel(e);
                        ed.execCommand("mceOutdent");
                        return false;
                    }
                }else if(e.keyCode == 13 && !e.shiftKey){ // enter key
                    var li = ed.dom.getParent(ed.selection.getNode(), 'li');
                    var list = ed.dom.getParent(ed.selection.getNode(), 'ul,ol');
                    if(li != null &&
                       li.nodeType == 1 &&
                       li.nextSibling == null &&
                       li.childNodes.length == 1 &&
                       $def(li.childNodes[0].nodeName.toLowerCase()) &&
                       li.childNodes[0].nodeName.toLowerCase() == "br"){
                        this.prepForEnter = true;
                        tinymce.dom.Event.cancel(e);

                        var par = list.parentNode;
                        if(par.nodeName.toLowerCase() == "ol" || par.nodeName.toLowerCase() == "ul"){
                            ed.execCommand("mceOutdent");
                        }else{
                            var p = ed.plugins.jivemacros.createEmptyPara(ed.getDoc());
                            if(par.childNodes[par.childNodes.length-1] == list){
                                par.appendChild(p);
                            }else{
                                par.insertBefore(p, list.nextSibling);
                            }
                            list.removeChild(li);
                            if(list.childNodes.length == 0){
                                list.parentNode.removeChild(list);
                            }
                            ed.selection.select(p.childNodes[0]);
                            ed.selection.collapse(true);
                        }
                    }
                }else if(e.keyCode == 13 && e.shiftKey){
                    window.breakNow = true;
                    // shift enter, so put in a <br>
                    var li = ed.dom.getParent(ed.selection.getNode(), 'li');
                    var list = ed.dom.getParent(ed.selection.getNode(), 'ul,ol');
                    var sel = ed.selection;
                    var mac = ed.plugins.jivemacros.isMacro(sel.getNode()) || ed.plugins.jivemacros.isLink(sel.getNode());
                    var node = sel.getNode();
                    var isEndOfBlock = sel.getRng().startOffset == (sel.getNode().childNodes[sel.getNode().childNodes.length-1].nodeValue.length) &&
                                       node.childNodes.length > 0 &&
                                        node.childNodes[node.childNodes.length-1].nodeType == 3 &&
                                        sel.getRng().startContainer == node.childNodes[node.childNodes.length-1];
                    if(li != null && isEndOfBlock){
                        this.prepForEnter = true;
                        tinymce.dom.Event.cancel(e);

                        var p = ed.getDoc().createElement('BR');
                        li.appendChild(p);
                        ed.selection.select(p);
                        ed.selection.collapse();
                    }
                }
                if(e.keyCode == 9 && !e.ctrlKey && !e.altKey){
                    var p = ed.dom.getParent(ed.selection.getNode(), 'ul,ol');
                    if(p != null){
                        tinymce.dom.Event.cancel(e);
                        return false;
                    }
                }
            }, this);
            ed.onKeyPress.add(function(ed, e) {
                if(e.keyCode == 8 && this.prepForDel){
                    tinymce.dom.Event.cancel(e);
                    return false;
                }
                if(e.keyCode == 13 && this.prepForEnter){
                    tinymce.dom.Event.cancel(e);
                }
                if(e.keyCode == 9 && !e.ctrlKey && !e.altKey){
                    var p = ed.dom.getParent(ed.selection.getNode(), 'ul,ol');
                    if(p != null){
                        tinymce.dom.Event.cancel(e);
                        return false;
                    }
                }
            }, this);
            ed.onKeyUp.add(function(ed, e) {
                if(e.keyCode == 8 && this.prepForDel){
                    tinymce.dom.Event.cancel(e);
                    return false;
                }
                if(e.keyCode == 13 && this.prepForEnter){
                    tinymce.dom.Event.cancel(e);
                }
                if(e.keyCode == 9 && !e.ctrlKey && !e.altKey){
                    var p = ed.dom.getParent(ed.selection.getNode(), 'ul,ol');
                    if(p != null){
                        if(e.shiftKey){
                            ed.execCommand("mceOutdent");
                        }else{
                            ed.execCommand("mceIndent");
                        }
                        tinymce.dom.Event.cancel(e);
                        return false;
                    }
                }
            }, this);

            
        },

        getInfo : function() {
            return {
                longname : 'Jive Lists',
                author : 'Jive Software',
                authorurl : 'http://jivesoftware.com',
                infourl : 'http://jivesoftware.com',
                version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
            };
        },


		/**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
			return null;
		}


    });
	// Register plugin
	tinymce.PluginManager.add('jivelists', tinymce.plugins.JiveListsPlugin);
})();


// UK lang variables
tinyMCE.addI18n('en.jivelists',{
    list_style : 'List Style',
    inherit : "Inherit",
    none : "None",
    'default' : "Default",
    ur : "Upper Roman",
    lr : "Lower Roman",
    dz : "Decimal with Leading Zero",
    d  : "Decimal",
    ua : "Upper Alpha",
    la : "Lower Alpha",
    lg : "Lower Greek",
    ki : "Katakana-Iroha",
    hii: "Hiragana-Iroha",
    k  : "Katakana",
    hi : "Hiragana",
    ci : "Cjk-Ideographic",
    g  : "Georgian",
    a  : "Armenian",
    he : "Hebrew",
    s  : "Squares",
    c  : "Circles",
    di : "Discs"
});



(function() {

    tinymce.create('tinymce.plugins.JiveStylePlugin', {

        /**
         * Returns information about the plugin as a name/value array.
         * The current keys are longname, author, authorurl, infourl and version.
         *
         * @returns Name/value array containing information about the plugin.
         * @type Array
         */
        getInfo : function() {
            return {
                longname : 'Image Picker',
                author : 'Jive Software',
                authorurl : 'http://www.jivesoftware.com',
                infourl : 'http://www.jivesoftware.com/',
                version : "1.0"
            };
        },

        isBold : false,
        isItalic : false,
        isStrikethrough : false,
        isColor : false,

        /**
         * Gets executed when a TinyMCE editor instance is initialized.
         *
         * @param {TinyMCE_Control} Initialized TinyMCE editor control instance.
         */
        init : function(ed, url) {
            ed.onInit.add(function(ed) {
                var a = document.getElementById(ed.id + '_jivestyle_action');
                if(a){
                    var span = a.childNodes[0];
                    while(span.childNodes.length > 0) span.removeChild(span.childNodes[0]);
                    span.appendChild(document.createTextNode(ed.getLang("jivestyle.title")))
                }
                ed.plugins.jivebuttons.initToolbar(ed.id);
            });
            // Add a node change handler, selects the button in the UI when a image is selected
			ed.onNodeChange.add(function(that){ return function(ed, cm, n) {
                var p = ed.dom.getParent(n, ed.dom.isBlock);
				if (p){
                    if(that.buttons.length > 0){
                        // we're updating an aleady rendered menu
                        var sel = ed.selection;
                        for(var i=0;i<that.buttons.length;i++){
                            var doc = window.document;
                            var dom = new tinymce.dom.DOMUtils(doc);
                            var selectMe = $def(p.tagName) && p.tagName.toLowerCase() == that.tagNames[i];
                            var row = doc.getElementById(that.buttons[i].id);
                            var cell = row.cells[0];
                            if(selectMe && (sel.isCollapsed() || sel.getStart() == sel.getEnd())){
                                dom.addClass(cell,"mceMenuItemSelected");
                            }else{
                                dom.removeClass(cell,"mceMenuItemSelected");
                            }
                        }
                    }
                }
			}}(this));

            /**
             * A class to parse color values
             * @author Stoyan Stefanov <sstoo@gmail.com>
             * @link   http://www.phpied.com/rgb-color-parser-in-javascript/
             * @license Use it if you like it
             */
            function RGBColor(color_string)
            {
                // strip any leading #
                if (color_string.charAt(0) == '#') { // remove # if any
                    color_string = color_string.substr(1,6);
                }

                color_string = color_string.replace(/ /g,'');
                color_string = color_string.toLowerCase();
                // emd of simple type-in colors

                // array of color definition objects
                var color_defs = [
                    {
                        re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
                        example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
                        process: function (bits){
                            return [
                                parseInt(bits[1]),
                                parseInt(bits[2]),
                                parseInt(bits[3])
                            ];
                        }
                    },
                    {
                        re: /^(\w{2})(\w{2})(\w{2})$/,
                        example: ['#00ff00', '336699'],
                        process: function (bits){
                            return [
                                parseInt(bits[1], 16),
                                parseInt(bits[2], 16),
                                parseInt(bits[3], 16)
                            ];
                        }
                    },
                    {
                        re: /^(\w{1})(\w{1})(\w{1})$/,
                        example: ['#fb0', 'f0f'],
                        process: function (bits){
                            return [
                                parseInt(bits[1] + bits[1], 16),
                                parseInt(bits[2] + bits[2], 16),
                                parseInt(bits[3] + bits[3], 16)
                            ];
                        }
                    }
                ];

                // search through the definitions to find a match
                for (var i = 0; i < color_defs.length; i++) {
                    var re = color_defs[i].re;
                    var processor = color_defs[i].process;
                    var bits = re.exec(color_string);
                    if (bits) {
                        channels = processor(bits);
                        this.r = channels[0];
                        this.g = channels[1];
                        this.b = channels[2];
                        this.ok = true;
                    }

                }
                if(!this.ok){
                    return "";
                }

                // validate/cleanup values
                this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
                this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
                this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);

                var r = this.r.toString(16);
                var g = this.g.toString(16);
                var b = this.b.toString(16);
                if (r.length == 1) r = '0' + r;
                if (g.length == 1) g = '0' + g;
                if (b.length == 1) b = '0' + b;
                return '#' + r + g + b;
            }




            ed.onKeyDown.addToTop(function(ed, evt){
                if(evt.keyCode == 13){
                    this.isBold = ed.controlManager.get("bold").isActive();
                    this.isItalic = ed.controlManager.get("italic").isActive();
                    this.isStrikethrough = ed.controlManager.get("strikethrough").isActive();
//                    this.isColor = RGBColor(jive.ext.x.xGetCS($('wysiwygtext_forecolor_preview'), "background-color"));
                }

            }, this);
            ed.onKeyUp.add(function(ed, evt){
                if(evt.keyCode == 13 && !tinymce.isIE){
                    if(this.isBold) ed.execCommand("Bold", false);
                    if(this.isItalic) ed.execCommand("Italic", false);
                    if(this.isStrikethrough) ed.execCommand("Strikethrough", false);
//                    ed.controlManager.get("forecolor").setColor(this.isColor);
//                    ed.execCommand('ForeColor', false, this.isColor);
                }
            }, this);

            this.buttons = new Array();
            this.tagNames = ["p","h1","h2","h3","h4","h5","h6"];
        },


        /**
         *
         * @param cn
         * @param cm
         */
        execCommand : function(cmd, ui, val){
            var format = null;
            var ed = tinyMCE.activeEditor;
            var ec = new tinymce.EditorCommands(ed);
            if(cmd == 'mceJiveStyle'){
                var cm = tinyMCE.activeEditor.controlManager;
                var button = cm.get('jivestyle');
                button.showMenu();
                return true;
            }else if(cmd == 'mceJiveStyleHeader1'){
                format = "h1";
            }else if(cmd == 'mceJiveStyleHeader2'){
                format = "h2";
            }else if(cmd == 'mceJiveStyleHeader3'){
                format = "h3";
            }else if(cmd == 'mceJiveStyleHeader4'){
                format = "h4";
            }else if(cmd == 'mceJiveStyleHeader5'){
                format = "h5";
            }else if(cmd == 'mceJiveStyleHeader6'){
                format = "h6";
            }else if(cmd == 'mceJiveStyleParagraph'){
                format = "p";
//            }else if(cmd == 'mceJiveStylePreformatted'){
//                ec.FormatBlock(false, "pre");
//                return true;
            }

            if(format != null){
                if(tinymce.isWebKit){
                    var newP = ed.getDoc().createElement(format);
                    var oldP = ed.dom.getParent(ed.selection.getNode(), "p,h1,h2,h3,h4,h5,h6");
                    if(oldP){
                        oldP.parentNode.insertBefore(newP, oldP);
                        while(oldP.childNodes.length) newP.appendChild(oldP.childNodes[0]);
                    }
                    oldP.parentNode.removeChild(oldP);
                    if(newP.childNodes.length) newP = newP.childNodes[newP.childNodes.length-1];
                    ed.selection.select(newP);
                    ed.selection.collapse();
                }else{
                    tinyMCE.activeEditor.execCommand("removeformat");
                    ec.FormatBlock(false, format);
                }
                return true;
            }
            return false;
        },

        isTag : function(tag){
            var ed = tinyMCE.activeEditor;
            var sel = ed.selection;
            if(sel.isCollapsed()){
                var n = sel.getNode();
                var par = ed.dom.getParent(n,tag);
                var selectMe = $def(par) && par != null && $def(par.tagName) && par.tagName.toLowerCase() == tag;
                // it's being rendered for the first time
                if(selectMe){
                    return "mceMenuItemSelected";
                }else{
                    return "";
                }
            }else{
                return "";
            }
        },

        addButton : function(c){
            this.buttons.push(c);
        },
        /**
         * Creates control instances based in the incomming name. This method is normally not
         * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
         * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
         * method can be used to create those.
         *
         * @param {String} n Name of the control to create.
         * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
         * @return {tinymce.ui.Control} New control instance or null if no control was created.
         */
        createControl: function(cn, cm) {
            switch(cn){
                case 'jivestyle':
                    var c = cm.createSplitButton('jivestyle', {
                        title : 'jivestyle.title',
                        cmd : "mceJiveStyle"
                    });
                    c.onRenderMenu.add(function(c, m) {
                        var plugin = tinyMCE.activeEditor.plugins.jivestyle;
//                        if(plugin.buttons.length < 8){
                            plugin.addButton(m.add({theTag: "p", title : "Paragraph", cmd : 'mceJiveStyleParagraph', 'class' : 'mce_formatPreview mce_p ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("p")}));
//                            plugin.addButton(m.add({theTag: "pre", title : "Preformatted", cmd : 'mceJiveStylePreformatted', 'class' : 'mce_formatPreview mce_pre ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("pre")}));
                            plugin.addButton(m.add({theTag: "h1", title : "Header 1", cmd : 'mceJiveStyleHeader1', 'class' : 'mce_formatPreview mce_h1 ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("h1")}));
                            plugin.addButton(m.add({theTag: "h2", title : "Header 2", cmd : 'mceJiveStyleHeader2', 'class' : 'mce_formatPreview mce_h2 ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("h2")}));
                            plugin.addButton(m.add({theTag: "h3", title : "Header 3", cmd : 'mceJiveStyleHeader3', 'class' : 'mce_formatPreview mce_h3 ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("h3")}));
                            plugin.addButton(m.add({theTag: "h4", title : "Header 4", cmd : 'mceJiveStyleHeader4', 'class' : 'mce_formatPreview mce_h4 ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("h4")}));
                            plugin.addButton(m.add({theTag: "h5", title : "Header 5", cmd : 'mceJiveStyleHeader5', 'class' : 'mce_formatPreview mce_h5 ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("h5")}));
                            plugin.addButton(m.add({theTag: "h6", title : "Header 6", cmd : 'mceJiveStyleHeader6', 'class' : 'mce_formatPreview mce_h6 ' + tinyMCE.activeEditor.plugins.jivestyle.isTag("h6")}));
//                        }else{
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("p");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("pre");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("h1");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("h2");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("h3");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("h4");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("h5");
//                            tinyMCE.activeEditor.plugins.jivestyle.isTag("h6");
//                        }
                    });
                    // Return the new menubutton instance
                    return c;
            }
            return null;
        }

    });
	// Register plugin
	tinymce.PluginManager.add('jivestyle', tinymce.plugins.JiveStylePlugin);
})();


tinyMCE.addI18n('en.jivestyle',{
    title : 'Style'
});


(function() {

    tinymce.PluginManager.requireLangPack('jivemacros');

    tinymce.create('tinymce.plugins.JiveMacrosPlugin', {


        _cache : new jive.ext.y.HashTable(),

        cache : function(id, url, title, type){
            var type_cache = this._cache.get(type);
            if(!type_cache){
                type_cache = new jive.ext.y.HashTable();
                this._cache.put(type, type_cache);
            }
            type_cache.put(id, [title,url]);
        },

        getTitleFor : function(type, id){
            var type_cache = this._cache.get(type);
            if(!type_cache) return false;
            return type_cache.get(id);
        },

        /**
         * This function will delete the macro from the document
         * @param ed
         * @param node
         */
        deleteMacro : function(ed, node){
            if(window.jiveRTE.macroEle){
                node = window.jiveRTE.macroEle;
                window.jiveRTE.macroEle = null;
            }
            console.log('deleting macro ' + node.nodeName);
            if($def(ed) && $def(node)){
                if(node == ed.getBody()) return;
                var dom = new tinymce.dom.DOMUtils(ed.getBody());
                var par = dom.getParent(node, function(x){ return x == ed.getBody() || x != node; });
                if(this.isExactMacro(node)){
                    var macro = this.getMacroFor(node);
                    if(macro.getMacroType() == "TEXT" || macro.getMacroType() == "INLINE"){
                        while(node.childNodes.length > 0){
                            var kid = node.removeChild(node.childNodes[0]);
                            if(kid.nodeType == 3){ // we only care about text nodes
                                var str = kid.nodeValue;
                                while(str.indexOf("\n") >= 0){
                                    var p = ed.getDoc().createElement('P');
                                    p.appendChild(ed.getDoc().createTextNode(str.substr(0, str.indexOf("\n"))))
                                    par.insertBefore(p, node);
                                    if(str.length > str.indexOf("\n")+1){
                                        str = str.substr(str.indexOf("\n")+1);
                                    }else{
                                        str = "";
                                    }
                                }
                                if(str.length > 0){
                                    var p = ed.getDoc().createElement('P');
                                    p.appendChild(ed.getDoc().createTextNode(str));
                                    par.insertBefore(p, node);
                                }
                            }else{
                                par.insertBefore(kid, node);
                            }
                        }
                    }
                    par.removeChild(node);
                }else{
                    this.deleteMacro(ed, par);
                }
                return;
            }

            var ed = tinyMCE.activeEditor;
            var sel = ed.selection;
            this.deleteMacro(ed, sel.getNode());
        },

        getNextSibling : function(ed, node){
            if(node == null) return node;
            if(node.nextSibling != null) return node.nextSibling;
            if(node == ed.getBody() && node.nextSibling == null) return null;
            return this.getNextSibling(ed, this.getParentNode(ed, node));
        },

        getParentNode : function(ed, node){
            return (new tinymce.dom.DOMUtils(ed.getDoc())).getParent(node, function(x){ return x == ed.getBody() || x != node; });
        },

        insertIntoPre : function(pre, ele){
            pre.appendChild(ele);
        },

        appendTextToPre : function(pre, node, doc){
            while(node.childNodes.length > 0){
                pre.appendChild(node.childNodes[0]);
            }
        },

        /**
         * returns an array of the nodes parents
         * [parent, grandparent, ..., body]
         * @param node1
         */
        getLineage : function(ed, node1){
            var ret;
            if(node1 == ed.getBody()){
                ret = new Array();
                ret.push(node1);
            }else{
                ret = this.getLineage(ed, this.getParentNode(ed, node1));
                ret.push(node1);
            }
            return ret;
        },

        /**
         * in a DOM tree, will return an array of two
         * nodes that are siblings and possibly parents
         * of the input nodes
         *
         * example:
         * body
         *  - div
         *     - p
         *       - node 1
         *  - pre
         *    - node 2
         *
         * [div, pre] will be returned
         * @param start must be a younger node than end
         * @param end must be an older node than start
         * @param doc the document that contains start/end
         */
        ageTheNodes : function(start, end, doc){
            if(start == doc.body) return false;
            if(end == doc.body) return false;
            if(start.parentNode == end.parentNode){
                return [start, end];
            }
            
            var ret;
            ret = this.ageTheNodes(start.parentNode, end, doc);
            if(ret) return ret;
            ret = this.ageTheNodes(start, end.parentNode, doc);
            if(ret) return ret;

            return false;
        },

        /**
         * in a DOM tree, will return the node that is
         * defined furthest in the document.
         * example:
         * body
         *  - div
         *     - p
         *       - node 1
         *  - pre
         *    - node 2
         *
         * node 2 will be returned
         * @param node1
         * @param node2
         */
        getOldestNode : function(ed, node1, node2){
            var lineage1 = this.getLineage(ed, node1);
            var lineage2 = this.getLineage(ed, node2);
            if(lineage1.length < lineage2.length){
                // make sure lineage1 is the longest
                var temp = lineage2;
                lineage2 = lineage1;
                lineage1 = temp;
                temp = node2;
                node2 = node1;
                node1 = temp;
            }
            // i starts at 1, b/c the 0th node
            // is always the body node, so that's
            // always common between the nodes
            for(var i=1;i<lineage2.length;i++){
                if(lineage2[i] != lineage1[i]){
                    // one of these is an older sibling
                    // choose that one
                    var parent = lineage2[i-1];
                    for(var j=0;j<parent.childNodes.length;j++){
                        if(parent.childNodes[j] == lineage1[i]){
                            return node2;
                        }else if(parent.childNodes[j] == lineage2[i]){
                            return node1;
                        }
                    }
                }
            }
            // node 1 is a child of node 2
            return node1;
        },

        appendTextToPreUntilHelper : function(ed, pre, currNode, lastNode){
            // if it's the final node, quit
            var ret = false;
            if(currNode == null) return true;
            if(currNode == lastNode){
//                console.log(currNode.nodeType == 3 ? "text" : currNode.tagName);
                ret = true;
            }
            var doc = ed.getDoc();
            if(currNode.nodeType == 3){ // text node
                this.insertIntoPre(pre, currNode);
                return false;
            }
            // add all my children
            while(currNode.childNodes.length > 0){
                var node = currNode.childNodes[0];
                if(this.appendTextToPreUntilHelper(ed, pre, node, lastNode)){
                    if(node.nodeType != 3) currNode.removeChild(node);
                    return true;
                }
                if(node.nodeType != 3) currNode.removeChild(node);
            }
            if(this.shouldEndLine(currNode)){
                this.insertIntoPre(pre, doc.createTextNode("\n"));
            }

            if(ret) return ret;

            // add my next sibling, if any
            if(currNode.nextSibling != null){
                if(this.appendTextToPreUntilHelper(ed, pre, currNode.nextSibling, lastNode)){
                    return true;
                }
            }
            return false;
        },

        appendTextToPreUntil : function(ed, pre, currNode, lastNode){
            var start = ed.dom.getParent(currNode, ed.dom.isBlock);
            var end = ed.dom.getParent(lastNode, ed.dom.isBlock);

            // base case, start == end
            if(start == end){
                pre.appendChild(start);
                return;
            }


            // make sure that start and end have the same parent
            if(start.parentNode != end.parentNode){
                // find parents
                var parents = this.ageTheNodes(start, end);
                start = parents[0];
                end = parents[1];
            }

            var next = null;
            while(start && (next = start.nextSibling) && start != lastNode){
                pre.appendChild(start);
                start = next;
            }
            pre.appendChild(lastNode);
            return;
        },

        getNodesBetweenHelper : function(ed, arr, nodeName, currNode, lastNode){
            // if it's the final node, quit
            var ret = false;
            if(ret) return ret;
            if(currNode == null) return ret;
            if(currNode == lastNode){
                ret = true;
            }
            if(currNode.nodeType == 1 && currNode.nodeName.toLowerCase() == nodeName){ // element node
                arr.push(currNode);
            }else if(currNode.nodeType == 1){
                // add all my children
                for(var i=0;i<currNode.childNodes.length;i++){
                    var node = currNode.childNodes[i];
                    // we still want to get the children of currNode, even if
                    // ret is true
                    ret = this.getNodesBetweenHelper(ed, arr, nodeName, node, lastNode) || ret;
                }

                if(ret) return ret;

                // add my next sibling, if any
                if(currNode.nextSibling != null){
                    ret = ret || this.getNodesBetweenHelper(ed, arr, nodeName, currNode.nextSibling, lastNode);
                }
            }
            return ret;

        },

        getNodesBetween : function(ed, nodeName, currNode, lastNode){
            var nodes = this.ageTheNodes(currNode, lastNode, ed.getDoc());
            var ret = new Array();
            this.getNodesBetweenHelper(ed, ret, nodeName, nodes[0], nodes[1]);
            return ret;
        },

        shouldEndLine : function(node){
            if(node.nodeType != 3 && (node.tagName.toLowerCase() == "div" ||
                                      node.tagName.toLowerCase() == "p" ||
                                      node.tagName.toLowerCase() == "pre" ||
                                      node.tagName.toLowerCase() == "br")){
                return true;
            }
            return false;
        },

        applyParameterSet : function(node, macro, paramSet){
            if(this.isMacro(node)){
                if(paramSet.deleteAll){
                    // delete all the macro parameters in the node
                    var params = macro.getAllowedParameters();
                    for(var i=0;i<params.length;i++){
                        node.removeAttribute("_" + params[i].name);
                    }
                }

                for(var i=0;i<paramSet.params.length;i++){
                    node.setAttribute("_" + paramSet.params[i].name, paramSet.params[i].value);
                }
            }
        },

        buildMacro :function(macro, paramSet){
            if(!$def(paramSet)){
                paramSet = 0;
            }
            var ed = tinyMCE.activeEditor;
            var sel = ed.selection;
            var doc = ed.getDoc();
            var name = macro.getName();
            var pre;
            var collapsed = sel.isCollapsed();
            if(macro.getMacroType() == "TEXT"){
                pre = doc.createElement('pre');
                pre.setAttribute("class", "jive_text_macro jive_macro_" + name);
                pre.className = "jive_text_macro jive_macro_" + name;
            }else if(macro.getMacroType() == "INLINE"){
                pre = doc.createElement("span");
                pre.appendChild(doc.createTextNode(name));
                pre.setAttribute("class", "jive_macro jive_macro_" + name);
                pre.className = "jive_macro jive_macro_" + name;
            }else{
                pre = doc.createElement("img");
                pre.setAttribute("class", "jive_macro jive_macro_" + name);
                pre.className = "jive_macro jive_macro_" + name;
                var src = window.parent.CS_BASE_URL + "/resources/scripts/tiny_mce3/plugins/jiveemoticons/images/spacer.gif";
                pre.setAttribute('src',src);
                pre.setAttribute('mce_src',src);
            }
            pre.setAttribute("jivemacro", name);
            var par;
            var theStart = null;
            if(!sel.isCollapsed() && macro.getMacroType() != "TEXT"){
                collapsed = true;
            }else if(!sel.isCollapsed() && macro.getMacroType() == "TEXT"){
                collapsed = false;
                var startN = sel.getStart();
                var endN = sel.getEnd();
                if(startN == endN){
//                        console.log("not collapsed, same node");
                    theStart = startN;
                    par = this.getParentNode(ed, theStart);
                    // insert it into the dom
                    par.insertBefore(pre, theStart);
                    // append all the text
                    if(macro.getMacroType() == "TEXT"){
                        this.appendTextToPre(pre, theStart, doc);
                    }
                    par.removeChild(theStart);
                }else{
//                        console.log("not collapsed, different nodes");
                    theStart = this.getOldestNode(ed, sel.getNode(), startN);
                    var nodes = this.ageTheNodes(theStart, endN, ed.getDoc());
                    theStart = nodes[0];
                    endN = nodes[1];
                    par = this.getParentNode(ed, theStart);
                    // insert it into the dom
                    par.insertBefore(pre, theStart);
                    // append all the text
                    if(macro.getMacroType() == "TEXT"){
                        this.appendTextToPreUntil(ed, pre, theStart, endN);
                    }
                }
                if(macro.getMacroType() == "TEXT"){
                    sel.select(pre.childNodes[0]);
                }else{
                    sel.select(pre);
                }
                sel.collapse();
            }else{
                collapsed = true;
                par = this.getParentNode(ed, sel.getNode());
                theStart = sel.getNode();
                if(par == theStart){
                    theStart = null;
                }
            }

            if(macro.getMacroType() == "TEXT" && pre.childNodes.length == 0){
                pre.appendChild(this.createEmptyPara(ed.getDoc()));
            }

            // now apply the preset, if any
            if(paramSet >= 0){
                var paramSets = macro.getParameterSets();
                if(paramSet < paramSets.length){
                    var paramSet = paramSets[paramSet];
                    this.applyParameterSet(pre, macro, paramSet);
                }
            }
            return pre;
        },

        insertMacro : function(macro, paramSet){
            return this.buildMacro(macro, paramSet);
        },

        execCommand : function(cmd, ui, val){
            var ed = tinyMCE.activeEditor;
            if(cmd.indexOf("mceMacroParamSet") == 0){
                var indexes = cmd.substr("mceMacroParamSet".length);
                var macro = parseInt(indexes.substr(0,indexes.indexOf("_")));
                var param = parseInt(indexes.substr(indexes.indexOf("_") + 1));
                var paramSet = jive.rte.macros[macro].getParameterSets()[param];

                var n = ed.selection.getNode();
                var macroEle = ed.dom.getParent(n, function(ele){
                    return $def(ele.attributes["jivemacro"]);
                });
                if(macroEle != null){
                    this.applyParameterSet(macroEle, jive.rte.macros[macro], paramSet);
                    ed.selection.collapse();
                }
                return true;
            }
            if(cmd.indexOf("mceAddJiveMacro") == 0){
                var index = cmd.substr("mceAddJiveMacro".length);
                var paramSet = 0;
                var under = index.indexOf("_");
                if(under >= 0){
                    paramSet = parseInt(index.substr(under+1));
                    index = parseInt(index.substr(0,under));
                }else{
                    index = parseInt(index);
                }
                var sel = ed.selection;
                var doc = ed.getDoc();
                var macro = jive.rte.macros[index];

                if(macro.getUrl().length > 0){
                    window.jiveRTE = null;
                    var w = ed.windowManager.open({
                        url : CS_BASE_URL + macro.getUrl(),
                        width : 500,
                        height : 400,
                        inline : 1
                    }, {
                        macro : macro
                    });
                }else{
                    var collapsed = ed.selection.isCollapsed();
                    var pre = ed.plugins.jivemacros.insertMacro(macro, paramSet);
                    if(collapsed){
                        ed.selection.setNode(pre);
                    }
                    var ele = ed.selection.getNode();
                    ed.plugins.jivemacros.removeDuplicateMacros(ed, ed.getBody());
                    // clean up
                    this.fixBodyParagraphs();
//                    ed.selection.select(ele);
//                    ed.selection.collapse();
//                    ed.focus();
                }
                return true;
            }
            return false;
        },

        isFirst : function(par, ele){
            if(par.childNodes.length == 0) return false;
            return (par.childNodes[0] == ele || this.isFirst(par.childNodes[0], ele));
        },

        isLink : function(ele){
            if(ele == null || ele.nodeName.toLowerCase() == "body") return false;
            // make sure it's an object and not a text node
            if($def(ele) &&
               $obj(ele) &&
               ele != null &&
               $def(ele.nodeType) &&
               ele.nodeType != 3){
                if(ele.nodeName.toLowerCase() == "a"){
                    return ele;
                }else{
                    return this.isLink(ele.parentNode);
                }

            }
            return false;
        },

        /**
         * checks if the input element, or any of its parents is a macro
         * @param ele
         */
        isMacro : function(ele){
            if(ele == null || ele.nodeName.toLowerCase() == "body") return false;
            // make sure it's an object and not a text node
            if(ele != null &&
               $obj(ele) &&
               $def(ele.nodeType) &&
               ele.nodeType == 1){
                if($def(ele.attributes["jivemacro"])){
                    return ele;
                }else{
                    return this.isMacro(ele.parentNode);
                }

            }
            return false;
        },

        /**
         * checks if the input element is a macro,
         * but doesn't check any of its parents
         * @param ele
         */
        isExactMacro : function(ele){
            if(ele == null || ele.nodeName.toLowerCase() == "body") return false;
            // make sure it's an object and not a text node
            if(ele != null &&
               $obj(ele) &&
               $def(ele.nodeType) &&
               ele.nodeType == 1){ // elements only
                if($def(ele.attributes["jivemacro"])){
                    return ele;
                }else{
                    return false;
                }

            }
            return false;
        },

        getMacroFor : function(ele){
            var mac = this.isMacro(ele);
            if(mac){
                var name = mac.attributes["jivemacro"].nodeValue;
                for(var i=0;i<jive.rte.macros.length;i++){
                    if(jive.rte.macros[i].getName() == name){
                        return jive.rte.macros[i];
                    }
                }
            }
            return null;
        },

        createMozBR : function(doc){
            var br = doc.createElement('BR');
            var attr;
            if($def(br.setAttribute)){
                br.setAttribute("_moz_dirty","");
                br.setAttribute("type","_moz");
            }else{
                attr = doc.createAttribute ('_moz_dirty');
                attr.value = "";
                br.attributes.setNamedItem(attr);
                attr = doc.createAttribute ('type');
                attr.value = "_moz";
                br.attributes.setNamedItem(attr);
            }
            return br;
        },

        createEmptyPara : function(doc){
            var p = doc.createElement('P');
            p.appendChild(this.createMozBR(doc));
            return p;
        },

        findLastKid : function(ele){
            if(ele.nodeType == 3) return null;
            if(ele.childNodes.length == 0) return null;
            if(this.isMacro(ele)
                       || $def(ele.tagName) && ele.tagName.toLowerCase() == "table"
                       || $def(ele.tagName) && ele.tagName.toLowerCase() == "pre"){
                return ele;
            }
            return this.findLastKid(ele.childNodes[ele.childNodes.length - 1]);
        },


        isBR : function(ele){
            if(ele.nodeType == 3) return false;
            return ele.nodeName.toLowerCase() == "br";
        },

        /**
         * converts <br><br> to <p></p>
         * also removes empty list nodes
         * @param ed
         * @param ele
         */
        convertDoubleLineBreaksToParagraphs : function(ed, ele){
            if(ele.nodeType != 1) return; // ignore non-Element elements
            if(!ed.dom.isBlock(ele) && ele.nodeName.toLowerCase() != "body") return;
            if(ele.nodeName.toLowerCase() == "pre") return;
            if(this.isExactMacro(ele)) return;
            var br2;
            if(ele.childNodes.length > 0){
                br2 = ele.childNodes[ele.childNodes.length-1];
            }
            for(var i=0;i<ele.childNodes.length - 1;i++){ // go until - 1, b/c we're checking for 2 <br>'s in a row
                var br1 = ele.childNodes[i];
                br2 = ele.childNodes[i+1];
                if(br1.nodeType == 1 && br1.nodeName.toLowerCase() == "ol" || br1.nodeName.toLowerCase() == "ul"){
                    if(br1.childNodes.length == 0){
                        ele.removeChild(br1);
                        i--;
                    }
                }else if(br2.nodeType == 1 && br2.nodeName.toLowerCase() == "ol" || br2.nodeName.toLowerCase() == "ul"){
                    if(br2.childNodes.length == 0){
                        ele.removeChild(br2);
                        i--;
                    }
                }else if(this.isBR(br1) && this.isBR(br2)){
                    var li = ed.dom.getParent(br1, "li");
                    if(li == null){ // don't fix linebreaks in lists
                        // copy the node type of the above node
                        var par = br1.parentNode;
                        var body = par.parentNode;
                        var newpar = par.cloneNode(true);
                        while(newpar.childNodes.length > 0) newpar.removeChild(newpar.childNodes[0]);
                        while(i<ele.childNodes.length - 2){
                            ele.childNodes[i+2];
                            newpar.appendChild(ele.childNodes[i+2]);
                        }
                        this.insertAfter(body, newpar, par);
                        this.insertAfter(body, this.createEmptyPara(ed.getDoc()), par);
                        par.removeChild(br1);
                        par.removeChild(br2);
                        br2 = newpar;
                    }
                }else{
                    this.convertDoubleLineBreaksToParagraphs(ed, br1);
                }
            }
            if($def(br2)){
                this.convertDoubleLineBreaksToParagraphs(ed, br2);
            }
        },

        fixBodyParagraphs : function(){
            var ed = tinyMCE.activeEditor;
            var body = ed.getBody();
            this.convertDoubleLineBreaksToParagraphs(ed, body);
            var lastKid = this.findLastKid(body);
            if(body.childNodes.length > 0 && lastKid){
                var doc = ed.getDoc();
                body.appendChild(this.createEmptyPara(doc));
            }
            if (!tinymce.isIE){
                for(var i=0;i<body.childNodes.length;i++){
                    var ele = body.childNodes[i];
                    if(ele.nodeType == 1 && ele.tagName.toLowerCase() == "p"){
                        if(ele.childNodes.length == 0){
                            ele.appendChild(this.createMozBR(ed.getDoc()));
                        }
                    }
                }
            }
        },

        nodeChanged : function(ed, cm, node,collapse){
            this.fixBodyParagraphs();
            var macro = this.getMacroFor(node);
            if(cm.get('justifyleft')){
                if(macro != null){
                    cm.get('justifyleft').setDisabled(true);
                    cm.get('justifycenter').setDisabled(true);
                    cm.get('justifyright').setDisabled(true);
                    cm.get('justifyfull').setDisabled(true);
                }else{
                    cm.get('justifyleft').setDisabled(false);
                    cm.get('justifycenter').setDisabled(false);
                    cm.get('justifyright').setDisabled(false);
                    cm.get('justifyfull').setDisabled(false);
                }
            }

            // fix smileys
//            if(this.before) ed.plugins.jiveemoticons.encodeSmileys(ed, this.before);
//            if(node.previousSibling) ed.plugins.jiveemoticons.encodeSmileys(ed, node.previousSibling);
//            ed.plugins.jiveemoticons.encodeSmileys(ed, node);
        },

        nextId : 0,

        /**
         * @param ed the Editor
         * @param ele the elmeent to look at
         * @ids the macros that we've come across so far
         */
        removeDuplicateMacros : function(ed, ele, ids){
//            if(!$def(ids)){
//                ids = new jive.ext.y.HashTable();
//            }
            if(ele.nodeType == 1 &&
               (ele.nodeName.toLowerCase() == "td" ||
                ele.nodeName.toLowerCase() == "th") &&
                ele.childNodes.length == 0 &&
                !tinymce.isIE){
                ele.appendChild(this.createMozBR(ed.getDoc()));
            }else if(this.isExactMacro(ele)){
                // read teh property and see if it's a duplicate
//                var id = ele.getAttribute("macro_id");
//                if(id == null){
//                    // it hasn't been tagged. tag it! and refresh it!
//                    ele.setAttribute("macro_id", this.nextId);
//                    this.nextId++;
                    var macro = this.getMacroFor(ele);
                    if(macro != null) macro.refresh(window.editor, ele);
//                }else{
//                    var hasIt = ids.get(id);
//                    if($obj(hasIt)){
////                        var par = ed.dom.getParent(ele, function(x){ return x != ele; });
////                        par.removeChild(ele);
//                    }else{
//                        ids.put(id, ele);
//                        var macro = this.getMacroFor(ele);
//                        if(macro != null) macro.refresh(window.editor, ele);
//                    }
//                }
            }
            for(var i=0;i<ele.childNodes.length;i++){
                this.removeDuplicateMacros(ed, ele.childNodes[i], ids);
            }
        },

        collapseToStart : null,

        insertAfter : function(par, newNode, refNode) {
            if(refNode.nextSibling) {
                return par.insertBefore(newNode, refNode.nextSibling);
            } else {
                return par.appendChild(newNode);
            }
        },

        putCursorAfter : function(ed, node){
            var sel = ed.selection;
            var select_me;
            if(node.nextSibling == null || node.nextSibling.nodeName.toLowerCase() == "br"){
                // add a blank text node as a sibling, and select it
                var par = ed.dom.getParent(node, function(node){ return function(x){ return x == ed.getBody() || x != node; }}(node));
                if(tinymce.isWebKit){
                    var text = ed.getDoc().createElement("span");
                    text.innerHTML = "&nbsp;";
                }else{
                    var text = ed.getDoc().createTextNode(" ");
                }
                this.insertAfter(par, text, node);
                select_me = text;
            }else{
                select_me = node.nextSibling;
            }
            sel.select(select_me);
            if(select_me.nodeType == 3 && select_me.nodeValue.length <= 1){
                // collapse to the beginning of the text node
                sel.collapse(true);
            }else if(select_me.nodeType == 3 && select_me.nodeValue.length > 1){
                // collapse to the beginning of the text node
                var r = sel.getRng();
                r.setStart(select_me, tinymce.isWebKit ? 1 : 0);
                r.setEnd(select_me,tinymce.isWebKit ? 1 : 0);
                sel.setRng(r);
//                sel.collapse(true);
            }else if(select_me.nodeType == 1 && select_me.childNodes.length > 0){
                var r = sel.getRng();
                r.setStart(select_me, tinymce.isWebKit ? 1 : 0);
                r.setEnd(select_me,tinymce.isWebKit ? 1 : 0);
                sel.setRng(r);
//                ed.selection.collapse(false);
            }else{
                sel.collapse(true);
            }
        },

        putCursorBefore : function(ed,node){
            if(tinymce.isWebKit){
                var select_me;
                if(node.previousSibling == null){
                    // add a blank text node as a sibling, and select it
                    var par = ed.dom.getParent(node, function(node){ return function(x){ return x == ed.getBody() || x != node; }}(node));
                    var text = ed.getDoc().createElement("span");
                    text.innerHTML = "&nbsp;";
                    par.insertBefore(text, node);
                    select_me = text;
                }else{
                    select_me = node.previousSibling;
                }
                var sel = ed.selection;
                sel.select(select_me);
                sel.collapse(true);
//                par.removeChild(text);
            }else{
                var sel = ed.selection;
                sel.select(node);
                sel.collapse(true);
            }
        },

        isLastNodeOf : function(sel, node, par){
            if(node.childNodes.length > 0 &&
               node.childNodes[node.childNodes.length-1].nodeType == 3 &&
               sel.getRng().startContainer != node.childNodes[node.childNodes.length-1]){
                return false;
            }
            if(node == par) return true;
            if(node.nextSibling != null) return false;
            return this.isLastNodeOf(node.parentNode, par);
        },

        isFirstNodeOf : function(sel, node, par){
            if(node.childNodes.length > 0 && 
               node.childNodes[0].nodeType == 3 &&
               sel.getRng().startContainer != node.childNodes[0]){
                return false;
            }
            if(node == par) return true;
            if(node.previousSibling != null) return false;
            return this.isLastNodeOf(node.parentNode, par);
        },

        fixParaStyleHeight : function(ed, ele){
            if(ele.nodeType != 3 && ele.nodeName.toLowerCase() == "p"){
                ele.style.height = "";
            }else if(ele.nodeType != 3){
                for(var i=0;i<ele.childNodes.length;i++){
                    ed.plugins.jivemacros.fixParaStyleHeight(ed, ele.childNodes[i]);
                }
            }
        },

        init : function(ed, url){
            this.url = url;
            for(var i=0;i<jive.rte.macros.length;i++){
                if(jive.rte.macros[i].getName() == "emoticon"){
                    macro = jive.rte.macros[i]
                }
            }
            this.emoticon = macro;
            var t = this;
            ed.addCommand('mceJiveMacros', function(){
                var cm = this.controlManager;
                var button = cm.get('jivemacros');
                button.showMenu();
            });

            ed.addCommand('mceEditJiveMacro', function() {
                var ed = tinyMCE.activeEditor;
                var macro = ed.plugins.jivemacros.macro;
                var height = macro.getAllowedParameters().length * 28 + 88;
                if(macro.getUrl().length > 0){
                    window.jiveRTE = new Object();
                    window.jiveRTE.macro = macro;
                    window.jiveRTE.node = ed.dom.getParent(ed.selection.getNode(), ed.plugins.jivemacros.isExactMacro)
                    var w = ed.windowManager.open({
                        url : CS_BASE_URL + macro.getUrl(),
                        width : 500,
                        height : 400,
                        inline : 1
                    }, {
                        macro : macro
                    });
                }else{
                    ed.windowManager.open({
                        url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/jivemacros/macro.htm',
                        width : 400,
                        height : height,
                        inline : 1
                    }, {
                        plugin_url : url,
                        macro : macro
                    });
                }
            });
            ed.addCommand('mceDeleteJiveMacro', function(){
                    var ed = tinyMCE.activeEditor;
                    this.deleteMacro(ed, ed.selection.getNode());
                }, this);

            ed.onInit.add(function() {
                var ed = tinyMCE.activeEditor;
                // now put ids on all the macros, so we can delete duplicates if/when they occur
                ed.plugins.jivemacros.removeDuplicateMacros(ed, ed.getBody());
                ed.plugins.jivemacros.fixParaStyleHeight(ed, ed.getBody());


                ed.onMouseUp.add(function(ed, e){
                    var html = e.target;
                    if(ed.selection.isCollapsed() && html.nodeName.toLowerCase() == "html"){
                        var body = ed.getBody();
                        if(body){
                            if(body.childNodes.length == 0){
                                ed.plugins.jivemacros.fixBodyParagraphs();
                            }
                            // find last node of everything and select it
                            while(body.nodeType == 1 && body.childNodes.length){
                                body = body.childNodes[body.childNodes.length - 1];
                            }
                        }
                        ed.selection.select(body);
                        if(body.nodeType == 1 && body.nodeName.toLowerCase() == "br"){
                            ed.selection.collapse(true);
                        }else{
                            ed.selection.collapse();
                        }
                    }
                });



                /**
                 * context menu for macros
                 */
                if (ed && ed.plugins.contextmenu) {
                    ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {
                        var sm, se = ed.selection, el = se.getNode() || ed.getBody();

                        var macroEle = ed.dom.getParent(el, function(ele){
                            return ele == ed.getBody() || ed.plugins.jivemacros.isExactMacro(ele);
                        });
                        if(!ed.plugins.jivemacros.isExactMacro(macroEle)) macroEle = null;

                        var macro = null;
                        var index = -1;
                        var isHTML = false;
                        if(macroEle != null){
                            // find the macro type that they right clicked on
                            for(var i=0;i<jive.rte.macros.length;i++){
                                if(jive.rte.macros[i].getName() == macroEle.attributes["jivemacro"].value){
                                    macro = jive.rte.macros[i];
                                    index = i;
                                    if(macro.getName() == "code"){
                                        // special handling for the code macro,
                                        // since there are two macros named "code"
                                        // 1 for html and 1 for everything else
                                        if(macroEle.attributes["___default_attr"].value == "html"){
                                            // macroEle is the html, and the macro var is the wrong macro
                                            isHTML = true;
                                        }
                                    }
                                }
                            }
                        }
                        window.jiveRTE = new Object();
                        window.jiveRTE.macroEle = macroEle;
                        if(macro != null){
                            ed.plugins.jivemacros.macro = macro;
                            m.removeAll();



                            // "link" macros, like document/blog/thread/etc
                            if(macroEle.nodeName.toLowerCase() == "a" &&
                                    (macro.getName() == "document" ||
                                     macro.getName() == "blogpost" ||
                                     macro.getName() == "thread" ||
                                     macro.getName() == "message" ||
                                     macro.getName() == "blog" ||
                                     macro.getName() == "community" ||
                                     macro.getName() == "space" ||
                                     macro.getName() == "user" ||
                                     macro.getName() == "project" ||
                                     macro.getName() == "task" ||
                                     macro.getName() == "group")){
                                m.add({title : 'jivemacros.unlink', cmd : "mceDeleteJiveMacro"});
                            }else{
                                // presets menu
                                var paramSets = macro.getParameterSets();
                                if(paramSets.length > 1){
                                    if(macro.getName() != "code" || !isHTML){
                                        var preset_title = ed.getLang("jivemacros.macro." + macro.getName() + ".presets","jivemacros.presets");
                                        sm = m.addMenu({title : preset_title,max_height : '200', 'class' : 'mceDropDown defaultSkin mceMacroMenu mceListBoxMenu', icon : "jiveMacroPreset"});
                                        for(var i=0;i<paramSets.length;i++){
                                            // mceMenuItemSelected
                                            if(macro.getName() != "code" || paramSets[i].name != "html"){
                                                var preset_name = ed.getLang("jivemacros.macro." + macro.getName() + ".preset." + paramSets[i].name, paramSets[i].name);
                                                sm.add({title : preset_name, cmd : 'mceMacroParamSet' + index + "_" + i, icon : "jiveMacro_" + macro.getName() + "_" + paramSets[i].name});
                                            }
                                        }
                                    }
                                }
                                if(macro.getAllowedParameters().length > 0 && macro.isShowSettings()){
                                    m.add({title : 'jivemacros.properties', icon : 'jiveMacroProperties', cmd : "mceEditJiveMacro", ui : true});
                                }
                                m.add({title : 'jivemacros.remove', icon : 'jiveMacroDelete', cmd : "mceDeleteJiveMacro", ui : true});
                            }
                        }else{
                            this.macro = null;
                        }
                    });
                }
            });

            ed.onNodeChange.add(this.nodeChanged, this);
            ed.onKeyPress.addToTop(function(ed, evt){
                if(this.collapseToEnd != null || this.collapseToStart != null){
                    tinymce.dom.Event.cancel(evt);
                }
                var mac = this.isMacro(ed.selection.getNode());
                if(ed.dom.hasClass(mac, "default_title")){
                    ed.dom.removeClass(mac, "default_title");
                }
                ed.selection.lastSelected = ed.selection.getNode();
            }, this);
            ed.onKeyDown.addToTop(function(ed, evt){
                ed.selection.lastSelected = ed.selection.getNode();
                var sel = ed.selection;
                var mac = this.isMacro(sel.getNode()) || this.isLink(sel.getNode());
                if((evt.keyCode == 37) && mac){
                    // we're inside a link, pressing the left arrow key
                    // check if we're at the first letter of the a tag
                    var startOffset = 0;
                    if(tinymce.isWebKit){
                        startOffset = 1;
                    }
                    var isFrontOfBlock = sel.getRng().startOffset == startOffset && this.isFirstNodeOf(sel, sel.getNode(), mac);
                    isFrontOfBlock = isFrontOfBlock || ed.dom.hasClass(mac, "default_title");
//                    console.log("is first? " + this.isFirstNodeOf(sel, sel.getNode(), mac) + " at front? " + isFrontOfBlock);
                    if(isFrontOfBlock){
//                        console.log("collapsing to front");
                        this.collapseToStart = mac;
                        sel.select(mac);
                        sel.collapse(true);
                        tinymce.dom.Event.cancel(evt);
                        return false;
                    }
                }
                if((evt.keyCode == 39) && mac){
                    // we're inside a link, pressing the right arrow key
                    // check if we're at the last letter of the a tag
                    if(mac.childNodes.length == 0){
                        this.collapseToEnd = mac;
                        tinymce.dom.Event.cancel(evt);
                        return false;
                    }else{
                        var isEndOfBlock = sel.getRng().startOffset == (sel.getNode().childNodes[sel.getNode().childNodes.length-1].nodeValue.length) && this.isLastNodeOf(sel, sel.getNode(), mac);
//                        console.log("sel node: " + sel.getNode());
//                        console.log("is last? " + this.isLastNodeOf(sel, sel.getNode(), mac));
                        isEndOfBlock = isEndOfBlock || ed.dom.hasClass(mac, "default_title");
                        console.log("isEndOfBlock: " + isEndOfBlock);
                        if(isEndOfBlock){
//                            console.log("collapsing to end");
                            this.collapseToEnd = mac;
                            sel.select(mac);
                            sel.collapse();
                            tinymce.dom.Event.cancel(evt);
                            return false;
                        }
                    }
                }


                var body = ed.getBody();
                if(body.childNodes.length > 0){
                    var n = ed.selection.getNode();
                    this.before = n;

                    if(evt.keyCode == 13 && this.before.tagName == "PRE"){
                        if (tinymce.isIE){

                            var pre = ed.dom.getParent(n, "pre");
                            tinymce.dom.Event.cancel(evt);
                            var rng = ed.selection.getSel().createRange();
                            rng.text= "\n ";
                            rng.select();
                            return;
                        }else if (tinymce.isWebKit){
                            tinymce.dom.Event.cancel(evt);
                            var childs = ed.selection.getNode().childNodes;
                            var range = ed.selection.getRng();

                            if(childs.length == 0){
                                ed.selection.getNode().appendChild(ed.getDoc().createTextNode("\n"));
                            }else{
                                var str = childs[0].nodeValue;
                                var index = Math.min(range.startOffset, str.length);
                                var start = str.substr(0, index);
                                var end = (index < str.length) ? str.substr(index) : "";
                                childs[0].nodeValue = start + "\n" + end;
                                var search = end.substr(0,1);
                                ed.getWin().find(search, true, false, false, false, false, false)
                                var se = ed.selection;
                                if(search == "\n" || search == "\r"){
                                    var r = se.getRng().cloneRange();
                                    ed.getDoc().execCommand('SelectAll', false, null);
                                    r.setEnd(r.endContainer,r.endOffset+1);
                                    r.setStart(r.startContainer,r.startOffset+1);
                                    se.setRng(r);
                                }
                                se.collapse(true);
                            }
                        }
                    }
                }

            }, this);
            ed.onKeyUp.add(function(ed, evt){
                if(ed.selection.getNode().nodeName.toLowerCase() == "body" && ed.selection.lastSelected && ed.selection.lastSelected.nodeName.toLowerCase() != "body"){
                    var selectme = ed.selection.lastSelected;
                    if(selectme.childNodes.length){
                        selectme = selectme.childNodes[selectme.childNodes.length-1];
                        ed.selection.select(selectme);
                        ed.selection.collapse(true);
                    }else{
                        ed.selection.select(selectme);
                        ed.selection.collapse();
                    }
                }
                var sel = ed.selection;
                if(this.collapseToStart != null){
                    this.putCursorBefore(ed,this.collapseToStart);
                    tinymce.dom.Event.cancel(evt);
                    this.collapseToStart = null;
                    return;
                }
                if(this.collapseToEnd != null){
                    this.putCursorAfter(ed, this.collapseToEnd);
                    tinymce.dom.Event.cancel(evt);
                    this.collapseToEnd = null;
                    return;
                }
                if(this.before != null && (evt.keyCode == 39 || evt.keyCode == 40)){
                    var after = ed.dom.getParent(ed.selection.getNode(), "PRE");
                    if(this.before.tagName != "PRE" && after){ // down or right
                        if(this.before.childNodes.length == 1 && this.before.childNodes[0].tagName == "BR"){
                            var par = ed.dom.getParent(this.before, function(node){ return function(x){ return x == ed.getBody() || x != node; }}(this.before));
                            if(par.childNodes[0] == this.before){
                                // only remove the blank paragraph if it's the
                                // first paragraph in the container
                                par.removeChild(this.before);
                                this.before = after;
                            }
                        }
                    }
                }

                ed.plugins.jiveemoticons.encodeSmileys(ed, ed.selection.getNode());


                if(evt.keyCode == 37 || evt.keyCode == 38){ // up or left
                    var sel = ed.selection;
                    // check if the pre is first in the body
                    // check if we're at the beginning of the pre
                    var node = ed.dom.getParent(sel.getNode(), function(ele){
                        return ele == ed.getBody() || ed.plugins.jivemacros.isExactMacro(ele);
                    });
                    if(!ed.plugins.jivemacros.isExactMacro(node)) node = null;
                    if(node == null) return;
                    
                    var isFirst = this.isFirst(ed.getBody(), node);
                    var isMacro = this.isMacro(node)
                    var isFrontOfPre =  sel.getBookmark().start == 0 || sel.getRng().startOffset == 0;
                    if(isFirst && isMacro && isFrontOfPre && sel.isCollapsed()){
                        var p = ed.getDoc().createElement('P');
                        var br = ed.getDoc().createElement('BR');
                        p.appendChild(br);
                        ed.getBody().insertBefore(p, ed.getBody().childNodes[0]);
                        return true;
                    }
                }
            }, this);
        },

        getInfo : function() {
            return {
                longname : 'Jive Macros',
                author : 'Jive Software',
                authorurl : 'http://jivesoftware.com',
                infourl : 'http://jivesoftware.com',
                version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
            };
        },

        generateMacroMenu : function(m, index){
                var ed = tinyMCE.activeEditor;
                var macro = jive.rte.macros[index];
                // presets menu
                var paramSets = macro.getParameterSets();
                var macro_name = ed.getLang("jivemacros.macro." + macro.getName(), macro.getName());
                if(paramSets.length > 1){
                    sm = m.addMenu({title : macro_name, max_height : '200', 'class' : 'mceDropDown defaultSkin mceMacroMenu mceListBoxMenu', icon : "jiveMacro_" + macro.getName()});
                    for(var i=0;i<paramSets.length;i++){
                        // mceMenuItemSelected
                        var preset_name = ed.getLang("jivemacros.macro." + macro.getName() + ".preset." + paramSets[i].name, paramSets[i].name);
                        if(paramSets[i].name == "html" && macro.getName() == "code"){
                            m.add({ cmd: "mceAddJiveMacro" + index + "_" + i,
                                    title: preset_name,
                                    max_height : '200',
                                    icon : "jiveMacro_" + macro.getName()
                            });
                        }else{
                            sm.add({title : preset_name, cmd : 'mceAddJiveMacro' + index + "_" + i, icon : macro.getName() + "_" + paramSets[i].name});
                        }
                    }
                }else if(paramSets.length == 1){
                    var preset_name = ed.getLang("jivemacros.macro." + macro.getName() + ".preset." + paramSets[0].name, paramSets[0].name);
                    m.add({ cmd: "mceAddJiveMacro"+index,
                            title: preset_name,
                            max_height : '200',
                            icon : "jiveMacro_" + macro.getName()
                    });
                }else{
                    m.add({ cmd: "mceAddJiveMacro"+index,
                            title: macro_name,
                            max_height : '200',
                            icon : "jiveMacro_" + macro.getName()
                    });
                }
        },


        /**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
			switch (n) {
				case 'jivemacros':
					var c = cm.createSplitButton('jivemacros', {
						title : 'jivemacros.add',
						cmd : "mceJiveMacros"
					});
                    for(var j=0;j<jive.rte.macros.length;j++){
                        if(jive.rte.macros[j].isShowInMacroList() &&
                           jive.rte.macros[j].getName() != "emoticon" &&
                           !jive.rte.macros[j].isButton()){
                            c.onRenderMenu.add(function(j){ return function(c, m) {
                                var ed = tinyMCE.activeEditor;
                                ed.plugins.jivemacros.generateMacroMenu(m, j);
                            }}(j));
                        }
                    }

				  // Return the new menubutton instance
				  return c;

                case 'extra':
                    var ed = tinyMCE.activeEditor;
                    /**
                    * add button macros to toolbar
                    */
                    for(var j=0;j<jive.rte.macros.length;j++){
                       if(jive.rte.macros[j].isShowInMacroList() &&
                          jive.rte.macros[j].getName() != "emoticon" &&
                          jive.rte.macros[j].isButton()){
                           // build a button
                           var macro = jive.rte.macros[j];
                           var paramSets = macro.getParameterSets();
                           var macro_name = ed.getLang("jivemacros.macro." + macro.getName(), macro.getName());
                           if(paramSets.length > 1){
                               // show a drop down menu
                               var c = cm.createSplitButton('extra', {
                                   title: macro_name,
                                   image : '../jscripts/tiny_mce/plugins/example/img/example.gif',
                                   icon : "jiveMacro_" + macro.getName(),
						            cmd : 'mceAddJiveMacro' + j
                               });
                               c.onRenderMenu.add(function(j){ return function(c, m) {
                                   for(var i=0;i<paramSets.length;i++){
                                       // mceMenuItemSelected
                                       var preset_name = ed.getLang("jivemacros.macro." + macro.getName() + ".preset." + paramSets[i].name, paramSets[i].name);
                                       m.add({title : preset_name, cmd : 'mceAddJiveMacro' + j + "_" + i, icon : macro.getName() + "_" + paramSets[i].name});
                                   }
                               }}(j));
                               return c;
                           }else{
                               var c = cm.createButton("extra", {
                                   title: macro_name,
                                   cmd: "mceAddJiveMacro"+j,
                                   icon : "jiveMacro_" + macro.getName()
                                });
                               return c;
                           }
                       }
                    }
            }

			return null;
		}


    });
	// Register plugin
	tinymce.PluginManager.add('jivemacros', tinymce.plugins.JiveMacrosPlugin);
})();


tinyMCE.addI18n('en.jivemacros',{
    unlink : "Unlink",
    add : 'Insert',
    remove : 'Remove',
    properties : 'Settings',
    title : 'Settings',
    presets : 'Presets',
    save: "Save",
    'macro.toc': "Table of Contents",
    'macro.quote': "Quote",
    'macro.user': "User",
    'macro.youtube': "YouTube",
    'macro.youtube.attr.__default_attr': "URL or Video ID",
    'macro.youtube.attr.width': "Width",
    'macro.youtube.attr.height': "Height",
    'macro.emoticon.presets': "Emoticons",
    'macro.code': "Syntax Highlighting",
    'macro.code.presets': "Format",
    'macro.code.attr.__default_attr': "Format",
    'macro.code.preset.plain': "Plain",
    'macro.code.preset.sql': "SQL",
    'macro.code.preset.java': "Java",
    'macro.code.preset.html': "Insert Raw HTML",
    'macro.code.preset.xml': "XML",
    'macro.emoticon.preset.angry': "Angry",
    'macro.emoticon.preset.blush': "Blush",
    'macro.emoticon.preset.confused': "Confused",
    'macro.emoticon.preset.cool': "Cool",
    'macro.emoticon.preset.cry': "Cry",
    'macro.emoticon.preset.devil': "Devil",
    'macro.emoticon.preset.grin': "Grin",
    'macro.emoticon.preset.happy': "Happy",
    'macro.emoticon.preset.laugh': "Laugh",
    'macro.emoticon.preset.love': "Love",
    'macro.emoticon.preset.mischief': "Mischief",
    'macro.emoticon.preset.plain': "Plain",
    'macro.emoticon.preset.sad': "Sad",
    'macro.emoticon.preset.shocked': "Shocked",
    'macro.emoticon.preset.silly': "Silly",
    'macro.emoticon.preset.wink': "Wink"
});


tinyMCE.addI18n('en.jivemacros',{
    add : 'Insert',
    remove : 'Remove',
    properties : 'Settings',
    title : 'Settings',
    params : 'Parameters',
    presets : 'Presets',
    save: "Save"
});


// Plugin static class
(function() {

    // Load plugin specific language pack
    tinymce.PluginManager.requireLangPack('jiveemoticons');

    tinymce.create('tinymce.plugins.JiveEmoticonsPlugin', {

        before : null,
        macro : null,

        shouldEncodeHuh : function(ed, n){
            var macroEle = ed.dom.getParent(n, function(ele){
                return ele == ed.getBody() || ed.plugins.jivemacros.isMacro(ele);
            });
            if(!ed.plugins.jivemacros.isMacro(macroEle)) macroEle = null;
            if(macroEle != null){
                var macro = ed.plugins.jivemacros.getMacroFor(macroEle);
                if(macro.getName() == "code") return false;
            }
            return true;
        },

        encodeSmileys : function(ed, n){
            var tofix = new Array();
            var that = this;
            var shorts = [
            [":D",  "grin",     ":D"],
            [":x",  "love",     ":x"],
            [";\\", "mischief", ";\\\\"],
            ["B-)", "cool",     "B-\\)"],
            ["]:)", "devil",    "\\]:\\)"],
            [":p",  "silly",    ":p"],
            [":P",  "silly",    ":P"],
            ["X-(", "angry",    "X\\-\\("],
            [":^0", "laugh",    ":^0"],
            [";)",  "wink",     ";\\)"],
            [":8}", "blush",    ":8}"],
            [":0",  "laugh",    ":0"],
            [":o",  "shocked",    ":o"],
            [":)",  "happy",    ":\\)"],
            [":(",  "sad",      ":\\("]];

            var w = "";
            // check to see if it even has smileys to fix or not:
            for(var i=0;i<shorts.length;i++){
                w += (w ? '|' : '') + shorts[i][2];
            }
            var r = new RegExp('(' + w + ')', 'g');
            if(!r.test(n.innerHTML)){
                return;
            }

            function encodeSmileysHelper(n){
                if(n.nodeType == 3 && that.shouldEncodeHuh(ed, n)){
                    var v = n.nodeValue;
                    var swaps = new Array();
                    for(var i=0;i<shorts.length;i++){
                        var name = shorts[i][1];
                        var imgstr = '<img class="jive_macro jive_emote" jivemacro="emoticon" ___jive_emoticon_name="' + name + '" src="' + window.CS_BASE_URL + '/images/emoticons/' + name + '.gif" mce_src="' + window.CS_BASE_URL + '/images/emoticons/' + name + '.gif">';
                        var r1 = new RegExp('(' + shorts[i][2] + ')', 'g');
                        var r2 = new RegExp('(\\S' + shorts[i][2] + ')', 'g');
                        var index = v.indexOf(shorts[i][0]);
                        if(r1.test(v)){
                            if(index >= 0 && index < (v.length-shorts[i][0].length)){
                                var str = v.substr(v.indexOf(shorts[i][0]) + shorts[i][0].length);
                                var t = ed.getDoc().createTextNode(str);
                                n.parentNode.insertBefore(t, n);
                                n.parentNode.insertBefore(n, t); // put the t after the n;
                                n.nodeValue = v.substr(0, v.indexOf(shorts[i][0]) + shorts[i][0].length);
                                v = n.nodeValue;
                            }
                            if(!r2.test(v)){
                                swaps.push([shorts[i][0], imgstr]);
                            }
                        }
                    }
                    if(swaps.length && that.shouldEncodeHuh(ed, n)){
                        v = v.escapeHTML();
                        for(var i=0;i<swaps.length;i++){
                            v = v.replace(swaps[i][0], swaps[i][1]);
                        }
                        tofix.push(n.parentNode);
                        ed.dom.replace(ed.dom.create('span', {'class' : 'remove_me'}, v), n);
                    }
                }else{
                    if(that.shouldEncodeHuh(ed, n)){
                        for(var i=0;i<n.childNodes.length;i++){
                            encodeSmileysHelper(n.childNodes[i]);
                        }
                    }
                }
            }
            var b = ed.selection.getBookmark();
            encodeSmileysHelper(n);
            if(tinymce.isWebKit){
                ed.selection.moveToBookmark(b);
            }

            function removeSurroundingSpans(n){
                if(n.childNodes && n.childNodes.length > 0){
                    for(var i=0;i<n.childNodes.length;i++){
                        if(n.className == "remove_me"){
                            n.parentNode.insertBefore(n.childNodes[i], n);
                            i--;
                        }else{
                            var len = n.childNodes.length;
                            removeSurroundingSpans(n.childNodes[i]);
                            if(len > n.childNodes.length) i -= (len - n.childNodes.length); // maintain proper counter
                        }
                    }
                }
                if(n.className == "remove_me"){
                    ed.selection.select(n);
                    ed.selection.collapse();
                    n.parentNode.removeChild(n);
                }
            }
            removeSurroundingSpans(ed.getBody());
//            for(var i=0;i<tofix.length;i++){
//                removeSurroundingSpans(tofix[i]);
//            }

        },


        nodeChanged : function(ed){
            if(this.before == null){
                this.before = ed.selection.getNode();
            }
            this.encodeSmileys(ed, this.before);
            this.before = ed.selection.getNode();
        },

        init : function(ed, url){
            var macro = null;
            for(var i=0;i<jive.rte.macros.length;i++){
                if(jive.rte.macros[i].getName() == "emoticon"){
                    macro = jive.rte.macros[i]
                }
            }
            if(macro != null){
                this.macro = macro;
//                ed.onNodeChange.add(this.nodeChanged, this);
            }
        },


        /**
         *
         * @param cn
         * @param cm
         */
        execCommand : function(cmd, ui, val){
            if(cmd == 'mceJiveEmoticon'){
                var cm = tinyMCE.activeEditor.controlManager;
                var button = cm.get('jiveemoticons');
                button.showMenu();
                return true;
            }
            return false;
        },

        getInfo : function() {
            return {
                longname : 'Emoticons',
                author : 'Jive Software',
                authorurl : 'http://jivesoftware.com',
                infourl : 'http://jivesoftware.com',
                version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
            };
        },

        /**
         * Returns the HTML contents of the emoticons control.
         */
        createControl : function(n, cm) {
            switch(n){
                case 'jiveemoticons':
                    var c = cm.createSplitButton('jiveemoticons', {
                        title : 'jiveemoticons.desc',
                        cmd : "mceJiveEmoticon"
                    });
                    c.onRenderMenu.add(function(c, m) {
                        var ed = tinyMCE.activeEditor;
                        for(var j=0;j<jive.rte.macros.length;j++){
                            var macro = jive.rte.macros[j];
                            if(macro.isShowInMacroList() && macro.getName() == "emoticon"){
                                // presets menu
                                var paramSets = macro.getParameterSets();
                                for(var i=0;i<paramSets.length;i++){
                                    // mceMenuItemSelected
                                    var preset_title = ed.getLang("jivemacros.macro." + macro.getName() + ".preset." + paramSets[i].name, paramSets[i].name);
                                    m.add({title : preset_title, cmd : 'mceAddJiveMacro' + j + "_" + i, icon : "jiveMacro_" + macro.getName() + "_" + paramSets[i].name});
                                }
                            }
                        }
                    });
                    // Return the new menubutton instance
                    return c;
            }
            return null;
        }


    });
	// Register plugin
	tinymce.PluginManager.add('jiveemoticons', tinymce.plugins.JiveEmoticonsPlugin);
})();


// UK lang variables
tinyMCE.addI18n('en.jiveemoticons',{
	title : 'Insert emoticon',
	desc : 'Emoticons'
});


(function() {

    tinymce.create('tinymce.plugins.JiveButtonsPlugin', {

        execCommand : function(cmd, ui, val){
//            console.log(cmd);
            if(cmd == "mceNull"){
                return true;
            }
            return false;
        },

        initToolbar : function(edid){
            var ed;
            if(!edid ||typeof(edid) != "string"){
                var eds = window.editor.toArray();
                for(var i=0;i<eds.length;i++){
                    if(eds[i] && eds[i].getID) this.initToolbar(eds[i].getID());
                }
                return;
            }else{
                ed = tinyMCE.get(edid);
            }
            var button1 = document.getElementById(edid + "_magicspacer0");
            var button2 = document.getElementById(edid + "_magicspacer1");
            var toolbar = document.getElementById(edid + "_toolbar1");
            if(button1){
                var cells = toolbar.rows[0].cells;
                var cell;
                var width1 = 0;
                for(var i=0;i<5;i++){
                    cell = cells[i];
                    width1 += jive.ext.x.xWidth(cell);
                }

                toolbar = document.getElementById(edid + "_toolbar2");
                cells = toolbar.rows[0].cells;
                var width2 = 0;
                for(i=0;i<7;i++){
                    cell = cells[i];
                    width2 += jive.ext.x.xWidth(cell);
                }

                var width = width1 - width2;
                if(width < 0){
                    jive.ext.x.xDisplayNone(button2);
                    jive.ext.x.xDisplayBlock(button1);
                    jive.ext.x.xWidth(button1, (width * -1) - 1);
                }else{
                    jive.ext.x.xDisplayNone(button1);
                    jive.ext.x.xDisplayBlock(button2);
                    jive.ext.x.xWidth(button2, width - 5);
                }
            }

            var spellTable = document.getElementById(edid + "_spellchecker");
            if(spellTable != null){
                var cell = ed.dom.getParent(spellTable, function(x){ return x != spellTable; });
                cell.style.width = "100%";
            }
        },

        init : function(ed, url){
            this.url = url;
            var t = this;
            this.index = 0;
            this.mindex = 0;
            ed.onInit.add(function(ed){
                if(!window.toolbarInitted){
                    jive.ext.x.xTimer.set("timeout",ed.plugins.jivebuttons, "initToolbar", 33);
                    jive.ext.x.xTimer.set("timeout",ed.plugins.jivebuttons, "initToolbar", 500);
                    jive.ext.x.xTimer.set("timeout",ed.plugins.jivebuttons, "initToolbar", 2000);
                    window.toolbarInitted = true;
                }
                ed.plugins.jivebuttons.initToolbar(ed.id);
                var list = new jive.rte.RTEListener();
                list.onShow = function(edid) { return function(){
                    try{
                        tinyMCE.get(edid).plugins.jivebuttons.initToolbar(edid);
                    }catch(e){ }
                }}(ed.id);
                list.initFinished = list.onShow;
                window.editor.get(ed.id).addListener(list);
                var toolbar = $(ed.id + "_toolbar1");
                var contain = toolbar.parentNode;
                if(tinymce.isIE){
                    contain.style.height = this.settings.default_height + "px";
                }else{
                    contain.style.height = (this.settings.default_height - 1) + "px";
                }
                contain.style.bottom = "0px";
            });

        },

        getInfo : function() {
            return {
                longname : 'Jive Buttons',
                author : 'Jive Software',
                authorurl : 'http://jivesoftware.com',
                infourl : 'http://jivesoftware.com',
                version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
            };
        },


		/**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
            if(n == "spacerbutton"){
                var c = cm.createButton("spacerbutton" + this.index, { "class" : "jive_spacer_button jive_spacer_button" + this.index, cmd : "mceNull"});
                this.index++;
                return c;
            }
            if(n == "magicspacer"){
                var c = cm.createButton("magicspacer" + this.mindex, { "class" : "jive_spacer_button jive_magicspacer_button", cmd : "mceNull"});
                this.mindex++;
                return c;
            }

			return null;
		}


    });
	// Register plugin
	tinymce.PluginManager.add('jivebuttons', tinymce.plugins.JiveButtonsPlugin);
})();


(function() {
    tinymce.PluginManager.requireLangPack('jivelink');
    tinymce.create('tinymce.plugins.JiveLinkPlugin', {


        /**
         * Returns information about the plugin as a name/value array.
         * The current keys are longname, author, authorurl, infourl and version.
         *
         * @returns Name/value array containing information about the plugin.
         * @type Array
         */
        getInfo : function() {
            return {
                longname : 'Content Picker',
                author : 'Jive Software',
                authorurl : 'http://www.jivesoftware.com',
                infourl : 'http://www.jivesoftware.com/',
                version : "1.0"
            };
        },

        init : function(ed, url){


            ed.onBeforeGetContent.add(function() {
				// remove the active_link class from the active link, if any
                if(this.lastActive != null){
                    // we changed active nodes
                    ed.dom.removeClass(this.lastActive, "active_link");
                    this.lastActive = null;
                }
            }, this);
            ed.onNodeChange.add(this.nodeChanged, this);
        },
        /**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(cn, cm) {
            switch (cn) {
                case "jivelink":
                    var c = cm.createButton('jivelink', {
                        title : 'jivelink.link_desc',
                        cmd : "mceJiveLink"
                    });
                    return c;
//                    return tinyMCE.getButtonHTML(cn, 'lang_jivespell_desc', '{$pluginurl}/images/jivespell.gif', 'mceJiveSpell', true);
            }

            return "";
		},

        /**
         * Executes a specific command, this function handles plugin commands.
         *
         * @param {string} editor_id TinyMCE editor instance id that issued the command.
         * @param {HTMLElement} element Body or root element for the editor instance.
         * @param {string} command Command name to be executed.
         * @param {string} user_interface True/false if a user interface should be presented.
         * @param {mixed} value Custom value argument, can be anything.
         * @return true/false if the command was executed by this plugin or not.
         * @type
         */
        execCommand : function(cmd, ui, val){
//        execCommand : function(editor_id, element, command, user_interface, value) {
            switch (cmd) {
                case "mceJiveLink":
                    var anySelection = false;
                    var ed = tinyMCE.activeEditor;
                    var selectedText = ed.selection.getContent();
                    var node = ed.selection.getNode();

                    var atag = ed.dom.getParent(node, "a");
                    if(atag != null){
                        ed.dom.removeClass(atag, "active_link");
                        if(ed.plugins.jivemacros.isMacro(atag)){
                            ed.plugins.jivemacros.deleteMacro(ed, atag);
                        }else{
                            ed.execCommand("unlink");
                        }
                        return true;
                    }else{
                        if (ed.selection.getNode())
                            anySelection = (ed.selection.getNode().nodeName.toLowerCase() == "img") || (selectedText && selectedText.length > 0);

                        ed.windowManager.open(
                            {
                                url: CS_BASE_URL + '/content-picker!input.jspa?name='+escape(selectedText) + "&instantiatedFromGUIEditor=true",
                                width: 700 + ed.getLang('jivelink.delta_width', 0),
                                height: 480 + ed.getLang('jivelink.delta_height', 0),
                                inline : "yes"},
                            {
                                editor_id : ed.id
                        });

                        return true;
                    }
            }

            return false;
        },

        lastActive : null,


        /**
         * Gets called ones the cursor/selection in a TinyMCE instance changes. This is useful to enable/disable
         * button controls depending on where the user are and what they have selected. This method gets executed
         * alot and should be as performance tuned as possible.
         *
         * @param {string} editor_id TinyMCE editor instance id that was changed.
         * @param {HTMLNode} node Current node location, where the cursor is in the DOM tree.
         * @param {int} undo_index The current undo index, if this is -1 custom undo/redo is disabled.
         * @param {int} undo_levels The current undo levels, if this is -1 custom undo/redo is disabled.
         * @param {boolean} visual_aid Is visual aids enabled/disabled ex: dotted lines on tables.
         * @param {boolean} any_selection Is there any selection at all or is there only a cursor.
         */
        nodeChanged : function(ed, cm, node,collapse){
//        handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
            if (node == null)
                return;
            var atag = ed.dom.getParent(node, "a");
            if (atag != null) {
                ed.controlManager.get("jivelink").setActive(true);
            }else{
                ed.controlManager.get("jivelink").setActive(false);
            }


            if(this.lastActive != null && this.lastActive != node){
                // we changed active nodes
                ed.dom.removeClass(this.lastActive, "active_link");
                this.lastActive = null;
            }
            var par = ed.dom.getParent(node, "a");
            if(par && par.nodeName.toLowerCase() == "a" && this.lastActive == null){
                this.lastActive = par;
                ed.dom.addClass(this.lastActive, "active_link");
            }
            var macro = ed.plugins.jivemacros.getMacroFor(node);
            if(macro != null && macro.getMacroType() == "INLINE"){
                if(this.lastActive == null || this.lastActive != node){
                    this.lastActive = this.isMacro(node);
                    ed.dom.addClass(this.lastActive, "active_link");
                }
                if(this.lastActive.childNodes.length > 0 && ed.dom.hasClass(this.lastActive, "default_title")){
                    ed.selection.select(this.lastActive.childNodes[0]);
                }
            }
        }
    });

    // Register plugin
	tinymce.PluginManager.add('jivelink', tinymce.plugins.JiveLinkPlugin);
})();


// UK lang variables
tinyMCE.addI18n('en.jivelink',{
    link_desc : 'Insert Link'
});



(function() {

    tinymce.create('tinymce.plugins.JiveImagePlugin', {

        /**
         * Returns information about the plugin as a name/value array.
         * The current keys are longname, author, authorurl, infourl and version.
         *
         * @returns Name/value array containing information about the plugin.
         * @type Array
         */
        getInfo : function() {
            return {
                longname : 'Image Picker',
                author : 'Jive Software',
                authorurl : 'http://www.jivesoftware.com',
                infourl : 'http://www.jivesoftware.com/',
                version : "1.0"
            };
        },

        /**
         * Gets executed when a TinyMCE editor instance is initialized.
         *
         * @param {TinyMCE_Control} Initialized TinyMCE editor control instance.
         */
        init : function(ed, url) {
            ed.onInit.add(function() {
                if(!ed.settings.images_enabled){
                    ed.controlManager.get("jiveimage").setDisabled(true);
                }
            }, this);
        },


        /**
         * Creates control instances based in the incomming name. This method is normally not
         * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
         * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
         * method can be used to create those.
         *
         * @param {String} n Name of the control to create.
         * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
         * @return {tinymce.ui.Control} New control instance or null if no control was created.
         */
        createControl: function(cn, cm) {
            switch (cn) {
                case "jiveimage":
                    var c = cm.createButton('jiveimage', {
                        title : 'jiveimage.link_desc',
                        cmd : "mceJiveImage"
                    });
                    return c;
//                    return tinyMCE.getButtonHTML(cn, 'lang_jivespell_desc', '{$pluginurl}/images/jivespell.gif', 'mceJiveSpell', true);
            }

            return "";
        },

        /**
         * Executes a specific command, this function handles plugin commands.
         *
         * @param {string} editor_id TinyMCE editor instance id that issued the command.
         * @param {HTMLElement} element Body or root element for the editor instance.
         * @param {string} command Command name to be executed.
         * @param {string} user_interface True/false if a user interface should be presented.
         * @param {mixed} value Custom value argument, can be anything.
         * @return true/false if the command was executed by this plugin or not.
         * @type
         */
        execCommand : function(cmd, ui, val){
            switch (cmd) {
                case "mceJiveImage":
                    var ed = tinyMCE.activeEditor;

                    if(tinyMCE.settings['jive_image_picker_url']){
                        ed.windowManager.open(
                            {
                                url: CS_BASE_URL + tinyMCE.settings['jive_image_picker_url'] + "&instantiatedFromGUIEditor=true",
                                width: 580 + ed.getLang('jiveimage.delta_width', 0),
                                height: 425 + ed.getLang('jiveimage.delta_height', 0),
                                inline : "yes"
                            },
                            {
                                editor_id : ed.id
                        });
                    }else{
                        ed.execCommand("mceAdvImage");
                    }
                    return true;
            }

            return false;
        },

        /**
         * Gets called ones the cursor/selection in a TinyMCE instance changes. This is useful to enable/disable
         * button controls depending on where the user are and what they have selected. This method gets executed
         * alot and should be as performance tuned as possible.
         *
         * @param {string} editor_id TinyMCE editor instance id that was changed.
         * @param {HTMLNode} node Current node location, where the cursor is in the DOM tree.
         * @param {int} undo_index The current undo index, if this is -1 custom undo/redo is disabled.
         * @param {int} undo_levels The current undo levels, if this is -1 custom undo/redo is disabled.
         * @param {boolean} visual_aid Is visual aids enabled/disabled ex: dotted lines on tables.
         * @param {boolean} any_selection Is there any selection at all or is there only a cursor.
         */
        handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
            if (node == null)
                return;

            do {
                if (node.nodeName == "IMG" && tinyMCE.getAttrib(node, 'src') != "") {
                    tinyMCE.switchClass(editor_id + '_jiveimage', 'mceButtonSelected');
                    return true;
                }
            } while ((node = node.parentNode));

            if (any_selection) {
                tinyMCE.switchClass(editor_id + '_jiveimage', 'mceButtonNormal');
                return true;
            }

            tinyMCE.switchClass(editor_id + '_jiveimage', 'mceButtonDisabled');

            return true;
        }

    });
	// Register plugin
	tinymce.PluginManager.add('jiveimage', tinymce.plugins.JiveImagePlugin);
})();


// UK lang variables
// UK lang variables
tinyMCE.addI18n('en.jiveimage',{
    link_desc : 'Insert Image'
});



(function() {

    tinymce.create('tinymce.plugins.JiveQuotePlugin', {

        /**
         * Returns information about the plugin as a name/value array.
         * The current keys are longname, author, authorurl, infourl and version.
         *
         * @returns Name/value array containing information about the plugin.
         * @type Array
         */
        getInfo : function() {
            return {
                longname : 'Jive Quote Button',
                author : 'Jive Software',
                authorurl : 'http://www.jivesoftware.com',
                infourl : 'http://www.jivesoftware.com/',
                version : "1.0"
            };
        },


        /**
         * Creates control instances based in the incomming name. This method is normally not
         * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
         * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
         * method can be used to create those.
         *
         * @param {String} n Name of the control to create.
         * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
         * @return {tinymce.ui.Control} New control instance or null if no control was created.
         */
        createControl: function(cn, cm) {
            switch (cn) {
                case "jivequote":
                    var c = cm.createButton('jivequote', {
                        title : 'jivequote.link_desc',
                        cmd : "mceJiveQuote"
                    });
                    return c;
//                    return tinyMCE.getButtonHTML(cn, 'lang_jivespell_desc', '{$pluginurl}/images/jivespell.gif', 'mceJiveSpell', true);
            }

            return "";
        },

        /**
         * Executes a specific command, this function handles plugin commands.
         *
         * @param {string} editor_id TinyMCE editor instance id that issued the command.
         * @param {HTMLElement} element Body or root element for the editor instance.
         * @param {string} command Command name to be executed.
         * @param {string} user_interface True/false if a user interface should be presented.
         * @param {mixed} value Custom value argument, can be anything.
         * @return true/false if the command was executed by this plugin or not.
         * @type
         */
        execCommand : function(cmd, ui, val){
            switch (cmd) {
                case "mceJiveQuote":
                        var ed = tinyMCE.activeEditor;
                        var sel = ed.selection;
                        var pre = null;
                        var collapsed = sel.isCollapsed();
                        // insert a quote macro
                        for(var i=0;i<jive.rte.macros.length;i++){
                            var macro = jive.rte.macros[i];
                            if(macro.getName() == "quote"){
                                pre = ed.plugins.jivemacros.buildMacro(macro);
                            }
                        }

                        if(pre && collapsed){
                            while(pre.childNodes.length > 0) pre.removeChild(pre.childNodes[0]);
                            if($def(window._jive_gui_quote_text)){
                                var div = ed.getDoc().createElement('DIV');
                                div.innerHTML = _jive_gui_quote_text;
                                if(div.childNodes.length > 0){
                                    for(var i=0;i<div.childNodes.length;i++){
                                        if(div.childNodes[i].nodeType == 1){
                                            var temp = div.childNodes[i];
                                            var block = temp.childNodes[0];
                                            // block is the block quote.
                                            while(block.childNodes.length > 0){
                                                if(block.childNodes[0].className == "jive-quote-header"){
                                                    // it's the header
                                                    var head = ed.getDoc().createElement('P');
                                                    head.appendChild(block.childNodes[0].childNodes[0]); // the title w/o the span
                                                    pre.appendChild(head);
                                                    pre.appendChild(ed.plugins.jivemacros.createEmptyPara(ed.getDoc()));;
                                                    block.removeChild(block.childNodes[0]); // the header span
                                                    block.removeChild(block.childNodes[0]); // br
                                                    block.removeChild(block.childNodes[0]); // br
                                                }else{
                                                    // add nodes to quote thing
                                                    pre.appendChild(block.childNodes[0]);
                                                }
                                            }
                                        }
                                    }
                                }
                            }else{
                                pre.appendChild(ed.plugins.jivemacros.createEmptyPara(ed.getDoc()));;
                            }

                            var par = ed.dom.getParent(sel.getNode(), "PRE");
                            var before = null;
                            if(!par){
                                par = sel.getNode().parentNode;
                                before = sel.getNode();
                            }else{
                                before = ed.dom.getParent(sel.getNode(), function(x){
                                    return x.nodeName.toLowerCase() == "body" || x.parentNode.nodeName == "PRE";
                                });
                            }
                            if(before.nodeName.toLowerCase() == "body") before = null;
                            if(par && before){
                                par.insertBefore(pre, before);
                            }else{
                                par.appendChild(pre);
                                par.appendChild(ed.plugins.jivemacros.createEmptyPara(ed.getDoc()));
                            }
                        }else{
                            // console.log("fail");
                        }

                    return true;
            }

            return false;
        }

    });
	// Register plugin
	tinymce.PluginManager.add('jivequote', tinymce.plugins.JiveQuotePlugin);
})();


tinyMCE.addI18n('en.jivequote',{
    link_desc : 'Quote Previous Message'
});


(function() {

    tinymce.create('tinymce.plugins.JiveUtilPlugin', {

        /**
         * Returns information about the plugin as a name/value array.
         * The current keys are longname, author, authorurl, infourl and version.
         *
         * @returns Name/value array containing information about the plugin.
         * @type Array
         */
        getInfo : function() {
            return {
                longname : 'Jive Utility Plugin',
                author : 'Jive Software',
                authorurl : 'http://www.jivesoftware.com',
                infourl : 'http://www.jivesoftware.com/',
                version : "1.0"
            };
        },


        /**
         * walks through all of the kids of startN until endN is found, if ever
         * and calls func() on each node

         * given:
         *
         * - node1
         *   - node11
         *   - node12
         *   - node13
         * - node2       * startN
         *   - node21
         *   - node22    * endN
         *   - node23
         *
         * func() will be called on: node2, 21, and 22
         * 
         * @param startN the node to call
         * @param endN
         * @param func
         */
        walkDOMTreeKids : function(startN, endN, func){
            // call the func on the DOM
            func(startN);
            // end case
            if(startN == endN) return true;
            // walk through all kids
            for(var i=0;i<startN.childNodes.length;i++){
                if(this.walkDOMTreeKids(startN.childNodes[i], endN, func)) return true;
            }
            return false;
        },

        /**
         * walk through a the DOM starting with start node
         * and ending with end node
         *
         * given:
         *
         * - node1
         *   - node11
         *   - node12    * startN
         *   - node13
         * - node2
         *   - node21
         *   - node22    * endN
         *   - node23
         *
         * func() will be called on: node12, 13, 2, 21, and 22
         * 
         * @param startN the node to start walking from, must be before endN
         * @param endN the last node that we should walk to
         * @param func the function to call with every node
         */
        walkDOMTree : function(startN, endN, func){
            // walk all of our kids
            if(this.walkDOMTreeKids(startN, endN, func)) return true;
            // find parent's next sibling, or parent's parent next sibling, etc

            while(startN.nextSibling != null){
                startN = startN.nextSibling;
                if(this.walkDOMTreeKids(startN, endN, func)) return true;
            }

            function getNext(node){
                if(node.nodeType == 1 && node.nodeName.toLowerCase() == "body") return node;
                if(node.parentNode.nextSibling == null) return getNext(node.parentNode);
                return node.parentNode.nextSibling;
            }
            var nextN = getNext(startN);
            if(nextN.nodeType == 1 && nextN.nodeName.toLowerCase() == "body") return true;
            return this.walkDOMTree(nextN, endN, func);
        },

        /**
         * returns the number of characters contained in node
         *
         * @param node
         */
        textLengthIn : function(node){
            var count = 0;
            this.walkDOMTreeKids(node, null, function(){
                if(node.nodeType == 3) count += node.nodeValue.length;
            });
            return count;
        },


        /**
         * Given a node and a character offset, this function will
         * return the node that the offset falls in
         *
         * given:
         * <node1> text here <node2> more text</node2></node1>
         * and 14
         *
         * then: node 2 will be returned, since the 14th letter falls inside of node 2
         *
         * edge case:
         *
         * given:
         * <node1> text here <node2> more text</node2></node1>
         * and 11
         *
         * then: node 2 will be returned, since the 11th letter ends at the very beginning of node 2
         */
        getNodeAt : function(node, offset){
            if(offset == 0) return node;
            if(node.nodeType != 1) return null;
            for(var i=0;i<node.childNodes.length;i++){
                var len = this.textLengthIn(node.childNodes[i]);
                if(offset == 0){
                    return node.childNodes[i];
                }else if(len > offset){
                    return this.getNodeAt(node.childNodes[i], offset);
                }else{
                    offset -= len;
                }
            }
        }

    });
	// Register plugin
	tinymce.PluginManager.add('jiveutil', tinymce.plugins.JiveUtilPlugin);
})();


(function() {

    tinymce.create('tinymce.plugins.JiveVideoPlugin', {

        /**
         * Returns information about the plugin as a name/value array.
         * The current keys are longname, author, authorurl, infourl and version.
         *
         * @returns Name/value array containing information about the plugin.
         * @type Array
         */
        getInfo : function() {
            return {
                longname : 'Video Picker',
                author : 'Jive Software',
                authorurl : 'http://www.jivesoftware.com',
                infourl : 'http://www.jivesoftware.com/',
                version : "1.0"
            };
        },

        enabled : [],

        /**
         * Gets executed when a TinyMCE editor instance is initialized.
         *
         * @param {TinyMCE_Control} Initialized TinyMCE editor control instance.
         */
        init : function(ed, url) {
            ed.onInit.add(function() {
                // deterime which video macros are installed
                var allowed = ["youtube","dailymotion","veoh","vimeo","google"];
                var count = 0;
                for(var i=0;i<jive.rte.macros.length;i++){
                    for(var j=0;j<allowed.length;j++){
                        if(allowed[j] == jive.rte.macros[i].getName() && jive.rte.macros[i].isEnabled()){
                            count++;
                            this.enabled.push(jive.rte.macros[i]);
                        }
                    }
                }
                if(count == 0){
                    ed.controlManager.get("jivevideo").setDisabled(true);
                }
            }, this);
        },


        /**
         * Creates control instances based in the incomming name. This method is normally not
         * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
         * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
         * method can be used to create those.
         *
         * @param {String} n Name of the control to create.
         * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
         * @return {tinymce.ui.Control} New control instance or null if no control was created.
         */
        createControl: function(cn, cm) {
            switch (cn) {
                case "jivevideo":
                    var c = cm.createButton('jivevideo', {
                        title : 'jivevideo.link_desc',
                        cmd : "mceJiveVideo"
                    });
                    return c;
//                    return tinyMCE.getButtonHTML(cn, 'lang_jivespell_desc', '{$pluginurl}/images/jivespell.gif', 'mceJiveSpell', true);
            }

            return "";
        },

        /**
         * Executes a specific command, this function handles plugin commands.
         *
         * @param {string} editor_id TinyMCE editor instance id that issued the command.
         * @param {HTMLElement} element Body or root element for the editor instance.
         * @param {string} command Command name to be executed.
         * @param {string} user_interface True/false if a user interface should be presented.
         * @param {mixed} value Custom value argument, can be anything.
         * @return true/false if the command was executed by this plugin or not.
         * @type
         */
        execCommand : function(cmd, ui, val){
            switch (cmd) {
                case "mceJiveVideo":
                    var ed = tinyMCE.activeEditor;

                    ed.windowManager.open({
                        url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/jivevideo/video.htm',
                        width : 550 + parseInt(ed.getLang('jivevideo.delta_width', 0)),
                        height : 308 + parseInt(ed.getLang('jivevideo.delta_height', 0)),
                        inline : 1
                    });

                    return true;
            }

            return false;
        }

    });
	// Register plugin
	tinymce.PluginManager.add('jivevideo', tinymce.plugins.JiveVideoPlugin);
})();


// UK lang variables
// UK lang variables
tinyMCE.addI18n('en.jivevideo',{
    link_desc : 'Insert Video'
});



tinyMCE.addI18n('en.jivevideo',{
    num1: "1. ",
    num2: "2. ",
    title_new:"Insert Video",
    general_props:"Video Properties",
    site_title:"Select a Video Site",
    embed_title: "Enter Video Information",
    embed: "Video URL or Embed Code (example: http://youtube.com/watch?v=videoIdHere)",
    embed_error : "Please enter a valid URL or embed code",
    name_youtube: "YouTube",
    name_vimeo: "Vimeo",
    name_veoh: "Veoh",
    name_dailymotion: "Dailymotion",
    name_google: "Google"
});

/**
 * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {

	tinymce.create('tinymce.plugins.TableToolbarPlugin', {
		/**
		 * Initializes the plugin, this will be executed after the plugin has been created.
		 * This call is done before the editor instance has finished it's initialization so use the onInit event
		 * of the editor instance to intercept that event.
		 *
		 * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
		 * @param {string} url Absolute URL to where the plugin is located.
		 */
		init : function(ed, url) {
            var t = this;
            ed.onInit.add(function() {
                tinymce.DOM.hide(ed.id + "_toolbar3");
            });
            // Hides the specified toolbar and resizes the iframe
            ed.onPostRender.add(function() {
                var table = tinymce.DOM.get(ed.id + "_toolbar3");
                if($def(table) && table != null){
                    var descCell = table.rows[0].cells[table.rows[0].cells.length-1];
                    var separator = table.rows[0].cells[0];
                    table.rows[0].insertBefore(separator, descCell);
                    for(var i=0;i<table.rows[0].cells.length;i++){
                        var cell = table.rows[0].cells[i];
                        var aTag = cell.childNodes[0];
                        if(aTag.nodeName.toLowerCase() == "a"){
                            Event.observe(cell, "mouseover", function(aTag){ return function(){
                                descCell.innerHTML = aTag.title;
                            }}(aTag));
                        }
                    }
                    Event.observe(table, "mouseout", function(){
                        descCell.innerHTML = " ";
                    });
                }
//                tinymce.DOM.hide(ed.id + "_toolbar3");
//                t._resizeIframe(ed, 28);
                tinymce.DOM.show(ed.id + "_toolbar3");
            });


			// Add a node change handler, selects the button in the UI when a image is selected
			ed.onNodeChange.add(function(that){ return function(ed, cm, n) {
                var par = ed.dom.getParent(n,"table");
                if($def(par) && par != null && $def(par.tagName)){
                    // the user clicked inside a table
                    that._showToolbar();
                }else{
                    that._hideToolbar();
                }

                var cm = ed.controlManager;
                var n = ed.selection.getNode();
                var tr = ed.dom.getParent(n, "tr");
                var td = ed.dom.getParent(n, "td,th");
                if(tr != null && td != null){
                    if(tr.nextSibling == null){
                        cm.get("row_down").setDisabled(true);
                    }else{
                        cm.get("row_down").setDisabled(false);
                    }
                    if(tr.previousSibling== null){
                        cm.get("row_up").setDisabled(true);
                    }else{
                        cm.get("row_up").setDisabled(false);
                    }
                    if(td.previousSibling== null){
                        cm.get("col_left").setDisabled(true);
                    }else{
                        cm.get("col_left").setDisabled(false);
                    }
                    if(td.nextSibling== null){
                        cm.get("col_right").setDisabled(true);
                    }else{
                        cm.get("col_right").setDisabled(false);
                    }
                }
            }}(this));

		},

        _hidden : true,

        settings : new Object(),

        _showToolbar : function(){
            var ed = tinyMCE.activeEditor;
            var cm = ed.controlManager;
            var toolbar = $(ed.id + "_toolbar3");
            if(toolbar){
                if(this._hidden){


                    tinymce.DOM.show(ed.id + "_toolbar3");
                    var contain = toolbar.parentNode;
                    if(tinymce.isIE){
                        contain.style.height = ed.settings.default_height + "px";
                    }else{
                        contain.style.height = (ed.settings.default_height - 1) + "px";
                    }
                    tinymce.DOM.show(ed.id + "_toolbar3");
                    this._hidden = false;

                    var slider = new jive.ext.y.ySlider(300, this.settings);
                    slider.addTicker(new jive.ext.y.SlideBottomToTicker(contain, 31));
                    slider.go();
                }
            }
        },

        _hideToolbar : function(){
            var ed = tinyMCE.activeEditor;
            var cm = ed.controlManager;
            var toolbar = $(ed.id + "_toolbar3");
            if(toolbar){
                if(!this._hidden){
                    var contain = toolbar.parentNode;
                    if(tinymce.isIE){
                        contain.style.height = ed.settings.default_height + "px";
                    }else{
                        contain.style.height = (ed.settings.default_height - 1) + "px";
                    }
                    tinymce.DOM.hide(ed.id + "_toolbar3");
                    this._hidden = true;

                    var slider = new jive.ext.y.ySlider(300, this.settings);
                    slider.addTicker(new jive.ext.y.SlideBottomToTicker(contain, 0));
                    slider.go();

                }
            }
        },

        _resizeIframe : function(ed, dy) {
            var ifr = ed.getContentAreaContainer().firstChild;
            tinymce.DOM.setStyle(ifr, 'height', ifr.clientHeight + dy); // Resize iframe
            ed.theme.deltaHeight += dy; // For resize cookie
        },

		/**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
			switch (n) {
				case 'tabletoolbar':
                    var c = cm.createButton('tabletoolbar', {
						title : 'tabletoolbar.title',
						cmd : "mceInsertTable",
                        ui : true,
                        value : {action : 'insert'}
                    });
	
				  // Return the new menubutton instance
				  return c;
			}
	
			return null;
		},

		/**
		 * Returns information about the plugin as a name/value array.
		 * The current keys are longname, author, authorurl, infourl and version.
		 *
		 * @return {Object} Name/value array containing information about the plugin.
		 */
		getInfo : function() {
			return {
				longname : 'Table Toolbar Button',
				author : 'Adam Wulf',
				authorurl : 'http://www.jivesoftware.com',
				infourl : '',
				version : "1.0"
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('tabletoolbar', tinymce.plugins.TableToolbarPlugin);
})();


tinyMCE.addI18n('en.tabletoolbar',{
    title : 'Insert Table'
});


/**
 * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {

	tinymce.create('tinymce.plugins.MediaToolbarPlugin', {
		/**
		 * Initializes the plugin, this will be executed after the plugin has been created.
		 * This call is done before the editor instance has finished it's initialization so use the onInit event
		 * of the editor instance to intercept that event.
		 *
		 * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
		 * @param {string} url Absolute URL to where the plugin is located.
		 */
		init : function(ed, url) {
            var t = this;
            ed.addCommand('mceHideMediaToolbar', function() {
                var cm = this.controlManager;
                if(!tinymce.DOM.isHidden(ed.id + "_toolbar2")){
                    tinymce.DOM.hide(ed.id + "_toolbar2");
                    t._resizeIframe(ed, 28);
                }
                cm.setActive('mediatoolbar', false);
            });
            ed.addCommand('mceToggleMediaToolbar', function() {
                var cm = this.controlManager;
                ed.execCommand('mceHideTableToolbar');
                ed.execCommand('mceHideExtraToolbar');
                if(!tinymce.DOM.isHidden(ed.id + "_toolbar2")){
                    ed.execCommand('mceHideMediaToolbar');
                }else{
                    cm.setActive('mediatoolbar', true);
                    tinymce.DOM.show(ed.id + "_toolbar2");
                    // Resizes the iframe by a relative height value
                    t._resizeIframe(ed, -28);
                }
            });

            // Hides the specified toolbar and resizes the iframe
            ed.onPostRender.add(function() {
                tinymce.DOM.hide(ed.id + "_toolbar2");
                t._resizeIframe(ed, 28);
            });

            this.that = this;
			
		},

        _resizeIframe : function(ed, dy) {
            var ifr = ed.getContentAreaContainer().firstChild;
            tinymce.DOM.setStyle(ifr, 'height', ifr.clientHeight + dy); // Resize iframe
            ed.theme.deltaHeight += dy; // For resize cookie
        },
        
        /**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
			switch (n) {
				case 'mediatoolbar':
                    var c = cm.createButton('mediatoolbar', {
						title : 'Media Toolbar',
						cmd : "mceToggleMediaToolbar",
						image : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/mediatoolbar/img/spacer.gif'
					});
	
				  // Return the new menubutton instance
				  return c;
			}
	
			return null;
		},

		/**
		 * Returns information about the plugin as a name/value array.
		 * The current keys are longname, author, authorurl, infourl and version.
		 *
		 * @return {Object} Name/value array containing information about the plugin.
		 */
		getInfo : function() {
			return {
				longname : 'Media Toolbar Button',
				author : 'Adam Wulf',
				authorurl : 'http://www.jivesoftware.com',
				infourl : '',
				version : "1.0"
			};
		}
	});

	// Register plugin
    tinymce.PluginManager.add('mediatoolbar', tinymce.plugins.MediaToolbarPlugin);
})();

/**
 * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {

	tinymce.create('tinymce.plugins.ExtraToolbarPlugin', {
		/**
		 * Initializes the plugin, this will be executed after the plugin has been created.
		 * This call is done before the editor instance has finished it's initialization so use the onInit event
		 * of the editor instance to intercept that event.
		 *
		 * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
		 * @param {string} url Absolute URL to where the plugin is located.
		 */
		init : function(ed, url) {
            var t = this;
            ed.addCommand('mceHideExtraToolbar', function() {
                var cm = this.controlManager;
                if(!tinymce.DOM.isHidden(ed.id + "_toolbar3")){
                    tinymce.DOM.hide(ed.id + "_toolbar3");
                    t._resizeIframe(ed, 28);
                }
                cm.setActive('extratoolbar', false);
            });
            ed.addCommand('mceToggleExtraToolbar', function() {
                var cm = this.controlManager;
                ed.execCommand('mceHideMediaToolbar');
                ed.execCommand('mceHideTableToolbar');
                if(!tinymce.DOM.isHidden(ed.id + "_toolbar3")){
                    ed.execCommand('mceHideExtraToolbar');
                }else{
                    cm.setActive('extratoolbar', true);
                    tinymce.DOM.show(ed.id + "_toolbar3");
                    // Resizes the iframe by a relative height value
                    t._resizeIframe(ed, -28);
                }
            });

            // Hides the specified toolbar and resizes the iframe
            ed.onPostRender.add(function() {
                tinymce.DOM.hide(ed.id + "_toolbar3");
                t._resizeIframe(ed, 28);
            });

			this.that = this;
			
		},

        _resizeIframe : function(ed, dy) {
            var ifr = ed.getContentAreaContainer().firstChild;
            tinymce.DOM.setStyle(ifr, 'height', ifr.clientHeight + dy); // Resize iframe
            ed.theme.deltaHeight += dy; // For resize cookie
        },

        /**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
			switch (n) {
				case 'extratoolbar':
                    var c = cm.createButton('extratoolbar', {
						title : 'Extra Toolbar',
						cmd : "mceToggleExtraToolbar",
						image : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/extratoolbar/img/spacer.gif'
					});
	
				  // Return the new menubutton instance
				  return c;
			}
	
			return null;
		},

		/**
		 * Returns information about the plugin as a name/value array.
		 * The current keys are longname, author, authorurl, infourl and version.
		 *
		 * @return {Object} Name/value array containing information about the plugin.
		 */
		getInfo : function() {
			return {
				longname : 'Extra Toolbar Button',
				author : 'Adam Wulf',
				authorurl : 'http://www.jivesoftware.com',
				infourl : '',
				version : "1.0"
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('extratoolbar', tinymce.plugins.ExtraToolbarPlugin);
})();

/**
 * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	// Load plugin specific language pack
	tinymce.PluginManager.requireLangPack('alignment');

	tinymce.create('tinymce.plugins.AlignmentPlugin', {
		/**
		 * Initializes the plugin, this will be executed after the plugin has been created.
		 * This call is done before the editor instance has finished it's initialization so use the onInit event
		 * of the editor instance to intercept that event.
		 *
		 * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
		 * @param {string} url Absolute URL to where the plugin is located.
		 */
		init : function(ed, url) {
			// Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceAlignment');
			ed.addCommand('mceAlignment', function() {
				var cm = this.controlManager;
				var button = cm.get('mysplitbutton');
//				if(!button.isActive()){
					button.showMenu();
//				}else{
//					// apply the style
//					ed.execCommand(button.lastExec, false);
//					button.setActive(false);
//				}
			});
            ed.addCommand('mceNoFloatImage',function(){
                var ed = tinyMCE.activeEditor;
                var se = ed.selection;
                var n = se.getNode();
                var dom = ed.dom;
                dom.setStyle(n, 'float', "");
            });

            ed.addCommand('MyJustifyLeft', function() {
				var n = this.selection.getNode();
				if(n.style.textAlign == "left") return;
				n.style.textAlign = "left";
			});


			ed.addCommand('MyJustifyRight', function() {
				var n = this.selection.getNode();
				if(n.style.textAlign == "right") return;
				n.style.textAlign = "right";
			});


			ed.addCommand('MyJustifyCenter', function() {
				var n = this.selection.getNode();
				if(n.style.textAlign == "center") return;
				n.style.textAlign = "center";
			});


			ed.addCommand('MyJustifyFull', function() {
				var n = this.selection.getNode();
				if(n.style.textAlign == "justify") return;
				n.style.textAlign = "justify";
			});

			// Add a node change handler, selects the button in the UI when a image is selected
			ed.onNodeChange.add(function(that){ return function(ed, cm, n) {
				var button = cm.get('mysplitbutton');
                if($def(button)){
                    if(typeof(button.lastClass) != "undefined" && button.lastClass != null){
                        button.setState(button.lastClass,false);
                    }
                    if(n.style.textAlign == "right"){
                        button.lastClass = "_justifyright";
                        button.lastExec = "MyJustifyRight";
    //					button.setActive(true);
                    }else if(n.style.textAlign == "center"){
                        button.lastClass = "_justifycenter";
                        button.lastExec = "MyJustifyCenter";
    //					button.setActive(true);
                    }else if(n.style.textAlign == "justify"){
                        button.lastClass = "_justifyfull";
                        button.lastExec = "MyJustifyFull";
    //					button.setActive(true);
                    }else if(n.style.textAlign == "left"){
                        button.lastClass = "_justifyleft";
                        button.lastExec = "MyJustifyLeft";
    //					button.setActive(true);
                    }else{
                        button.lastClass = "_justifyleft";
                        button.lastExec = "MyJustifyLeft";
    //					button.setActive(false);
                    }
                    if(button.lastClass != null){
                        button.setState(button.lastClass,true);
                    }
                }
//				button.setActive(true);
			}}(this));
			
			this.url = url;
			this.that = this;
			
		},

		/**
		 * Creates control instances based in the incomming name. This method is normally not
		 * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
		 * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
		 * method can be used to create those.
		 *
		 * @param {String} n Name of the control to create.
		 * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
		 * @return {tinymce.ui.Control} New control instance or null if no control was created.
		 */
		createControl: function(n, cm) {
			switch (n) {
				case 'alignment':
					var c = cm.createSplitButton('mysplitbutton', {
						title : 'Alignment',
						cmd : "mceAlignment"
					});
					c.onRenderMenu.add(function(c, m) {
						m.add({ cmd: "MyJustifyLeft",
								'class' : "mceSplitButton_justifyleft",
								title: "alignament.left"
						})
					});
					c.onRenderMenu.add(function(c, m) {
						m.add({ cmd: "MyJustifyCenter",
								'class' : "mceSplitButton_justifycenter",
								title: "alignment.center"
						})
					});
					c.onRenderMenu.add(function(c, m) {
						m.add({ cmd: "MyJustifyRight",
								'class' : "mceSplitButton_justifyright",
								title: "alignment.right"
						})
					});
					c.onRenderMenu.add(function(c, m) {
						m.add({ cmd: "MyJustifyFull",
								'class' : "mceSplitButton_justifyfull",
								title: "alignment.full"
						})
					});
	
				  // Return the new menubutton instance
				  return c;
			}
	
			return null;
		},

		/**
		 * Returns information about the plugin as a name/value array.
		 * The current keys are longname, author, authorurl, infourl and version.
		 *
		 * @return {Object} Name/value array containing information about the plugin.
		 */
		getInfo : function() {
			return {
				longname : 'Fancy Justify Button',
				author : 'Adam Wulf',
				authorurl : 'http://www.jivesoftware.com',
				infourl : '',
				version : "1.0"
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('alignment', tinymce.plugins.AlignmentPlugin);
})();

/**
 * $Id: editor_plugin_src.js 264 2007-04-26 20:53:09Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	var Event = tinymce.dom.Event, grep = tinymce.grep, each = tinymce.each, inArray = tinymce.inArray, isOldWebKit = tinymce.isOldWebKit;

	tinymce.create('tinymce.plugins.Safari', {
		init : function(ed) {
			var t = this, dom;

			// Ignore on non webkit
			if (!tinymce.isWebKit)
				return;

			t.editor = ed;
			t.webKitFontSizes = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large'];
			t.namedFontSizes = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];
            t.namedFontSizes = ['8pt', '10pt', '12pt', '14pt', '18pt', '24pt', '36pt'];

            // Safari will crash if the build in createlink command is used
/*			ed.addCommand('CreateLink', function(u, v) {
				ed.execCommand("mceInsertContent", false, '<a href="' + dom.encode(v) + '">' + ed.selection.getContent() + '</a>');
			});*/

			ed.onPaste.add(function(ed, e) {
				function removeStyles(e) {
					e = e.target;

					if (e.nodeType == 1) {
						e.style.cssText = '';

						each(ed.dom.select('*', e), function(e) {
							e.style.cssText = '';
						});
					}
				};

				Event.add(ed.getDoc(), 'DOMNodeInserted', removeStyles);

				window.setTimeout(function() {
					Event.remove(ed.getDoc(), 'DOMNodeInserted', removeStyles);
				}, 0);
			});

			ed.onKeyUp.add(function(ed, e) {
				var h, b;

				// If backspace or delete key
				if (e.keyCode == 46 || e.keyCode == 8) {
					b = ed.getBody();
					h = b.innerHTML;

					// If there is no text content or images or hr elements then remove everything
					if (b.childNodes.length == 1 && !/<(img|hr)/.test(h) && tinymce.trim(h.replace(/<[^>]+>/g, '')).length == 0)
						ed.setContent('', {format : 'raw'});
				}
			});

			// Workaround for FormatBlock bug, http://bugs.webkit.org/show_bug.cgi?id=16004
			ed.addCommand('FormatBlock', function(u, v) {
				var dom = ed.dom, e = dom.getParent(ed.selection.getNode(), dom.isBlock);

				if (e)
					dom.replace(dom.create(v), e, 1);
				else
					ed.getDoc().execCommand("FormatBlock", false, v);
			});

			// Workaround for InsertHTML bug, http://bugs.webkit.org/show_bug.cgi?id=16382
			ed.addCommand('mceInsertContent', function(u, v) {
				ed.getDoc().execCommand("InsertText", false, 'mce_marker');
				ed.getBody().innerHTML = ed.getBody().innerHTML.replace(/mce_marker/g, v + '<span id="_mce_tmp">XX</span>');
				ed.selection.select(ed.dom.get('_mce_tmp'));
				ed.getDoc().execCommand("Delete", false, ' ');
			});

			// Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973
			ed.onKeyPress.add(function(ed, e) {
				if (e.keyCode == 13 && (e.shiftKey || ed.settings.force_br_newlines && ed.selection.getNode().nodeName != 'LI')) {
					t._insertBR(ed);
					Event.cancel(e);
				}
			});

			// Safari returns incorrect values
			ed.addQueryValueHandler('FontSize', function(u, v) {
				var e, v, ret;

				// Check for the real font size at the start of selection
				if ((e = ed.dom.getParent(ed.selection.getStart(), 'span')) && (v = e.style.fontSize)){
                    ret = tinymce.inArray(t.namedFontSizes, v) + 1;
                    return ret;
                }

                // Check for the real font size at the end of selection
				if ((e = ed.dom.getParent(ed.selection.getEnd(), 'span')) && (v = e.style.fontSize)){
					ret = tinymce.inArray(t.namedFontSizes, v) + 1;
                    return ret;
                }

                // Return default value it's better than nothing right!
                ret = ed.getDoc().queryCommandValue('FontSize');
                return ret;
			});

			// Safari returns incorrect values
			ed.addQueryValueHandler('FontName', function(u, v) {
				var e, v;

				// Check for the real font name at the start of selection
				if ((e = ed.dom.getParent(ed.selection.getStart(), 'span')) && (v = e.style.fontFamily))
					return v.replace(/, /g, ',');

				// Check for the real font name at the end of selection
				if ((e = ed.dom.getParent(ed.selection.getEnd(), 'span')) && (v = e.style.fontFamily))
					return v.replace(/, /g, ',');

				// Return default value it's better than nothing right!
				return ed.getDoc().queryCommandValue('FontName');
			});

			// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
			ed.onClick.add(function(ed, e) {
				e = e.target;

				if (e.nodeName == 'IMG') {
					t.selElm = e;
					ed.selection.select(e);
				} else
					t.selElm = null;
			});

/*			ed.onBeforeExecCommand.add(function(ed, c, b) {
				var r = t.bookmarkRng;

				// Restore selection
				if (r) {
					ed.selection.setRng(r);
					t.bookmarkRng = null;
					//console.debug('restore', r.startContainer, r.startOffset, r.endContainer, r.endOffset);
				}
			});*/

			ed.onInit.add(function() {
				t._fixWebKitSpans();

/*				ed.windowManager.onOpen.add(function() {
					var r = ed.selection.getRng();

					// Store selection if valid
					if (r.startContainer != ed.getDoc()) {
						t.bookmarkRng = r.cloneRange();
						//console.debug('store', r.startContainer, r.startOffset, r.endContainer, r.endOffset);
					}
				});

				ed.windowManager.onClose.add(function() {
					t.bookmarkRng = null;
				});*/

				if (isOldWebKit)
					t._patchSafari2x(ed);
			});

			ed.onSetContent.add(function() {
				dom = ed.dom;

				// Convert strong,b,em,u,strike to spans
				each(['strong','b','em','u','strike','sub','sup','a'], function(v) {
					each(grep(dom.select(v)).reverse(), function(n) {
						var nn = n.nodeName.toLowerCase(), st;

						// Convert anchors into images
						if (nn == 'a') {
							if (n.name)
								dom.replace(dom.create('img', {mce_name : 'a', name : n.name, 'class' : 'mceItemAnchor'}), n);

							return;
						}

						switch (nn) {
							case 'b':
							case 'strong':
								if (nn == 'b')
									nn = 'strong';

								st = 'font-weight: bold;';
								break;

							case 'em':
								st = 'font-style: italic;';
								break;

							case 'u':
								st = 'text-decoration: underline;';
								break;

							case 'sub':
								st = 'vertical-align: sub;';
								break;

							case 'sup':
								st = 'vertical-align: super;';
								break;

							case 'strike':
								st = 'text-decoration: line-through;';
								break;
						}

						dom.replace(dom.create('span', {mce_name : nn, style : st, 'class' : 'Apple-style-span'}), n, 1);
					});
				});
			});

			ed.onPreProcess.add(function(ed, o) {
				dom = ed.dom;

				each(grep(o.node.getElementsByTagName('span')).reverse(), function(n) {
					var v, bg;

					if (o.get) {
						if (dom.hasClass(n, 'Apple-style-span')) {
							bg = n.style.backgroundColor;

							switch (dom.getAttrib(n, 'mce_name')) {
								case 'font':
									if (!ed.settings.convert_fonts_to_spans)
										dom.setAttrib(n, 'style', '');
									break;

								case 'strong':
								case 'em':
								case 'sub':
								case 'sup':
									dom.setAttrib(n, 'style', '');
									break;

								case 'strike':
								case 'u':
									if (!ed.settings.inline_styles)
										dom.setAttrib(n, 'style', '');
									else
										dom.setAttrib(n, 'mce_name', '');

									break;

								default:
									if (!ed.settings.inline_styles)
										dom.setAttrib(n, 'style', '');
							}


							if (bg)
								n.style.backgroundColor = bg;
						}
					}

					if (dom.hasClass(n, 'mceItemRemoved'))
						dom.remove(n, 1);
				});
			});

			ed.onPostProcess.add(function(ed, o) {
				// Safari adds BR at end of all block elements
				o.content = o.content.replace(/<br \/><\/(h[1-6]|div|p|address|pre)>/g, '</$1>');

				// Safari adds id="undefined" to HR elements
				o.content = o.content.replace(/ id=\"undefined\"/g, '');
			});
		},

		getInfo : function() {
			return {
				longname : 'Safari compatibility',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/safari',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		},

		// Internal methods

		_fixWebKitSpans : function() {
			var t = this, ed = t.editor;

			if (!isOldWebKit) {
				// Use mutator events on new WebKit
				Event.add(ed.getDoc(), 'DOMNodeInserted', function(e) {
					e = e.target;

					if (e && e.nodeType == 1)
						t._fixAppleSpan(e);
				});
			} else {
				// Do post command processing in old WebKit since the browser crashes on Mutator events :(
				ed.onExecCommand.add(function() {
					each(ed.dom.select('span'), function(n) {
						t._fixAppleSpan(n);
					});

					ed.nodeChanged();
				});
			}
		},

		_fixAppleSpan : function(e) {
			var ed = this.editor, dom = ed.dom, fz = this.webKitFontSizes, fzn = this.namedFontSizes, s = ed.settings, st, p;
			if (dom.getAttrib(e, 'mce_fixed'))
				return;

			// Handle Apple style spans
			if (e.nodeName == 'SPAN' && e.className == 'Apple-style-span') {
				st = e.style;

				if (!s.convert_fonts_to_spans) {
					if (st.fontSize) {
						dom.setAttrib(e, 'mce_name', 'font');
						dom.setAttrib(e, 'size', inArray(fz, st.fontSize) + 1);
					}

					if (st.fontFamily) {
						dom.setAttrib(e, 'mce_name', 'font');
						dom.setAttrib(e, 'face', st.fontFamily);
					}

					if (st.color) {
						dom.setAttrib(e, 'mce_name', 'font');
						dom.setAttrib(e, 'color', dom.toHex(st.color));
					}

					if (st.backgroundColor) {
						dom.setAttrib(e, 'mce_name', 'font');
						dom.setStyle(e, 'background-color', st.backgroundColor);
					}
				} else {
					if (st.fontSize){
                        dom.setStyle(e, 'fontSize', fzn[inArray(fz, st.fontSize)]);
                    }
                }

				if (st.fontWeight == 'bold')
					dom.setAttrib(e, 'mce_name', 'strong');

				if (st.fontStyle == 'italic')
					dom.setAttrib(e, 'mce_name', 'em');

				if (st.textDecoration == 'underline')
					dom.setAttrib(e, 'mce_name', 'u');

				if (st.textDecoration == 'line-through')
					dom.setAttrib(e, 'mce_name', 'strike');

				if (st.verticalAlign == 'super')
					dom.setAttrib(e, 'mce_name', 'sup');

				if (st.verticalAlign == 'sub')
					dom.setAttrib(e, 'mce_name', 'sub');

				dom.setAttrib(e, 'mce_fixed', '1');
			}
		},

		_patchSafari2x : function(ed) {
			var t = this, setContent, getNode, dom = ed.dom, lr;

			// Inline dialogs
			if (ed.windowManager.onBeforeOpen) {
				ed.windowManager.onBeforeOpen.add(function() {
					r = ed.selection.getRng();
				});
			}

			// Fake select on 2.x
			ed.selection.select = function(n) {
				this.getSel().setBaseAndExtent(n, 0, n, 1);
			};

			getNode = ed.selection.getNode;
			ed.selection.getNode = function() {
				return t.selElm || getNode.call(this);
			};

			// Fake range on Safari 2.x
			ed.selection.getRng = function() {
				var t = this, s = t.getSel(), d = ed.getDoc(), r, rb, ra, di;

				// Fake range on Safari 2.x
				if (s.anchorNode) {
					r = d.createRange();

					try {
						// Setup before range
						rb = d.createRange();
						rb.setStart(s.anchorNode, s.anchorOffset);
						rb.collapse(1);

						// Setup after range
						ra = d.createRange();
						ra.setStart(s.focusNode, s.focusOffset);
						ra.collapse(1);

						// Setup start/end points by comparing locations
						di = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
						r.setStart(di ? s.anchorNode : s.focusNode, di ? s.anchorOffset : s.focusOffset);
						r.setEnd(di ? s.focusNode : s.anchorNode, di ? s.focusOffset : s.anchorOffset);

						lr = r;
					} catch (ex) {
						// Sometimes fails, at least we tried to do it by the book. I hope Safari 2.x will go disappear soooon!!!
					}
				}

				return r || lr;
			};

			// Fix setContent so it works
			setContent = ed.selection.setContent;
			ed.selection.setContent = function(h, s) {
				var r = this.getRng(), b;

				try {
					setContent.call(this, h, s);
				} catch (ex) {
					// Workaround for Safari 2.x
					b = dom.create('body');
					b.innerHTML = h;

					each(b.childNodes, function(n) {
						r.insertNode(n.cloneNode(true));
					});
				}
			};
		},

		_insertBR : function(ed) {
			var dom = ed.dom, s = ed.selection, r = s.getRng(), br;

			// Insert BR element
			r.insertNode(br = dom.create('br'));

			// Place caret after BR
			r.setStartAfter(br);
			r.setEndAfter(br);
			s.setRng(r);

			// Could not place caret after BR then insert an nbsp entity and move the caret
			if (s.getSel().focusNode == br.previousSibling) {
				s.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));
				s.collapse(1);
			}

			// Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117
			ed.getWin().scrollTo(0, dom.getPos(s.getRng().startContainer).y);
		}
	});

	// Register plugin
	tinymce.PluginManager.add('safari', tinymce.plugins.Safari);
})();



/**
 * $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM;

	tinymce.create('tinymce.plugins.SpellcheckerPlugin', {
		getInfo : function() {
			return {
				longname : 'Spellchecker',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		},

		init : function(ed, url) {
			var t = this, cm;

			t.url = url;
			t.editor = ed;

            ed.onBeginSpelling = new tinymce.util.Dispatcher(ed);
            ed.onEndSpelling = new tinymce.util.Dispatcher(ed);


            // Register commands
			ed.addCommand('mceSpellCheck', function() {
				if (!t.active) {
                    ed.onBeginSpelling.dispatch();
                    ed.setProgressState(1);
                    t._cleanBody();
                    t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) {
						if (r.length > 0) {
							t.active = 1;
                            t._markWords(r);
							ed.setProgressState(0);
							ed.nodeChanged();
						} else {
							ed.setProgressState(0);
							ed.windowManager.alert('spellchecker.no_mpell');
						}
					});
				} else{
					t._done();
                }
            });

			ed.onInit.add(function() {
				if (ed.settings.content_css !== false)
                    ed.dom.loadCSS(ed.baseURI.toAbsolute("plugins/spellchecker/css/content.css"));
//					ed.dom.loadCSS(url + '/css/content.css');
			});

			ed.onClick.add(t._showMenu, t);
			ed.onContextMenu.add(t._showMenu, t);
			ed.onBeforeGetContent.add(function() {
				if (t.active)
					t._removeWords();
			});

			ed.onNodeChange.add(function(ed, cm) {
				cm.setActive('spellchecker', t.active);
			});

			ed.onSetContent.add(function() {
				t._done();
			});

			ed.onBeforeGetContent.add(function() {
				t._done();
			});

			ed.onBeforeExecCommand.add(function(ed, cmd) {
				if (cmd == 'mceFullScreen')
					t._done();
			});

			// Find selected language
			t.languages = {};
			each(ed.getParam('spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv', 'hash'), function(v, k) {
				if (k.indexOf('+') === 0) {
					k = k.substring(1);
					t.selectedLang = v;
				}

				t.languages[k] = v;
			});
		},

		createControl : function(n, cm) {
			var t = this, c, ed = t.editor;

			if (n == 'spellchecker') {
                // begin edits //
                // https://brewspace.jiveland.com/docs/DOC-10757 //
				c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t, offset_x : -150});
                // old code follows... //
//                c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
                // end edits //

                c.onRenderMenu.add(function(c, m) {
					m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
					each(t.languages, function(v, k) {
						var o = {icon : 1}, mi;

						o.onclick = function() {
							mi.setSelected(1);
							t.selectedItem.setSelected(0);
							t.selectedItem = mi;
							t.selectedLang = v;
						};

						o.title = k;
						mi = m.add(o);
						mi.setSelected(v == t.selectedLang);

						if (v == t.selectedLang)
							t.selectedItem = mi;
					})
				});

				return c;
			}
		},

		// Internal functions

		_walk : function(n, f) {
			var d = this.editor.getDoc(), w;

			if (d.createTreeWalker) {
				w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);

				while ((n = w.nextNode()) != null)
					f.call(this, n);
			} else
				tinymce.walk(n, f, 'childNodes');
		},

		_getSeparators : function() {
			var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');

			// Build word separator regexp
			for (i=0; i<str.length; i++)
				re += '\\' + str.charAt(i);

			return re;
		},

        _cleanBody : function() {
            var ed = this.editor, wl = [], tx = '', lo = {}, dom = ed.dom;

			// collapse adjacent text nodes
			this._walk(ed.getBody(), function(n) {
				while($def(n) && n.nodeType == 3 && n.nextSibling && n.nextSibling.nodeType == 3){
                    n.nodeValue = n.nodeValue + n.nextSibling.nodeValue;
                    n.nextSibling.parentNode.removeChild(n.nextSibling);
                }
            });
        },

        _getWords : function() {
			var ed = this.editor, wl = [], tx = '', lo = {}, dom = ed.dom;

			// Get area text
			this._walk(ed.getBody(), function(n) {
				if (n.nodeType == 3){
                    var span = dom.getParent(n, "span");
                    var pre = dom.getParent(n, "pre");
                    if(span != null && $obj(span) && ed.plugins.jivemacros.isMacro(span) || pre != null && $obj(pre) && ed.plugins.jivemacros.isMacro(pre)){
                        // don't spell check
                    }else{
					    tx += n.nodeValue + ' ';
                    }
                }
            });

			// Split words by separator
			tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');
			tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));

			// Build word array and remove duplicates
			each(tx.split(' '), function(v) {
				if (!lo[v]) {
					wl.push(v);
					lo[v] = 1;
				}
			});

			return wl;
		},

		_removeWords : function(w) {
			var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();

			each(dom.select('span').reverse(), function(n) {
				if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
					if (!w || dom.decode(n.innerHTML) == w)
						dom.remove(n, 1);
				}
			});

			se.moveToBookmark(b);
		},

		_markWords : function(wl) {
			var r1, r2, r3, r4, r5,r6,r7,r8, w = '', ed = this.editor, re = this._getSeparators(), dom = ed.dom, nl = [];
			var se = ed.selection, b = se.getBookmark();

			each(wl, function(v) {
				w += (w ? '|' : '') + v;
			});

			r1 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');
			r2 = new RegExp('^(' + w + ')', 'g');
			r3 = new RegExp('(' + w + ')([' + re + ']?)$', 'g');
			r4 = new RegExp('^(' + w + ')([' + re + ']?)$', 'g');
			r5 = new RegExp('(' + w + ')([' + re + '])', 'g');

            r6 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');
            r7 = new RegExp('([' + re + '])(' + w + ')$', 'g');
            r8 = new RegExp('^(' + w + ')([' + re + '])', 'g');
            r9 = new RegExp('^(' + w + ')$', 'g');

            // Collect all text nodes
			this._walk(this.editor.getBody(), function(n) {
				if (n.nodeType == 3) {
					nl.push(n);
				}
			});

			// Wrap incorrect words in spans
			each(nl, function(n) {
				var v;


                if (n.nodeType == 3) {
                    var span = dom.getParent(n, "span");
                    var pre = dom.getParent(n, "pre");
                    if($obj(span) && ed.plugins.jivemacros.isMacro(span) || $obj(pre) && ed.plugins.jivemacros.isMacro(pre)){
                        // don't spell check macros
                    }else{
                        v = n.nodeValue;
//                        console.log("testing: " + v);
//                        console.log("r1,r2,r3,r4: " + r1.test(v) + "," + r2.test(v) + "," + r3.test(v) + "," + r4.test(v));
//                        console.log("r6,r7,r8: " + r6.test(v) + "," + r7.test(v) + "," + r8.test(v));

                        if (r1.test(v) || r2.test(v) || r3.test(v) || r4.test(v)) {
                            v = dom.encode(v);
//                            v = v.replace(r5, '<span class="mceItemHiddenSpellWord">$1</span>$2');
//                            v = v.replace(r3, '<span class="mceItemHiddenSpellWord">$1</span>$2');
                            v = v.replace(r6, '$1<span class="mceItemHiddenSpellWord">$2</span>$3');
                            v = v.replace(r7, '$1<span class="mceItemHiddenSpellWord">$2</span>');
                            v = v.replace(r8, '<span class="mceItemHiddenSpellWord">$1</span>$2');
                            v = v.replace(r9, '<span class="mceItemHiddenSpellWord">$1</span>');

                            dom.replace(dom.create('span', {'class' : 'mceItemHidden'}, v), n);
                        }
                    }
                }
			});

			se.moveToBookmark(b);
		},

		_showMenu : function(ed, e) {
			var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin());

			if (!m) {
				p1 = DOM.getPos(ed.getContentAreaContainer());
				//p2 = DOM.getPos(ed.getContainer());

				m = ed.controlManager.createDropMenu('spellcheckermenu', {
					offset_x : p1.x,
					offset_y : p1.y,
					'class' : 'mceNoIcons'
				});

				t._menu = m;
			}

			if (dom.hasClass(e.target, 'mceItemHiddenSpellWord')) {
				m.removeAll();
				m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);

				t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) {
					m.removeAll();

					if (r.length > 0) {
						m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
						each(r, function(v) {
							m.add({title : v, onclick : function() {
								dom.replace(ed.getDoc().createTextNode(v), e.target);
                                ed.undoManager.add();
                                t._checkDone();
							}});
						});

						m.addSeparator();
					} else
						m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);

					m.add({
						title : 'spellchecker.ignore_word',
						onclick : function() {
							dom.remove(e.target, 1);
							t._checkDone();
						}
					});

					m.add({
						title : 'spellchecker.ignore_words',
						onclick : function() {
							t._removeWords(dom.decode(e.target.innerHTML));
							t._checkDone();
						}
					});

					m.update();
				});

				ed.selection.select(e.target);
				p1 = dom.getPos(e.target);
				m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y);

				return tinymce.dom.Event.cancel(e);
			} else
				m.hideMenu();
		},

		_checkDone : function() {
			var t = this, ed = t.editor, dom = ed.dom, o;

			each(dom.select('span'), function(n) {
				if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) {
					o = true;
					return false;
				}
			});

			if (!o)
				t._done();
		},

		_done : function() {
			var t = this, la = t.active;

			if (t.active) {
                t.e