// Modified by Jive Software to remove dynamic portions
/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Declare an object to which we can add real functions.
 */
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

/**
 * Set an alternative error handler from the default alert box.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setErrorHandler = function(handler) {
  dwr.engine._errorHandler = handler;
};

/**
 * Set an alternative warning handler from the default alert box.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setWarningHandler = function(handler) {
  dwr.engine._warningHandler = handler;
};

/**
 * Setter for the text/html handler - what happens if a DWR request gets an HTML
 * reply rather than the expected Javascript. Often due to login timeout
 */
dwr.engine.setTextHtmlHandler = function(handler) {
  dwr.engine._textHtmlHandler = handler;
};

/**
 * Set a default timeout value for all calls. 0 (the default) turns timeouts off.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setTimeout = function(timeout) {
  dwr.engine._timeout = timeout;
};

/**
 * The Pre-Hook is called before any DWR remoting is done.
 * @see getahead.org/dwr/browser/engine/hooks
 */
dwr.engine.setPreHook = function(handler) {
  dwr.engine._preHook = handler;
};

/**
 * The Post-Hook is called after any DWR remoting is done.
 * @see getahead.org/dwr/browser/engine/hooks
 */
dwr.engine.setPostHook = function(handler) {
  dwr.engine._postHook = handler;
};

/**
 * Custom headers for all DWR calls
 * @see getahead.org/dwr/????
 */
dwr.engine.setHeaders = function(headers) {
  dwr.engine._headers = headers;
};

/**
 * Custom parameters for all DWR calls
 * @see getahead.org/dwr/????
 */
dwr.engine.setParameters = function(parameters) {
  dwr.engine._parameters = parameters;
};

/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.XMLHttpRequest = 1;

/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.IFrame = 2;

/** XHR remoting type constant. See dwr.engine.setRpcType() */
dwr.engine.ScriptTag = 3;

/**
 * Set the preferred remoting type.
 * @param newType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setRpcType = function(newType) {
  if (newType != dwr.engine.XMLHttpRequest && newType != dwr.engine.IFrame && newType != dwr.engine.ScriptTag) {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidRpcType", message:"RpcType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag" });
    return;
  }
  dwr.engine._rpcType = newType;
};

/**
 * Which HTTP method do we use to send results? Must be one of "GET" or "POST".
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setHttpMethod = function(httpMethod) {
  if (httpMethod != "GET" && httpMethod != "POST") {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidHttpMethod", message:"Remoting method must be one of GET or POST" });
    return;
  }
  dwr.engine._httpMethod = httpMethod;
};

/**
 * Ensure that remote calls happen in the order in which they were sent? (Default: false)
 * @see getahead.org/dwr/browser/engine/ordering
 */
dwr.engine.setOrdered = function(ordered) {
  dwr.engine._ordered = ordered;
};

/**
 * Do we ask the XHR object to be asynchronous? (Default: true)
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setAsync = function(async) {
  dwr.engine._async = async;
};

/**
 * Does DWR poll the server for updates? (Default: false)
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
  if (activeReverseAjax) {
    // Bail if we are already started
    if (dwr.engine._activeReverseAjax) return;
    dwr.engine._activeReverseAjax = true;
    dwr.engine._poll();
  }
  else {
    // Can we cancel an existing request?
    if (dwr.engine._activeReverseAjax && dwr.engine._pollReq) dwr.engine._pollReq.abort();
    dwr.engine._activeReverseAjax = false;
  }
  // TODO: in iframe mode, if we start, stop, start then the second start may
  // well kick off a second iframe while the first is still about to return
  // we should cope with this but we don't
};

/**
 * The default message handler.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.defaultErrorHandler = function(message, ex) {
  dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);
  if (message == null || message == "") alert("A server error has occurred.");
  // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
  else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
  else alert(message);
};

/**
 * The default warning handler.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.defaultWarningHandler = function(message, ex) {
  dwr.engine._debug(message);
};

/**
 * For reduced latency you can group several remote calls together using a batch.
 * @see getahead.org/dwr/browser/engine/batch
 */
dwr.engine.beginBatch = function() {
  if (dwr.engine._batch) {
    dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
    return;
  }
  dwr.engine._batch = dwr.engine._createBatch();
};

/**
 * Finished grouping a set of remote calls together. Go and execute them all.
 * @see getahead.org/dwr/browser/engine/batch
 */
dwr.engine.endBatch = function(options) {
  var batch = dwr.engine._batch;
  if (batch == null) {
    dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
    return;
  }
  dwr.engine._batch = null;
  if (batch.map.callCount == 0) return;

  // The hooks need to be merged carefully to preserve ordering
  if (options) dwr.engine._mergeBatch(batch, options);

  // In ordered mode, we don't send unless the list of sent items is empty
  if (dwr.engine._ordered && dwr.engine._batchesLength != 0) {
    dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
  }
  else {
    dwr.engine._sendData(batch);
  }
};

/** @deprecated */
dwr.engine.setPollMethod = function(type) { dwr.engine.setPollType(type); };
dwr.engine.setMethod = function(type) { dwr.engine.setRpcType(type); };
dwr.engine.setVerb = function(verb) { dwr.engine.setHttpMethod(verb); };
dwr.engine.setPollType = function() { dwr.engine._debug("Manually setting the Poll Type is not supported"); };

//==============================================================================
// Only private stuff below here
//==============================================================================

/** The original page id sent from the server */
// dwr.engine._origScriptSessionId = "${scriptSessionId}";

/** The session cookie name */
// dwr.engine._sessionCookieName = "${sessionCookieName}"; // JSESSIONID

/** Is GET enabled for the benefit of Safari? */
// dwr.engine._allowGetForSafariButMakeForgeryEasier = "${allowGetForSafariButMakeForgeryEasier}";

/** The script prefix to strip in the case of scriptTagProtection. */
// dwr.engine._scriptTagProtection = "${scriptTagProtection}";

/** The default path to the DWR servlet */
// dwr.engine._defaultPath = "${defaultPath}";

/** Do we use XHR for reverse ajax because we are not streaming? */
// dwr.engine._pollWithXhr = "${pollWithXhr}";

/** The read page id that we calculate */
dwr.engine._scriptSessionId = null;

/** The function that we use to fetch/calculate a session id */
dwr.engine._getScriptSessionId = function() {
  if (dwr.engine._scriptSessionId == null) {
    dwr.engine._scriptSessionId = dwr.engine._origScriptSessionId + Math.floor(Math.random() * 1000);
  }
  return dwr.engine._scriptSessionId;
};

/** A function to call if something fails. */
dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;

/** For debugging when something unexplained happens. */
dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;

/** A function to be called before requests are marshalled. Can be null. */
dwr.engine._preHook = null;

/** A function to be called after replies are received. Can be null. */
dwr.engine._postHook = null;

/** An map of the batches that we have sent and are awaiting a reply on. */
dwr.engine._batches = {};

/** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
dwr.engine._batchesLength = 0;

/** In ordered mode, the array of batches waiting to be sent */
dwr.engine._batchQueue = [];

/** What is the default rpc type */
dwr.engine._rpcType = dwr.engine.XMLHttpRequest;

/** What is the default remoting method (ie GET or POST) */
dwr.engine._httpMethod = "POST";

/** Do we attempt to ensure that calls happen in the order in which they were sent? */
dwr.engine._ordered = false;

/** Do we make the calls async? */
dwr.engine._async = true;

/** The current batch (if we are in batch mode) */
dwr.engine._batch = null;

/** The global timeout */
dwr.engine._timeout = 0;

/** ActiveX objects to use when we want to convert an xml string into a DOM object. */
dwr.engine._DOMDocument = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];

/** The ActiveX objects to use when we want to do an XMLHttpRequest call. */
dwr.engine._XMLHTTP = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];

/** Are we doing comet or polling? */
dwr.engine._activeReverseAjax = false;

/** The iframe that we are using to poll */
dwr.engine._outstandingIFrames = [];

/** The xhr object that we are using to poll */
dwr.engine._pollReq = null;

/** How many milliseconds between internal comet polls */
dwr.engine._pollCometInterval = 200;

/** How many times have we re-tried to poll? */
dwr.engine._pollRetries = 0;
dwr.engine._maxPollRetries = 0;

/** Do we do a document.reload if we get a text/html reply? */
dwr.engine._textHtmlHandler = null;

/** If you wish to send custom headers with every request */
dwr.engine._headers = null;

/** If you wish to send extra custom request parameters with each request */
dwr.engine._parameters = null;

/** Undocumented interceptors - do not use */
dwr.engine._postSeperator = "\n";
dwr.engine._defaultInterceptor = function(data) { return data; };
dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;

/** Batch ids allow us to know which batch the server is answering */
dwr.engine._nextBatchId = 0;

/** A list of the properties that need merging from calls to a batch */
dwr.engine._propnames = [ "rpcType", "httpMethod", "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];

/** Do we stream, or can be hacked to do so? */
dwr.engine._partialResponseNo = 0;
dwr.engine._partialResponseYes = 1;
dwr.engine._partialResponseFlush = 2;

/** Is this page in the process of unloading? */
dwr.engine._unloading = false;

/**
 * @private Send a request. Called by the Javascript interface stub
 * @param path part of URL after the host and before the exec bit without leading or trailing /s
 * @param scriptName The class to execute
 * @param methodName The method on said class to execute
 * @param func The callback function to which any returned data should be passed
 *       if this is null, any returned data will be ignored
 * @param vararg_params The parameters to pass to the above class
 */
dwr.engine._execute = function(path, scriptName, methodName, vararg_params) {
  var singleShot = false;
  if (dwr.engine._batch == null) {
    dwr.engine.beginBatch();
    singleShot = true;
  }
  var batch = dwr.engine._batch;
  // To make them easy to manipulate we copy the arguments into an args array
  var args = [];
  for (var i = 0; i < arguments.length - 3; i++) {
    args[i] = arguments[i + 3];
  }
  // All the paths MUST be to the same servlet
  if (batch.path == null) {
    batch.path = path;
  }
  else {
    if (batch.path != path) {
      dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
      return;
    }
  }
  // From the other params, work out which is the function (or object with
  // call meta-data) and which is the call parameters
  var callData;
  var lastArg = args[args.length - 1];
  if (typeof lastArg == "function" || lastArg == null) callData = { callback:args.pop() };
  else callData = args.pop();

  // Merge from the callData into the batch
  dwr.engine._mergeBatch(batch, callData);
  batch.handlers[batch.map.callCount] = {
    exceptionHandler:callData.exceptionHandler,
    callback:callData.callback
  };

  // Copy to the map the things that need serializing
  var prefix = "c" + batch.map.callCount + "-";
  batch.map[prefix + "scriptName"] = scriptName;
  batch.map[prefix + "methodName"] = methodName;
  batch.map[prefix + "id"] = batch.map.callCount;
  for (i = 0; i < args.length; i++) {
    dwr.engine._serializeAll(batch, [], args[i], prefix + "param" + i);
  }

  // Now we have finished remembering the call, we incr the call count
  batch.map.callCount++;
  if (singleShot) dwr.engine.endBatch();
};

/** @private Poll the server to see if there is any data waiting */
dwr.engine._poll = function() {
  if (!dwr.engine._activeReverseAjax) return;

  var batch = dwr.engine._createBatch();
  batch.map.id = 0; // TODO: Do we need this??
  batch.map.callCount = 1;
  batch.isPoll = true;
  if (dwr.engine._pollWithXhr == "true") {
    batch.rpcType = dwr.engine.XMLHttpRequest;
    batch.map.partialResponse = dwr.engine._partialResponseNo;
  }
  else {
    if (navigator.userAgent.indexOf("Gecko/") != -1) {
      batch.rpcType = dwr.engine.XMLHttpRequest;
      batch.map.partialResponse = dwr.engine._partialResponseYes;
    }
    else {
      batch.rpcType = dwr.engine.XMLHttpRequest;
      batch.map.partialResponse = dwr.engine._partialResponseNo;
    }
  }
  batch.httpMethod = "POST";
  batch.async = true;
  batch.timeout = 0;
  batch.path = dwr.engine._defaultPath;
  batch.preHooks = [];
  batch.postHooks = [];
  batch.errorHandler = dwr.engine._pollErrorHandler;
  batch.warningHandler = dwr.engine._pollErrorHandler;
  batch.handlers[0] = {
    callback:function(pause) {
      dwr.engine._pollRetries = 0;
      setTimeout(dwr.engine._poll, pause);
    }
  };

  // Send the data
  dwr.engine._sendData(batch);
  if (batch.rpcType == dwr.engine.XMLHttpRequest && batch.map.partialResponse == dwr.engine._partialResponseYes) {
    dwr.engine._checkCometPoll();
  }
};

/** Try to recover from polling errors */
dwr.engine._pollErrorHandler = function(msg, ex) {
  // if anything goes wrong then just silently try again (up to 3x) after 10s
  dwr.engine._pollRetries++;
  dwr.engine._debug("Reverse Ajax poll failed (pollRetries=" + dwr.engine._pollRetries + "): " + ex.name + " : " + ex.message);
  if (dwr.engine._pollRetries < dwr.engine._maxPollRetries) {
    setTimeout(dwr.engine._poll, 10000);
  }
  else {
    dwr.engine._activeReverseAjax = false;
    dwr.engine._debug("Giving up.");
  }
};

/** @private Generate a new standard batch */
dwr.engine._createBatch = function() {
  var batch = {
    map:{
      callCount:0,
      page:window.location.pathname + window.location.search,
      httpSessionId:dwr.engine._getJSessionId(),
      scriptSessionId:dwr.engine._getScriptSessionId()
    },
    charsProcessed:0, paramCount:0,
    parameters:{}, headers:{},
    isPoll:false, handlers:{}, preHooks:[], postHooks:[],
    rpcType:dwr.engine._rpcType,
    httpMethod:dwr.engine._httpMethod,
    async:dwr.engine._async,
    timeout:dwr.engine._timeout,
    errorHandler:dwr.engine._errorHandler,
    warningHandler:dwr.engine._warningHandler,
    textHtmlHandler:dwr.engine._textHtmlHandler
  };
  if (dwr.engine._preHook) batch.preHooks.push(dwr.engine._preHook);
  if (dwr.engine._postHook) batch.postHooks.push(dwr.engine._postHook);
  var propname, data;
  if (dwr.engine._headers) {
    for (propname in dwr.engine._headers) {
      data = dwr.engine._headers[propname];
      if (typeof data != "function") batch.headers[propname] = data;
    }
  }
  if (dwr.engine._parameters) {
    for (propname in dwr.engine._parameters) {
      data = dwr.engine._parameters[propname];
      if (typeof data != "function") batch.parameters[propname] = data;
    }
  }
  return batch;
};

/** @private Take further options and merge them into */
dwr.engine._mergeBatch = function(batch, overrides) {
  var propname, data;
  for (var i = 0; i < dwr.engine._propnames.length; i++) {
    propname = dwr.engine._propnames[i];
    if (overrides[propname] != null) batch[propname] = overrides[propname];
  }
  if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
  if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
  if (overrides.headers) {
    for (propname in overrides.headers) {
      data = overrides.headers[propname];
      if (typeof data != "function") batch.headers[propname] = data;
    }
  }
  if (overrides.parameters) {
    for (propname in overrides.parameters) {
      data = overrides.parameters[propname];
      if (typeof data != "function") batch.map["p-" + propname] = "" + data;
    }
  }
};

/** @private What is our session id? */
dwr.engine._getJSessionId =  function() {
  var cookies = document.cookie.split(';');
  for (var i = 0; i < cookies.length; i++) {
    var cookie = cookies[i];
    while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
    if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) {
      return cookie.substring(dwr.engine._sessionCookieName.length + 1, cookie.length);
    }
  }
  return "";
};

/** @private Check for reverse Ajax activity */
dwr.engine._checkCometPoll = function() {
  for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
    var text = "";
    var iframe = dwr.engine._outstandingIFrames[i];
    try {
      text = dwr.engine._getTextFromCometIFrame(iframe);
    }
    catch (ex) {
      dwr.engine._handleWarning(iframe.batch, ex);
    }
    if (text != "") dwr.engine._processCometResponse(text, iframe.batch);
  }
  if (dwr.engine._pollReq) {
    var req = dwr.engine._pollReq;
    var text = req.responseText;
    if (text != null) dwr.engine._processCometResponse(text, req.batch);
  }

  // If the poll resources are still there, come back again
  if (dwr.engine._outstandingIFrames.length > 0 || dwr.engine._pollReq) {
    setTimeout(dwr.engine._checkCometPoll, dwr.engine._pollCometInterval);
  }
};

/** @private Extract the whole (executed an all) text from the current iframe */
dwr.engine._getTextFromCometIFrame = function(frameEle) {
  var body = frameEle.contentWindow.document.body;
  if (body == null) return "";
  var text = body.innerHTML;
  // We need to prevent IE from stripping line feeds
  if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) {
    text = text.substring(5, text.length - 7);
  }
  return text;
};

/** @private Some more text might have come in, test and execute the new stuff */
dwr.engine._processCometResponse = function(response, batch) {
  if (batch.charsProcessed == response.length) return;
  if (response.length == 0) {
    batch.charsProcessed = 0;
    return;
  }

  var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
  if (firstStartTag == -1) {
    // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
    batch.charsProcessed = response.length;
    return;
  }
  // if (firstStartTag > 0) {
  //   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
  // }

  var lastEndTag = response.lastIndexOf("//#DWR-END#");
  if (lastEndTag == -1) {
    // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
    return;
  }

  // Skip the end tag too for next time, remembering CR and LF
  if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
    batch.charsProcessed = lastEndTag + 13;
  }
  else {
    batch.charsProcessed = lastEndTag + 11;
  }

  var exec = response.substring(firstStartTag + 13, lastEndTag);

  dwr.engine._receivedBatch = batch;
  dwr.engine._eval(exec);
  dwr.engine._receivedBatch = null;
};

/** @private Actually send the block of data in the batch object. */
dwr.engine._sendData = function(batch) {
  batch.map.batchId = dwr.engine._nextBatchId;
  dwr.engine._nextBatchId++;
  dwr.engine._batches[batch.map.batchId] = batch;
  dwr.engine._batchesLength++;
  batch.completed = false;

  for (var i = 0; i < batch.preHooks.length; i++) {
    batch.preHooks[i]();
  }
  batch.preHooks = null;
  // Set a timeout
  if (batch.timeout && batch.timeout != 0) {
    batch.timeoutId = setTimeout(function() { dwr.engine._abortRequest(batch); }, batch.timeout);
  }
  // Get setup for XMLHttpRequest if possible
  if (batch.rpcType == dwr.engine.XMLHttpRequest) {
    if (window.XMLHttpRequest) {
      batch.req = new XMLHttpRequest();
    }
    // IE5 for the mac claims to support window.ActiveXObject, but throws an error when it's used
    else if (window.ActiveXObject && !(navigator.userAgent.indexOf("Mac") >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) {
      batch.req = dwr.engine._newActiveXObject(dwr.engine._XMLHTTP);
    }
  }

  var prop, request;
  if (batch.req) {
    // Proceed using XMLHttpRequest
    if (batch.async) {
      batch.req.onreadystatechange = function() {
        if (typeof dwr != 'undefined') dwr.engine._stateChange(batch);
      };
    }
    // If we're polling, record this for monitoring
    if (batch.isPoll) {
      dwr.engine._pollReq = batch.req;
      // In IE XHR is an ActiveX control so you can't augment it like this
      if (!(document.all && !window.opera)) batch.req.batch = batch;
    }
    // Workaround for Safari 1.x POST bug
    var indexSafari = navigator.userAgent.indexOf("Safari/");
    if (indexSafari >= 0) {
      var version = navigator.userAgent.substring(indexSafari + 7);
      if (parseInt(version, 10) < 400) {
        if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") batch.httpMethod = "GET";
        else dwr.engine._handleWarning(batch, { name:"dwr.engine.oldSafari", message:"Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier." });
      }
    }
    batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
    request = dwr.engine._constructRequest(batch);
    try {
      batch.req.open(batch.httpMethod, request.url, batch.async);
      try {
        for (prop in batch.headers) {
          var value = batch.headers[prop];
          if (typeof value == "string") batch.req.setRequestHeader(prop, value);
        }
        if (!batch.headers["Content-Type"]) batch.req.setRequestHeader("Content-Type", "text/plain");
      }
      catch (ex) {
        dwr.engine._handleWarning(batch, ex);
      }
      batch.req.send(request.body);
      if (!batch.async) dwr.engine._stateChange(batch);
    }
    catch (ex) {
      dwr.engine._handleError(batch, ex);
    }
  }
  else if (batch.rpcType != dwr.engine.ScriptTag) {
    var idname = batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map.batchId;
    // Removed htmlfile implementation. Don't expect it to return before v3
    batch.div = document.createElement("div");
    // Add the div to the document first, otherwise IE 6 will ignore onload handler.
    document.body.appendChild(batch.div);
    batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' style='width:0px;height:0px;border:0;' id='" + idname + "' name='" + idname + "' onload='dwr.engine._iframeLoadingComplete (" + batch.map.batchId + ");'></iframe>";
    batch.document = document;
    batch.iframe = batch.document.getElementById(idname);
    batch.iframe.batch = batch;
    batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
    if (batch.isPoll) dwr.engine._outstandingIFrames.push(batch.iframe);
    request = dwr.engine._constructRequest(batch);
    if (batch.httpMethod == "GET") {
      batch.iframe.setAttribute("src", request.url);
    }
    else {
      batch.form = batch.document.createElement("form");
      batch.form.setAttribute("id", "dwr-form");
      batch.form.setAttribute("action", request.url);
      batch.form.setAttribute("style", "display:none;");
      batch.form.setAttribute("target", idname);
      batch.form.target = idname;
      batch.form.setAttribute("method", batch.httpMethod);
      for (prop in batch.map) {
        var value = batch.map[prop];
        if (typeof value != "function") {
          var formInput = batch.document.createElement("input");
          formInput.setAttribute("type", "hidden");
          formInput.setAttribute("name", prop);
          formInput.setAttribute("value", value);
          batch.form.appendChild(formInput);
        }
      }
      batch.document.body.appendChild(batch.form);
      batch.form.submit();
    }
  }
  else {
    batch.httpMethod = "GET"; // There's no such thing as ScriptTag using POST
    batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
    request = dwr.engine._constructRequest(batch);
    batch.script = document.createElement("script");
    batch.script.id = "dwr-st-" + batch.map["c0-id"];
    batch.script.src = request.url;
    document.body.appendChild(batch.script);
  }
};

dwr.engine._ModePlainCall = "/call/plaincall/";
dwr.engine._ModeHtmlCall = "/call/htmlcall/";
dwr.engine._ModePlainPoll = "/call/plainpoll/";
dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";

/** @private Work out what the URL should look like */
dwr.engine._constructRequest = function(batch) {
  // A quick string to help people that use web log analysers
  var request = { url:batch.path + batch.mode, body:null };
  if (batch.isPoll == true) {
    request.url += "ReverseAjax.dwr";
  }
  else if (batch.map.callCount == 1) {
    request.url += batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr";
  }
  else {
    request.url += "Multiple." + batch.map.callCount + ".dwr";
  }
  // Play nice with url re-writing
  var sessionMatch = location.href.match(/jsessionid=([^?]+)/);
  if (sessionMatch != null) {
    request.url += ";jsessionid=" + sessionMatch[1];
  }

  var prop;
  if (batch.httpMethod == "GET") {
    // Some browsers (Opera/Safari2) seem to fail to convert the callCount value
    // to a string in the loop below so we do it manually here.
    batch.map.callCount = "" + batch.map.callCount;
    request.url += "?";
    for (prop in batch.map) {
      if (typeof batch.map[prop] != "function") {
        request.url += encodeURIComponent(prop) + "=" + encodeURIComponent(batch.map[prop]) + "&";
      }
    }
    request.url = request.url.substring(0, request.url.length - 1);
  }
  else {
    // PERFORMANCE: for iframe mode this is thrown away.
    request.body = "";
    if (document.all && !window.opera) {
      // Use array joining on IE (fastest)
      var buf = [];
      for (prop in batch.map) {
        if (typeof batch.map[prop] != "function") {
          buf.push(prop + "=" + batch.map[prop] + dwr.engine._postSeperator);
        }
      }
      request.body = buf.join("");
    }
    else {
      // Use string concat on other browsers (fastest)
      for (prop in batch.map) {
        if (typeof batch.map[prop] != "function") {
          request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
        }
      }
    }
    request.body = dwr.engine._contentRewriteHandler(request.body);
  }
  request.url = dwr.engine._urlRewriteHandler(request.url);
  return request;
};

/** @private Called by XMLHttpRequest to indicate that something has happened */
dwr.engine._stateChange = function(batch) {
  var toEval;

  if (batch.completed) {
    dwr.engine._debug("Error: _stateChange() with batch.completed");
    return;
  }

  var req = batch.req;
  try {
    if (req.readyState != 4) return;
  }
  catch (ex) {
    dwr.engine._handleWarning(batch, ex);
    // It's broken - clear up and forget this call
    dwr.engine._clearUp(batch);
    return;
  }

  if (dwr.engine._unloading) {
    dwr.engine._debug("Ignoring reply from server as page is unloading.");
    return;
  }

  try {
    var reply = req.responseText;
    reply = dwr.engine._replyRewriteHandler(reply);
    var status = req.status; // causes Mozilla to except on page moves

    if (reply == null || reply == "") {
      dwr.engine._handleWarning(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
    }
    else if (status != 200) {
      dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText });
    }
    else {
      var contentType = req.getResponseHeader("Content-Type");
      if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
        if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") {
          batch.textHtmlHandler({ status:status, responseText:reply, contentType:contentType });
        }
        else {
          dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
        }
      }
      else {
        // Comet replies might have already partially executed
        if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
          dwr.engine._processCometResponse(reply, batch);
        }
        else {
          if (reply.search("//#DWR") == -1) {
            dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
          }
          else {
            toEval = reply;
          }
        }
      }
    }
  }
  catch (ex) {
    dwr.engine._handleWarning(batch, ex);
  }

  dwr.engine._callPostHooks(batch);

  // Outside of the try/catch so errors propogate normally:
  dwr.engine._receivedBatch = batch;
  if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
  dwr.engine._eval(toEval);
  dwr.engine._receivedBatch = null;
  dwr.engine._validateBatch(batch);
  if (!batch.completed) dwr.engine._clearUp(batch);
};

/**
 * @private This function is invoked when a batch reply is received.
 * It checks that there is a response for every call in the batch. Otherwise,
 * an error will be signaled (a call without a response indicates that the
 * server failed to send complete batch response).
 */
dwr.engine._validateBatch = function(batch) {
  // If some call left unreplied, report an error.
  if (!batch.completed) {
    for (var i = 0; i < batch.map.callCount; i++) {
      if (batch.handlers[i] != null) {
        dwr.engine._handleWarning(batch, { name:"dwr.engine.incompleteReply", message:"Incomplete reply from server" });
        break;
      }
    }
  }
}

/** @private Called from iframe onload, check batch using batch-id */
dwr.engine._iframeLoadingComplete = function(batchId) {
  // dwr.engine._checkCometPoll();
  var batch = dwr.engine._batches[batchId];
  if (batch) dwr.engine._validateBatch(batch);
}

/** @private Called by the server: Execute a callback */
dwr.engine._remoteHandleCallback = function(batchId, callId, reply) {
  var batch = dwr.engine._batches[batchId];
  if (batch == null) {
    dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true);
    return;
  }
  // Error handlers inside here indicate an error that is nothing to do
  // with DWR so we handle them differently.
  try {
    var handlers = batch.handlers[callId];
    batch.handlers[callId] = null;
    if (!handlers) {
      dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
    }
    else if (typeof handlers.callback == "function") handlers.callback(reply);
  }
  catch (ex) {
    dwr.engine._handleError(batch, ex);
  }
};

/** @private Called by the server: Handle an exception for a call */
dwr.engine._remoteHandleException = function(batchId, callId, ex) {
  var batch = dwr.engine._batches[batchId];
  if (batch == null) { dwr.engine._debug("Warning: null batch in remoteHandleException", true); return; }
  var handlers = batch.handlers[callId];
  batch.handlers[callId] = null;
  if (handlers == null) { dwr.engine._debug("Warning: null handlers in remoteHandleException", true); return; }
  if (ex.message == undefined) ex.message = "";
  if (typeof handlers.exceptionHandler == "function") handlers.exceptionHandler(ex.message, ex);
  else if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
};

/** @private Called by the server: The whole batch is broken */
dwr.engine._remoteHandleBatchException = function(ex, batchId) {
  var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
  if (searchBatch) {
    dwr.engine._receivedBatch = dwr.engine._batches[batchId];
  }
  if (ex.message == undefined) ex.message = "";
  dwr.engine._handleError(dwr.engine._receivedBatch, ex);
  if (searchBatch) {
    dwr.engine._receivedBatch = null;
    dwr.engine._clearUp(dwr.engine._batches[batchId]);
  }
};

/** @private Called by the server: Reverse ajax should not be used */
dwr.engine._remotePollCometDisabled = function(ex, batchId) {
  dwr.engine.setActiveReverseAjax(false);
  var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
  if (searchBatch) {
    dwr.engine._receivedBatch = dwr.engine._batches[batchId];
  }
  if (ex.message == undefined) ex.message = "";
  dwr.engine._handleError(dwr.engine._receivedBatch, ex);
  if (searchBatch) {
    dwr.engine._receivedBatch = null;
    dwr.engine._clearUp(dwr.engine._batches[batchId]);
  }
};

/** @private Called by the server: An IFrame reply is about to start */
dwr.engine._remoteBeginIFrameResponse = function(iframe, batchId) {
  if (iframe != null) dwr.engine._receivedBatch = iframe.batch;
  dwr.engine._callPostHooks(dwr.engine._receivedBatch);
};

/** @private Called by the server: An IFrame reply is just completing */
dwr.engine._remoteEndIFrameResponse = function(batchId) {
  dwr.engine._clearUp(dwr.engine._receivedBatch);
  dwr.engine._receivedBatch = null;
};

/** @private This is a hack to make the context be this window */
dwr.engine._eval = function(script) {
  if (script == null) return null;
  if (script == "") { dwr.engine._debug("Warning: blank script", true); return null; }
  // dwr.engine._debug("Exec: [" + script + "]", true);
  return eval(script);
};

/** @private Called as a result of a request timeout */
dwr.engine._abortRequest = function(batch) {
  if (batch && !batch.completed) {
    dwr.engine._clearUp(batch);
    if (batch.req) batch.req.abort();
    dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
  }
};

/** @private call all the post hooks for a batch */
dwr.engine._callPostHooks = function(batch) {
  if (batch.postHooks) {
    for (var i = 0; i < batch.postHooks.length; i++) {
      batch.postHooks[i]();
    }
    batch.postHooks = null;
  }
};

/** @private A call has finished by whatever means and we need to shut it all down. */
dwr.engine._clearUp = function(batch) {
  if (!batch) { dwr.engine._debug("Warning: null batch in dwr.engine._clearUp()", true); return; }
  if (batch.completed) { dwr.engine._debug("Warning: Double complete", true); return; }

  // IFrame tidyup
  if (batch.div) batch.div.parentNode.removeChild(batch.div);
  if (batch.iframe) {
    // If this is a poll frame then stop comet polling
    for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
      if (dwr.engine._outstandingIFrames[i] == batch.iframe) {
        dwr.engine._outstandingIFrames.splice(i, 1);
      }
    }
    batch.iframe.parentNode.removeChild(batch.iframe);
  }
  if (batch.form) batch.form.parentNode.removeChild(batch.form);

  // XHR tidyup: avoid IE handles increase
  if (batch.req) {
    // If this is a poll frame then stop comet polling
    if (batch.req == dwr.engine._pollReq) dwr.engine._pollReq = null;
    delete batch.req;
  }

  // Timeout tidyup
  if (batch.timeoutId) {
    clearTimeout(batch.timeoutId);
    delete batch.timeoutId;
  }

  if (batch.map && (batch.map.batchId || batch.map.batchId == 0)) {
    delete dwr.engine._batches[batch.map.batchId];
    dwr.engine._batchesLength--;
  }

  batch.completed = true;

  // If there is anything on the queue waiting to go out, then send it.
  // We don't need to check for ordered mode, here because when ordered mode
  // gets turned off, we still process *waiting* batches in an ordered way.
  if (dwr.engine._batchQueue.length != 0) {
    var sendbatch = dwr.engine._batchQueue.shift();
    dwr.engine._sendData(sendbatch);
  }
};

/** @private Abort any XHRs in progress at page unload (solves zombie socket problems in IE). */
dwr.engine._unloader = function() {
  dwr.engine._unloading = true;

  // Empty queue of waiting ordered requests
  dwr.engine._batchQueue.length = 0;

  // Abort any ongoing XHRs and clear their batches
  for (var batchId in dwr.engine._batches) {
    var batch = dwr.engine._batches[batchId];
    // Only process objects that look like batches (avoid prototype additions!)
    if (batch && batch.map) {
      if (batch.req) {
        batch.req.abort();
      }
      dwr.engine._clearUp(batch);
    }
  }
};
// Now register the unload handler
if (window.addEventListener) window.addEventListener('unload', dwr.engine._unloader, false);
else if (window.attachEvent) window.attachEvent('onunload', dwr.engine._unloader);

/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleError = function(batch, ex) {
  if (typeof ex == "string") ex = { name:"unknown", message:ex };
  if (ex.message == null) ex.message = "";
  if (ex.name == null) ex.name = "unknown";
  if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
  else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
  if (batch) dwr.engine._clearUp(batch);
};

/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleWarning = function(batch, ex) {
  if (typeof ex == "string") ex = { name:"unknown", message:ex };
  if (ex.message == null) ex.message = "";
  if (ex.name == null) ex.name = "unknown";
  if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
  else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
  if (batch) dwr.engine._clearUp(batch);
};

/**
 * @private Marshall a data item
 * @param batch A map of variables to how they have been marshalled
 * @param referto An array of already marshalled variables to prevent recurrsion
 * @param data The data to be marshalled
 * @param name The name of the data being marshalled
 */
dwr.engine._serializeAll = function(batch, referto, data, name) {
  if (data == null) {
    batch.map[name] = "null:null";
    return;
  }

  switch (typeof data) {
  case "boolean":
    batch.map[name] = "boolean:" + data;
    break;
  case "number":
    batch.map[name] = "number:" + data;
    break;
  case "string":
    batch.map[name] = "string:" + encodeURIComponent(data);
    break;
  case "object":
    if (data instanceof String) batch.map[name] = "String:" + encodeURIComponent(data);
    else if (data instanceof Boolean) batch.map[name] = "Boolean:" + data;
    else if (data instanceof Number) batch.map[name] = "Number:" + data;
    else if (data instanceof Date) batch.map[name] = "Date:" + data.getTime();
    else if (data && data.join) batch.map[name] = dwr.engine._serializeArray(batch, referto, data, name);
    else batch.map[name] = dwr.engine._serializeObject(batch, referto, data, name);
    break;
  case "function":
    // We just ignore functions.
    break;
  default:
    dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
    batch.map[name] = "default:" + data;
    break;
  }
};

/** @private Have we already converted this object? */
dwr.engine._lookup = function(referto, data, name) {
  var lookup;
  // Can't use a map: getahead.org/ajax/javascript-gotchas
  for (var i = 0; i < referto.length; i++) {
    if (referto[i].data == data) {
      lookup = referto[i];
      break;
    }
  }
  if (lookup) return "reference:" + lookup.name;
  referto.push({ data:data, name:name });
  return null;
};

/** @private Marshall an object */
dwr.engine._serializeObject = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  // This check for an HTML is not complete, but is there a better way?
  // Maybe we should add: data.hasChildNodes typeof "function" == true
  if (data.nodeName && data.nodeType) {
    return dwr.engine._serializeXml(batch, referto, data, name);
  }

  // treat objects as an associative arrays
  var reply = "Object_" + dwr.engine._getObjectClassName(data) + ":{";
  var element;
  for (element in data) {
    if (typeof data[element] != "function") {
      batch.paramCount++;
      var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
      dwr.engine._serializeAll(batch, referto, data[element], childName);

      reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
    }
  }

  if (reply.substring(reply.length - 2) == ", ") {
    reply = reply.substring(0, reply.length - 2);
  }
  reply += "}";

  return reply;
};

/** @private Returns the classname of supplied argument obj */
dwr.engine._errorClasses = { "Error":Error, "EvalError":EvalError, "RangeError":RangeError, "ReferenceError":ReferenceError, "SyntaxError":SyntaxError, "TypeError":TypeError, "URIError":URIError };
dwr.engine._getObjectClassName = function(obj) {
  // Try to find the classname by stringifying the object's constructor
  // and extract <class> from "function <class>".
  if (obj && obj.constructor && obj.constructor.toString)
  {
    var str = obj.constructor.toString();
    var regexpmatch = str.match(/function\s+(\w+)/);
    if (regexpmatch && regexpmatch.length == 2) {
      return regexpmatch[1];
    }
  }

  // Now manually test against the core Error classes, as these in some
  // browsers successfully match to the wrong class in the
  // Object.toString() test we will do later
  if (obj && obj.constructor) {
	for (var errorname in dwr.engine._errorClasses) {
      if (obj.constructor == dwr.engine._errorClasses[errorname]) return errorname;
    }
  }

  // Try to find the classname by calling Object.toString() on the object
  // and extracting <class> from "[object <class>]"
  if (obj) {
    var str = Object.prototype.toString.call(obj);
    var regexpmatch = str.match(/\[object\s+(\w+)/);
    if (regexpmatch && regexpmatch.length==2) {
      return regexpmatch[1];
    }
  }

  // Supplied argument was probably not an object, but what is better?
  return "Object";
};

/** @private Marshall an object */
dwr.engine._serializeXml = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  var output;
  if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
  else if (data.toXml) output = data.toXml;
  else output = data.innerHTML;

  return "XML:" + encodeURIComponent(output);
};

/** @private Marshall an array */
dwr.engine._serializeArray = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  if (document.all && !window.opera) {
    // Use array joining on IE (fastest)
    var buf = ["Array:["];
    for (var i = 0; i < data.length; i++) {
      if (i != 0) buf.push(",");
      batch.paramCount++;
      var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
      dwr.engine._serializeAll(batch, referto, data[i], childName);
      buf.push("reference:");
      buf.push(childName);
    }
    buf.push("]");
    reply = buf.join("");
  }
  else {
    // Use string concat on other browsers (fastest)
    var reply = "Array:[";
    for (var i = 0; i < data.length; i++) {
      if (i != 0) reply += ",";
      batch.paramCount++;
      var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
      dwr.engine._serializeAll(batch, referto, data[i], childName);
      reply += "reference:";
      reply += childName;
    }
    reply += "]";
  }

  return reply;
};

/** @private Convert an XML string into a DOM object. */
dwr.engine._unserializeDocument = function(xml) {
  var dom;
  if (window.DOMParser) {
    var parser = new DOMParser();
    dom = parser.parseFromString(xml, "text/xml");
    if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
      var message = dom.documentElement.firstChild.data;
      message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
      throw message;
    }
    return dom;
  }
  else if (window.ActiveXObject) {
    dom = dwr.engine._newActiveXObject(dwr.engine._DOMDocument);
    dom.loadXML(xml); // What happens on parse fail with IE?
    return dom;
  }
  else {
    var div = document.createElement("div");
    div.innerHTML = xml;
    return div;
  }
};

/** @param axarray An array of strings to attempt to create ActiveX objects from */
dwr.engine._newActiveXObject = function(axarray) {
  var returnValue;
  for (var i = 0; i < axarray.length; i++) {
    try {
      returnValue = new ActiveXObject(axarray[i]);
      break;
    }
    catch (ex) { /* ignore */ }
  }
  return returnValue;
};

/** @private Used internally when some message needs to get to the programmer */
dwr.engine._debug = function(message, stacktrace) {
  var written = false;
  try {
    if (window.console) {
      if (stacktrace && window.console.trace) window.console.trace();
      window.console.log(message);
      written = true;
    }
    else if (window.opera && window.opera.postError) {
      window.opera.postError(message);
      written = true;
    }
  }
  catch (ex) { /* ignore */ }

  if (!written) {
    var debug = document.getElementById("dwr-debug");
    if (debug) {
      var contents = message + "<br/>" + debug.innerHTML;
      if (contents.length > 2048) contents = contents.substring(0, 2048);
      debug.innerHTML = contents;
    }
  }
};


/*!
 * jQuery JavaScript Library v1.3.2
 * http://jquery.com/
 *
 * Copyright (c) 2009 John Resig
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
 * Revision: 6246
 */
function foo(){

var
	// Will speed up references to window, and allows munging its name.
	window = this,
	// Will speed up references to undefined, and allows munging its name.
	undefined,
	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,
	// Map over the $ in case of overwrite
	_$ = window.$,

	jQuery = window.jQuery = window.$ = function( selector, context ) {
		// The jQuery object is actually just the init constructor 'enhanced'
		return new jQuery.fn.init( selector, context );
	},

	// A simple way to check for HTML strings or ID strings
	// (both of which we optimize for)
	quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
	// Is it a simple selector
	isSimple = /^.[^:#\[\.,]*$/;

jQuery.fn = jQuery.prototype = {
	init: function( selector, context ) {
		// Make sure that a selection was provided
		selector = selector || document;

		// Handle $(DOMElement)
		if ( selector.nodeType ) {
			this[0] = selector;
			this.length = 1;
			this.context = selector;
			return this;
		}
		// Handle HTML strings
		if ( typeof selector === "string" ) {
			// Are we dealing with HTML string or an ID?
			var match = quickExpr.exec( selector );

			// Verify a match, and that no context was specified for #id
			if ( match && (match[1] || !context) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[1] )
					selector = jQuery.clean( [ match[1] ], context );

				// HANDLE: $("#id")
				else {
					var elem = document.getElementById( match[3] );

					// Handle the case where IE and Opera return items
					// by name instead of ID
					if ( elem && elem.id != match[3] )
						return jQuery().find( selector );

					// Otherwise, we inject the element directly into the jQuery object
					var ret = jQuery( elem || [] );
					ret.context = document;
					ret.selector = selector;
					return ret;
				}

			// HANDLE: $(expr, [context])
			// (which is just equivalent to: $(content).find(expr)
			} else
				return jQuery( context ).find( selector );

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) )
			return jQuery( document ).ready( selector );

		// Make sure that old selector state is passed along
		if ( selector.selector && selector.context ) {
			this.selector = selector.selector;
			this.context = selector.context;
		}

		return this.setArray(jQuery.isArray( selector ) ?
			selector :
			jQuery.makeArray(selector));
	},

	// Start with an empty selector
	selector: "",

	// The current version of jQuery being used
	jquery: "1.3.2",

	// The number of elements contained in the matched element set
	size: function() {
		return this.length;
	},

	// Get the Nth element in the matched element set OR
	// Get the whole matched element set as a clean array
	get: function( num ) {
		return num === undefined ?

			// Return a 'clean' array
			Array.prototype.slice.call( this ) :

			// Return just the object
			this[ num ];
	},

	// Take an array of elements and push it onto the stack
	// (returning the new matched element set)
	pushStack: function( elems, name, selector ) {
		// Build a new jQuery matched element set
		var ret = jQuery( elems );

		// Add the old object onto the stack (as a reference)
		ret.prevObject = this;

		ret.context = this.context;

		if ( name === "find" )
			ret.selector = this.selector + (this.selector ? " " : "") + selector;
		else if ( name )
			ret.selector = this.selector + "." + name + "(" + selector + ")";

		// Return the newly-formed element set
		return ret;
	},

	// Force the current matched set of elements to become
	// the specified array of elements (destroying the stack in the process)
	// You should use pushStack() in order to do this, but maintain the stack
	setArray: function( elems ) {
		// Resetting the length to 0, then using the native Array push
		// is a super-fast way to populate an object with array-like properties
		this.length = 0;
		Array.prototype.push.apply( this, elems );

		return this;
	},

	// Execute a callback for every element in the matched set.
	// (You can seed the arguments with an array of args, but this is
	// only used internally.)
	each: function( callback, args ) {
		return jQuery.each( this, callback, args );
	},

	// Determine the position of an element within
	// the matched set of elements
	index: function( elem ) {
		// Locate the position of the desired element
		return jQuery.inArray(
			// If it receives a jQuery object, the first element is used
			elem && elem.jquery ? elem[0] : elem
		, this );
	},

	attr: function( name, value, type ) {
		var options = name;

		// Look for the case where we're accessing a style value
		if ( typeof name === "string" )
			if ( value === undefined )
				return this[0] && jQuery[ type || "attr" ]( this[0], name );

			else {
				options = {};
				options[ name ] = value;
			}

		// Check to see if we're setting style values
		return this.each(function(i){
			// Set all the styles
			for ( name in options )
				jQuery.attr(
					type ?
						this.style :
						this,
					name, jQuery.prop( this, options[ name ], type, i, name )
				);
		});
	},

	css: function( key, value ) {
		// ignore negative width and height values
		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
			value = undefined;
		return this.attr( key, value, "curCSS" );
	},

	text: function( text ) {
		if ( typeof text !== "object" && text != null )
			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );

		var ret = "";

		jQuery.each( text || this, function(){
			jQuery.each( this.childNodes, function(){
				if ( this.nodeType != 8 )
					ret += this.nodeType != 1 ?
						this.nodeValue :
						jQuery.fn.text( [ this ] );
			});
		});

		return ret;
	},

	wrapAll: function( html ) {
		if ( this[0] ) {
			// The elements to wrap the target around
			var wrap = jQuery( html, this[0].ownerDocument ).clone();

			if ( this[0].parentNode )
				wrap.insertBefore( this[0] );

			wrap.map(function(){
				var elem = this;

				while ( elem.firstChild )
					elem = elem.firstChild;

				return elem;
			}).append(this);
		}

		return this;
	},

	wrapInner: function( html ) {
		return this.each(function(){
			jQuery( this ).contents().wrapAll( html );
		});
	},

	wrap: function( html ) {
		return this.each(function(){
			jQuery( this ).wrapAll( html );
		});
	},

	append: function() {
		return this.domManip(arguments, true, function(elem){
			if (this.nodeType == 1)
				this.appendChild( elem );
		});
	},

	prepend: function() {
		return this.domManip(arguments, true, function(elem){
			if (this.nodeType == 1)
				this.insertBefore( elem, this.firstChild );
		});
	},

	before: function() {
		return this.domManip(arguments, false, function(elem){
			this.parentNode.insertBefore( elem, this );
		});
	},

	after: function() {
		return this.domManip(arguments, false, function(elem){
			this.parentNode.insertBefore( elem, this.nextSibling );
		});
	},

	end: function() {
		return this.prevObject || jQuery( [] );
	},

	// For internal use only.
	// Behaves like an Array's method, not like a jQuery method.
	push: [].push,
	sort: [].sort,
	splice: [].splice,

	find: function( selector ) {
		if ( this.length === 1 ) {
			var ret = this.pushStack( [], "find", selector );
			ret.length = 0;
			jQuery.find( selector, this[0], ret );
			return ret;
		} else {
			return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
				return jQuery.find( selector, elem );
			})), "find", selector );
		}
	},

	clone: function( events ) {
		// Do the clone
		var ret = this.map(function(){
			if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
				// IE copies events bound via attachEvent when
				// using cloneNode. Calling detachEvent on the
				// clone will also remove the events from the orignal
				// In order to get around this, we use innerHTML.
				// Unfortunately, this means some modifications to
				// attributes in IE that are actually only stored
				// as properties will not be copied (such as the
				// the name attribute on an input).
				var html = this.outerHTML;
				if ( !html ) {
					var div = this.ownerDocument.createElement("div");
					div.appendChild( this.cloneNode(true) );
					html = div.innerHTML;
				}

				return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
			} else
				return this.cloneNode(true);
		});

		// Copy the events from the original to the clone
		if ( events === true ) {
			var orig = this.find("*").andSelf(), i = 0;

			ret.find("*").andSelf().each(function(){
				if ( this.nodeName !== orig[i].nodeName )
					return;

				var events = jQuery.data( orig[i], "events" );

				for ( var type in events ) {
					for ( var handler in events[ type ] ) {
						jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
					}
				}

				i++;
			});
		}

		// Return the cloned set
		return ret;
	},

	filter: function( selector ) {
		return this.pushStack(
			jQuery.isFunction( selector ) &&
			jQuery.grep(this, function(elem, i){
				return selector.call( elem, i );
			}) ||

			jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
				return elem.nodeType === 1;
			}) ), "filter", selector );
	},

	closest: function( selector ) {
		var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
			closer = 0;

		return this.map(function(){
			var cur = this;
			while ( cur && cur.ownerDocument ) {
				if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
					jQuery.data(cur, "closest", closer);
					return cur;
				}
				cur = cur.parentNode;
				closer++;
			}
		});
	},

	not: function( selector ) {
		if ( typeof selector === "string" )
			// test special case where just one selector is passed in
			if ( isSimple.test( selector ) )
				return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
			else
				selector = jQuery.multiFilter( selector, this );

		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
		return this.filter(function() {
			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
		});
	},

	add: function( selector ) {
		return this.pushStack( jQuery.unique( jQuery.merge(
			this.get(),
			typeof selector === "string" ?
				jQuery( selector ) :
				jQuery.makeArray( selector )
		)));
	},

	is: function( selector ) {
		return !!selector && jQuery.multiFilter( selector, this ).length > 0;
	},

	hasClass: function( selector ) {
		return !!selector && this.is( "." + selector );
	},

	val: function( value ) {
		if ( value === undefined ) {
			var elem = this[0];

			if ( elem ) {
				if( jQuery.nodeName( elem, 'option' ) )
					return (elem.attributes.value || {}).specified ? elem.value : elem.text;

				// We need to handle select boxes special
				if ( jQuery.nodeName( elem, "select" ) ) {
					var index = elem.selectedIndex,
						values = [],
						options = elem.options,
						one = elem.type == "select-one";

					// Nothing was selected
					if ( index < 0 )
						return null;

					// Loop through all the selected options
					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
						var option = options[ i ];

						if ( option.selected ) {
							// Get the specifc value for the option
							value = jQuery(option).val();

							// We don't need an array for one selects
							if ( one )
								return value;

							// Multi-Selects return an array
							values.push( value );
						}
					}

					return values;
				}

				// Everything else, we just grab the value
				return (elem.value || "").replace(/\r/g, "");

			}

			return undefined;
		}

		if ( typeof value === "number" )
			value += '';

		return this.each(function(){
			if ( this.nodeType != 1 )
				return;

			if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
					jQuery.inArray(this.name, value) >= 0);

			else if ( jQuery.nodeName( this, "select" ) ) {
				var values = jQuery.makeArray(value);

				jQuery( "option", this ).each(function(){
					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
						jQuery.inArray( this.text, values ) >= 0);
				});

				if ( !values.length )
					this.selectedIndex = -1;

			} else
				this.value = value;
		});
	},

	html: function( value ) {
		return value === undefined ?
			(this[0] ?
				this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
				null) :
			this.empty().append( value );
	},

	replaceWith: function( value ) {
		return this.after( value ).remove();
	},

	eq: function( i ) {
		return this.slice( i, +i + 1 );
	},

	slice: function() {
		return this.pushStack( Array.prototype.slice.apply( this, arguments ),
			"slice", Array.prototype.slice.call(arguments).join(",") );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map(this, function(elem, i){
			return callback.call( elem, i, elem );
		}));
	},

	andSelf: function() {
		return this.add( this.prevObject );
	},

	domManip: function( args, table, callback ) {
		if ( this[0] ) {
			var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
				scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
				first = fragment.firstChild;

			if ( first )
				for ( var i = 0, l = this.length; i < l; i++ )
					callback.call( root(this[i], first), this.length > 1 || i > 0 ?
							fragment.cloneNode(true) : fragment );

			if ( scripts )
				jQuery.each( scripts, evalScript );
		}

		return this;

		function root( elem, cur ) {
			return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
				(elem.getElementsByTagName("tbody")[0] ||
				elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
				elem;
		}
	}
};

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

function evalScript( i, elem ) {
	if ( elem.src )
		jQuery.ajax({
			url: elem.src,
			async: false,
			dataType: "script"
		});

	else
		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );

	if ( elem.parentNode )
		elem.parentNode.removeChild( elem );
}

function now(){
	return +new Date;
}

jQuery.extend = jQuery.fn.extend = function() {
	// copy reference to target object
	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !jQuery.isFunction(target) )
		target = {};

	// extend jQuery itself if only one argument is passed
	if ( length == i ) {
		target = this;
		--i;
	}

	for ( ; i < length; i++ )
		// Only deal with non-null/undefined values
		if ( (options = arguments[ i ]) != null )
			// Extend the base object
			for ( var name in options ) {
				var src = target[ name ], copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy )
					continue;

				// Recurse if we're merging object values
				if ( deep && copy && typeof copy === "object" && !copy.nodeType )
					target[ name ] = jQuery.extend( deep,
						// Never move original objects, clone them
						src || ( copy.length != null ? [ ] : { } )
					, copy );

				// Don't bring in undefined values
				else if ( copy !== undefined )
					target[ name ] = copy;

			}

	// Return the modified object
	return target;
};

// exclude the following css properties to add px
var	exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
	// cache defaultView
	defaultView = document.defaultView || {},
	toString = Object.prototype.toString;

jQuery.extend({
	noConflict: function( deep ) {
		window.$ = _$;

		if ( deep )
			window.jQuery = _jQuery;

		return jQuery;
	},

	// See test/unit/core.js for details concerning isFunction.
	// Since version 1.3, DOM methods and functions like alert
	// aren't supported. They return false on IE (#2968).
	isFunction: function( obj ) {
		return toString.call(obj) === "[object Function]";
	},

	isArray: function( obj ) {
		return toString.call(obj) === "[object Array]";
	},

	// check if an element is in a (or is an) XML document
	isXMLDoc: function( elem ) {
		return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
			!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
	},

	// Evalulates a script in a global context
	globalEval: function( data ) {
		if ( data && /\S/.test(data) ) {
			// Inspired by code by Andrea Giammarchi
			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
			var head = document.getElementsByTagName("head")[0] || document.documentElement,
				script = document.createElement("script");

			script.type = "text/javascript";
			if ( jQuery.support.scriptEval )
				script.appendChild( document.createTextNode( data ) );
			else
				script.text = data;

			// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
			// This arises when a base node is used (#2709).
			head.insertBefore( script, head.firstChild );
			head.removeChild( script );
		}
	},

	nodeName: function( elem, name ) {
		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
	},

	// args is for internal usage only
	each: function( object, callback, args ) {
		var name, i = 0, length = object.length;

		if ( args ) {
			if ( length === undefined ) {
				for ( name in object )
					if ( callback.apply( object[ name ], args ) === false )
						break;
			} else
				for ( ; i < length; )
					if ( callback.apply( object[ i++ ], args ) === false )
						break;

		// A special, fast, case for the most common use of each
		} else {
			if ( length === undefined ) {
				for ( name in object )
					if ( callback.call( object[ name ], name, object[ name ] ) === false )
						break;
			} else
				for ( var value = object[0];
					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
		}

		return object;
	},

	prop: function( elem, value, type, i, name ) {
		// Handle executable functions
		if ( jQuery.isFunction( value ) )
			value = value.call( elem, i );

		// Handle passing in a number to a CSS property
		return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
			value + "px" :
			value;
	},

	className: {
		// internal only, use addClass("class")
		add: function( elem, classNames ) {
			jQuery.each((classNames || "").split(/\s+/), function(i, className){
				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
					elem.className += (elem.className ? " " : "") + className;
			});
		},

		// internal only, use removeClass("class")
		remove: function( elem, classNames ) {
			if (elem.nodeType == 1)
				elem.className = classNames !== undefined ?
					jQuery.grep(elem.className.split(/\s+/), function(className){
						return !jQuery.className.has( classNames, className );
					}).join(" ") :
					"";
		},

		// internal only, use hasClass("class")
		has: function( elem, className ) {
			return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
		}
	},

	// A method for quickly swapping in/out CSS properties to get correct calculations
	swap: function( elem, options, callback ) {
		var old = {};
		// Remember the old values, and insert the new ones
		for ( var name in options ) {
			old[ name ] = elem.style[ name ];
			elem.style[ name ] = options[ name ];
		}

		callback.call( elem );

		// Revert the old values
		for ( var name in options )
			elem.style[ name ] = old[ name ];
	},

	css: function( elem, name, force, extra ) {
		if ( name == "width" || name == "height" ) {
			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];

			function getWH() {
				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;

				if ( extra === "border" )
					return;

				jQuery.each( which, function() {
					if ( !extra )
						val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
					if ( extra === "margin" )
						val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
					else
						val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
				});
			}

			if ( elem.offsetWidth !== 0 )
				getWH();
			else
				jQuery.swap( elem, props, getWH );

			return Math.max(0, Math.round(val));
		}

		return jQuery.curCSS( elem, name, force );
	},

	curCSS: function( elem, name, force ) {
		var ret, style = elem.style;

		// We need to handle opacity special in IE
		if ( name == "opacity" && !jQuery.support.opacity ) {
			ret = jQuery.attr( style, "opacity" );

			return ret == "" ?
				"1" :
				ret;
		}

		// Make sure we're using the right name for getting the float value
		if ( name.match( /float/i ) )
			name = styleFloat;

		if ( !force && style && style[ name ] )
			ret = style[ name ];

		else if ( defaultView.getComputedStyle ) {

			// Only "float" is needed here
			if ( name.match( /float/i ) )
				name = "float";

			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();

			var computedStyle = defaultView.getComputedStyle( elem, null );

			if ( computedStyle )
				ret = computedStyle.getPropertyValue( name );

			// We should always get a number back from opacity
			if ( name == "opacity" && ret == "" )
				ret = "1";

		} else if ( elem.currentStyle ) {
			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
				return letter.toUpperCase();
			});

			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];

			// From the awesome hack by Dean Edwards
			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

			// If we're not dealing with a regular pixel number
			// but a number that has a weird ending, we need to convert it to pixels
			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
				// Remember the original values
				var left = style.left, rsLeft = elem.runtimeStyle.left;

				// Put in the new values to get a computed value out
				elem.runtimeStyle.left = elem.currentStyle.left;
				style.left = ret || 0;
				ret = style.pixelLeft + "px";

				// Revert the changed values
				style.left = left;
				elem.runtimeStyle.left = rsLeft;
			}
		}

		return ret;
	},

	clean: function( elems, context, fragment ) {
		context = context || document;

		// !context.createElement fails in IE with an error but returns typeof 'object'
		if ( typeof context.createElement === "undefined" )
			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;

		// If a single string is passed in and it's a single tag
		// just do a createElement and skip the rest
		if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
			var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
			if ( match )
				return [ context.createElement( match[1] ) ];
		}

		var ret = [], scripts = [], div = context.createElement("div");

		jQuery.each(elems, function(i, elem){
			if ( typeof elem === "number" )
				elem += '';

			if ( !elem )
				return;

			// Convert html string into DOM nodes
			if ( typeof elem === "string" ) {
				// Fix "XHTML"-style tags in all browsers
				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
						all :
						front + "></" + tag + ">";
				});

				// Trim whitespace, otherwise indexOf won't work as expected
				var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();

				var wrap =
					// option or optgroup
					!tags.indexOf("<opt") &&
					[ 1, "<select multiple='multiple'>", "</select>" ] ||

					!tags.indexOf("<leg") &&
					[ 1, "<fieldset>", "</fieldset>" ] ||

					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
					[ 1, "<table>", "</table>" ] ||

					!tags.indexOf("<tr") &&
					[ 2, "<table><tbody>", "</tbody></table>" ] ||

				 	// <thead> matched above
					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||

					!tags.indexOf("<col") &&
					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||

					// IE can't serialize <link> and <script> tags normally
					!jQuery.support.htmlSerialize &&
					[ 1, "div<div>", "</div>" ] ||

					[ 0, "", "" ];

				// Go to html and back, then peel off extra wrappers
				div.innerHTML = wrap[1] + elem + wrap[2];

				// Move to the right depth
				while ( wrap[0]-- )
					div = div.lastChild;

				// Remove IE's autoinserted <tbody> from table fragments
				if ( !jQuery.support.tbody ) {

					// String was a <table>, *may* have spurious <tbody>
					var hasBody = /<tbody/i.test(elem),
						tbody = !tags.indexOf("<table") && !hasBody ?
							div.firstChild && div.firstChild.childNodes :

						// String was a bare <thead> or <tfoot>
						wrap[1] == "<table>" && !hasBody ?
							div.childNodes :
							[];

					for ( var j = tbody.length - 1; j >= 0 ; --j )
						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
							tbody[ j ].parentNode.removeChild( tbody[ j ] );

					}

				// IE completely kills leading whitespace when innerHTML is used
				if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
					div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );

				elem = jQuery.makeArray( div.childNodes );
			}

			if ( elem.nodeType )
				ret.push( elem );
			else
				ret = jQuery.merge( ret, elem );

		});

		if ( fragment ) {
			for ( var i = 0; ret[i]; i++ ) {
				if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
				} else {
					if ( ret[i].nodeType === 1 )
						ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
					fragment.appendChild( ret[i] );
				}
			}

			return scripts;
		}

		return ret;
	},

	attr: function( elem, name, value ) {
		// don't set attributes on text and comment nodes
		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
			return undefined;

		var notxml = !jQuery.isXMLDoc( elem ),
			// Whether we are setting (or getting)
			set = value !== undefined;

		// Try to normalize/fix the name
		name = notxml && jQuery.props[ name ] || name;

		// Only do all the following if this is a node (faster for style)
		// IE elem.getAttribute passes even for style
		if ( elem.tagName ) {

			// These attributes require special treatment
			var special = /href|src|style/.test( name );

			// Safari mis-reports the default selected property of a hidden option
			// Accessing the parent's selectedIndex property fixes it
			if ( name == "selected" && elem.parentNode )
				elem.parentNode.selectedIndex;

			// If applicable, access the attribute via the DOM 0 way
			if ( name in elem && notxml && !special ) {
				if ( set ){
					// We can't allow the type property to be changed (since it causes problems in IE)
					if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
						throw "type property can't be changed";

					elem[ name ] = value;
				}

				// browsers index elements by id/name on forms, give priority to attributes.
				if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
					return elem.getAttributeNode( name ).nodeValue;

				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				if ( name == "tabIndex" ) {
					var attributeNode = elem.getAttributeNode( "tabIndex" );
					return attributeNode && attributeNode.specified
						? attributeNode.value
						: elem.nodeName.match(/(button|input|object|select|textarea)/i)
							? 0
							: elem.nodeName.match(/^(a|area)$/i) && elem.href
								? 0
								: undefined;
				}

				return elem[ name ];
			}

			if ( !jQuery.support.style && notxml &&  name == "style" )
				return jQuery.attr( elem.style, "cssText", value );

			if ( set )
				// convert the value to a string (all browsers do this but IE) see #1070
				elem.setAttribute( name, "" + value );

			var attr = !jQuery.support.hrefNormalized && notxml && special
					// Some attributes require a special call on IE
					? elem.getAttribute( name, 2 )
					: elem.getAttribute( name );

			// Non-existent attributes return null, we normalize to undefined
			return attr === null ? undefined : attr;
		}

		// elem is actually elem.style ... set the style

		// IE uses filters for opacity
		if ( !jQuery.support.opacity && name == "opacity" ) {
			if ( set ) {
				// IE has trouble with opacity if it does not have layout
				// Force it by setting the zoom level
				elem.zoom = 1;

				// Set the alpha filter to set the opacity
				elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
					(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
			}

			return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
				(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
				"";
		}

		name = name.replace(/-([a-z])/ig, function(all, letter){
			return letter.toUpperCase();
		});

		if ( set )
			elem[ name ] = value;

		return elem[ name ];
	},

	trim: function( text ) {
		return (text || "").replace( /^\s+|\s+$/g, "" );
	},

	makeArray: function( array ) {
		var ret = [];

		if( array != null ){
			var i = array.length;
			// The window, strings (and functions) also have 'length'
			if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
				ret[0] = array;
			else
				while( i )
					ret[--i] = array[i];
		}

		return ret;
	},

	inArray: function( elem, array ) {
		for ( var i = 0, length = array.length; i < length; i++ )
		// Use === because on IE, window == document
			if ( array[ i ] === elem )
				return i;

		return -1;
	},

	merge: function( first, second ) {
		// We have to loop this way because IE & Opera overwrite the length
		// expando of getElementsByTagName
		var i = 0, elem, pos = first.length;
		// Also, we need to make sure that the correct elements are being returned
		// (IE returns comment nodes in a '*' query)
		if ( !jQuery.support.getAll ) {
			while ( (elem = second[ i++ ]) != null )
				if ( elem.nodeType != 8 )
					first[ pos++ ] = elem;

		} else
			while ( (elem = second[ i++ ]) != null )
				first[ pos++ ] = elem;

		return first;
	},

	unique: function( array ) {
		var ret = [], done = {};

		try {

			for ( var i = 0, length = array.length; i < length; i++ ) {
				var id = jQuery.data( array[ i ] );

				if ( !done[ id ] ) {
					done[ id ] = true;
					ret.push( array[ i ] );
				}
			}

		} catch( e ) {
			ret = array;
		}

		return ret;
	},

	grep: function( elems, callback, inv ) {
		var ret = [];

		// Go through the array, only saving the items
		// that pass the validator function
		for ( var i = 0, length = elems.length; i < length; i++ )
			if ( !inv != !callback( elems[ i ], i ) )
				ret.push( elems[ i ] );

		return ret;
	},

	map: function( elems, callback ) {
		var ret = [];

		// Go through the array, translating each of the items to their
		// new value (or values).
		for ( var i = 0, length = elems.length; i < length; i++ ) {
			var value = callback( elems[ i ], i );

			if ( value != null )
				ret[ ret.length ] = value;
		}

		return ret.concat.apply( [], ret );
	}
});

// Use of jQuery.browser is deprecated.
// It's included for backwards compatibility and plugins,
// although they should work to migrate away.

var userAgent = navigator.userAgent.toLowerCase();

// Figure out what browser is being used
jQuery.browser = {
	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
	safari: /webkit/.test( userAgent ),
	opera: /opera/.test( userAgent ),
	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
};

jQuery.each({
	parent: function(elem){return elem.parentNode;},
	parents: function(elem){return jQuery.dir(elem,"parentNode");},
	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
	children: function(elem){return jQuery.sibling(elem.firstChild);},
	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
}, function(name, fn){
	jQuery.fn[ name ] = function( selector ) {
		var ret = jQuery.map( this, fn );

		if ( selector && typeof selector == "string" )
			ret = jQuery.multiFilter( selector, ret );

		return this.pushStack( jQuery.unique( ret ), name, selector );
	};
});

jQuery.each({
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after",
	replaceAll: "replaceWith"
}, function(name, original){
	jQuery.fn[ name ] = function( selector ) {
		var ret = [], insert = jQuery( selector );

		for ( var i = 0, l = insert.length; i < l; i++ ) {
			var elems = (i > 0 ? this.clone(true) : this).get();
			jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
			ret = ret.concat( elems );
		}

		return this.pushStack( ret, name, selector );
	};
});

jQuery.each({
	removeAttr: function( name ) {
		jQuery.attr( this, name, "" );
		if (this.nodeType == 1)
			this.removeAttribute( name );
	},

	addClass: function( classNames ) {
		jQuery.className.add( this, classNames );
	},

	removeClass: function( classNames ) {
		jQuery.className.remove( this, classNames );
	},

	toggleClass: function( classNames, state ) {
		if( typeof state !== "boolean" )
			state = !jQuery.className.has( this, classNames );
		jQuery.className[ state ? "add" : "remove" ]( this, classNames );
	},

	remove: function( selector ) {
		if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
			// Prevent memory leaks
			jQuery( "*", this ).add([this]).each(function(){
				jQuery.event.remove(this);
				jQuery.removeData(this);
			});
			if (this.parentNode)
				this.parentNode.removeChild( this );
		}
	},

	empty: function() {
		// Remove element nodes and prevent memory leaks
		jQuery(this).children().remove();

		// Remove any remaining nodes
		while ( this.firstChild )
			this.removeChild( this.firstChild );
	}
}, function(name, fn){
	jQuery.fn[ name ] = function(){
		return this.each( fn, arguments );
	};
});

// Helper function used by the dimensions and offset modules
function num(elem, prop) {
	return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
}
var expando = "jQuery" + now(), uuid = 0, windowData = {};

jQuery.extend({
	cache: {},

	data: function( elem, name, data ) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ];

		// Compute a unique ID for the element
		if ( !id )
			id = elem[ expando ] = ++uuid;

		// Only generate the data cache if we're
		// trying to access or manipulate it
		if ( name && !jQuery.cache[ id ] )
			jQuery.cache[ id ] = {};

		// Prevent overriding the named cache with undefined values
		if ( data !== undefined )
			jQuery.cache[ id ][ name ] = data;

		// Return the named cache data, or the ID for the element
		return name ?
			jQuery.cache[ id ][ name ] :
			id;
	},

	removeData: function( elem, name ) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ];

		// If we want to remove a specific section of the element's data
		if ( name ) {
			if ( jQuery.cache[ id ] ) {
				// Remove the section of cache data
				delete jQuery.cache[ id ][ name ];

				// If we've removed all the data, remove the element's cache
				name = "";

				for ( name in jQuery.cache[ id ] )
					break;

				if ( !name )
					jQuery.removeData( elem );
			}

		// Otherwise, we want to remove all of the element's data
		} else {
			// Clean up the element expando
			try {
				delete elem[ expando ];
			} catch(e){
				// IE has trouble directly removing the expando
				// but it's ok with using removeAttribute
				if ( elem.removeAttribute )
					elem.removeAttribute( expando );
			}

			// Completely remove the data cache
			delete jQuery.cache[ id ];
		}
	},
	queue: function( elem, type, data ) {
		if ( elem ){

			type = (type || "fx") + "queue";

			var q = jQuery.data( elem, type );

			if ( !q || jQuery.isArray(data) )
				q = jQuery.data( elem, type, jQuery.makeArray(data) );
			else if( data )
				q.push( data );

		}
		return q;
	},

	dequeue: function( elem, type ){
		var queue = jQuery.queue( elem, type ),
			fn = queue.shift();

		if( !type || type === "fx" )
			fn = queue[0];

		if( fn !== undefined )
			fn.call(elem);
	}
});

jQuery.fn.extend({
	data: function( key, value ){
		var parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			if ( data === undefined && this.length )
				data = jQuery.data( this[0], key );

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;
		} else
			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
				jQuery.data( this, key, value );
			});
	},

	removeData: function( key ){
		return this.each(function(){
			jQuery.removeData( this, key );
		});
	},
	queue: function(type, data){
		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
		}

		if ( data === undefined )
			return jQuery.queue( this[0], type );

		return this.each(function(){
			var queue = jQuery.queue( this, type, data );

			 if( type == "fx" && queue.length == 1 )
				queue[0].call(this);
		});
	},
	dequeue: function(type){
		return this.each(function(){
			jQuery.dequeue( this, type );
		});
	}
});/*!
 * Sizzle CSS Selector Engine - v0.9.3
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
	done = 0,
	toString = Object.prototype.toString;

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	context = context || document;

	if ( context.nodeType !== 1 && context.nodeType !== 9 )
		return [];

	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, check, mode, extra, prune = true;

	// Reset the position of the chunker regexp (start from head)
	chunker.lastIndex = 0;

	while ( (m = chunker.exec(selector)) !== null ) {
		parts.push( m[1] );

		if ( m[2] ) {
			extra = RegExp.rightContext;
			break;
		}
	}

	if ( parts.length > 1 && origPOS.exec( selector ) ) {
		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );
		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] )
					selector += parts.shift();

				set = posProcess( selector, set );
			}
		}
	} else {
		var ret = seed ?
			{ expr: parts.pop(), set: makeArray(seed) } :
			Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
		set = Sizzle.filter( ret.expr, ret.set );

		if ( parts.length > 0 ) {
			checkSet = makeArray(set);
		} else {
			prune = false;
		}

		while ( parts.length ) {
			var cur = parts.pop(), pop = cur;

			if ( !Expr.relative[ cur ] ) {
				cur = "";
			} else {
				pop = parts.pop();
			}

			if ( pop == null ) {
				pop = context;
			}

			Expr.relative[ cur ]( checkSet, pop, isXML(context) );
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		throw "Syntax error, unrecognized expression: " + (cur || selector);
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );
		} else if ( context.nodeType === 1 ) {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}
	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, context, results, seed );

		if ( sortOrder ) {
			hasDuplicate = false;
			results.sort(sortOrder);

			if ( hasDuplicate ) {
				for ( var i = 1; i < results.length; i++ ) {
					if ( results[i] === results[i-1] ) {
						results.splice(i--, 1);
					}
				}
			}
		}
	}

	return results;
};

Sizzle.matches = function(expr, set){
	return Sizzle(expr, null, null, set);
};

Sizzle.find = function(expr, context, isXML){
	var set, match;

	if ( !expr ) {
		return [];
	}

	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
		var type = Expr.order[i], match;

		if ( (match = Expr.match[ type ].exec( expr )) ) {
			var left = RegExp.leftContext;

			if ( left.substr( left.length - 1 ) !== "\\" ) {
				match[1] = (match[1] || "").replace(/\\/g, "");
				set = Expr.find[ type ]( match, context, isXML );
				if ( set != null ) {
					expr = expr.replace( Expr.match[ type ], "" );
					break;
				}
			}
		}
	}

	if ( !set ) {
		set = context.getElementsByTagName("*");
	}

	return {set: set, expr: expr};
};

Sizzle.filter = function(expr, set, inplace, not){
	var old = expr, result = [], curLoop = set, match, anyFound,
		isXMLFilter = set && set[0] && isXML(set[0]);

	while ( expr && set.length ) {
		for ( var type in Expr.filter ) {
			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
				var filter = Expr.filter[ type ], found, item;
				anyFound = false;

				if ( curLoop == result ) {
					result = [];
				}

				if ( Expr.preFilter[ type ] ) {
					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

					if ( !match ) {
						anyFound = found = true;
					} else if ( match === true ) {
						continue;
					}
				}

				if ( match ) {
					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
						if ( item ) {
							found = filter( item, match, i, curLoop );
							var pass = not ^ !!found;

							if ( inplace && found != null ) {
								if ( pass ) {
									anyFound = true;
								} else {
									curLoop[i] = false;
								}
							} else if ( pass ) {
								result.push( item );
								anyFound = true;
							}
						}
					}
				}

				if ( found !== undefined ) {
					if ( !inplace ) {
						curLoop = result;
					}

					expr = expr.replace( Expr.match[ type ], "" );

					if ( !anyFound ) {
						return [];
					}

					break;
				}
			}
		}

		// Improper expression
		if ( expr == old ) {
			if ( anyFound == null ) {
				throw "Syntax error, unrecognized expression: " + expr;
			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],
	match: {
		ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
	},
	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
		"+": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag && !isXML ) {
				part = part.toUpperCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
		">": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = isXML ? part : part.toUpperCase();

				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName === part ? parent : false;
					}
				}
			} else {
				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
		"": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
		}
	},
	find: {
		ID: function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? [m] : [];
			}
		},
		NAME: function(match, context, isXML){
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [], results = context.getElementsByName(match[1]);

				for ( var i = 0, l = results.length; i < l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},
		TAG: function(match, context){
			return context.getElementsByTagName(match[1]);
		}
	},
	preFilter: {
		CLASS: function(match, curLoop, inplace, result, not, isXML){
			match = " " + match[1].replace(/\\/g, "") + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
						if ( !inplace )
							result.push( elem );
					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},
		ID: function(match){
			return match[1].replace(/\\/g, "");
		},
		TAG: function(match, curLoop){
			for ( var i = 0; curLoop[i] === false; i++ ){}
			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
		},
		CHILD: function(match){
			if ( match[1] == "nth" ) {
				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				// calculate the numbers (first)n+(last) including if they are negative
				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}

			// TODO: Move to normal caching system
			match[0] = done++;

			return match;
		},
		ATTR: function(match, curLoop, inplace, result, not, isXML){
			var name = match[1].replace(/\\/g, "");

			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},
		PSEUDO: function(match, curLoop, inplace, result, not){
			if ( match[1] === "not" ) {
				// If we're dealing with a complex expression, or a simple one
				if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);
				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
					if ( !inplace ) {
						result.push.apply( result, ret );
					}
					return false;
				}
			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}

			return match;
		},
		POS: function(match){
			match.unshift( true );
			return match;
		}
	},
	filters: {
		enabled: function(elem){
			return elem.disabled === false && elem.type !== "hidden";
		},
		disabled: function(elem){
			return elem.disabled === true;
		},
		checked: function(elem){
			return elem.checked === true;
		},
		selected: function(elem){
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			elem.parentNode.selectedIndex;
			return elem.selected === true;
		},
		parent: function(elem){
			return !!elem.firstChild;
		},
		empty: function(elem){
			return !elem.firstChild;
		},
		has: function(elem, i, match){
			return !!Sizzle( match[3], elem ).length;
		},
		header: function(elem){
			return /h\d/i.test( elem.nodeName );
		},
		text: function(elem){
			return "text" === elem.type;
		},
		radio: function(elem){
			return "radio" === elem.type;
		},
		checkbox: function(elem){
			return "checkbox" === elem.type;
		},
		file: function(elem){
			return "file" === elem.type;
		},
		password: function(elem){
			return "password" === elem.type;
		},
		submit: function(elem){
			return "submit" === elem.type;
		},
		image: function(elem){
			return "image" === elem.type;
		},
		reset: function(elem){
			return "reset" === elem.type;
		},
		button: function(elem){
			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
		},
		input: function(elem){
			return /input|select|textarea|button/i.test(elem.nodeName);
		}
	},
	setFilters: {
		first: function(elem, i){
			return i === 0;
		},
		last: function(elem, i, match, array){
			return i === array.length - 1;
		},
		even: function(elem, i){
			return i % 2 === 0;
		},
		odd: function(elem, i){
			return i % 2 === 1;
		},
		lt: function(elem, i, match){
			return i < match[3] - 0;
		},
		gt: function(elem, i, match){
			return i > match[3] - 0;
		},
		nth: function(elem, i, match){
			return match[3] - 0 == i;
		},
		eq: function(elem, i, match){
			return match[3] - 0 == i;
		}
	},
	filter: {
		PSEUDO: function(elem, match, i, array){
			var name = match[1], filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var i = 0, l = not.length; i < l; i++ ) {
					if ( not[i] === elem ) {
						return false;
					}
				}

				return true;
			}
		},
		CHILD: function(elem, match){
			var type = match[1], node = elem;
			switch (type) {
				case 'only':
				case 'first':
					while (node = node.previousSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					if ( type == 'first') return true;
					node = elem;
				case 'last':
					while (node = node.nextSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					return true;
				case 'nth':
					var first = match[2], last = match[3];

					if ( first == 1 && last == 0 ) {
						return true;
					}

					var doneName = match[0],
						parent = elem.parentNode;

					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;
							}
						}
						parent.sizcache = doneName;
					}

					var diff = elem.nodeIndex - last;
					if ( first == 0 ) {
						return diff == 0;
					} else {
						return ( diff % first == 0 && diff / first >= 0 );
					}
			}
		},
		ID: function(elem, match){
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},
		TAG: function(elem, match){
			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value != check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;

for ( var type in Expr.match ) {
	Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}

var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}

	return array;
};

// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
try {
	Array.prototype.slice.call( document.documentElement.childNodes );

// Provide a fallback method if it does not work
} catch(e){
	makeArray = function(array, results) {
		var ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );
		} else {
			if ( typeof array.length === "number" ) {
				for ( var i = 0, l = array.length; i < l; i++ ) {
					ret.push( array[i] );
				}
			} else {
				for ( var i = 0; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

var sortOrder;

if ( document.documentElement.compareDocumentPosition ) {
	sortOrder = function( a, b ) {
		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( "sourceIndex" in document.documentElement ) {
	sortOrder = function( a, b ) {
		var ret = a.sourceIndex - b.sourceIndex;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( document.createRange ) {
	sortOrder = function( a, b ) {
		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
		aRange.selectNode(a);
		aRange.collapse(true);
		bRange.selectNode(b);
		bRange.collapse(true);
		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
}

// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
	// We're going to inject a fake input element with a specified name
	var form = document.createElement("form"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<input name='" + id + "'/>";

	// Inject it into the root element, check its status, and remove it quickly
	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	// The workaround has to do additional checks after a getElementById
	// Which slows things down for other browsers (hence the branching)
	if ( !!document.getElementById( id ) ) {
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
})();

(function(){
	// Check to see if the browser returns only elements
	// when doing getElementsByTagName("*")

	// Create a fake element
	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	// Make sure no comments are found
	if ( div.getElementsByTagName("*").length > 0 ) {
		Expr.find.TAG = function(match, context){
			var results = context.getElementsByTagName(match[1]);

			// Filter out possible comments
			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	// Check to see if an attribute returns normalized href attributes
	div.innerHTML = "<a href='#'></a>";
	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {
		Expr.attrHandle.href = function(elem){
			return elem.getAttribute("href", 2);
		};
	}
})();

if ( document.querySelectorAll ) (function(){
	var oldSizzle = Sizzle, div = document.createElement("div");
	div.innerHTML = "<p class='TEST'></p>";

	// Safari can't handle uppercase or unicode characters when
	// in quirks mode.
	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
		return;
	}

	Sizzle = function(query, context, extra, seed){
		context = context || document;

		// Only use querySelectorAll on non-XML documents
		// (ID selectors don't work in non-HTML documents)
		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
			try {
				return makeArray( context.querySelectorAll(query), extra );
			} catch(e){}
		}

		return oldSizzle(query, context, extra, seed);
	};

	Sizzle.find = oldSizzle.find;
	Sizzle.filter = oldSizzle.filter;
	Sizzle.selectors = oldSizzle.selectors;
	Sizzle.matches = oldSizzle.matches;
})();

if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
	var div = document.createElement("div");
	div.innerHTML = "<div class='test e'></div><div class='test'></div>";

	// Opera can't find a second classname (in 9.6)
	if ( div.getElementsByClassName("e").length === 0 )
		return;

	// Safari caches class attributes, doesn't catch changes (in 3.2)
	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 )
		return;

	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function(match, context, isXML) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ){
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ) {
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}
					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

var contains = document.compareDocumentPosition ?  function(a, b){
	return a.compareDocumentPosition(b) & 16;
} : function(a, b){
	return a !== b && (a.contains ? a.contains(b) : true);
};

var isXML = function(elem){
	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
		!!elem.ownerDocument && isXML( elem.ownerDocument );
};

var posProcess = function(selector, context){
	var tmpSet = [], later = "", match,
		root = context.nodeType ? [context] : context;

	// Position selectors must be done after the filter
	// And so must :not(positional) so we move all PSEUDOs to the end
	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
		later += match[0];
		selector = selector.replace( Expr.match.PSEUDO, "" );
	}

	selector = Expr.relative[selector] ? selector + "*" : selector;

	for ( var i = 0, l = root.length; i < l; i++ ) {
		Sizzle( selector, root[i], tmpSet );
	}

	return Sizzle.filter( later, tmpSet );
};

// EXPOSE
jQuery.find = Sizzle;
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;

Sizzle.selectors.filters.hidden = function(elem){
	return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};

Sizzle.selectors.filters.visible = function(elem){
	return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};

Sizzle.selectors.filters.animated = function(elem){
	return jQuery.grep(jQuery.timers, function(fn){
		return elem === fn.elem;
	}).length;
};

jQuery.multiFilter = function( expr, elems, not ) {
	if ( not ) {
		expr = ":not(" + expr + ")";
	}

	return Sizzle.matches(expr, elems);
};

jQuery.dir = function( elem, dir ){
	var matched = [], cur = elem[dir];
	while ( cur && cur != document ) {
		if ( cur.nodeType == 1 )
			matched.push( cur );
		cur = cur[dir];
	}
	return matched;
};

jQuery.nth = function(cur, result, dir, elem){
	result = result || 1;
	var num = 0;

	for ( ; cur; cur = cur[dir] )
		if ( cur.nodeType == 1 && ++num == result )
			break;

	return cur;
};

jQuery.sibling = function(n, elem){
	var r = [];

	for ( ; n; n = n.nextSibling ) {
		if ( n.nodeType == 1 && n != elem )
			r.push( n );
	}

	return r;
};

return;

window.Sizzle = Sizzle;

})();
/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code originated from
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

	// Bind an event to an element
	// Original by Dean Edwards
	add: function(elem, types, handler, data) {
		if ( elem.nodeType == 3 || elem.nodeType == 8 )
			return;

		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( elem.setInterval && elem != window )
			elem = window;

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid )
			handler.guid = this.guid++;

		// if data is passed, bind to handler
		if ( data !== undefined ) {
			// Create temporary function pointer to original handler
			var fn = handler;

			// Create unique handler function, wrapped around original handler
			handler = this.proxy( fn );

			// Store data in unique handler
			handler.data = data;
		}

		// Init the element's event structure
		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
				// Handle the second event of a trigger and when
				// an event is called after a page has unloaded
				return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
					jQuery.event.handle.apply(arguments.callee.elem, arguments) :
					undefined;
			});
		// Add elem as a property of the handle function
		// This is to prevent a memory leak with non-native
		// event in IE.
		handle.elem = elem;

		// Handle multiple events separated by a space
		// jQuery(...).bind("mouseover mouseout", fn);
		jQuery.each(types.split(/\s+/), function(index, type) {
			// Namespaced event handlers
			var namespaces = type.split(".");
			type = namespaces.shift();
			handler.type = namespaces.slice().sort().join(".");

			// Get the current list of functions bound to this event
			var handlers = events[type];

			if ( jQuery.event.specialAll[type] )
				jQuery.event.specialAll[type].setup.call(elem, data, namespaces);

			// Init the event handler queue
			if (!handlers) {
				handlers = events[type] = {};

				// Check for a special event handler
				// Only use addEventListener/attachEvent if the special
				// events handler returns false
				if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
					// Bind the global event handler to the element
					if (elem.addEventListener)
						elem.addEventListener(type, handle, false);
					else if (elem.attachEvent)
						elem.attachEvent("on" + type, handle);
				}
			}

			// Add the function to the element's handler list
			handlers[handler.guid] = handler;

			// Keep track of which events have been used, for global triggering
			jQuery.event.global[type] = true;
		});

		// Nullify elem to prevent memory leaks in IE
		elem = null;
	},

	guid: 1,
	global: {},

	// Detach an event or set of events from an element
	remove: function(elem, types, handler) {
		// don't do events on text and comment nodes
		if ( elem.nodeType == 3 || elem.nodeType == 8 )
			return;

		var events = jQuery.data(elem, "events"), ret, index;

		if ( events ) {
			// Unbind all events for the element
			if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
				for ( var type in events )
					this.remove( elem, type + (types || "") );
			else {
				// types is actually an event object here
				if ( types.type ) {
					handler = types.handler;
					types = types.type;
				}

				// Handle multiple events seperated by a space
				// jQuery(...).unbind("mouseover mouseout", fn);
				jQuery.each(types.split(/\s+/), function(index, type){
					// Namespaced event handlers
					var namespaces = type.split(".");
					type = namespaces.shift();
					var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");

					if ( events[type] ) {
						// remove the given handler for the given type
						if ( handler )
							delete events[type][handler.guid];

						// remove all handlers for the given type
						else
							for ( var handle in events[type] )
								// Handle the removal of namespaced events
								if ( namespace.test(events[type][handle].type) )
									delete events[type][handle];

						if ( jQuery.event.specialAll[type] )
							jQuery.event.specialAll[type].teardown.call(elem, namespaces);

						// remove generic event handler if no more handlers exist
						for ( ret in events[type] ) break;
						if ( !ret ) {
							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
								if (elem.removeEventListener)
									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
								else if (elem.detachEvent)
									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
							}
							ret = null;
							delete events[type];
						}
					}
				});
			}

			// Remove the expando if it's no longer used
			for ( ret in events ) break;
			if ( !ret ) {
				var handle = jQuery.data( elem, "handle" );
				if ( handle ) handle.elem = null;
				jQuery.removeData( elem, "events" );
				jQuery.removeData( elem, "handle" );
			}
		}
	},

	// bubbling is internal
	trigger: function( event, data, elem, bubbling ) {
		// Event object or event type
		var type = event.type || event;

		if( !bubbling ){
			event = typeof event === "object" ?
				// jQuery.Event object
				event[expando] ? event :
				// Object literal
				jQuery.extend( jQuery.Event(type), event ) :
				// Just the event type (string)
				jQuery.Event(type);

			if ( type.indexOf("!") >= 0 ) {
				event.type = type = type.slice(0, -1);
				event.exclusive = true;
			}

			// Handle a global trigger
			if ( !elem ) {
				// Don't bubble custom events when global (to avoid too much overhead)
				event.stopPropagation();
				// Only trigger if we've ever bound an event for it
				if ( this.global[type] )
					jQuery.each( jQuery.cache, function(){
						if ( this.events && this.events[type] )
							jQuery.event.trigger( event, data, this.handle.elem );
					});
			}

			// Handle triggering a single element

			// don't do events on text and comment nodes
			if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
				return undefined;

			// Clean up in case it is reused
			event.result = undefined;
			event.target = elem;

			// Clone the incoming data, if any
			data = jQuery.makeArray(data);
			data.unshift( event );
		}

		event.currentTarget = elem;

		// Trigger the event, it is assumed that "handle" is a function
		var handle = jQuery.data(elem, "handle");
		if ( handle )
			handle.apply( elem, data );

		// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
		if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
			event.result = false;

		// Trigger the native events (except for clicks on links)
		if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
			this.triggered = true;
			try {
				elem[ type ]();
			// prevent IE from throwing an error for some hidden elements
			} catch (e) {}
		}

		this.triggered = false;

		if ( !event.isPropagationStopped() ) {
			var parent = elem.parentNode || elem.ownerDocument;
			if ( parent )
				jQuery.event.trigger(event, data, parent, true);
		}
	},

	handle: function(event) {
		// returned undefined or false
		var all, handlers;

		event = arguments[0] = jQuery.event.fix( event || window.event );
		event.currentTarget = this;

		// Namespaced event handlers
		var namespaces = event.type.split(".");
		event.type = namespaces.shift();

		// Cache this now, all = true means, any handler
		all = !namespaces.length && !event.exclusive;

		var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");

		handlers = ( jQuery.data(this, "events") || {} )[event.type];

		for ( var j in handlers ) {
			var handler = handlers[j];

			// Filter the functions by class
			if ( all || namespace.test(handler.type) ) {
				// Pass in a reference to the handler function itself
				// So that we can later remove it
				event.handler = handler;
				event.data = handler.data;

				var ret = handler.apply(this, arguments);

				if( ret !== undefined ){
					event.result = ret;
					if ( ret === false ) {
						event.preventDefault();
						event.stopPropagation();
					}
				}

				if( event.isImmediatePropagationStopped() )
					break;

			}
		}
	},

	props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),

	fix: function(event) {
		if ( event[expando] )
			return event;

		// store a copy of the original event object
		// and "clone" to set read-only properties
		var originalEvent = event;
		event = jQuery.Event( originalEvent );

		for ( var i = this.props.length, prop; i; ){
			prop = this.props[ --i ];
			event[ prop ] = originalEvent[ prop ];
		}

		// Fix target property, if necessary
		if ( !event.target )
			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either

		// check if target is a textnode (safari)
		if ( event.target.nodeType == 3 )
			event.target = event.target.parentNode;

		// Add relatedTarget, if necessary
		if ( !event.relatedTarget && event.fromElement )
			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == null && event.clientX != null ) {
			var doc = document.documentElement, body = document.body;
			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
		}

		// Add which for key events
		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
			event.which = event.charCode || event.keyCode;

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if ( !event.metaKey && event.ctrlKey )
			event.metaKey = event.ctrlKey;

		// Add which for click: 1 == left; 2 == middle; 3 == right
		// Note: button is not normalized, so don't use it
		if ( !event.which && event.button )
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));

		return event;
	},

	proxy: function( fn, proxy ){
		proxy = proxy || function(){ return fn.apply(this, arguments); };
		// Set the guid of unique handler to the same of original handler, so it can be removed
		proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
		// So proxy can be declared as an argument
		return proxy;
	},

	special: {
		ready: {
			// Make sure the ready event is setup
			setup: bindReady,
			teardown: function() {}
		}
	},

	specialAll: {
		live: {
			setup: function( selector, namespaces ){
				jQuery.event.add( this, namespaces[0], liveHandler );
			},
			teardown:  function( namespaces ){
				if ( namespaces.length ) {
					var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");

					jQuery.each( (jQuery.data(this, "events").live || {}), function(){
						if ( name.test(this.type) )
							remove++;
					});

					if ( remove < 1 )
						jQuery.event.remove( this, namespaces[0], liveHandler );
				}
			}
		}
	}
};

jQuery.Event = function( src ){
	// Allow instantiation without the 'new' keyword
	if( !this.preventDefault )
		return new jQuery.Event(src);

	// Event object
	if( src && src.type ){
		this.originalEvent = src;
		this.type = src.type;
	// Event type
	}else
		this.type = src;

	// timeStamp is buggy for some events on Firefox(#3843)
	// So we won't rely on the native value
	this.timeStamp = now();

	// Mark it as fixed
	this[expando] = true;
};

function returnFalse(){
	return false;
}
function returnTrue(){
	return true;
}

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
	preventDefault: function() {
		this.isDefaultPrevented = returnTrue;

		var e = this.originalEvent;
		if( !e )
			return;
		// if preventDefault exists run it on the original event
		if (e.preventDefault)
			e.preventDefault();
		// otherwise set the returnValue property of the original event to false (IE)
		e.returnValue = false;
	},
	stopPropagation: function() {
		this.isPropagationStopped = returnTrue;

		var e = this.originalEvent;
		if( !e )
			return;
		// if stopPropagation exists run it on the original event
		if (e.stopPropagation)
			e.stopPropagation();
		// otherwise set the cancelBubble property of the original event to true (IE)
		e.cancelBubble = true;
	},
	stopImmediatePropagation:function(){
		this.isImmediatePropagationStopped = returnTrue;
		this.stopPropagation();
	},
	isDefaultPrevented: returnFalse,
	isPropagationStopped: returnFalse,
	isImmediatePropagationStopped: returnFalse
};
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
var withinElement = function(event) {
	// Check if mouse(over|out) are still within the same parent element
	var parent = event.relatedTarget;
	// Traverse up the tree
	while ( parent && parent != this )
		try { parent = parent.parentNode; }
		catch(e) { parent = this; }

	if( parent != this ){
		// set the correct event type
		event.type = event.data;
		// handle event if we actually just moused on to a non sub-element
		jQuery.event.handle.apply( this, arguments );
	}
};

jQuery.each({
	mouseover: 'mouseenter',
	mouseout: 'mouseleave'
}, function( orig, fix ){
	jQuery.event.special[ fix ] = {
		setup: function(){
			jQuery.event.add( this, orig, withinElement, fix );
		},
		teardown: function(){
			jQuery.event.remove( this, orig, withinElement );
		}
	};
});

jQuery.fn.extend({
	bind: function( type, data, fn ) {
		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
			jQuery.event.add( this, type, fn || data, fn && data );
		});
	},

	one: function( type, data, fn ) {
		var one = jQuery.event.proxy( fn || data, function(event) {
			jQuery(this).unbind(event, one);
			return (fn || data).apply( this, arguments );
		});
		return this.each(function(){
			jQuery.event.add( this, type, one, fn && data);
		});
	},

	unbind: function( type, fn ) {
		return this.each(function(){
			jQuery.event.remove( this, type, fn );
		});
	},

	trigger: function( type, data ) {
		return this.each(function(){
			jQuery.event.trigger( type, data, this );
		});
	},

	triggerHandler: function( type, data ) {
		if( this[0] ){
			var event = jQuery.Event(type);
			event.preventDefault();
			event.stopPropagation();
			jQuery.event.trigger( event, data, this[0] );
			return event.result;
		}
	},

	toggle: function( fn ) {
		// Save reference to arguments for access in closure
		var args = arguments, i = 1;

		// link all the functions, so any of them can unbind this click handler
		while( i < args.length )
			jQuery.event.proxy( fn, args[i++] );

		return this.click( jQuery.event.proxy( fn, function(event) {
			// Figure out which function to execute
			this.lastToggle = ( this.lastToggle || 0 ) % i;

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[ this.lastToggle++ ].apply( this, arguments ) || false;
		}));
	},

	hover: function(fnOver, fnOut) {
		return this.mouseenter(fnOver).mouseleave(fnOut);
	},

	ready: function(fn) {
		// Attach the listeners
		bindReady();

		// If the DOM is already ready
		if ( jQuery.isReady )
			// Execute the function immediately
			fn.call( document, jQuery );

		// Otherwise, remember the function for later
		else
			// Add the function to the wait list
			jQuery.readyList.push( fn );

		return this;
	},

	live: function( type, fn ){
		var proxy = jQuery.event.proxy( fn );
		proxy.guid += this.selector + type;

		jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );

		return this;
	},

	die: function( type, fn ){
		jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
		return this;
	}
});

function liveHandler( event ){
	var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
		stop = true,
		elems = [];

	jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
		if ( check.test(fn.type) ) {
			var elem = jQuery(event.target).closest(fn.data)[0];
			if ( elem )
				elems.push({ elem: elem, fn: fn });
		}
	});

	elems.sort(function(a,b) {
		return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
	});

	jQuery.each(elems, function(){
		if ( this.fn.call(this.elem, event, this.fn.data) === false )
			return (stop = false);
	});

	return stop;
}

function liveConvert(type, selector){
	return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
}

jQuery.extend({
	isReady: false,
	readyList: [],
	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Remember that the DOM is ready
			jQuery.isReady = true;

			// If there are functions bound, to execute
			if ( jQuery.readyList ) {
				// Execute all of them
				jQuery.each( jQuery.readyList, function(){
					this.call( document, jQuery );
				});

				// Reset the list of functions
				jQuery.readyList = null;
			}

			// Trigger any bound ready events
			jQuery(document).triggerHandler("ready");
		}
	}
});

var readyBound = false;

function bindReady(){
	if ( readyBound ) return;
	readyBound = true;

	// Mozilla, Opera and webkit nightlies currently support this event
	if ( document.addEventListener ) {
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", function(){
			document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
			jQuery.ready();
		}, false );

	// If IE event model is used
	} else if ( document.attachEvent ) {
		// ensure firing before onload,
		// maybe late but safe also for iframes
		document.attachEvent("onreadystatechange", function(){
			if ( document.readyState === "complete" ) {
				document.detachEvent( "onreadystatechange", arguments.callee );
				jQuery.ready();
			}
		});

		// If IE and not an iframe
		// continually check to see if the document is ready
		if ( document.documentElement.doScroll && window == window.top ) (function(){
			if ( jQuery.isReady ) return;

			try {
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch( error ) {
				setTimeout( arguments.callee, 0 );
				return;
			}

			// and execute any waiting functions
			jQuery.ready();
		})();
	}

	// A fallback to window.onload, that will always work
	jQuery.event.add( window, "load", jQuery.ready );
}

jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
	"mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
	"change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){

	// Handle event binding
	jQuery.fn[name] = function(fn){
		return fn ? this.bind(name, fn) : this.trigger(name);
	};
});

// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover in other browsers
// Window isn't included so as not to unbind existing unload events
jQuery( window ).bind( 'unload', function(){
	for ( var id in jQuery.cache )
		// Skip the window
		if ( id != 1 && jQuery.cache[ id ].handle )
			jQuery.event.remove( jQuery.cache[ id ].handle.elem );
});
(function(){

	jQuery.support = {};

	var root = document.documentElement,
		script = document.createElement("script"),
		div = document.createElement("div"),
		id = "script" + (new Date).getTime();

	div.style.display = "none";
	div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';

	var all = div.getElementsByTagName("*"),
		a = div.getElementsByTagName("a")[0];

	// Can't get basic test support
	if ( !all || !all.length || !a ) {
		return;
	}

	jQuery.support = {
		// IE strips leading whitespace when .innerHTML is used
		leadingWhitespace: div.firstChild.nodeType == 3,

		// Make sure that tbody elements aren't automatically inserted
		// IE will insert them into empty tables
		tbody: !div.getElementsByTagName("tbody").length,

		// Make sure that you can get all elements in an <object> element
		// IE 7 always returns no results
		objectAll: !!div.getElementsByTagName("object")[0]
			.getElementsByTagName("*").length,

		// Make sure that link elements get serialized correctly by innerHTML
		// This requires a wrapper element in IE
		htmlSerialize: !!div.getElementsByTagName("link").length,

		// Get the style information from getAttribute
		// (IE uses .cssText insted)
		style: /red/.test( a.getAttribute("style") ),

		// Make sure that URLs aren't manipulated
		// (IE normalizes it by default)
		hrefNormalized: a.getAttribute("href") === "/a",

		// Make sure that element opacity exists
		// (IE uses filter instead)
		opacity: a.style.opacity === "0.5",

		// Verify style float existence
		// (IE uses styleFloat instead of cssFloat)
		cssFloat: !!a.style.cssFloat,

		// Will be defined later
		scriptEval: false,
		noCloneEvent: true,
		boxModel: null
	};

	script.type = "text/javascript";
	try {
		script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
	} catch(e){}

	root.insertBefore( script, root.firstChild );

	// Make sure that the execution of code works by injecting a script
	// tag with appendChild/createTextNode
	// (IE doesn't support this, fails, and uses .text instead)
	if ( window[ id ] ) {
		jQuery.support.scriptEval = true;
		delete window[ id ];
	}

	root.removeChild( script );

	if ( div.attachEvent && div.fireEvent ) {
		div.attachEvent("onclick", function(){
			// Cloning a node shouldn't copy over any
			// bound event handlers (IE does this)
			jQuery.support.noCloneEvent = false;
			div.detachEvent("onclick", arguments.callee);
		});
		div.cloneNode(true).fireEvent("onclick");
	}

	// Figure out if the W3C box model works as expected
	// document.body must exist before we can do this
	jQuery(function(){
		var div = document.createElement("div");
		div.style.width = div.style.paddingLeft = "1px";

		document.body.appendChild( div );
		jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
		document.body.removeChild( div ).style.display = 'none';
	});
})();

var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";

jQuery.props = {
	"for": "htmlFor",
	"class": "className",
	"float": styleFloat,
	cssFloat: styleFloat,
	styleFloat: styleFloat,
	readonly: "readOnly",
	maxlength: "maxLength",
	cellspacing: "cellSpacing",
	rowspan: "rowSpan",
	tabindex: "tabIndex"
};
jQuery.fn.extend({
	// Keep a copy of the old load
	_load: jQuery.fn.load,

	load: function( url, params, callback ) {
		if ( typeof url !== "string" )
			return this._load( url );

		var off = url.indexOf(" ");
		if ( off >= 0 ) {
			var selector = url.slice(off, url.length);
			url = url.slice(0, off);
		}

		// Default to a GET request
		var type = "GET";

		// If the second parameter was provided
		if ( params )
			// If it's a function
			if ( jQuery.isFunction( params ) ) {
				// We assume that it's the callback
				callback = params;
				params = null;

			// Otherwise, build a param string
			} else if( typeof params === "object" ) {
				params = jQuery.param( params );
				type = "POST";
			}

		var self = this;

		// Request the remote document
		jQuery.ajax({
			url: url,
			type: type,
			dataType: "html",
			data: params,
			complete: function(res, status){
				// If successful, inject the HTML into all the matched elements
				if ( status == "success" || status == "notmodified" )
					// See if a selector was specified
					self.html( selector ?
						// Create a dummy div to hold the results
						jQuery("<div/>")
							// inject the contents of the document in, removing the scripts
							// to avoid any 'Permission Denied' errors in IE
							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))

							// Locate the specified elements
							.find(selector) :

						// If not, just inject the full result
						res.responseText );

				if( callback )
					self.each( callback, [res.responseText, status, res] );
			}
		});
		return this;
	},

	serialize: function() {
		return jQuery.param(this.serializeArray());
	},
	serializeArray: function() {
		return this.map(function(){
			return this.elements ? jQuery.makeArray(this.elements) : this;
		})
		.filter(function(){
			return this.name && !this.disabled &&
				(this.checked || /select|textarea/i.test(this.nodeName) ||
					/text|hidden|password|search/i.test(this.type));
		})
		.map(function(i, elem){
			var val = jQuery(this).val();
			return val == null ? null :
				jQuery.isArray(val) ?
					jQuery.map( val, function(val, i){
						return {name: elem.name, value: val};
					}) :
					{name: elem.name, value: val};
		}).get();
	}
});

// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
	jQuery.fn[o] = function(f){
		return this.bind(o, f);
	};
});

var jsc = now();

jQuery.extend({

	get: function( url, data, callback, type ) {
		// shift arguments if data argument was ommited
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = null;
		}

		return jQuery.ajax({
			type: "GET",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	getScript: function( url, callback ) {
		return jQuery.get(url, null, callback, "script");
	},

	getJSON: function( url, data, callback ) {
		return jQuery.get(url, data, callback, "json");
	},

	post: function( url, data, callback, type ) {
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = {};
		}

		return jQuery.ajax({
			type: "POST",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	ajaxSetup: function( settings ) {
		jQuery.extend( jQuery.ajaxSettings, settings );
	},

	ajaxSettings: {
		url: location.href,
		global: true,
		type: "GET",
		contentType: "application/x-www-form-urlencoded",
		processData: true,
		async: true,
		/*
		timeout: 0,
		data: null,
		username: null,
		password: null,
		*/
		// Create the request object; Microsoft failed to properly
		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
		// This function can be overriden by calling jQuery.ajaxSetup
		xhr:function(){
			return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
		},
		accepts: {
			xml: "application/xml, text/xml",
			html: "text/html",
			script: "text/javascript, application/javascript",
			json: "application/json, text/javascript",
			text: "text/plain",
			_default: "*/*"
		}
	},

	// Last-Modified header cache for next request
	lastModified: {},

	ajax: function( s ) {
		// Extend the settings, but re-extend 's' so that it can be
		// checked again later (in the test suite, specifically)
		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));

		var jsonp, jsre = /=\?(&|$)/g, status, data,
			type = s.type.toUpperCase();

		// convert data if not already a string
		if ( s.data && s.processData && typeof s.data !== "string" )
			s.data = jQuery.param(s.data);

		// Handle JSONP Parameter Callbacks
		if ( s.dataType == "jsonp" ) {
			if ( type == "GET" ) {
				if ( !s.url.match(jsre) )
					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
			} else if ( !s.data || !s.data.match(jsre) )
				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
			s.dataType = "json";
		}

		// Build temporary JSONP function
		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
			jsonp = "jsonp" + jsc++;

			// Replace the =? sequence both in the query string and the data
			if ( s.data )
				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
			s.url = s.url.replace(jsre, "=" + jsonp + "$1");

			// We need to make sure
			// that a JSONP style response is executed properly
			s.dataType = "script";

			// Handle JSONP-style loading
			window[ jsonp ] = function(tmp){
				data = tmp;
				success();
				complete();
				// Garbage collect
				window[ jsonp ] = undefined;
				try{ delete window[ jsonp ]; } catch(e){}
				if ( head )
					head.removeChild( script );
			};
		}

		if ( s.dataType == "script" && s.cache == null )
			s.cache = false;

		if ( s.cache === false && type == "GET" ) {
			var ts = now();
			// try replacing _= if it is there
			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
			// if nothing was replaced, add timestamp to the end
			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
		}

		// If data is available, append data to url for get requests
		if ( s.data && type == "GET" ) {
			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;

			// IE likes to send both get and post data, prevent this
			s.data = null;
		}

		// Watch for a new set of requests
		if ( s.global && ! jQuery.active++ )
			jQuery.event.trigger( "ajaxStart" );

		// Matches an absolute URL, and saves the domain
		var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );

		// If we're requesting a remote document
		// and trying to load JSON or Script with a GET
		if ( s.dataType == "script" && type == "GET" && parts
			&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){

			var head = document.getElementsByTagName("head")[0];
			var script = document.createElement("script");
			script.src = s.url;
			if (s.scriptCharset)
				script.charset = s.scriptCharset;

			// Handle Script loading
			if ( !jsonp ) {
				var done = false;

				// Attach handlers for all browsers
				script.onload = script.onreadystatechange = function(){
					if ( !done && (!this.readyState ||
							this.readyState == "loaded" || this.readyState == "complete") ) {
						done = true;
						success();
						complete();

						// Handle memory leak in IE
						script.onload = script.onreadystatechange = null;
						head.removeChild( script );
					}
				};
			}

			head.appendChild(script);

			// We handle everything using the script element injection
			return undefined;
		}

		var requestDone = false;

		// Create the request object
		var xhr = s.xhr();

		// Open the socket
		// Passing null username, generates a login popup on Opera (#2865)
		if( s.username )
			xhr.open(type, s.url, s.async, s.username, s.password);
		else
			xhr.open(type, s.url, s.async);

		// Need an extra try/catch for cross domain requests in Firefox 3
		try {
			// Set the correct header, if data is being sent
			if ( s.data )
				xhr.setRequestHeader("Content-Type", s.contentType);

			// Set the If-Modified-Since header, if ifModified mode.
			if ( s.ifModified )
				xhr.setRequestHeader("If-Modified-Since",
					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );

			// Set header so the called script knows that it's an XMLHttpRequest
			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

			// Set the Accepts header for the server, depending on the dataType
			xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
				s.accepts[ s.dataType ] + ", */*" :
				s.accepts._default );
		} catch(e){}

		// Allow custom headers/mimetypes and early abort
		if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active )
				jQuery.event.trigger( "ajaxStop" );
			// close opended socket
			xhr.abort();
			return false;
		}

		if ( s.global )
			jQuery.event.trigger("ajaxSend", [xhr, s]);

		// Wait for a response to come back
		var onreadystatechange = function(isTimeout){
			// The request was aborted, clear the interval and decrement jQuery.active
			if (xhr.readyState == 0) {
				if (ival) {
					// clear poll interval
					clearInterval(ival);
					ival = null;
					// Handle the global AJAX counter
					if ( s.global && ! --jQuery.active )
						jQuery.event.trigger( "ajaxStop" );
				}
			// The transfer is complete and the data is available, or the request timed out
			} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
				requestDone = true;

				// clear poll interval
				if (ival) {
					clearInterval(ival);
					ival = null;
				}

				status = isTimeout == "timeout" ? "timeout" :
					!jQuery.httpSuccess( xhr ) ? "error" :
					s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
					"success";

				if ( status == "success" ) {
					// Watch for, and catch, XML document parse errors
					try {
						// process the data (runs the xml through httpData regardless of callback)
						data = jQuery.httpData( xhr, s.dataType, s );
					} catch(e) {
						status = "parsererror";
					}
				}

				// Make sure that the request was successful or notmodified
				if ( status == "success" ) {
					// Cache Last-Modified header, if ifModified mode.
					var modRes;
					try {
						modRes = xhr.getResponseHeader("Last-Modified");
					} catch(e) {} // swallow exception thrown by FF if header is not available

					if ( s.ifModified && modRes )
						jQuery.lastModified[s.url] = modRes;

					// JSONP handles its own success callback
					if ( !jsonp )
						success();
				} else
					jQuery.handleError(s, xhr, status);

				// Fire the complete handlers
				complete();

				if ( isTimeout )
					xhr.abort();

				// Stop memory leaks
				if ( s.async )
					xhr = null;
			}
		};

		if ( s.async ) {
			// don't attach the handler to the request, just poll it instead
			var ival = setInterval(onreadystatechange, 13);

			// Timeout checker
			if ( s.timeout > 0 )
				setTimeout(function(){
					// Check to see if the request is still happening
					if ( xhr && !requestDone )
						onreadystatechange( "timeout" );
				}, s.timeout);
		}

		// Send the data
		try {
			xhr.send(s.data);
		} catch(e) {
			jQuery.handleError(s, xhr, null, e);
		}

		// firefox 1.5 doesn't fire statechange for sync requests
		if ( !s.async )
			onreadystatechange();

		function success(){
			// If a local callback was specified, fire it and pass it the data
			if ( s.success )
				s.success( data, status );

			// Fire the global callback
			if ( s.global )
				jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
		}

		function complete(){
			// Process result
			if ( s.complete )
				s.complete(xhr, status);

			// The request was completed
			if ( s.global )
				jQuery.event.trigger( "ajaxComplete", [xhr, s] );

			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active )
				jQuery.event.trigger( "ajaxStop" );
		}

		// return XMLHttpRequest to allow aborting the request etc.
		return xhr;
	},

	handleError: function( s, xhr, status, e ) {
		// If a local callback was specified, fire it
		if ( s.error ) s.error( xhr, status, e );

		// Fire the global callback
		if ( s.global )
			jQuery.event.trigger( "ajaxError", [xhr, s, e] );
	},

	// Counter for holding the number of active queries
	active: 0,

	// Determines if an XMLHttpRequest was successful or not
	httpSuccess: function( xhr ) {
		try {
			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
			return !xhr.status && location.protocol == "file:" ||
				( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
		} catch(e){}
		return false;
	},

	// Determines if an XMLHttpRequest returns NotModified
	httpNotModified: function( xhr, url ) {
		try {
			var xhrRes = xhr.getResponseHeader("Last-Modified");

			// Firefox always returns 200. check Last-Modified date
			return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
		} catch(e){}
		return false;
	},

	httpData: function( xhr, type, s ) {
		var ct = xhr.getResponseHeader("content-type"),
			xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
			data = xml ? xhr.responseXML : xhr.responseText;

		if ( xml && data.documentElement.tagName == "parsererror" )
			throw "parsererror";

		// Allow a pre-filtering function to sanitize the response
		// s != null is checked to keep backwards compatibility
		if( s && s.dataFilter )
			data = s.dataFilter( data, type );

		// The filter can actually parse the response
		if( typeof data === "string" ){

			// If the type is "script", eval it in global context
			if ( type == "script" )
				jQuery.globalEval( data );

			// Get the JavaScript object, if JSON is used.
			if ( type == "json" )
				data = window["eval"]("(" + data + ")");
		}

		return data;
	},

	// Serialize an array of form elements or a set of
	// key/values into a query string
	param: function( a ) {
		var s = [ ];

		function add( key, value ){
			s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
		};

		// If an array was passed in, assume that it is an array
		// of form elements
		if ( jQuery.isArray(a) || a.jquery )
			// Serialize the form elements
			jQuery.each( a, function(){
				add( this.name, this.value );
			});

		// Otherwise, assume that it's an object of key/value pairs
		else
			// Serialize the key/values
			for ( var j in a )
				// If the value is an array then the key names need to be repeated
				if ( jQuery.isArray(a[j]) )
					jQuery.each( a[j], function(){
						add( j, this );
					});
				else
					add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );

		// Return the resulting serialization
		return s.join("&").replace(/%20/g, "+");
	}

});
var elemdisplay = {},
	timerId,
	fxAttrs = [
		// height animations
		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
		// width animations
		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
		// opacity animations
		[ "opacity" ]
	];

function genFx( type, num ){
	var obj = {};
	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
		obj[ this ] = type;
	});
	return obj;
}

jQuery.fn.extend({
	show: function(speed,callback){
		if ( speed ) {
			return this.animate( genFx("show", 3), speed, callback);
		} else {
			for ( var i = 0, l = this.length; i < l; i++ ){
				var old = jQuery.data(this[i], "olddisplay");

				this[i].style.display = old || "";

				if ( jQuery.css(this[i], "display") === "none" ) {
					var tagName = this[i].tagName, display;

					if ( elemdisplay[ tagName ] ) {
						display = elemdisplay[ tagName ];
					} else {
						var elem = jQuery("<" + tagName + " />").appendTo("body");

						display = elem.css("display");
						if ( display === "none" )
							display = "block";

						elem.remove();

						elemdisplay[ tagName ] = display;
					}

					jQuery.data(this[i], "olddisplay", display);
				}
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var i = 0, l = this.length; i < l; i++ ){
				this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
			}

			return this;
		}
	},

	hide: function(speed,callback){
		if ( speed ) {
			return this.animate( genFx("hide", 3), speed, callback);
		} else {
			for ( var i = 0, l = this.length; i < l; i++ ){
				var old = jQuery.data(this[i], "olddisplay");
				if ( !old && old !== "none" )
					jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var i = 0, l = this.length; i < l; i++ ){
				this[i].style.display = "none";
			}

			return this;
		}
	},

	// Save the old toggle function
	_toggle: jQuery.fn.toggle,

	toggle: function( fn, fn2 ){
		var bool = typeof fn === "boolean";

		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
			this._toggle.apply( this, arguments ) :
			fn == null || bool ?
				this.each(function(){
					var state = bool ? fn : jQuery(this).is(":hidden");
					jQuery(this)[ state ? "show" : "hide" ]();
				}) :
				this.animate(genFx("toggle", 3), fn, fn2);
	},

	fadeTo: function(speed,to,callback){
		return this.animate({opacity: to}, speed, callback);
	},

	animate: function( prop, speed, easing, callback ) {
		var optall = jQuery.speed(speed, easing, callback);

		return this[ optall.queue === false ? "each" : "queue" ](function(){

			var opt = jQuery.extend({}, optall), p,
				hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
				self = this;

			for ( p in prop ) {
				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
					return opt.complete.call(this);

				if ( ( p == "height" || p == "width" ) && this.style ) {
					// Store display property
					opt.display = jQuery.css(this, "display");

					// Make sure that nothing sneaks out
					opt.overflow = this.style.overflow;
				}
			}

			if ( opt.overflow != null )
				this.style.overflow = "hidden";

			opt.curAnim = jQuery.extend({}, prop);

			jQuery.each( prop, function(name, val){
				var e = new jQuery.fx( self, opt, name );

				if ( /toggle|show|hide/.test(val) )
					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
				else {
					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
						start = e.cur(true) || 0;

					if ( parts ) {
						var end = parseFloat(parts[2]),
							unit = parts[3] || "px";

						// We need to compute starting value
						if ( unit != "px" ) {
							self.style[ name ] = (end || 1) + unit;
							start = ((end || 1) / e.cur(true)) * start;
							self.style[ name ] = start + unit;
						}

						// If a +=/-= token was provided, we're doing a relative animation
						if ( parts[1] )
							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;

						e.custom( start, end, unit );
					} else
						e.custom( start, val, "" );
				}
			});

			// For JS strict compliance
			return true;
		});
	},

	stop: function(clearQueue, gotoEnd){
		var timers = jQuery.timers;

		if (clearQueue)
			this.queue([]);

		this.each(function(){
			// go in reverse order so anything added to the queue during the loop is ignored
			for ( var i = timers.length - 1; i >= 0; i-- )
				if ( timers[i].elem == this ) {
					if (gotoEnd)
						// force the next step to be the last
						timers[i](true);
					timers.splice(i, 1);
				}
		});

		// start the next in the queue if the last step wasn't forced
		if (!gotoEnd)
			this.dequeue();

		return this;
	}

});

// Generate shortcuts for custom animations
jQuery.each({
	slideDown: genFx("show", 1),
	slideUp: genFx("hide", 1),
	slideToggle: genFx("toggle", 1),
	fadeIn: { opacity: "show" },
	fadeOut: { opacity: "hide" }
}, function( name, props ){
	jQuery.fn[ name ] = function( speed, callback ){
		return this.animate( props, speed, callback );
	};
});

jQuery.extend({

	speed: function(speed, easing, fn) {
		var opt = typeof speed === "object" ? speed : {
			complete: fn || !fn && easing ||
				jQuery.isFunction( speed ) && speed,
			duration: speed,
			easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
		};

		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
			jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;

		// Queueing
		opt.old = opt.complete;
		opt.complete = function(){
			if ( opt.queue !== false )
				jQuery(this).dequeue();
			if ( jQuery.isFunction( opt.old ) )
				opt.old.call( this );
		};

		return opt;
	},

	easing: {
		linear: function( p, n, firstNum, diff ) {
			return firstNum + diff * p;
		},
		swing: function( p, n, firstNum, diff ) {
			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
		}
	},

	timers: [],

	fx: function( elem, options, prop ){
		this.options = options;
		this.elem = elem;
		this.prop = prop;

		if ( !options.orig )
			options.orig = {};
	}

});

jQuery.fx.prototype = {

	// Simple function for setting a style value
	update: function(){
		if ( this.options.step )
			this.options.step.call( this.elem, this.now, this );

		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );

		// Set display property to block for height/width animations
		if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
			this.elem.style.display = "block";
	},

	// Get the current size
	cur: function(force){
		if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
			return this.elem[ this.prop ];

		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
	},

	// Start an animation from one number to another
	custom: function(from, to, unit){
		this.startTime = now();
		this.start = from;
		this.end = to;
		this.unit = unit || this.unit || "px";
		this.now = this.start;
		this.pos = this.state = 0;

		var self = this;
		function t(gotoEnd){
			return self.step(gotoEnd);
		}

		t.elem = this.elem;

		if ( t() && jQuery.timers.push(t) && !timerId ) {
			timerId = setInterval(function(){
				var timers = jQuery.timers;

				for ( var i = 0; i < timers.length; i++ )
					if ( !timers[i]() )
						timers.splice(i--, 1);

				if ( !timers.length ) {
					clearInterval( timerId );
					timerId = undefined;
				}
			}, 13);
		}
	},

	// Simple 'show' function
	show: function(){
		// Remember where we started, so that we can go back to it later
		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
		this.options.show = true;

		// Begin the animation
		// Make sure that we start at a small width/height to avoid any
		// flash of content
		this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());

		// Start by showing the element
		jQuery(this.elem).show();
	},

	// Simple 'hide' function
	hide: function(){
		// Remember where we started, so that we can go back to it later
		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
		this.options.hide = true;

		// Begin the animation
		this.custom(this.cur(), 0);
	},

	// Each step of an animation
	step: function(gotoEnd){
		var t = now();

		if ( gotoEnd || t >= this.options.duration + this.startTime ) {
			this.now = this.end;
			this.pos = this.state = 1;
			this.update();

			this.options.curAnim[ this.prop ] = true;

			var done = true;
			for ( var i in this.options.curAnim )
				if ( this.options.curAnim[i] !== true )
					done = false;

			if ( done ) {
				if ( this.options.display != null ) {
					// Reset the overflow
					this.elem.style.overflow = this.options.overflow;

					// Reset the display
					this.elem.style.display = this.options.display;
					if ( jQuery.css(this.elem, "display") == "none" )
						this.elem.style.display = "block";
				}

				// Hide the element if the "hide" operation was done
				if ( this.options.hide )
					jQuery(this.elem).hide();

				// Reset the properties, if the item has been hidden or shown
				if ( this.options.hide || this.options.show )
					for ( var p in this.options.curAnim )
						jQuery.attr(this.elem.style, p, this.options.orig[p]);

				// Execute the complete function
				this.options.complete.call( this.elem );
			}

			return false;
		} else {
			var n = t - this.startTime;
			this.state = n / this.options.duration;

			// Perform the easing function, defaults to swing
			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
			this.now = this.start + ((this.end - this.start) * this.pos);

			// Perform the next step of the animation
			this.update();
		}

		return true;
	}

};

jQuery.extend( jQuery.fx, {
	speeds:{
		slow: 600,
 		fast: 200,
 		// Default speed
 		_default: 400
	},
	step: {

		opacity: function(fx){
			jQuery.attr(fx.elem.style, "opacity", fx.now);
		},

		_default: function(fx){
			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
				fx.elem.style[ fx.prop ] = fx.now + fx.unit;
			else
				fx.elem[ fx.prop ] = fx.now;
		}
	}
});
if ( document.documentElement["getBoundingClientRect"] )
	jQuery.fn.offset = function() {
		if ( !this[0] ) return { top: 0, left: 0 };
		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
		var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
			clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
			top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
			left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
		return { top: top, left: left };
	};
else
	jQuery.fn.offset = function() {
		if ( !this[0] ) return { top: 0, left: 0 };
		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
		jQuery.offset.initialized || jQuery.offset.initialize();

		var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
			body = doc.body, defaultView = doc.defaultView,
			prevComputedStyle = defaultView.getComputedStyle(elem, null),
			top = elem.offsetTop, left = elem.offsetLeft;

		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
			computedStyle = defaultView.getComputedStyle(elem, null);
			top -= elem.scrollTop, left -= elem.scrollLeft;
			if ( elem === offsetParent ) {
				top += elem.offsetTop, left += elem.offsetLeft;
				if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
					top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
					left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
				prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
			}
			if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
				top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
				left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
			prevComputedStyle = computedStyle;
		}

		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
			top  += body.offsetTop,
			left += body.offsetLeft;

		if ( prevComputedStyle.position === "fixed" )
			top  += Math.max(docElem.scrollTop, body.scrollTop),
			left += Math.max(docElem.scrollLeft, body.scrollLeft);

		return { top: top, left: left };
	};

jQuery.offset = {
	initialize: function() {
		if ( this.initialized ) return;
		var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
			html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';

		rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
		for ( prop in rules ) container.style[prop] = rules[prop];

		container.innerHTML = html;
		body.insertBefore(container, body.firstChild);
		innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;

		this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
		this.doesAddBorderForTableAndCells = (td.offsetTop === 5);

		innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
		this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

		body.style.marginTop = '1px';
		this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
		body.style.marginTop = bodyMarginTop;

		body.removeChild(container);
		this.initialized = true;
	},

	bodyOffset: function(body) {
		jQuery.offset.initialized || jQuery.offset.initialize();
		var top = body.offsetTop, left = body.offsetLeft;
		if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
			top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
			left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
		return { top: top, left: left };
	}
};


jQuery.fn.extend({
	position: function() {
		var left = 0, top = 0, results;

		if ( this[0] ) {
			// Get *real* offsetParent
			var offsetParent = this.offsetParent(),

			// Get correct offsets
			offset       = this.offset(),
			parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();

			// Subtract element margins
			// note: when an element has margin: auto the offsetLeft and marginLeft
			// are the same in Safari causing offset.left to incorrectly be 0
			offset.top  -= num( this, 'marginTop'  );
			offset.left -= num( this, 'marginLeft' );

			// Add offsetParent borders
			parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
			parentOffset.left += num( offsetParent, 'borderLeftWidth' );

			// Subtract the two offsets
			results = {
				top:  offset.top  - parentOffset.top,
				left: offset.left - parentOffset.left
			};
		}

		return results;
	},

	offsetParent: function() {
		var offsetParent = this[0].offsetParent || document.body;
		while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
			offsetParent = offsetParent.offsetParent;
		return jQuery(offsetParent);
	}
});


// Create scrollLeft and scrollTop methods
jQuery.each( ['Left', 'Top'], function(i, name) {
	var method = 'scroll' + name;

	jQuery.fn[ method ] = function(val) {
		if (!this[0]) return null;

		return val !== undefined ?

			// Set the scroll offset
			this.each(function() {
				this == window || this == document ?
					window.scrollTo(
						!i ? val : jQuery(window).scrollLeft(),
						 i ? val : jQuery(window).scrollTop()
					) :
					this[ method ] = val;
			}) :

			// Return the scroll offset
			this[0] == window || this[0] == document ?
				self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
					jQuery.boxModel && document.documentElement[ method ] ||
					document.body[ method ] :
				this[0][ method ];
	};
});
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function(i, name){

	var tl = i ? "Left"  : "Top",  // top or left
		br = i ? "Right" : "Bottom", // bottom or right
		lower = name.toLowerCase();

	// innerHeight and innerWidth
	jQuery.fn["inner" + name] = function(){
		return this[0] ?
			jQuery.css( this[0], lower, false, "padding" ) :
			null;
	};

	// outerHeight and outerWidth
	jQuery.fn["outer" + name] = function(margin) {
		return this[0] ?
			jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
			null;
	};

	var type = name.toLowerCase();

	jQuery.fn[ type ] = function( size ) {
		// Get window width or height
		return this[0] == window ?
			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
			document.body[ "client" + name ] :

			// Get document width or height
			this[0] == document ?
				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
				Math.max(
					document.documentElement["client" + name],
					document.body["scroll" + name], document.documentElement["scroll" + name],
					document.body["offset" + name], document.documentElement["offset" + name]
				) :

				// Get or set width or height on the element
				size === undefined ?
					// Get width or height on the element
					(this.length ? jQuery.css( this[0], type ) : null) :

					// Set the width or height on the element (default to pixels if value is unitless)
					this.css( type, typeof size === "string" ? size : size + "px" );
	};

});
}

foo();
var $j = jQuery.noConflict();

/* Copyright (c) 2007 Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Version: 1.0.2
 * Requires jQuery 1.1.3+
 * Docs: http://docs.jquery.com/Plugins/livequery
 */

(function($) {
	
$.extend($.fn, {
	livequery: function(type, fn, fn2) {
		var self = this, q;
		
		// Handle different call patterns
		if ($.isFunction(type))
			fn2 = fn, fn = type, type = undefined;
			
		// See if Live Query already exists
		$.each( $.livequery.queries, function(i, query) {
			if ( self.selector == query.selector && self.context == query.context &&
				type == query.type && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) )
					// Found the query, exit the each loop
					return (q = query) && false;
		});
		
		// Create new Live Query if it wasn't found
		q = q || new $.livequery(this.selector, this.context, type, fn, fn2);
		
		// Make sure it is running
		q.stopped = false;
		
		// Run it
		$.livequery.run( q.id );
		
		// Contnue the chain
		return this;
	},
	
	expire: function(type, fn, fn2) {
		var self = this;
		
		// Handle different call patterns
		if ($.isFunction(type))
			fn2 = fn, fn = type, type = undefined;
			
		// Find the Live Query based on arguments and stop it
		$.each( $.livequery.queries, function(i, query) {
			if ( self.selector == query.selector && self.context == query.context && 
				(!type || type == query.type) && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) && !this.stopped )
					$.livequery.stop(query.id);
		});
		
		// Continue the chain
		return this;
	}
});

$.livequery = function(selector, context, type, fn, fn2) {
	this.selector = selector;
	this.context  = context || document;
	this.type     = type;
	this.fn       = fn;
	this.fn2      = fn2;
	this.elements = [];
	this.stopped  = false;
	
	// The id is the index of the Live Query in $.livequery.queries
	this.id = $.livequery.queries.push(this)-1;
	
	// Mark the functions for matching later on
	fn.$lqguid = fn.$lqguid || $.livequery.guid++;
	if (fn2) fn2.$lqguid = fn2.$lqguid || $.livequery.guid++;
	
	// Return the Live Query
	return this;
};

$.livequery.prototype = {
	stop: function() {
		var query = this;
		
		if ( this.type )
			// Unbind all bound events
			this.elements.unbind(this.type, this.fn);
		else if (this.fn2)
			// Call the second function for all matched elements
			this.elements.each(function(i, el) {
				query.fn2.apply(el);
			});
			
		// Clear out matched elements
		this.elements = [];
		
		// Stop the Live Query from running until restarted
		this.stopped = true;
	},
	
	run: function() {
		// Short-circuit if stopped
		if ( this.stopped ) return;
		var query = this;
		
		var oEls = this.elements,
			els  = $(this.selector, this.context),
			nEls = els.not(oEls);
		
		// Set elements to the latest set of matched elements
		this.elements = els;
		
		if (this.type) {
			// Bind events to newly matched elements
			nEls.bind(this.type, this.fn);
			
			// Unbind events to elements no longer matched
			if (oEls.length > 0)
				$.each(oEls, function(i, el) {
					if ( $.inArray(el, els) < 0 )
						$.event.remove(el, query.type, query.fn);
				});
		}
		else {
			// Call the first function for newly matched elements
			nEls.each(function() {
				query.fn.apply(this);
			});
			
			// Call the second function for elements no longer matched
			if ( this.fn2 && oEls.length > 0 )
				$.each(oEls, function(i, el) {
					if ( $.inArray(el, els) < 0 )
						query.fn2.apply(el);
				});
		}
	}
};

$.extend($.livequery, {
	guid: 0,
	queries: [],
	queue: [],
	running: false,
	timeout: null,
	
	checkQueue: function() {
		if ( $.livequery.running && $.livequery.queue.length ) {
			var length = $.livequery.queue.length;
			// Run each Live Query currently in the queue
			while ( length-- )
				$.livequery.queries[ $.livequery.queue.shift() ].run();
		}
	},
	
	pause: function() {
		// Don't run anymore Live Queries until restarted
		$.livequery.running = false;
	},
	
	play: function() {
		// Restart Live Queries
		$.livequery.running = true;
		// Request a run of the Live Queries
		$.livequery.run();
	},
	
	registerPlugin: function() {
		$.each( arguments, function(i,n) {
			// Short-circuit if the method doesn't exist
			if (!$.fn[n]) return;
			
			// Save a reference to the original method
			var old = $.fn[n];
			
			// Create a new method
			$.fn[n] = function() {
				// Call the original method
				var r = old.apply(this, arguments);
				
				// Request a run of the Live Queries
				$.livequery.run();
				
				// Return the original methods result
				return r;
			}
		});
	},
	
	run: function(id) {
		if (id != undefined) {
			// Put the particular Live Query in the queue if it doesn't already exist
			if ( $.inArray(id, $.livequery.queue) < 0 )
				$.livequery.queue.push( id );
		}
		else
			// Put each Live Query in the queue if it doesn't already exist
			$.each( $.livequery.queries, function(id) {
				if ( $.inArray(id, $.livequery.queue) < 0 )
					$.livequery.queue.push( id );
			});
		
		// Clear timeout if it already exists
		if ($.livequery.timeout) clearTimeout($.livequery.timeout);
		// Create a timeout to check the queue and actually run the Live Queries
		$.livequery.timeout = setTimeout($.livequery.checkQueue, 20);
	},
	
	stop: function(id) {
		if (id != undefined)
			// Stop are particular Live Query
			$.livequery.queries[ id ].stop();
		else
			// Stop all Live Queries
			$.each( $.livequery.queries, function(id) {
				$.livequery.queries[ id ].stop();
			});
	}
});

// Register core DOM manipulation methods
$.livequery.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');

// Run Live Queries when the Document is ready
$(function() { $.livequery.play(); });


// Save a reference to the original init method
var init = $.prototype.init;

// Create a new init method that exposes two new properties: selector and context
$.prototype.init = function(a,c) {
	// Call the original init and save the result
	var r = init.apply(this, arguments);
	
	// Copy over properties if they exist already
	if (a && a.selector)
		r.context = a.context, r.selector = a.selector;
		
	// Set properties
	if ( typeof a == 'string' )
		r.context = c || document, r.selector = a;
	
	// Return the result
	return r;
};

// Give the init function the jQuery prototype for later instantiation (needed after Rev 4091)
$.prototype.init.prototype = $.prototype;
	
})(jQuery);

/*
 * jQuery Form Plugin
 * version: 2.17 (06-NOV-2008)
 * @requires jQuery v1.2.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id$
 */
;(function($) {

/*
    Usage Note:  
    -----------
    Do not use both ajaxSubmit and ajaxForm on the same form.  These
    functions are intended to be exclusive.  Use ajaxSubmit if you want
    to bind your own submit handler to the form.  For example,

    $(document).ready(function() {
        $('#myForm').bind('submit', function() {
            $(this).ajaxSubmit({
                target: '#output'
            });
            return false; // <-- important!
        });
    });

    Use ajaxForm when you want the plugin to manage all the event binding
    for you.  For example,

    $(document).ready(function() {
        $('#myForm').ajaxForm({
            target: '#output'
        });
    });
        
    When using ajaxForm, the ajaxSubmit function will be invoked for you
    at the appropriate time.  
*/

/**
 * ajaxSubmit() provides a mechanism for immediately submitting 
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
    if (!this.length) {
        log('ajaxSubmit: skipping submit process - no element selected');
        return this;
    }

    if (typeof options == 'function')
        options = { success: options };

    options = $.extend({
        url:  this.attr('action') || window.location.toString(),
        type: this.attr('method') || 'GET'
    }, options || {});

    // hook for manipulating the form data before it is extracted;
    // convenient for use with rich editors like tinyMCE or FCKEditor
    var veto = {};
    this.trigger('form-pre-serialize', [this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
        return this;
    }

    // provide opportunity to alter form data before it is serialized
    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSerialize callback');
        return this;
    }    
   
    var a = this.formToArray(options.semantic);
    if (options.data) {
        options.extraData = options.data;
        for (var n in options.data) {
          if(options.data[n] instanceof Array) {
            for (var k in options.data[n])
              a.push( { name: n, value: options.data[n][k] } )
          }  
          else
             a.push( { name: n, value: options.data[n] } );
        }
    }

    // give pre-submit callback an opportunity to abort the submit
    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSubmit callback');
        return this;
    }    

    // fire vetoable 'validate' event
    this.trigger('form-submit-validate', [a, this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
        return this;
    }    

    var q = $.param(a);

    if (options.type.toUpperCase() == 'GET') {
        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
        options.data = null;  // data is null for 'get'
    }
    else
        options.data = q; // data is the query string for 'post'

    var $form = this, callbacks = [];
    if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
    if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

    // perform a load on the target only if dataType is not provided
    if (!options.dataType && options.target) {
        var oldSuccess = options.success || function(){};
        callbacks.push(function(data) {
            $(options.target).html(data).each(oldSuccess, arguments);
        });
    }
    else if (options.success)
        callbacks.push(options.success);

    options.success = function(data, status) {
        for (var i=0, max=callbacks.length; i < max; i++)
            callbacks[i].apply(options, [data, status, $form]);
    };

    // are there files to upload?
    var files = $('input:file', this).fieldValue();
    var found = false;
    for (var j=0; j < files.length; j++)
        if (files[j])
            found = true;

    // options.iframe allows user to force iframe mode
   if (options.iframe || found) { 
       // hack to fix Safari hang (thanks to Tim Molendijk for this)
       // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
       if ($.browser.safari && options.closeKeepAlive)
           $.get(options.closeKeepAlive, fileUpload);
       else
           fileUpload();
       }
   else
       $.ajax(options);

    // fire 'notify' event
    this.trigger('form-submit-notify', [this, options]);
    return this;


    // private function for handling file uploads (hat tip to YAHOO!)
    function fileUpload() {
        var form = $form[0];
        
        if ($(':input[@name=submit]', form).length) {
            alert('Error: Form elements must not be named "submit".');
            return;
        }
        
        var opts = $.extend({}, $.ajaxSettings, options);
		var s = jQuery.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

        var id = 'jqFormIO' + (new Date().getTime());
        var $io = $('<iframe id="' + id + '" name="' + id + '" />');
        var io = $io[0];

        if ($.browser.msie || $.browser.opera) 
            io.src = 'javascript:false;document.write("");';
        $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

        var xhr = { // mock object
            aborted: 0,
            responseText: null,
            responseXML: null,
            status: 0,
            statusText: 'n/a',
            getAllResponseHeaders: function() {},
            getResponseHeader: function() {},
            setRequestHeader: function() {},
            abort: function() { 
                this.aborted = 1; 
                $io.attr('src','about:blank'); // abort op in progress
            }
        };

        var g = opts.global;
        // trigger ajax global events so that activity/block indicators work like normal
        if (g && ! $.active++) $.event.trigger("ajaxStart");
        if (g) $.event.trigger("ajaxSend", [xhr, opts]);

		if (s.beforeSend && s.beforeSend(xhr, s) === false) {
			s.global && jQuery.active--;
			return;
        }
        if (xhr.aborted)
            return;
        
        var cbInvoked = 0;
        var timedOut = 0;

        // add submitting element to data if we know it
        var sub = form.clk;
        if (sub) {
            var n = sub.name;
            if (n && !sub.disabled) {
                options.extraData = options.extraData || {};
                options.extraData[n] = sub.value;
                if (sub.type == "image") {
                    options.extraData[name+'.x'] = form.clk_x;
                    options.extraData[name+'.y'] = form.clk_y;
                }
            }
        }

        // take a breath so that pending repaints get some cpu time before the upload starts
        setTimeout(function() {
            // make sure form attrs are set
            var t = $form.attr('target'), a = $form.attr('action');
            $form.attr({
                target:   id,
                method:   'POST',
                action:   opts.url
            });
            
            // ie borks in some cases when setting encoding
            if (! options.skipEncodingOverride) {
                $form.attr({
                    encoding: 'multipart/form-data',
                    enctype:  'multipart/form-data'
                });
            }

            // support timout
            if (opts.timeout)
                setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

            // add "extra" data to form if provided in options
            var extraInputs = [];
            try {
                if (options.extraData)
                    for (var n in options.extraData)
                        extraInputs.push(
                            $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
                                .appendTo(form)[0]);
            
                // add iframe to doc and submit the form
                $io.appendTo('body');
                io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
                form.submit();
            }
            finally {
                // reset attrs and remove "extra" input elements
                $form.attr('action', a);
                t ? $form.attr('target', t) : $form.removeAttr('target');
                $(extraInputs).remove();
            }
        }, 10);

        function cb() {
            if (cbInvoked++) return;
            
            io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);

            var operaHack = 0;
            var ok = true;
            try {
                if (timedOut) throw 'timeout';
                // extract the server response from the iframe
                var data, doc;

                doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
                
                if (doc.body == null && !operaHack && $.browser.opera) {
                    // In Opera 9.2.x the iframe DOM is not always traversable when
                    // the onload callback fires so we give Opera 100ms to right itself
                    operaHack = 1;
                    cbInvoked--;
                    setTimeout(cb, 100);
                    return;
                }
                
                xhr.responseText = doc.body ? doc.body.innerHTML : null;
                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                xhr.getResponseHeader = function(header){
                    var headers = {'content-type': opts.dataType};
                    return headers[header];
                };

                if (opts.dataType == 'json' || opts.dataType == 'script') {
                    var ta = doc.getElementsByTagName('textarea')[0];
                    xhr.responseText = ta ? ta.value : xhr.responseText;
                }
                else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
                    xhr.responseXML = toXml(xhr.responseText);
                }
                data = $.httpData(xhr, opts.dataType);
            }
            catch(e){
                ok = false;
                $.handleError(opts, xhr, 'error', e);
            }

            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
            if (ok) {
                opts.success(data, 'success');
                if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
            }
            if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
            if (g && ! --$.active) $.event.trigger("ajaxStop");
            if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

            // clean up
            setTimeout(function() {
                $io.remove();
                xhr.responseXML = null;
            }, 100);
        };

        function toXml(s, doc) {
            if (window.ActiveXObject) {
                doc = new ActiveXObject('Microsoft.XMLDOM');
                doc.async = 'false';
                doc.loadXML(s);
            }
            else
                doc = (new DOMParser()).parseFromString(s, 'text/xml');
            return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
        };
    };
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *    is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *    used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */ 
$.fn.ajaxForm = function(options) {
    return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
        $(this).ajaxSubmit(options);
        return false;
    }).each(function() {
        // store options in hash
        $(":submit,input:image", this).bind('click.form-plugin',function(e) {
            var form = this.form;
            form.clk = this;
            if (this.type == 'image') {
                if (e.offsetX != undefined) {
                    form.clk_x = e.offsetX;
                    form.clk_y = e.offsetY;
                } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
                    var offset = $(this).offset();
                    form.clk_x = e.pageX - offset.left;
                    form.clk_y = e.pageY - offset.top;
                } else {
                    form.clk_x = e.pageX - this.offsetLeft;
                    form.clk_y = e.pageY - this.offsetTop;
                }
            }
            // clear form vars
            setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10);
        });
    });
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
    this.unbind('submit.form-plugin');
    return this.each(function() {
        $(":submit,input:image", this).unbind('click.form-plugin');
    });

};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
    var a = [];
    if (this.length == 0) return a;

    var form = this[0];
    var els = semantic ? form.getElementsByTagName('*') : form.elements;
    if (!els) return a;
    for(var i=0, max=els.length; i < max; i++) {
        var el = els[i];
        var n = el.name;
        if (!n) continue;

        if (semantic && form.clk && el.type == "image") {
            // handle image inputs on the fly when semantic == true
            if(!el.disabled && form.clk == el)
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
            continue;
        }

        var v = $.fieldValue(el, true);
        if (v && v.constructor == Array) {
            for(var j=0, jmax=v.length; j < jmax; j++)
                a.push({name: n, value: v[j]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: n, value: v});
    }

    if (!semantic && form.clk) {
        // input type=='image' are not found in elements array! handle them here
        var inputs = form.getElementsByTagName("input");
        for(var i=0, max=inputs.length; i < max; i++) {
            var input = inputs[i];
            var n = input.name;
            if(n && !input.disabled && input.type == "image" && form.clk == input)
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
        }
    }
    return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
    //hand off to jQuery.param for proper encoding
    return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
    var a = [];
    this.each(function() {
        var n = this.name;
        if (!n) return;
        var v = $.fieldValue(this, successful);
        if (v && v.constructor == Array) {
            for (var i=0,max=v.length; i < max; i++)
                a.push({name: n, value: v[i]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: this.name, value: v});
    });
    //hand off to jQuery.param for proper encoding
    return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 *
 *  <form><fieldset>
 *      <input name="A" type="text" />
 *      <input name="A" type="text" />
 *      <input name="B" type="checkbox" value="B1" />
 *      <input name="B" type="checkbox" value="B2"/>
 *      <input name="C" type="radio" value="C1" />
 *      <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *       array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
    for (var val=[], i=0, max=this.length; i < max; i++) {
        var el = this[i];
        var v = $.fieldValue(el, successful);
        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
            continue;
        v.constructor == Array ? $.merge(val, v) : val.push(v);
    }
    return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
    if (typeof successful == 'undefined') successful = true;

    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
        (t == 'checkbox' || t == 'radio') && !el.checked ||
        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
        tag == 'select' && el.selectedIndex == -1))
            return null;

    if (tag == 'select') {
        var index = el.selectedIndex;
        if (index < 0) return null;
        var a = [], ops = el.options;
        var one = (t == 'select-one');
        var max = (one ? index+1 : ops.length);
        for(var i=(one ? index : 0); i < max; i++) {
            var op = ops[i];
            if (op.selected) {
                // extra pain for IE...
                var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
                if (one) return v;
                a.push(v);
            }
        }
        return a;
    }
    return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
    return this.each(function() {
        $('input,select,textarea', this).clearFields();
    });
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
    return this.each(function() {
        var t = this.type, tag = this.tagName.toLowerCase();
        if (t == 'text' || t == 'password' || tag == 'textarea')
            this.value = '';
        else if (t == 'checkbox' || t == 'radio')
            this.checked = false;
        else if (tag == 'select')
            this.selectedIndex = -1;
    });
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
    return this.each(function() {
        // guard against an input with the name of 'reset'
        // note that IE reports the reset function as an 'object'
        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
            this.reset();
    });
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) { 
    if (b == undefined) b = true;
    return this.each(function() { 
        this.disabled = !b 
    });
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.selected = function(select) {
    if (select == undefined) select = true;
    return this.each(function() { 
        var t = this.type;
        if (t == 'checkbox' || t == 'radio')
            this.checked = select;
        else if (this.tagName.toLowerCase() == 'option') {
            var $sel = $(this).parent('select');
            if (select && $sel[0] && $sel[0].type == 'select-one') {
                // deselect all other options
                $sel.find('option').selected(false);
            }
            this.selected = select;
        }
    });
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
    if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
        window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
};

})(jQuery);


/*
 jQuery delayed observer - 0.5
 http://code.google.com/p/jquery-utils/

 (c) Maxime Haineault <haineault@gmail.com>
 http://haineault.com
 
 MIT License (http://www.opensource.org/licenses/mit-license.php)
 
 Changelog
 =========
 0.2 using closure, special thanks to Stephen Goguen & Tane Piper.
 0.3 now allow object chaining, added license
 0.4 code cleanup, added support for other events than keyup, fixed variable scope
 0.5 changed filename, included in jquery-utils 
 0.6 complete rewrite, same structure but more compact, 
     now using jquery's "data" method instead of a stack to store data
     it's now possible to change the condition, by default it's "if new this.val == this.oldval"
     now using this.each to support multiple observed elements
*/

(function($){
    $.extend($.fn, {
        delayedObserver: function(callback, delay, options){
            this.each(function(){
                var $obj    = $(this);
                var options = options || {};
                $obj.data('oldval',    $obj.val())
                    .data('delay',     delay || 0.5)
                    .data('condition', options.condition || function() {
                        return ($(this).data('oldval') == $(this).val());
                    })
                    .data('callback',  callback)
                    [(options.event||'keyup')](function(){
                        if ($obj.data('condition').apply($obj)) return;
                        else {
                            if ($obj.data('timer')) clearTimeout($obj.data('timer'));
                          
                            $obj.data('timer', setTimeout(function(){
                                $obj.data('callback').apply($obj);
                            }, $obj.data('delay') * 1000));
                          
                            $obj.data('oldval', $obj.val());
                        }
                    });
                });
        }
    });
})(jQuery);


// Delay Plugin for jQuery
// - http://www.evanbot.com
// - © 2008 Evan Byrne

jQuery.fn.delay = function(time,func){
	return this.each(function(){
		setTimeout(func,time);
	});
};

/*
 * jQuery history plugin
 *
 * Copyright (c) 2006 Taku Sano (Mikage Sawatari)
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Modified by Lincoln Cooper to add Safari support and only call the callback once during initialization
 * for msie when no initial hash supplied.
 */

jQuery.extend({
    historyCurrentHash: undefined,

    historyCallback: undefined,

    historyInit: function(callback) {
        jQuery.historyCallback = callback;
        var current_hash = location.hash;

        jQuery.historyCurrentHash = current_hash;
        if (jQuery.browser.msie) {
            // To stop the callback firing twice during initilization if no hash present
            if (jQuery.historyCurrentHash == '') {
                jQuery.historyCurrentHash = '#';
            }

            // add hidden iframe for IE
            jQuery("body").prepend('<iframe id="jQuery_history" style="display: none;"></iframe>');
            var ihistory = jQuery("#jQuery_history")[0];
            var iframe = ihistory.contentWindow.document;
            iframe.open();
            iframe.close();
            iframe.location.hash = current_hash;
        }
        else if (jQuery.browser.safari) {
            // etablish back/forward stacks
            jQuery.historyBackStack = [];
            jQuery.historyBackStack.length = history.length;
            jQuery.historyForwardStack = [];

            jQuery.isFirst = true;
        }
        jQuery.historyCallback(current_hash.replace(/^#/, ''));
        setInterval(jQuery.historyCheck, 100);
    },

    historyAddHistory: function(hash) {
        // This makes the looping function do something
        jQuery.historyBackStack.push(hash);

        jQuery.historyForwardStack.length = 0; // clear forwardStack (true click occured)
        this.isFirst = true;
    },

    historyCheck: function() {
        if (jQuery.browser.msie) {
            // On IE, check for location.hash of iframe
            var ihistory = jQuery("#jQuery_history")[0];
            var iframe = ihistory.contentDocument || ihistory.contentWindow.document;
            var current_hash = iframe.location.hash;
            if (current_hash != jQuery.historyCurrentHash) {

                location.hash = current_hash;
                jQuery.historyCurrentHash = current_hash;
                jQuery.historyCallback(current_hash.replace(/^#/, ''));

            }
        }
        else if (jQuery.browser.safari) {
            if (!jQuery.dontCheck) {
                var historyDelta = history.length - jQuery.historyBackStack.length;

                if (historyDelta) { // back or forward button has been pushed
                    jQuery.isFirst = false;
                    if (historyDelta < 0) { // back button has been pushed
                        // move items to forward stack
                        for (var i = 0; i < Math.abs(historyDelta);
                             i++) jQuery.historyForwardStack.unshift(jQuery.historyBackStack.pop());
                    }
                    else
                    { // forward button has been pushed
                        // move items to back stack
                        for (var i = 0; i < historyDelta;
                             i++) jQuery.historyBackStack.push(jQuery.historyForwardStack.shift());
                    }
                    var cachedHash = jQuery.historyBackStack[jQuery.historyBackStack.length - 1];
                    if (cachedHash != undefined) {
                        jQuery.historyCurrentHash = location.hash;
                        jQuery.historyCallback(cachedHash);
                    }
                }
                else if (jQuery.historyBackStack[jQuery.historyBackStack.length - 1] == undefined && !jQuery.isFirst) {
                    // back button has been pushed to beginning and URL already pointed to hash (e.g. a bookmark)
                    // document.URL doesn't change in Safari
                    if (document.URL.indexOf('#') >= 0) {
                        jQuery.historyCallback(document.URL.split('#')[1]);
                    }
                    else
                    {
                        var current_hash = location.hash;
                        jQuery.historyCallback('');
                    }
                    jQuery.isFirst = true;
                }
            }
        }
        else
        {
            // otherwise, check for location.hash
            var current_hash = location.hash;
            if (current_hash != jQuery.historyCurrentHash) {
                jQuery.historyCurrentHash = current_hash;
                jQuery.historyCallback(current_hash.replace(/^#/, ''));
            }
        }
    },
    historyLoad: function(hash) {
        var newhash;

        if (jQuery.browser.safari) {
            newhash = hash;
        }
        else
        {
            newhash = '#' + hash;
            location.hash = newhash;
        }
        jQuery.historyCurrentHash = newhash;

        if (jQuery.browser.msie) {
            var ihistory = jQuery("#jQuery_history")[0];
            var iframe = ihistory.contentWindow.document;
            iframe.open();
            iframe.close();
            iframe.location.hash = newhash;
            jQuery.historyCallback(hash);
        }
        else if (jQuery.browser.safari) {
            jQuery.dontCheck = true;
            // Manually keep track of the history values for Safari
            this.historyAddHistory(hash);

            // Wait a while before allowing checking so that Safari has time to update the "history" object
            // correctly (otherwise the check loop would detect a false change in hash).
            var fn = function() {
                jQuery.dontCheck = false;
            };
            window.setTimeout(fn, 200);
            jQuery.historyCallback(hash);
            // N.B. "location.hash=" must be the last line of code for Safari as execution stops afterwards.
            //      By explicitly using the "location.hash" command (instead of using a variable set to "location.hash") the
            //      URL in the browser and the "history" object are both updated correctly.
            location.hash = newhash;
        }
        else
        {
            jQuery.historyCallback(hash);
        }
    }
});




/* 
 * Auto Expanding Text Area (1.2.2)
 * by Chrys Bader (www.chrysbader.com)
 * chrysb@gmail.com
 *
 * Special thanks to:
 * Jake Chapa - jake@hybridstudio.com
 * John Resig - jeresig@gmail.com
 *
 * Copyright (c) 2008 Chrys Bader (www.chrysbader.com)
 * Licensed under the GPL (GPL-LICENSE.txt) license. 
 *
 *
 * NOTE: This script requires jQuery to work.  Download jQuery at www.jquery.com
 *
 */
 
(function(jQuery) {
		  
	var self = null;
 
	jQuery.fn.autogrow = function(o)
	{	
		return this.each(function() {
			new jQuery.autogrow(this, o);
		});
	};
	

    /**
     * The autogrow object.
     *
     * @constructor
     * @name jQuery.autogrow
     * @param Object e The textarea to create the autogrow for.
     * @param Hash o A set of key/value pairs to set as configuration properties.
     * @cat Plugins/autogrow
     */
	
	jQuery.autogrow = function (e, o)
	{
		this.options		  	= o || {};
		this.dummy			  	= null;
		this.interval	 	  	= null;
		this.line_height	  	= this.options.lineHeight || parseInt(jQuery(e).css('line-height'));
		this.min_height		  	= this.options.minHeight || parseInt(jQuery(e).css('min-height'));
		this.max_height		  	= this.options.maxHeight || parseInt(jQuery(e).css('max-height'));;
		this.textarea		  	= jQuery(e);
		
		if(this.line_height == NaN)
		  this.line_height = 0;
		
		// Only one textarea activated at a time, the one being used
		this.init();
	};
	
	jQuery.autogrow.fn = jQuery.autogrow.prototype = {
    autogrow: '1.2.2'
  };
	
 	jQuery.autogrow.fn.extend = jQuery.autogrow.extend = jQuery.extend;
	
	jQuery.autogrow.fn.extend({
						 
		init: function() {			
			var self = this;			
			this.textarea.css({overflow: 'hidden', display: 'block'});
			this.textarea.bind('focus', function() { self.startExpand() } ).bind('blur', function() { self.stopExpand() });
			this.checkExpand();	
		},
						 
		startExpand: function() {				
		  var self = this;
			this.interval = window.setInterval(function() {self.checkExpand()}, 400);
		},
		
		stopExpand: function() {
			clearInterval(this.interval);	
		},
		
		checkExpand: function() {
			
			if (this.dummy == null)
			{
				this.dummy = jQuery('<div></div>');
				this.dummy.css({
												'font-size'  : this.textarea.css('font-size'),
												'font-family': this.textarea.css('font-family'),
												'width'      : this.textarea.css('width'),
												'padding'    : this.textarea.css('padding'),
												'line-height': this.line_height + 'px',
												'overflow-x' : 'hidden',
												'position'   : 'absolute',
												'top'        : 0,
												'left'		 : -9999
												}).appendTo('body');
			}
			
			// Strip HTML tags
			var html = this.textarea.val().replace(/(<|>)/g, '');
			
			// IE is different, as per usual
			if (jQuery.browser.msie)
			{
				html = html.replace(/\n/g, '<BR>new');
			}
			else
			{
				html = html.replace(/\n/g, '<br>new');
			}
			
			if (this.dummy.html() != html)
			{
				this.dummy.html(html);	
				
				if (this.max_height > 0 && (this.dummy.height() + this.line_height > this.max_height))
				{
					this.textarea.css('overflow-y', 'auto');	
				}
				else
				{
					this.textarea.css('overflow-y', 'hidden');
					if (this.textarea.height() < this.dummy.height() + this.line_height || (this.dummy.height() < this.textarea.height()))
					{	
						this.textarea.animate({height: (this.dummy.height() + this.line_height) + 'px'}, 100);	
					}
				}
			}
		}
						 
	 });
})(jQuery);

/*
 * jQuery Lightbox_Me
 * By: Trent Richardson (jQuery impromptu)
 * Adapted By : Buck Wilson
 * Version : .8
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


(function(jQuery) {
	
	jQuery.fn.lightbox_me = function(options) {
		var defaults = {
			 prefix: 'lb', 
			loaded:function(){}, 
			callback:function(){}, 
			opacity:0.6, 
			zIndex: 999,
			closeButton: '#close',
			overlayspeed:'slow',
			Lightboxspeed:'fast', 
			scrollWithPage:true,
			show:'show',
			clickOverlayToClose: false
		}
		
		var o = jQuery.extend(defaults, options);
		
		return this.each(function() {
			var ie6 = (jQuery.browser.msie && jQuery.browser.version < 7);	
			var $body = jQuery(document.body);
			var $window = jQuery(window);
			var ele_id = jQuery(this).attr('id');

			var $container = jQuery('<div id="' + o.prefix + '_container"></div>');

			if(ie6) jQuery('select').css('visibility','hidden');
			$container.append(jQuery(this)).append(jQuery('<div class="' + o.prefix + '_overlay" id="' + o.prefix + '_overlay"></div>'));


			$body.append($container);
			var $passed_ele = jQuery(this);
			var $overlay = $container.children('#' + o.prefix + '_overlay');
			

			var getWindowScrollOffset = function(){ 
				return (document.documentElement.scrollTop || document.body.scrollTop) + 'px'; 
			};		

			var getWindowSize = function(){ 
				var size = {
					width: window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth),
					height: window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight)
				};
				return size;
			};

			var ie6scroll = function(){ 
				$container.css({ top: getWindowScrollOffset() }); 
			};

			var escapeKeyCloseLightbox = function(e){
				if(e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)) removeLightbox();
			};

			var positionLightbox = function(){
				var wsize = getWindowSize();
				if (o.scrollWithPage) {
					$container.css({ position: (ie6)? "absolute" : "fixed", height: wsize.height, width: "100%", top: (ie6)? getWindowScrollOffset():0, left: 0, right: 0, bottom: 0, zIndex: o.zIndex });
				} else {
					$container.css({width: "100%", top: (ie6)? getWindowScrollOffset():0, left: 0, right: 0, bottom: 0, zIndex: o.zIndex });
				}
				var wheight = ((o.scrollWithPage)? wsize.height : $body.height());
				$overlay.css({ position: "absolute", height: wheight, width: "100%", top: 0, left: 0, right: 0, bottom: 0 });
				$passed_ele.css({ position: "absolute", top: "40px", left: "50%", marginLeft: (((($passed_ele.css("paddingLeft").split("px")[0]*1) + $passed_ele.width())/2)*-1) });					
			};

			var styleLightbox = function(){
				$overlay.css({ zIndex: o.zIndex, display: "none", opacity: o.opacity });
				$passed_ele.css({ zIndex: o.zIndex+1, display: "none" });
			}

			var removeLightbox = function(clicked){
				jQuery('body').prepend($passed_ele.hide());
				if(ie6 && o.scrollWithPage) $body.unbind('scroll',ie6scroll);//ie6, remove the scroll event
				$window.unbind('resize',positionLightbox);			
				$overlay.fadeOut('normal',function(){
					$overlay.remove();
					if(o.callback()) o.callback();
					$window.unbind('keypress',escapeKeyCloseLightbox);
					if(ie6) jQuery('select').css('visibility','visible');
					$container.remove();
				});
                return false;
            };

			positionLightbox();
			styleLightbox();

			if(ie6 && o.scrollWithPage) $window.scroll(ie6scroll);//ie6, add a scroll event to fix position:fixed
			$window.resize(positionLightbox);
			$window.keypress(escapeKeyCloseLightbox);
			$passed_ele.find(o.closeButton).click(removeLightbox);
			if (o.clickOverlayToClose) {
				$overlay.click(removeLightbox);
			}
			
			
			//Show it
			$overlay.fadeIn(o.overlayspeed);
			$passed_ele[o.show](o.Lightboxspeed,o.loaded);	
		});
		
	}
})(jQuery);

/**
 * Flash (http://jquery.lukelutman.com/plugins/flash)
 * A jQuery plugin for embedding Flash movies.
 * 
 * Version 1.0
 * November 9th, 2006
 *
 * Copyright (c) 2006 Luke Lutman (http://www.lukelutman.com)
 * Dual licensed under the MIT and GPL licenses.
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.opensource.org/licenses/gpl-license.php
 * 
 * Inspired by:
 * SWFObject (http://blog.deconcept.com/swfobject/)
 * UFO (http://www.bobbyvandersluis.com/ufo/)
 * sIFR (http://www.mikeindustries.com/sifr/)
 * 
 * IMPORTANT: 
 * The packed version of jQuery breaks ActiveX control
 * activation in Internet Explorer. Use JSMin to minifiy
 * jQuery (see: http://jquery.lukelutman.com/plugins/flash#activex).
 *
 **/ 
;(function(){
	
var $$;

/**
 * 
 * @desc Replace matching elements with a flash movie.
 * @author Luke Lutman
 * @version 1.0.1
 *
 * @name flash
 * @param Hash htmlOptions Options for the embed/object tag.
 * @param Hash pluginOptions Options for detecting/updating the Flash plugin (optional).
 * @param Function replace Custom block called for each matched element if flash is installed (optional).
 * @param Function update Custom block called for each matched if flash isn't installed (optional).
 * @type jQuery
 *
 * @cat plugins/flash
 * 
 * @example $('#hello').flash({ src: 'hello.swf' });
 * @desc Embed a Flash movie.
 *
 * @example $('#hello').flash({ src: 'hello.swf' }, { version: 8 });
 * @desc Embed a Flash 8 movie.
 *
 * @example $('#hello').flash({ src: 'hello.swf' }, { expressInstall: true });
 * @desc Embed a Flash movie using Express Install if flash isn't installed.
 *
 * @example $('#hello').flash({ src: 'hello.swf' }, { update: false });
 * @desc Embed a Flash movie, don't show an update message if Flash isn't installed.
 *
**/
$$ = jQuery.fn.flash = function(htmlOptions, pluginOptions, replace, update) {
	
	// Set the default block.
	var block = replace || $$.replace;
	
	// Merge the default and passed plugin options.
	pluginOptions = $$.copy($$.pluginOptions, pluginOptions);
	
	// Detect Flash.
	if(!$$.hasFlash(pluginOptions.version)) {
		// Use Express Install (if specified and Flash plugin 6,0,65 or higher is installed).
		if(pluginOptions.expressInstall && $$.hasFlash(6,0,65)) {
			// Add the necessary flashvars (merged later).
			var expressInstallOptions = {
				flashvars: {  	
					MMredirectURL: location,
					MMplayerType: 'PlugIn',
					MMdoctitle: jQuery('title').text() 
				}					
			};
		// Ask the user to update (if specified).
		} else if (pluginOptions.update) {
			// Change the block to insert the update message instead of the flash movie.
			block = update || $$.update;
		// Fail
		} else {
			// The required version of flash isn't installed.
			// Express Install is turned off, or flash 6,0,65 isn't installed.
			// Update is turned off.
			// Return without doing anything.
			return this;
		}
	}
	
	// Merge the default, express install and passed html options.
	htmlOptions = $$.copy($$.htmlOptions, expressInstallOptions, htmlOptions);
	
	// Invoke $block (with a copy of the merged html options) for each element.
	return this.each(function(){
		block.call(this, $$.copy(htmlOptions));
	});
	
};
/**
 *
 * @name flash.copy
 * @desc Copy an arbitrary number of objects into a new object.
 * @type Object
 * 
 * @example $$.copy({ foo: 1 }, { bar: 2 });
 * @result { foo: 1, bar: 2 };
 *
**/
$$.copy = function() {
	var options = {}, flashvars = {};
	for(var i = 0; i < arguments.length; i++) {
		var arg = arguments[i];
		if(arg == undefined) continue;
		jQuery.extend(options, arg);
		// don't clobber one flash vars object with another
		// merge them instead
		if(arg.flashvars == undefined) continue;
		jQuery.extend(flashvars, arg.flashvars);
	}
	options.flashvars = flashvars;
	return options;
};
/*
 * @name flash.hasFlash
 * @desc Check if a specific version of the Flash plugin is installed
 * @type Boolean
 *
**/
$$.hasFlash = function() {
	// look for a flag in the query string to bypass flash detection
	if(/hasFlash\=true/.test(location)) return true;
	if(/hasFlash\=false/.test(location)) return false;
	var pv = $$.hasFlash.playerVersion().match(/\d+/g);
	var rv = String([arguments[0], arguments[1], arguments[2]]).match(/\d+/g) || String($$.pluginOptions.version).match(/\d+/g);
	for(var i = 0; i < 3; i++) {
		pv[i] = parseInt(pv[i] || 0);
		rv[i] = parseInt(rv[i] || 0);
		// player is less than required
		if(pv[i] < rv[i]) return false;
		// player is greater than required
		if(pv[i] > rv[i]) return true;
	}
	// major version, minor version and revision match exactly
	return true;
};
/**
 *
 * @name flash.hasFlash.playerVersion
 * @desc Get the version of the installed Flash plugin.
 * @type String
 *
**/
$$.hasFlash.playerVersion = function() {
	// ie
	try {
		try {
			// avoid fp6 minor version lookup issues
			// see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
			var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
			try { axo.AllowScriptAccess = 'always';	} 
			catch(e) { return '6,0,0'; }				
		} catch(e) {}
		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
	// other browsers
	} catch(e) {
		try {
			if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
				return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
			}
		} catch(e) {}		
	}
	return '0,0,0';
};
/**
 *
 * @name flash.htmlOptions
 * @desc The default set of options for the object or embed tag.
 *
**/
$$.htmlOptions = {
	height: 240,
	flashvars: {},
	pluginspage: 'http://www.adobe.com/go/getflashplayer',
	src: '#',
	type: 'application/x-shockwave-flash',
	width: 320		
};
/**
 *
 * @name flash.pluginOptions
 * @desc The default set of options for checking/updating the flash Plugin.
 *
**/
$$.pluginOptions = {
	expressInstall: false,
	update: true,
	version: '6.0.65'
};
/**
 *
 * @name flash.replace
 * @desc The default method for replacing an element with a Flash movie.
 *
**/
$$.replace = function(htmlOptions) {
	this.innerHTML = '<div class="alt">'+this.innerHTML+'</div>';
	jQuery(this)
		.addClass('flash-replaced')
		.prepend($$.transform(htmlOptions));
};
/**
 *
 * @name flash.update
 * @desc The default method for replacing an element with an update message.
 *
**/
$$.update = function(htmlOptions) {
	var url = String(location).split('?');
	url.splice(1,0,'?hasFlash=true&');
	url = url.join('');
	var msg = '<p>This content requires the Flash Player. <a href="http://www.adobe.com/go/getflashplayer">Download Flash Player</a>. Already have Flash Player? <a href="'+url+'">Click here.</a></p>';
	this.innerHTML = '<span class="alt">'+this.innerHTML+'</span>';
	jQuery(this)
		.addClass('flash-update')
		.prepend(msg);
};
/**
 *
 * @desc Convert a hash of html options to a string of attributes, using Function.apply(). 
 * @example toAttributeString.apply(htmlOptions)
 * @result foo="bar" foo="bar"
 *
**/
function toAttributeString() {
	var s = '';
	for(var key in this)
		if(typeof this[key] != 'function')
			s += key+'="'+this[key]+'" ';
	return s;		
};
/**
 *
 * @desc Convert a hash of flashvars to a url-encoded string, using Function.apply(). 
 * @example toFlashvarsString.apply(flashvarsObject)
 * @result foo=bar&foo=bar
 *
**/
function toFlashvarsString() {
	var s = '';
	for(var key in this)
		if(typeof this[key] != 'function')
			s += key+'='+encodeURIComponent(this[key])+'&';
	return s.replace(/&$/, '');		
};
/**
 *
 * @name flash.transform
 * @desc Transform a set of html options into an embed tag.
 * @type String 
 *
 * @example $$.transform(htmlOptions)
 * @result <embed src="foo.swf" ... />
 *
 * Note: The embed tag is NOT standards-compliant, but it 
 * works in all current browsers. flash.transform can be
 * overwritten with a custom function to generate more 
 * standards-compliant markup.
 *
**/
$$.transform = function(htmlOptions) {
	htmlOptions.toString = toAttributeString;
	if(htmlOptions.flashvars) htmlOptions.flashvars.toString = toFlashvarsString;
	return '<embed ' + String(htmlOptions) + '/>';		
};

/**
 *
 * Flash Player 9 Fix (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
 *
**/
if (window.attachEvent) {
	window.attachEvent("onbeforeunload", function(){
		__flash_unloadHandler = function() {};
		__flash_savedUnloadHandler = function() {};
	});
}
	
})();

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

/*
 * Metadata - jQuery plugin for parsing metadata from elements
 *
 * Copyright (c) 2006 John Resig, Yehuda Katz, Jï¿½Ã¶rn Zaefferer, Paul McLanahan
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.metadata.js 3640 2007-10-11 18:34:38Z pmclanahan $
 *
 */

/**
 * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
 * in the JSON will become a property of the element itself.
 *
 * There are three supported types of metadata storage:
 *
 *   attr:  Inside an attribute. The name parameter indicates *which* attribute.
 *          
 *   class: Inside the class attribute, wrapped in curly braces: { }
 *   
 *   elem:  Inside a child element (e.g. a script tag). The
 *          name parameter indicates *which* element.
 *          
 * The metadata for an element is loaded the first time the element is accessed via jQuery.
 *
 * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
 * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
 * 
 * @name $.metadata.setType
 *
 * @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("class")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from the class attribute
 * 
 * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("attr", "data")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a "data" attribute
 * 
 * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
 * @before $.metadata.setType("elem", "script")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a nested script element
 * 
 * @param String type The encoding type
 * @param String name The name of the attribute to be used to get metadata (optional)
 * @cat Plugins/Metadata
 * @descr Sets the type of encoding to be used when loading metadata for the first time
 * @type undefined
 * @see metadata()
 */

(function($) {

$.extend({
	metadata : {
		defaults : {
			type: 'class',
			name: 'metadata',
			cre: /({.*})/,
			single: 'metadata'
		},
		setType: function( type, name ){
			this.defaults.type = type;
			this.defaults.name = name;
		},
		get: function( elem, opts ){
			var settings = $.extend({},this.defaults,opts);
			// check for empty string in single property
			if ( !settings.single.length ) settings.single = 'metadata';
			
			var data = $.data(elem, settings.single);
			// returned cached data if it already exists
			if ( data ) return data;
			
			data = "{}";
			
			if ( settings.type == "class" ) {
				var m = settings.cre.exec( elem.className );
				if ( m )
					data = m[1];
			} else if ( settings.type == "elem" ) {
				if( !elem.getElementsByTagName ) return;
				var e = elem.getElementsByTagName(settings.name);
				if ( e.length )
					data = $.trim(e[0].innerHTML);
			} else if ( elem.getAttribute != undefined ) {
				var attr = elem.getAttribute( settings.name );
				if ( attr )
					data = attr;
			}
			
			if ( data.indexOf( '{' ) <0 )
			data = "{" + data + "}";
			
			data = eval("(" + data + ")");
			
			$.data( elem, settings.single, data );
			return data;
		}
	}
});

/**
 * Returns the metadata object for the first member of the jQuery object.
 *
 * @name metadata
 * @descr Returns element's metadata object
 * @param Object opts An object contianing settings to override the defaults
 * @type jQuery
 * @cat Plugins/Metadata
 */
$.fn.metadata = function( opts ){
	return $.metadata.get( this[0], opts );
};

})(jQuery);

/*
 * jQuery Media Plugin for converting elements into rich media content.
 *
 * Examples and documentation at: http://malsup.com/jquery/media/
 * Copyright (c) 2007-2008 M. Alsup
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * @author: M. Alsup
 * @version: 0.85 (07-FEB-2009)
 * @requires jQuery v1.1.2 or later
 * $Id: jquery.media.js 2460 2007-07-23 02:53:15Z malsup $
 *
 * Supported Media Players:
 *    - Flash
 *    - Quicktime
 *    - Real Player
 *    - Silverlight
 *    - Windows Media Player
 *    - iframe
 *
 * Supported Media Formats:
 *   Any types supported by the above players, such as:
 *     Video: asf, avi, flv, mov, mpg, mpeg, mp4, qt, smil, swf, wmv, 3g2, 3gp
 *     Audio: aif, aac, au, gsm, mid, midi, mov, mp3, m4a, snd, rm, wav, wma
 *     Other: bmp, html, pdf, psd, qif, qtif, qti, tif, tiff, xaml
 *
 * Thanks to Mark Hicken and Brent Pedersen for helping me debug this on the Mac!
 * Thanks to Dan Rossi for numerous bug reports and code bits!
 */
;(function($) {

/**
 * Chainable method for converting elements into rich media.
 *
 * @param options
 * @param callback fn invoked for each matched element before conversion
 * @param callback fn invoked for each matched element after conversion
 */
$.fn.media = function(options, f1, f2) {
    return this.each(function() {
        if (typeof options == 'function') {
            f2 = f1;
            f1 = options;
            options = {};
        }
        var o = getSettings(this, options);
        // pre-conversion callback, passes original element and fully populated options
        if (typeof f1 == 'function') f1(this, o);
        
        var r = getTypesRegExp();
        var m = r.exec(o.src) || [''];
        o.type ? m[0] = o.type : m.shift();
        for (var i=0; i < m.length; i++) {
            fn = m[i].toLowerCase();
            if (isDigit(fn[0])) fn = 'fn' + fn; // fns can't begin with numbers
            if (!$.fn.media[fn]) 
                continue;  // unrecognized media type
            // normalize autoplay settings
            var player = $.fn.media[fn+'_player'];
            if (!o.params) o.params = {};
            if (player) {
                var num = player.autoplayAttr == 'autostart';
                o.params[player.autoplayAttr || 'autoplay'] = num ? (o.autoplay ? 1 : 0) : o.autoplay ? true : false;
            }
            var $div = $.fn.media[fn](this, o);

            $div.css('backgroundColor', o.bgColor).width(o.width);
            // post-conversion callback, passes original element, new div element and fully populated options
            if (typeof f2 == 'function') f2(this, $div[0], o, player.name);
            break;
        }
    });
};

/**
 * Non-chainable method for adding or changing file format / player mapping
 * @name mapFormat
 * @param String format File format extension (ie: mov, wav, mp3)
 * @param String player Player name to use for the format (one of: flash, quicktime, realplayer, winmedia, silverlight or iframe
 */
$.fn.media.mapFormat = function(format, player) {
    if (!format || !player || !$.fn.media.defaults.players[player]) return; // invalid
    format = format.toLowerCase();
    if (isDigit(format[0])) format = 'fn' + format;
    $.fn.media[format] = $.fn.media[player];
    $.fn.media[format+'_player'] = $.fn.media.defaults.players[player];
};

// global defautls; override as needed
$.fn.media.defaults = {
    width:         400,
    height:        400,
    autoplay:      0,         // normalized cross-player setting
    bgColor:       '#ffffff', // background color
    params:        { wmode: 'transparent'},  // added to object element as param elements; added to embed element as attrs
    attrs:         {},        // added to object and embed elements as attrs
    flvKeyName:    'file',    // key used for object src param (thanks to Andrea Ercolino)
    flashvars:     {},        // added to flash content as flashvars param/attr
    flashVersion:  '7',       // required flash version
    expressInstaller: null,   // src for express installer
    
    // default flash video and mp3 player (@see: http://jeroenwijering.com/?item=Flash_Media_Player)
    flvPlayer:     'mediaplayer.swf',
    mp3Player:     'mediaplayer.swf',
    
    // @see http://msdn2.microsoft.com/en-us/library/bb412401.aspx
    silverlight: {
        inplaceInstallPrompt: 'true', // display in-place install prompt?
        isWindowless:         'true', // windowless mode (false for wrapping markup)
        framerate:            '24',   // maximum framerate
        version:              '0.9',  // Silverlight version
        onError:              null,   // onError callback
        onLoad:               null,   // onLoad callback
        initParams:           null,   // object init params
        userContext:          null    // callback arg passed to the load callback
    }
};

// Media Players; think twice before overriding
$.fn.media.defaults.players = {
    flash: {
        name:         'flash',
        types:        'flv,mp3,swf',
        oAttrs:   {
            classid:  'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
            type:     'application/x-oleobject',
            codebase: 'http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=' + $.fn.media.defaults.flashVersion
        },
        eAttrs: {
            type:         'application/x-shockwave-flash',
            pluginspage:  'http://www.adobe.com/go/getflashplayer'
        }        
    },
    quicktime: {
        name:         'quicktime',
        types:        'aif,aiff,aac,au,bmp,gsm,mov,mid,midi,mpg,mpeg,mp4,m4a,psd,qt,qtif,qif,qti,snd,tif,tiff,wav,3g2,3gp',
        oAttrs:   {
            classid:  'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B',
            codebase: 'http://www.apple.com/qtactivex/qtplugin.cab'
        },
        eAttrs: {
            pluginspage:  'http://www.apple.com/quicktime/download/'
        }
    },
    realplayer: {
        name:         'real',
        types:        'ra,ram,rm,rpm,rv,smi,smil',
        autoplayAttr: 'autostart',
        oAttrs:   {
            classid:  'clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'
        },
        eAttrs: {
            type:         'audio/x-pn-realaudio-plugin',
            pluginspage:  'http://www.real.com/player/'
        }
    },
    winmedia: {
        name:         'winmedia',
        types:        'asx,asf,avi,wma,wmv',
        autoplayAttr: 'autostart',
        oUrl:         'url',
        oAttrs:   {
            classid:  'clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6',
            type:     'application/x-oleobject'
        },
        eAttrs: {
            type:         $.browser.mozilla && isFirefoxWMPPluginInstalled() ? 'application/x-ms-wmp' : 'application/x-mplayer2',
            pluginspage:  'http://www.microsoft.com/Windows/MediaPlayer/'
        }        
    },
    // special cases
    iframe: {
        name:  'iframe',
        types: 'html,pdf'
    },
    silverlight: {
        name:  'silverlight',
        types: 'xaml'
    }
};

//
//  everything below here is private
//


// detection script for FF WMP plugin (http://www.therossman.org/experiments/wmp_play.html)
// (hat tip to Mark Ross for this script)
function isFirefoxWMPPluginInstalled() {
    var plugs = navigator.plugins;
    for (i = 0; i < plugs.length; i++) {
        var plugin = plugs[i];
        if (plugin['filename'] == 'np-mswmp.dll')
            return true;
    }
    return false;
}

var counter = 1;

for (var player in $.fn.media.defaults.players) {
    var types = $.fn.media.defaults.players[player].types;
    $.each(types.split(','), function(i,o) {
        if (isDigit(o[0])) o = 'fn' + o;
        $.fn.media[o] = $.fn.media[player] = getGenerator(player);
        $.fn.media[o+'_player'] = $.fn.media.defaults.players[player];
    });
};

function getTypesRegExp() {
    var types = '';
    for (var player in $.fn.media.defaults.players) {
        if (types.length) types += ',';
        types += $.fn.media.defaults.players[player].types;
    };
    return new RegExp('\\.(' + types.replace(/,/g,'|') + ')$\\b');
};

function getGenerator(player) {
    return function(el, options) {
        return generate(el, options, player);
    };
};

function isDigit(c) {
    return '0123456789'.indexOf(c) > -1;
};

// flatten all possible options: global defaults, meta, option obj
function getSettings(el, options) {
    options = options || {};
    var $el = $(el);
    var cls = el.className || '';
    // support metadata plugin (v1.0 and v2.0)
    var meta = $.metadata ? $el.metadata() : $.meta ? $el.data() : {};
    meta = meta || {};
    var w = meta.width  || parseInt(((cls.match(/w:(\d+)/)||[])[1]||0));
    var h = meta.height || parseInt(((cls.match(/h:(\d+)/)||[])[1]||0));
   
    if (w) meta.width  = w;
    if (h) meta.height = h;
    if (cls) meta.cls = cls;

    var a = $.fn.media.defaults;
    var b = options;
    var c = meta;

    var p = { params: { bgColor: options.bgColor || $.fn.media.defaults.bgColor } };
    var opts = $.extend({}, a, b, c);
    $.each(['attrs','params','flashvars','silverlight'], function(i,o) {
        opts[o] = $.extend({}, p[o] || {}, a[o] || {}, b[o] || {}, c[o] || {});
    });

    if (typeof opts.caption == 'undefined') opts.caption = $el.text();

    // make sure we have a source!
    opts.src = opts.src || $el.attr('href') || $el.attr('src') || 'unknown';
    return opts;
};

//
//  Flash Player
//

// generate flash using SWFObject library if possible
$.fn.media.swf = function(el, opts) {
    if (!window.SWFObject && !window.swfobject) {
        // roll our own
        if (opts.flashvars) {
            var a = [];
            for (var f in opts.flashvars)
                a.push(f + '=' + opts.flashvars[f]);
            if (!opts.params) opts.params = {};
            opts.params.flashvars = a.join('&');
        }
        return generate(el, opts, 'flash');
    }

    var id = el.id ? (' id="'+el.id+'"') : '';
    var cls = opts.cls ? (' class="' + opts.cls + '"') : '';
    var $div = $('<div' + id + cls + '>');

    // swfobject v2+
    if (window.swfobject) {
        $(el).after($div).appendTo($div);
        if (!el.id) el.id = 'movie_player_' + counter++;

        // replace el with swfobject content
        swfobject.embedSWF(opts.src, el.id, opts.width, opts.height, opts.flashVersion, 
            opts.expressInstaller, opts.flashvars, opts.params, opts.attrs);
    }
    // swfobject < v2
    else {
        $(el).after($div).remove();
        var so = new SWFObject(opts.src, 'movie_player_' + counter++, opts.width, opts.height, opts.flashVersion, opts.bgColor);
        if (opts.expressInstaller) so.useExpressInstall(opts.expressInstaller);    

        for (var p in opts.params)
            if (p != 'bgColor') so.addParam(p, opts.params[p]);
        for (var f in opts.flashvars)
            so.addVariable(f, opts.flashvars[f]);
        so.write($div[0]);
    }

    if (opts.caption) $('<div>').appendTo($div).html(opts.caption);
    return $div;
};

// map flv and mp3 files to the swf player by default
$.fn.media.flv = $.fn.media.mp3 = function(el, opts) {
    var src = opts.src;
    var player = /\.mp3\b/i.test(src) ? $.fn.media.defaults.mp3Player : $.fn.media.defaults.flvPlayer;
    var key = opts.flvKeyName;
    src = encodeURIComponent(src);
    opts.src = player;
    opts.src = opts.src + '?'+key+'=' + (src);
    var srcObj = {};
    srcObj[key] = src;
    opts.flashvars = $.extend({}, srcObj, opts.flashvars );
    return $.fn.media.swf(el, opts);
};

//
//  Silverlight
//
$.fn.media.xaml = function(el, opts) {
    if (!window.Sys || !window.Sys.Silverlight) {
        if ($.fn.media.xaml.warning) return;
        $.fn.media.xaml.warning = 1;
        alert('You must include the Silverlight.js script.');
        return;
    }

    var props = {
        width: opts.width,
        height: opts.height,
        background: opts.bgColor,
        inplaceInstallPrompt: opts.silverlight.inplaceInstallPrompt,
        isWindowless: opts.silverlight.isWindowless,
        framerate: opts.silverlight.framerate,
        version: opts.silverlight.version
    };
    var events = {
        onError: opts.silverlight.onError,
        onLoad: opts.silverlight.onLoad
    };

    var id1 = el.id ? (' id="'+el.id+'"') : '';
    var id2 = opts.id || 'AG' + counter++;
    // convert element to div
    var cls = opts.cls ? (' class="' + opts.cls + '"') : '';
    var $div = $('<div' + id1 + cls + '>');
    $(el).after($div).remove();
    
    Sys.Silverlight.createObjectEx({
        source: opts.src,
        initParams: opts.silverlight.initParams,
        userContext: opts.silverlight.userContext,
        id: id2,
        parentElement: $div[0],
        properties: props,
        events: events
    });

    if (opts.caption) $('<div>').appendTo($div).html(opts.caption);
    return $div;
};

//
// generate object/embed markup
//
function generate(el, opts, player) {
    var $el = $(el);
    var o = $.fn.media.defaults.players[player];
    
    if (player == 'iframe') {
        var o = $('<iframe' + ' width="' + opts.width + '" height="' + opts.height + '" >');
        o.attr('src', opts.src);
        o.css('backgroundColor', o.bgColor);
    }
    else if ($.browser.msie) {
        var a = ['<object width="' + opts.width + '" height="' + opts.height + '" '];
        for (var key in opts.attrs)
            a.push(key + '="'+opts.attrs[key]+'" ');
        for (var key in o.oAttrs || {}) {
            var v = o.oAttrs[key];
            if (key == 'codebase' && window.location.protocol == 'https')
                v = v.replace('http','https');
            a.push(key + '="'+v+'" ');
        }
        a.push('></ob'+'ject'+'>');
        var p = ['<param name="' + (o.oUrl || 'src') +'" value="' + opts.src + '">'];
        for (var key in opts.params)
            p.push('<param name="'+ key +'" value="' + opts.params[key] + '">');
        var o = document.createElement(a.join(''));
        for (var i=0; i < p.length; i++)
            o.appendChild(document.createElement(p[i]));
    }
    else {
        var a = ['<embed width="' + opts.width + '" height="' + opts.height + '" style="display:block"'];
        if (opts.src) a.push(' src="' + opts.src + '" ');
        for (var key in opts.attrs)
            a.push(key + '="'+opts.attrs[key]+'" ');
        for (var key in o.eAttrs || {})
            a.push(key + '="'+o.eAttrs[key]+'" ');
        for (var key in opts.params)
            if (key != 'wmode') // FF3/Quicktime borks on wmode
                a.push(key + '="'+opts.params[key]+'" ');
        a.push('></em'+'bed'+'>');
    }
    // convert element to div
    var id = el.id ? (' id="'+el.id+'"') : '';
    var cls = opts.cls ? (' class="' + opts.cls + '"') : '';
    var $div = $('<div' + id + cls + '>');
    $el.after($div).remove();
    ($.browser.msie || player == 'iframe') ? $div.append(o) : $div.html(a.join(''));
    if (opts.caption) $('<div>').appendTo($div).html(opts.caption);
    return $div;
};


})(jQuery);


/*

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","javascript:false;");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.xGetCSFunc = 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){
            return function(s){ return function(p){
                var v = s.getPropertyValue(p);
                if($str(v) && v.indexOf("px") > 0){
                    v = v.substr(0,v.indexOf("px"));
                    v = parseInt(v);
                }
                return v;
            } }(s);
        }
      }
      else if(e.currentStyle) {
            return function(cs){ return function(p){
                var v = cs[jive.ext.x.xCamelize(p)];
                if($str(v) && v.indexOf("px") > 0){
                    v = v.substr(0,v.indexOf("px"));
                    v = parseInt(v);
                }
                return v;
            } }(e.currentStyle);
      }
      else return function(){ return "undefined"; };
    }catch(e){
        return function(){ return "undefined"; };
    }
}
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 a;
	}
}


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

    var places_cache = new jive.model.PlacesCache(that);

    this.getPlacesCache = function(){
        return places_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.PlacesCache = function(control){

    var that = this;

    // enum, matches Java enum Places.Type
    var types = new Array();
    types.push("FOLLOWED_ALL"); // Followed
    types.push("FOLLOWED_COMMUNITY"); // Communities
    types.push("FOLLOWED_PROJECT"); // Communities
    types.push("FOLLOWED_GROUP"); // Communities
    types.push("COMMUNITY"); // Communities
    types.push("PROJECT"); // Projects
    types.push("GROUP"); // Groups

    // cache of 7 lists of types: FollowedPlaces (4 types), Communities, Groups, Projects.
    var cache = new jive.ext.y.HashTable();
    for (var i = 0; i < types.length; i++) {
        cache.put(types[i], new Array());
    }

    this.getPlaces = function(type){
        return cache.get(type);
    }

    var initialized = false;
    this.isInitialized = function(){
        return initialized;
    }

    // loop over list of place objects, place in the appropriate cache.
    this.loadPlaces = function(list, type){

        // currentPosition is based on the type argument
        var currentPosition = cache.get(type).length;
        for (var i = 0; i < list.length; i++) {
            var place = list[i];
            // use the place.type for obtaining the cache, since followed types
            // are combined in one list when loaded.
            var placesCache = cache.get(place.type);
            placesCache.push(place);
            cache.put(place.type, placesCache);
        }
        return currentPosition;
    }

    // fetches more places for the specified type
    this.morePlaces = function(placesArgs){
        that.notifyLoadBegin();
        // DWR call, call loadPlaces with results
        if (placesArgs.type.startsWith("FOLLOWED")) {
            PlacesActionBean.getFollowedPlaces(placesArgs.page, {
                callback:function(data) {
                    var currentPosition = that.loadPlaces(data, placesArgs.type);
                    if (placesArgs.refreshAllFollowedTypes) {
                        that.notifyLoadFinish({'type':"FOLLOWED_ALL", 'startIndex': 0});
                        that.notifyLoadFinish({'type':"FOLLOWED_COMMUNITY", 'startIndex':0});
                        that.notifyLoadFinish({'type':"FOLLOWED_PROJECT", 'startIndex':0});
                        that.notifyLoadFinish({'type':"FOLLOWED_GROUP", 'startIndex':0});
                    }
                    else {
                        that.notifyLoadFinish(placesArgs, currentPosition);
                    }
                },
                errorHandler:function(msg, e) {
                    that.notifyLoadFail();
                }
            });
        }
        else if ("COMMUNITY" == placesArgs.type) {
            PlacesActionBean.getCommunities(placesArgs.communityID, placesArgs.page, {
                callback:function(data) {
                    var currentPosition = that.loadPlaces(data, placesArgs.type);
                    that.notifyLoadFinish(placesArgs, currentPosition);
                },
                errorHandler:function(msg, e) {
                    that.notifyLoadFail();
                }
            });
        }
        else if ("GROUP" == placesArgs.type) {
            PlacesActionBean.getGroups(placesArgs.page, {
                callback:function(data) {
                    var currentPosition = that.loadPlaces(data, placesArgs.type);
                    that.notifyLoadFinish(placesArgs, currentPosition);
                },
                errorHandler:function(msg, e) {
                    that.notifyLoadFail();
                }
            });
        }
        else if ("PROJECT" == placesArgs.type) {
            PlacesActionBean.getProjects(placesArgs.page, {
                callback:function(data) {
                    var currentPosition = that.loadPlaces(data, placesArgs.type);
                    that.notifyLoadFinish(placesArgs, currentPosition);
                },
                errorHandler:function(msg, e) {
                    that.notifyLoadFail();
                }
            });
        }
    }

    // expects an Array of Places DWR objects - on page load, this is called from FTL w/ json feed
    this.loadExternalPlaces = function(all){
        // populated by the FTL from widget properties.
         try{
             var loadTypes = all.toKeysArray();

             // for some reason toKeysArray() returns a ton of extra keys, like 'each', etc.
             // match the key agains the set of known types.
             for (var i = 0; i < loadTypes.length; i++) {
                 for (var j = 0; j < types.length; j++) {
                     if (loadTypes[i] == types[j]) {
                        that.loadPlaces(all.get(loadTypes[i]), loadTypes[i]);
                        that.notifyLoadPlaces(loadTypes[i]);
                     }
                 }
             }

        }catch(e){
            that.notifyLoadFail();
        }
        initialized = true;
        return null;
    }

    this.reloadPlaces = function(type) {

        // currently only implemented for Followed places, since communities, groups, and projects
        // don't need to dynamically refresh.
        if (type.startsWith("FOLLOWED")) {
            that.notifyResetPlaces("FOLLOWED_ALL");
            cache.put("FOLLOWED_ALL", new Array());
            cache.put("FOLLOWED_COMMUNITY", new Array());
            cache.put("FOLLOWED_GROUP", new Array());
            cache.put("FOLLOWED_PROJECT", new Array());

            that.morePlaces({'type':"FOLLOWED_ALL", 'page':-1, 'refreshAllFollowedTypes':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.notifyLoadPlaces = function(type){
        working++;
        for(var i=0;i<listeners.length;i++){
            listeners[i].loadPlaces(type);
        }
        working--;
        that.executeListenerActions();
    }

    this.notifyLoadBegin = function(){
        working++;
        for(var i=0;i<listeners.length;i++){
            listeners[i].beginLoadingPlaces();
        }
        working--;
        that.executeListenerActions();
    }

    this.notifyLoadFinish = function(placesArgs, currentPosition){
        working++;
        for(var i=0;i<listeners.length;i++){
            listeners[i].doneLoadingPlaces(placesArgs, currentPosition);
        }
        working--;
        that.executeListenerActions();
    }

	this.notifyLoadFail = function(){
        working++;
		for(var i=0;i<listeners.length;i++){
			listeners[i].loadingPlacesFailed();
		}
        working--;
		that.executeListenerActions();
	}

    this.notifyResetPlaces = function(type) {
        working++;
		for(var i=0;i<listeners.length;i++){
			listeners[i].resetPlaces(type);
		}
        working--;
		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 _jive_video_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 _jive_video_picker__url = _jive_video_picker__url;
        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 {
        ie7_css : "a{\nborder: 1px solid transparent;\n}\nspan.jive_macro.active_link, a.jive_macro.active_link, a.active_link{\nborder: 1px solid blue;\n}\nspan.jive_macro, a.jive_macro{\nborder: 1px solid transparent;\n}",
        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,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_video_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
     * @param customImage optional location of a custom image to be displayed in the rte.
     */
    this.addMacro = function(macro, paramset, customImage){
        var ed = getEditor();
        var collapsed = ed.selection.isCollapsed();
        var pre = ed.plugins.jivemacros.insertMacro(macro,null,customImage);
        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>";
        }
        return this.replaceWhiteSpace(body);
    }
    /**
     * Replaces white space with nbsp.
     * On large strings (100k+), this method is
     * about 100-1000 times faster than string concatination.
     * @param str
     */
    this.replaceWhiteSpace = function(str){
        var buffer = [];
        var start = 0;
        var end = -1;
        while( (end = str.indexOf("  ", start))>= 0){
                if(end < start){
                        break;
                }
                buffer.push(str.substring(start, end));
                buffer.push("&nbsp;");
                        start = end + 1;
                }

        if(start < str.length){
                buffer.push(str.substring(start))
        }
        return buffer.join("");
    }
    
    /**
     * 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"){
                if (ele.src == "") {
                    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(){ }
}

/**
 * a task dom, like in day view
 */
jive.gui.CPDOM = function(control, cp, clickFunc, dblClickFunc){
	var that = this;


	//
	// these styles are overridden in month view :(
	//
	var holder = document.createElement('DIV');
	holder.setAttribute("class", "jive-link-checkpoint jiveTT-hover-checkpoint");
	holder.className = "jive-link-checkpoint jiveTT-hover-checkpoint";

	var title = document.createElement('SPAN');

	// title
	var tmp_title = cp.getName();
	if(tmp_title.length == 0) tmp_title = "(no title)";
	var n = document.createTextNode(tmp_title);

	// description
	var desc = document.createElement('SPAN');
	desc.setAttribute("class", "month_day_cell_event_desc");
	desc.className = "month_day_cell_event_desc";
	var tmp_desc = "";
	if(cp.getDescription().unescapeHTML().length > 0){
		tmp_desc = ": " + cp.getDescription().unescapeHTML();
	}
	desc.appendChild(document.createTextNode(tmp_desc));

	title.appendChild(n);
	title.appendChild(desc);
	holder.appendChild(title);

	holder.getCheckPoint = function(cp){ return function(){ return cp; }; }(cp);

	this.lighten = function(){
		holder.setAttribute("class", "month_day_cell_item month_lighten_dom");
		holder.className = "month_day_cell_item month_lighten_dom";
	}

	this.darken = function(){
		holder.setAttribute("class", "month_day_cell_item");
		holder.className = "month_day_cell_item";
	}

	// this is a hook to update the text on this item
	this.refresh = function(){
		// remove the task title and description
		title.removeChild(title.childNodes[1]);
		title.removeChild(title.childNodes[0]);
		// add it back
		title.appendChild(document.createTextNode(cp.getName()));

		var tmp_desc = "";
		if(cp.getDescription().unescapeHTML().length > 0){
			tmp_desc = ": " + cp.getDescription().unescapeHTML();
		}
		desc.removeChild(desc.childNodes[0]);
		desc.appendChild(document.createTextNode(tmp_desc));
		title.appendChild(desc);
	}

	this.showDescription = function(b){
		if(b){
			jive.ext.x.xDisplayInline(desc);
		}else{
			jive.ext.x.xDisplayNone(desc);
		}
	}

	jive.ext.x.xAddEventListener(holder, "click", function(clickFunc, cp, title){
		return function(e){
			clickFunc(cp);
			jive.ext.x.xStopPropagation(e);
			title.setAttribute("class", "month_view_day_task_title");
			title.className = "month_view_day_task_title";
		}
	}(clickFunc, cp, title));
	jive.ext.x.xAddEventListener(holder, "dblclick", function(dblClickFunc, cp, title){
		return function(e){
			dblClickFunc(cp);
			jive.ext.x.xStopPropagation(e);
			title.setAttribute("class", "month_view_day_task_title");
			title.className = "month_view_day_task_title";
		}
	}(dblClickFunc, cp, title));


	this.getDOM = function(){
		return holder;
	}

	this.killYourself = function(){
		cp = null;
        control = null;
    }

	this.getCheckPoint = holder.getCheckPoint;

	/**
	 * will style this dom as highlighted
	 * if b is true
	 * otherwise, it'll style the default style
	 */
	this.setHighlight = function(b){
		if(b){
			holder.setAttribute("class", "month_day_cell_item_highlight");
			holder.className = "month_day_cell_item_highlight";
		}else{
			holder.setAttribute("class", "month_day_cell_item");
			holder.className = "month_day_cell_item";
		}
	}

}

/**
 * a task dom, like in day view
 */
jive.gui.TaskDOM = function(control, ttask, clickFunc, dblClickFunc){
	var that = this;


	//
	// these styles are overridden in month view :(
	//
	var holder = document.createElement('A');
	holder.setAttribute("class", "month_day_cell_item");
	holder.className = "month_day_cell_item";

	var title = document.createElement('SPAN');
	title.setAttribute("class", "month_view_day_task_title");
	title.className = "month_view_day_task_title";

	// title
	var tmp_title = ttask.getSubject();
	if(tmp_title.length == 0) tmp_title = "(no title)";
	var n = document.createTextNode(tmp_title);

	// description
	var desc = document.createElement('SPAN');
	desc.setAttribute("class", "month_day_cell_event_desc");
	desc.className = "month_day_cell_event_desc";
	var tmp_desc = "";
	if(ttask.getDescription().unescapeHTML().length > 0){
		tmp_desc = ": " + ttask.getDescription().unescapeHTML();
	}
	desc.appendChild(document.createTextNode(tmp_desc));

	//
	//
	// different browsers handle the onClick event differently
	// firefox calls the event AFTER the checked status has changed,
	// but safari calls it BEFORE the checked status has changed.
	//
	// we handle this by maintaining 2 .checked properties.
	// the .checked property is managed by teh browser, and tells if teh
	// checkbox is on/off.
	//
	// the .checkedCache is our internal cache for what the last value was.
	//
	// if .checked == .checkedCache when the click function is called,
	// then we're in safari, and need to toggle both .checked and .checkedCache
	//
	// if .checked != .checkedCache when the click function is called,
	// then we're in firefox, and we only need to update the .checkedCache
	//
	//
	var check = document.createElement('INPUT');
	check.setAttribute("type", "checkbox");
	check.type = "checkbox";
	if(ttask.isComplete()){
		check.checked = true;
		check.checkedCache = true;
	}else{
		check.checkedCache = false;
	}

	jive.ext.x.xAddEventListener(check, "click", function(check, ttask){ return function(e){
		try{
			if(check.checkedCache == check.checked){
				check.checkedCache = !check.checkedCache;
				check.checked = !check.checked;
			}else{
				check.checkedCache = !check.checkedCache;
			}

			if(check.checked){
				ttask.setComplete(true);
			}else{
				ttask.setComplete(false);
			}
			ttask.confirm();
			jive.ext.x.xStopPropagation(e);
		}catch(e){
			alert(e);
		}
	}}(check, ttask));

	holder.appendChild(check);
	title.appendChild(n);
	title.appendChild(desc);
	holder.appendChild(title);

	holder.getTask = function(ttask){ return function(){ return ttask; }; }(ttask);

	holder.setDisabled = function(check){ return function(foo){
		check.disabled = foo;
	}}(check);

	holder.isDisabledHuh = function(check){ return function(){
		return check.disabled;
	}}(check);

	holder.setChecked = function(check){ return function(foo){
		check.checked = foo;
	}}(check);
	holder.isCheckedHuh = function(check){ return function(){
		return check.checked;
	}}(check);


	this.lighten = function(){
		holder.setAttribute("class", "month_day_cell_item month_lighten_dom");
		holder.className = "month_day_cell_item month_lighten_dom";
	}

	this.darken = function(){
		holder.setAttribute("class", "month_day_cell_item");
		holder.className = "month_day_cell_item";
	}

	// this is a hook to update the text on this item
	this.refresh = function(){
		// remove the task title and description
		title.removeChild(title.childNodes[1]);
		title.removeChild(title.childNodes[0]);
		// add it back
		title.appendChild(document.createTextNode(ttask.getSubject()));

		var tmp_desc = "";
		if(ttask.getDescription().unescapeHTML().length > 0){
			tmp_desc = ": " + ttask.getDescription().unescapeHTML();
		}
		desc.removeChild(desc.childNodes[0]);
		desc.appendChild(document.createTextNode(tmp_desc));
		title.appendChild(desc);

		// update the checkbox
		if(ttask.isComplete()){
			holder.setChecked(true);
		}else{
			holder.setChecked(false);
		}
	}

	this.showDescription = function(b){
		if(b){
			jive.ext.x.xDisplayInline(desc);
		}else{
			jive.ext.x.xDisplayNone(desc);
		}
	}

	// set default value
	holder.setChecked(ttask.isComplete());



	jive.ext.x.xAddEventListener(holder, "click", function(clickFunc, ttask, title){
		return function(e){
			clickFunc(ttask);
			jive.ext.x.xStopPropagation(e);
			title.setAttribute("class", "month_view_day_task_title");
			title.className = "month_view_day_task_title";
		}
	}(clickFunc, ttask, title));
	jive.ext.x.xAddEventListener(holder, "dblclick", function(dblClickFunc, ttask, title){
		return function(e){
			dblClickFunc(ttask);
			jive.ext.x.xStopPropagation(e);
			title.setAttribute("class", "month_view_day_task_title");
			title.className = "month_view_day_task_title";
		}
	}(dblClickFunc, ttask, title));


	this.getDOM = function(){
		return holder;
	}

	this.killYourself = function(){
		ttask = null;
        control = null;
    }

	this.getTask = holder.getTask;
	this.setDisabled = holder.setDisabled;
	this.isDisabledHuh = holder.isDisabledHuh;
	this.setChecked = holder.setChecked;
	this.isCheckedHuh = holder.isCheckedHuh;

	/**
	 * will style this dom as highlighted
	 * if b is true
	 * otherwise, it'll style the default style
	 */
	this.setHighlight = function(b){
		if(b){
			holder.setAttribute("class", "month_day_cell_item_highlight");
			holder.className = "month_day_cell_item_highlight";
		}else{
			holder.setAttribute("class", "month_day_cell_item");
			holder.className = "month_day_cell_item";
		}
	}

	/**
	 * set the taskdom as disabled if
	 * we don't have write permission to
	 * its calendar
	 */
	function setProject(proj){
		if(proj == null || proj.isEditable()){
            holder.setDisabled(false);
		}else{
            holder.setDisabled(true);
		}
	}

    if(ttask.getProjectID() == 0){
        setProject(null);
    }else{
        var proj = control.getProjectCache().getProject(ttask.getProjectID());
        if($obj(proj) && proj != null){
            setProject(proj);
        }else{
            var list = new jive.model.ProjectCacheListener();
            list.loadProject = function(proj){
                if(proj.getID() == ttask.getProjectID()){
                    setProject(proj);
                    control.getProjectCache().removeListener(this);
                }
            }
            control.getProjectCache().addListener(list);
        }
    }
}

jive.gui.isMonthEventDOM = function(item){
	return $def(item) && $def(item.getEvent);
}
jive.gui.isMonthTaskDOM = function(item){
	return $def(item) && $def(item.getTask);
}


/**
 * a month panel is composed of day cell holders, which in turn hold
 * the actual day cells. each holder holds 4 weeks of day cells. these
 * holders can be added to the beginning/end of the month view.
 *
 * to add the month panel to the UI, call it's getDOM() method
 * which returns a dom object
 */
jive.gui.MonthView = function(control, aurora_gui){


	var that = this;

	var expanded = false;


	var has_add_view = null;

	this.hasAddView = function(){
		if(has_add_view == null){
			has_add_view = $obj(aurora_gui.getView("add_event"));
		}
		return has_add_view;
	}

	/**
	 * holds day cells
	 */
	this.dayCells = new jive.ext.y.HashTable();

	/**
	 * holds the spans that hold the event titles
	 * will return an array for a key
	 * the array will hold all of the title spans for that event
	 *
	 * ie, if an event spans the days, it will have an array of 3 spans
	 * one for each day cell
	 */
	this.eventDOMHolders = new jive.ext.y.HashTable();
	this.taskDOMHolders = new jive.ext.y.HashTable();

	/**
	 * set to true if showMonth() should ignore
	 * optimizing by not showing month if the date is the same
	 * as is currently shown.
	 *
	 * ie, set to true to force showMonth to do something
	 * meaningful
	 */
	var force_month_view = false;

	/**
	 * we're also going to cache
	 * the event and task by its calendar id
	 */
	var taskDOMbyCalHolder = new jive.ext.y.HashTable();
	var eventDOMbyCalHolder = new jive.ext.y.HashTable();

	/**
	 * track which calendar id's we're caching each task/event by
	 */
	var task2cal = new jive.ext.y.HashTable();
	var event2cal = new jive.ext.y.HashTable();

	/**
	 * this is the main panel that holds all the day cell holders
	 */
	var main_panel = document.createElement('DIV');
	main_panel.setAttribute("class", "month_view_holder");
	main_panel.className = "month_view_holder";


	var visi = true;
	this.setItemVisibility = function(b){
		visi = b;
	}
	this.getItemVisibility = function(){
		return visi;
	}

	/**********************************************************************
	***********************************************************************
	***********************************************************************
	**
	** BEGIN VIEW INTERFACE TO AURORA GUI
	**
	***********************************************************************
	***********************************************************************
	***********************************************************************/

	/**
	 * return true if we can print this view, false otherwise
	 */
	this.hasPrintView = function(){
		return true;
	}

	/**
	 * this returns true if week view is the current view, false otherwise
	 */
	this.isExpandedHuh = function(){
		return expanded;
	}

	/**
	 * this function is called when week view comes into view
	 * we have officially 'switched' the view to week view
	 */
	this.expand = function(){
		expanded = true;
		aurora_gui.showArrows();
		jive.ext.x.xDisplayBlock(main_panel);
	}

	/**
	 * this function is called when week view goes out of view
	 * we have officially 'switched out' the view from week view
	 */
	this.collapse = function(){
		expanded = false;
		jive.ext.x.xDisplayNone(main_panel);
	}

	// the function to switch to the previous month
	// (used in the previous button)
	function prevMonthFunc(){
		var d = new Date();
		d.setTime(aurora_gui.getCurrentDate().getTime());
		jive.model.dateMinusMonth(d);
		aurora_gui.setCurrentDate(d);
		aurora_gui.notifyMonthClicked(d);
	}
	// the function to switch to the next month
	// (used in the next button)
	function nextMonthFunc(){
		var d = new Date();
		d.setTime(aurora_gui.getCurrentDate().getTime());
		d.setMonth(d.getMonth()+1);
		while(d.getMonth() > aurora_gui.getCurrentDate().getMonth()+1) jive.model.dateMinusDay(d);
		aurora_gui.setCurrentDate(d);
		aurora_gui.notifyMonthClicked(d);
	}

	/**
	 * return the function to go back a month
	 */
	this.getPrevViewFunc = function(){
		// the function to switch to the previous week
		// (used in the previous button)
		return prevMonthFunc;
	}

	/**
	 * return the function to go forward a month
	 */
	this.getNextViewFunc = function(){
		// the function to switch to the next week
		// (used in the next button)
		return nextMonthFunc;
	}

	/**
	 * when this view is in range, calcualte the min date that should be in range
	 * given month view's getCurrentDate() function
	 */
	this.getMinDate = function(){
		return aurora_gui.getMinDate();
	}

	/**
	 * when this view is in range, calcualte the min date that should be in range
	 * given month view's getCurrentDate() function
	 */
	this.getMaxDate = function(){
		return aurora_gui.getMaxDate();
	}

	/**
	 * returns teh text that should be in month view's header
	 * this is based on month_view's getCurrentDate() function
	 */
	this.getHeaderText = function(){
		var lang = control.getLanguageManager().getActiveLanguage();

		var d = new Date();
		d.setTime(aurora_gui.getCurrentDate().getTime());

		return lang.longMonth(d.getMonth());
	}

	/**
	 * shows the month according to month view's current date
	 */
	this.go = function(d){
		aurora_gui.setCurrentDate(d);
		that.showMonth(aurora_gui.getCurrentDate());
	}

	/**
	 * return the name of this view
	 */
	this.getName = function(){
		return "month";
	}

	/**
	 * return the unique hash for this view
	 */
	this.getHash = function(){
		return "month";
	}


	/**
	 * the language has been updated,
	 */
	this.updateText = function(){
		if(main_panel.childNodes.length > 0){
			var start_on = control.getSettingsManager().getStartWeekOn();
			var lang = control.getLanguageManager().getActiveLanguage();
			for(var i=start_on;i-start_on<7;i++){
				var td = main_panel.childNodes[0].childNodes[0].childNodes[(i-start_on)%7]; // the table cell
				if(td.childNodes.length > 0) td.removeChild(td.childNodes[0]);
				td.appendChild(document.createTextNode(lang.longDay(i%7)));
				td.setAttribute("height", "2");
				td.height = "2";
			}
		}
	}

	/**
	 * return the DOM object that represents this month view
	 */
	this.getDOM = function(){
		return main_panel;
	}


	/**
	 * this flushes a calendar entirely. it removes it from teh views,
	 * and also removes any DOM's we have cached for this calendar's
	 * events
	 */
	var filter_time;
	function filterNode(node, str){
		var title;
		var desc;
		var cal_id;
		if($def(node.getEvent)){
			var event = node.getEvent();
			title = event.getSubject().toLowerCase();
			desc = event.getDescription().toLowerCase();
			cal_id = event.getCalendarId();
		}else
		if($def(node.getTask)){
			var task = node.getTask();
			title = task.getSubject().toLowerCase();
			desc = task.getDescription().toLowerCase();
			cal_id = task.getProjectID();
		}
		if(control.isCalendarVisibleHuh(cal_id)){
			if(str.length == 0  || str.length > 0 && (title.indexOf(str) >= 0 || desc.indexOf(str) >= 0)){
//				alert("showing: " + cell.childNodes[i].innerText);
				node.darken();
			}else{
//				alert("hiding: " + cell.childNodes[i].innerText);
				node.lighten();
			}
		}
	}
	var last_filter = "";
	var last_filter_month = (new Date()).getMonth();
	this.filter = function(str){
		//
		// don't bother filtering if it's
		// the same thing we did last time
		if(last_filter != str || last_filter_month != aurora_gui.getCurrentDate().getMonth()){
			last_filter = str;
			last_filter_month = aurora_gui.getCurrentDate().getMonth()
			if(that.isExpandedHuh()){
				filter_time = "" + (new Date()).getTime() + "" + Math.random();
				var my_time = filter_time;
				var dt = new Date();
				var min = aurora_gui.getMinDate();
				var max = aurora_gui.getMaxDate();
				dt.setTime(min.getTime());
				str = str.toLowerCase();
				while(my_time == filter_time && (dt.getTime() < max.getTime() + 24*60*60*1000)){
					var cell = getDayCell(dt);
					for(var i=1; i < cell.childNodes.length; i++){
						filterNode(cell.childNodes[i], str);
					}
					dt.setTime(dt.getTime() + 24*60*60*1000);
				}
			}
		}
	}


	/**
	 * add (or refresh) an event to display on this month view
	 * i need to update this function. its not handling teh drag listeners
	 * correctly. i need to remove old listeners before i add new ones...
	 */
	this.addEvent = function(tevent){
		try{
			var settings = control.getSettingsManager();

			var e_obj = getDOMArray(tevent);



			var iter = new Date();
			if(tevent.isAllDay()){
				iter.setTime(tevent.getStart().getTime());
			}else{
				iter.setTime(settings.adjustDate(tevent.getStart()).getTime());
			}
			var i=0;
			var end_iter = new Date();
			if(tevent.isAllDay()){
				end_iter.setTime(tevent.getEnd().getTime());
			}else{
				end_iter.setTime(settings.adjustDate(tevent.getEnd()).getTime());
			}

			if(jive.model.dateLT(iter, control.getEventCache().getMinTime())){
				// iter is smaller, so change iter to min date
				iter.setTime(control.getEventCache().getMinTime().getTime());
			}
			if(jive.model.dateGT(end_iter, control.getEventCache().getMaxTime())){
				// end_iter is too large, so change end_iter to max date
				end_iter.setTime(control.getEventCache().getMaxTime().getTime());
			}


			while(jive.model.dateLTEQ(iter, end_iter)){
				var d = getDayCell(iter);
				// update the listener
				if($def(control.getDragManager)){
					var dm = control.getDragManager();
					var dragDate = new Date();
					dragDate.setTime(iter.getTime());
					var dlist = new jive.gui.CellDragListener(control, tevent, dragDate, control.notifyStopDrag, control.notifyDragging);
					if($obj(e_obj[i].monthViewDList) && e_obj[i].monthViewDList != null){
						dm.removeDragListener(e_obj[i], e_obj[i].monthViewDList);
					}
					e_obj[i].monthViewDList = dlist;
					dm.enableDrag(e_obj[i]);
					dm.addDragListener(e_obj[i], dlist);
				}
				/**
				 * hide the event if the calendar is hidden
				 */
				if(!control.isCalendarVisibleHuh(tevent.getCalendarId())){
					jive.ext.x.xDisplayNone(e_obj[i]);
				}else{
					jive.ext.x.xDisplayBlock(e_obj[i]);
				}
				var t = aurora_gui.getFilterText();
				filterNode(e_obj[i], t)
				d.appendEventDOM(e_obj[i]);
				// update our parent
				e_obj[i].myParent = d;

				i++;
				iter.setDate(iter.getDate() + 1);

			}
		}catch(e){
			alert("error adding event to month view: " + e);
		}
	}


	/**
	 * add a task to display on this month view
	 */
	this.addTask = function(ttask){
		try{

			var settings = control.getSettingsManager();

			var e_obj = getTaskDOM(ttask);

			var d = getDayCell(ttask.getDueDate());
			d.appendTaskDOM(e_obj);
			// update our parent
			e_obj.myParent = d;

			// update text/checkbox
			e_obj.refresh();

			// update the drag listener
			if($def(control.getDragManager)){
				var dm = control.getDragManager();
				var dragDate = new Date();
				dragDate.setTime(ttask.getDueDate());
				var dlist = new jive.gui.CellDragListener(control, ttask, dragDate, control.notifyStopDrag, control.notifyDragging);
				if($obj(e_obj.monthViewDList) && e_obj.monthViewDList != null){
					dm.removeDragListener(e_obj, e_obj.monthViewDList);
				}
				e_obj.monthViewDList = dlist;
				dm.enableDrag(e_obj);
				dm.addDragListener(e_obj, dlist);
			}


			/**
			 * hide the event if the calendar is hidden
			 */
			if(!control.isCalendarVisibleHuh(ttask.getProjectID())){
				jive.ext.x.xDisplayNone(e_obj);
			}else{
				jive.ext.x.xDisplayBlock(e_obj);
			}
		}catch(e){
			alert(e);
		}
	}

	/**
	 * removes an event from the month view
	 *
	 * this removes any drag listeners from span elements
	 * for that event, and disables the drag
	 */
	this.removeEvent = function(tevent){
		try{
			var settings = control.getSettingsManager();
			// i have completed the drag
			// so remove the listener from
			// all of this events listings in month view
			//
			// i'll be added again by the day cell...
			var arr = getDOMArray(tevent, true);
			// var arr = this.eventDOMHolders.get(event.getId());
			if(jive.ext.y.yArr(arr)){
				for(var j=0;j<arr.length;j++){
					if($def(control.getDragManager())){
						/**
						 * if we don't remove the drag listener
						 * then the event will always think it
						 * started dragging on the wrong day
						 * (the same day really, the day the event
						 *  was on when the page loaded)
						 */
						var dm = control.getDragManager();
						dm.removeDragListener(arr[j], arr[j].monthViewDList);
						dm.disableDrag(arr[j]);
					}
					if($obj(jive.ext.x.xParent(arr[j])) && jive.ext.x.xParent(arr[j]) != null){
						jive.ext.x.xParent(arr[j]).removeChild(arr[j]);
						arr[j].myParent = null;
					}else if($obj(arr[j].myParent) && arr[j].myParent != null){
						arr[j].myParent.removeChild(arr[j]);
						arr[j].myParent = null;
					}
					arr[j].killYourself();
				}
			}
		}catch(e){
			alert("error removing event: " + e);
		}
	}

	/**
	 * removes an event from the month view
	 *
	 * this removes any drag listeners from span elements
	 * for that event, and disables the drag
	 */
	this.removeTask = function(ttask){
		try{
			var settings = control.getSettingsManager();

			var arr = getTaskDOM(ttask);
			// var arr = this.eventDOMHolders.get(event.getId());
			if($obj(arr)){
				/**
				 * if we don't remove the drag listener
				 * then the event will always think it
				 * started dragging on the wrong day
				 * (the same day really, the day the event
				 *  was on when the page loaded)
				 */

				if($def(control.getDragManager)){
					// i have completed the drag
					// so remove the listener from
					// all of this events listings in month view
					//
					// i'll be added again by the day cell...
					var dm = control.getDragManager();
					dm.removeDragListener(arr, arr.monthViewDList);
					dm.disableDrag(arr);
				}

                var d = getDayCell(ttask.getDueDate());
                d.removeTaskDOM(arr);
                // update our parent
                arr.myParent = null;

//                if($obj(jive.ext.x.xParent(arr)) && jive.ext.x.xParent(arr) != null){
//					jive.ext.x.xParent(arr).removeChild(arr);
//					arr.myParent = null;
//				}else if($obj(arr.myParent) && arr.myParent != null){
//					arr.myParent.removeChild(arr);
//					arr.myParent = null;
//				}
				arr.killYourself();
			}
		}catch(e){
			alert("error removing task: " + ttask.getSubject() + "\nexception: " + e);
		}
	}


	/**
	 * this flushes a calendar entirely. it removes it from teh views,
	 * and also removes any DOM's we have cached for this calendar's
	 * events
	 */
	this.flushCalendar = function(cal){
		var events = that.eventDOMHolders.toArray(jive.gui.isMonthEventDOM);
		for(var i=0;i<events.length;i++){
			if(events[i].getEvent().getCalendarId() == cal.getId()){
				that.flushEvent(events[i].getEvent());
			}
		}
		var tasks = that.taskDOMHolders.toArray(jive.gui.isMonthTaskDOM);
		for(var i=0;i<tasks.length;i++){
			if(tasks[i].getTask().getCalendarId() == cal.getId()){
				that.flushTask(tasks[i].getTask());
			}
		}
	}

	/**
	 * this flushes an event entirely. it removes it from the views,
	 * and also removes any DOM's we have cached for that event
	 */
	this.flushEvent = function(tevent){
		try{
			that.removeEvent(tevent);
			that.eventDOMHolders.clear(tevent.getId());
			// also clear it from teh cache by calendar
			var cal_id = event2cal.get(tevent.getId());
			// clear the task from the old calendar cache
			var calHash = eventDOMbyCalHolder.get(cal_id);
			// we don't check for calHash existing here, b/c it must
			// b/c we added it to the cache in the removeEvent() func
			calHash.clear(tevent.getId());
		}catch(e){
			alert("error flushing event");
		}
	}

	/**
	 * this flushes a task entirely. it removes it from the views,
	 * and also removes any DOM's we have cached for that task
	 */
	this.flushTask = function(ttask){
		try{
			that.removeTask(ttask);
			that.taskDOMHolders.clear(ttask.getID());
			// also clear it from teh cache by calendar
			var cal_id = task2cal.get(ttask.getID());
			// clear the task from the old calendar cache
			var calHash = taskDOMbyCalHolder.get(cal_id);
			// we don't check for calHash existing here, b/c it must
			// b/c we added it to the cache in the getTaskDOM() func
			calHash.clear(ttask.getID());
		}catch(e){
			alert("flushEvent: " + e);
		}
	}


	/**
	 * refresh the text on the bar, possibly because
	 * of a settings change, etc
	 */
	this.refresh = function(){
		var settings = control.getSettingsManager();
		var arr = that.eventDOMHolders.toArray(jive.gui.isMonthEventDOM);
		try{
			for(var ri=0;ri<arr.length;ri++){
				//
				// compare the event against the two timezones,
				// and if teh start/end dates are different,
				// the referesh it
				//
				var tevent = arr[ri].getEvent()
				var old = settings.getOldTimezone();
				if(!jive.model.dateEQ(settings.adjustDate(tevent.getStart()), settings.adjustDate(tevent.getStart(), old)) ||
				   !jive.model.dateEQ(settings.adjustDate(tevent.getEnd()), settings.adjustDate(tevent.getEnd(), old))){
					that.flushEvent(tevent);
					that.addEvent(tevent);
				}
			}
		}catch(e){
			alert(e);
		}

		if(that.isExpandedHuh()){
			that.showMonth(aurora_gui.getCurrentDate());
		}
		that.refreshShading();
	}

	/**
	 * we've just now been placed in our parent div, so
	 * adjust height of our holders etc to fit
	 */
	this.init = function(veryinner){
		veryinner.appendChild(main_panel);
	}

	this.killYourself = function(){
		control = null;
		aurora_gui = null;
	}

	this.refreshWeather = function(){
		// loop through all dates in month view
		// and set weather
		var min = new Date();
		min.setTime(aurora_gui.getMinDate().getTime());

		while(jive.model.dateLTEQ(min, aurora_gui.getMaxDate())){

			var cell = getDayCell(min);
			var image = control.getSettingsManager().getWeatherImage(min);
			var color = cell.style.backgroundColor;
			if(image.length > 0){
				var left = 22;
				if(min.getDate() == 1){
					left = 42;
				}else{
					left = 22;
				}
//				cell.setAttribute("style", "background: url(" + image + ") " + left + "px 2px no-repeat " + cell.style.backgroundColor);
				cell.style.background =  "url(" + image + ") " + left + "px 2px no-repeat " + cell.style.backgroundColor;
			}else{
//				cell.setAttribute("style", "");
				cell.style.background =  "";
			}
			cell.style.backgroundColor = color;

			min.setDate(min.getDate() + 1);
		}

	}



	/**
	 * shows the month view for
	 * Date d
	 * @param d the date of the month to show
	 */
	this.refreshShading = function(){
//		if(loading_state == 1){
			// we've just loaded month view for the first time.
			// so no events/tasks are even in here yet, so don't
			// bother updating shading, when there aren't any
			// events/tasks anyways.
			//
//			return;
//		}
		var settings = control.getSettingsManager();
		var start_on = settings.getStartWeekOn();

		var dtemp = new Date();
		dtemp.setTime(aurora_gui.getMaxDate().getTime());

		var dt = new Date();
		dt.setTime(aurora_gui.getMinDate().getTime());

		// daylight savings time has a problem here...
		// so set the hour to 17, that way daylight savings
		// won't change the date by 2 days if we move 24 hours forward/back
		// temp_panel = this.getDayCellHolder(d);
		dt.setHours(17);
		// now set the date to the sunday (before|that) this month starts
		dt.setDate(1);

		var sub = dt.getDay();
		if(start_on != 0 && sub == 0){
			sub = 7;
		}
		dt.setDate(dt.getDate() - sub + start_on);

		// this will help us as we iterate through the days
		// it will keep track of what month the sunday for that
		// month is
		// this way, we can bail on the while loop once we've
		// finished showing this month
		var currMonth = new Date();
		currMonth.setTime(dt.getTime());

		var backgrounds = new Array();
		backgrounds[0] = "#FFFFFF";
		backgrounds[1] = "#EFEFEF";
		backgrounds[2] = "#DFDFDF";
		backgrounds[3] = "#CFCFCF";
		backgrounds[4] = "#BFBFBF";

		var now = settings.getNOW();

		var smart_shading = settings.getSmartShading();
		var current_month = aurora_gui.getCurrentDate().getMonth();
		// add all the day cells
		while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
			var cell = getDayCell(dt);
			var dt_is_today = jive.model.dateEQ(dt, now);
			if(smart_shading){
				var num_events = cell.childNodes.length - 1;
				var true_num = 0;
				for(var i=0;i<num_events && true_num <= 4;i++){
					if(jive.ext.x.xDisplay(cell.childNodes[i+1]) == "block"){
						true_num++;
					}
				}

				if(true_num > 4){
					true_num = 4;
				}else if(true_num < 0){
					true_num = 0;
				}
				if(dt_is_today){
					cell.style.backgroundColor = "#e4f6e7";
					cell.outColor = "#e4f6e7";
				}else{
					cell.style.backgroundColor = backgrounds[num_events];
					cell.outColor = backgrounds[num_events];
				}
			}else if(cell.getDate().getMonth() != current_month){
				cell.style.backgroundColor = backgrounds[2];
				cell.outColor = backgrounds[2];
			}else{
				cell.style.backgroundColor = backgrounds[0];
				cell.outColor = backgrounds[0];
			}
			if(dt_is_today){
				cell.style.backgroundColor = "#e4f6e7";
				cell.setAttribute("class", "month_cell month_today_cell");
				cell.className = "month_cell month_today_cell";
				cell.overColor = "#ffffda";
			}else{
				cell.setAttribute("class", "month_cell month_day_cell");
				cell.className = "month_cell month_day_cell";
//				cell.overColor = "#f0f6fc";
				cell.overColor = "#ffffda";
			}

			dt.setDate(dt.getDate() + 1);
			if(dt.getDay() == 0){
				currMonth.setTime(dt.getTime());
			}
		}
		that.refreshWeather();
	}



	/**
	 * toggle event visibility if
	 * the calendar visibility is switched (ie, in the sidebar)
	 */
	this.calendarVisible = function(calendar, visibleHuh){
		var show_tasks_huh = control.getSettingsManager().getShowTasks();
		var objs = taskDOMbyCalHolder.get(calendar.getId());
		if($obj(objs) && objs != null){
			objs = objs.toArray();
			for(var i=0;i<objs.length;i++){
				if(visibleHuh && show_tasks_huh){
					jive.ext.x.xDisplayBlock(objs[i]);
				}else{
					jive.ext.x.xDisplayNone(objs[i]);
				}
			}
		}
		var objs = eventDOMbyCalHolder.get(calendar.getId());
		if($obj(objs) && objs != null){
			objs = objs.toArray();
			for(var i=0;i<objs.length;i++){
				for(var j=0; j<objs[i].length; j++){
					if(visibleHuh){
						jive.ext.x.xDisplayBlock(objs[i][j]);
					}else{
						jive.ext.x.xDisplayNone(objs[i][j]);
					}
				}
			}
		}
		that.refreshShading();
	}

	/**
	 * notify everybody else that a drag/drop happened
	 */
	this.stopDrag = function(tevent, dt, left, top){
		var doneHuh = false;
		if(that.isExpandedHuh()){
			/**
			 * loop through table cells
			 * if a point matches, then drop it in it's day cell
			 */
			if(main_panel.childNodes.length > 0){
				var table = main_panel.childNodes[0];
				for(var i=1;i<table.childNodes.length;i++){
					tr = table.childNodes[i];
					for(var j=0;j<tr.childNodes.length;j++){
						if(tr.childNodes[j].childNodes.length > 0){
							var day = tr.childNodes[j];
							if(jive.ext.x.xHasPoint(day, left, top)){
								if($def(day.dropPoint)){
									day.dropPoint(tevent,dt);
									doneHuh = true;
								}
							}
						}
					}
				}
			}
		}
		return doneHuh;
	}

	// this is the last day cell that
	// we've hovered over when dragging
	// an event or task
	var hovered_day_cell = null;
	var threadNum = 0;
	this.dragging = function(tevent, dt, left, tp){
		// this function will be called as they drag around an event
		// which means it'll be called probably as its running
		//
		// the threadNum and myThread variables will make sure that
		// I drop out of execution asap as a new thread starts.
		threadNum++;
		var myThread = threadNum;

		// track if we show a drop zone or not
		var zonedHuh = false;

		// let's check our currently hovered day cell
		// to see if that's what we're still hovered over.
		// if we're not over the same cell anymore, lets
		// just check each cell sequentially.
		//
		// later, we can optimize this to check nearby cells
		// first, instead of just checking /left->bottom/right
		//
		if(hovered_day_cell != null){
			if(jive.ext.x.xHasPoint(hovered_day_cell, left, tp)){
				var w = Math.floor(.95 * jive.ext.x.xWidth(hovered_day_cell));
				control.showHoverOver(jive.ext.x.xPageX(hovered_day_cell), jive.ext.x.xPageY(hovered_day_cell), w, jive.ext.x.xHeight(hovered_day_cell));
				zonedHuh = true;
			}
		}
		// loop through all dates in month view
		// and set weather
		if(main_panel.childNodes.length > 0 && !zonedHuh){
			var table = main_panel.childNodes[0];
			for(var i=1;i<table.childNodes.length && myThread == threadNum && !zonedHuh;i++){
				tr = table.childNodes[i];
				for(var j=0;j<tr.childNodes.length && myThread == threadNum && !zonedHuh;j++){
					var day = tr.childNodes[j];
					if(jive.ext.x.xHasPoint(day, left, tp)){
						if($def(day.dropPoint)){
							// hooray!
							// move the drag area here
							// and save this cell as
							// the last hovered
							var w = Math.floor(.95 * jive.ext.x.xWidth(day));
							control.showHoverOver(jive.ext.x.xPageX(day), jive.ext.x.xPageY(day), w, jive.ext.x.xHeight(day));
							hovered_day_cell = day;
							zonedHuh = true;
						}
					}
				}
			}
			if(myThread != threadNum){
				return;
			}
		}
		if(!zonedHuh){
			control.hideHover();
		}
		return zonedHuh;
	}


	/**********************************************************************
	***********************************************************************
	***********************************************************************
	**
	** END VIEW INTERFACE TO MONTH_VIEW
	**
	***********************************************************************
	***********************************************************************
	***********************************************************************/

	this.fixHeight = function(for_rows){
		try{
//			alert("updating height: " + for_rows);
			jive.ext.x.xHeight(main_panel, for_rows);
			var for_rows = for_rows - 20; // subtract 20 b/c of the header row
			if(main_panel.childNodes.length > 0){
				var table = main_panel.childNodes[0];
				for_rows += for_rows % (table.childNodes.length - 1);
				for(var i=1;i<table.childNodes.length;i++){
					var tr = table.childNodes[i];
					jive.ext.x.xHeight(tr, Math.floor(for_rows / (table.childNodes.length - 1)));
					if(jive.ext.x.xIE4Up){
//						alert("updating foo!");
						for(var j=0;j<tr.childNodes.length;j++){
							jive.ext.x.xDisplayNone(tr.childNodes[j]);
							jive.ext.x.xDisplayBlock(tr.childNodes[j]);
						}
					}
				}
			}
		}catch(e){
			alert(e);
		}
	}

	/**
	 * gets the events for a specific day
	 * @param dt a date object
	 */
	this.getEventsOn = function(dt){
		// we're going to load the day cell for this event
		// and get the events out of that
		var events = new Array();
		var cell = getDayCell(dt);
		for(var i=1; i < cell.childNodes.length; i++){
			if($def(cell.childNodes[i].getEvent)){
				events.push(cell.childNodes[i].getEvent());
			}
		}
		return events;
	}

	/**
	 * gets the tasks for a specific day
	 * @param dt a Date object
	 */
	this.getTasksOn = function(dt){
		// we're going to load the day cell for this event
		// and get the events out of that
		var tasks = new Array();
		var cell = getDayCell(dt);
        return cell.getTasks();
	}


	/**
	 * gets an array of dom objects for the event
	 * there will be enough dom objects to have 1 per day
	 * that the event spans
	 */
	function getDOMArray(tevent, skip){

		try{
			var settings = control.getSettingsManager();

			//
			// we store 1 DOM entry for each day the event is on
			//
			// however, if the dom entry would extend past teh min or max date
			// in month view, then we only cache the necessary dom entries.
			//
			// so if we're already caching the dom array, then lets make sure
			// that we're caching all of it. it could be that we're caching this months,
			// but we just loaded a whole new month and extended max date, so we need
			// to load in more dom's onto the array.
			//
			// if nothing is in the cache yet, then lets load in enough dom's
			// to either take care of the entire event's duration, or extend
			// until min or max date, whichever is shorter
			//
			//

			var iter = new Date();
			var end_iter = new Date();
			if(tevent.isAllDay()){
				iter.setTime(tevent.getStart().getTime());
				end_iter.setTime(tevent.getEnd().getTime());
			}else{
				iter.setTime(settings.adjustDate(tevent.getStart()).getTime());
				end_iter.setTime(settings.adjustDate(tevent.getEnd()).getTime());
			}

			//
			// iter is the start of the event
			// end_iter is the end of the event
			//
			// now lets make sure that iter >= minDate and end_iter <= maxDate
			//

			if(jive.model.dateLT(iter, control.getEventCache().getMinTime())){
				// iter is smaller, so change iter to min date
				iter.setTime(control.getEventCache().getMinTime().getTime());
			}
			if(jive.model.dateGT(end_iter, control.getEventCache().getMaxTime())){
				// end_iter is too large, so change end_iter to max date
				end_iter.setTime(control.getEventCache().getMaxTime().getTime());
			}
		}catch(e){
			alert("top of getdomarray: " + e);
		}

		try{

			var e_obj = that.eventDOMHolders.get(tevent.getId());
			if(!$obj(e_obj)){
				if($def(skip) && skip){
					return null;
				}
				e_obj = new Array();
				e_obj.getEvent = function(){ return tevent; }
				that.eventDOMHolders.put(tevent.getId(), e_obj);

				event2cal.put(tevent.getId(), tevent.getCalendarId());
				var calHash = eventDOMbyCalHolder.get(tevent.getCalendarId());
				if(!$obj(calHash) || calHash == null){
					calHash = new jive.ext.y.HashTable();
					eventDOMbyCalHolder.put(tevent.getCalendarId(), calHash);
				}
				calHash.put(tevent.getId(), e_obj);
			}

			var i=0;
			while(jive.model.dateLTEQ(iter, end_iter)){
				if(e_obj.length <= i){

					var formatDate = function(d){
						return function(d2){
							var dh = new jive.model.DateHelper(control);
							if(jive.model.dateEQ(settings.adjustDate(d2), d)){
								return dh.formatTo12HourTime(settings.adjustDate(d2));
							}else{
								return dh.formatToShortDate(settings.adjustDate(d2));
							}
						}
					}(iter);

					var txt = control.getEventDOMFactory().getEventDOM(tevent, aurora_gui.notifyEventClicked, aurora_gui.notifyEventDblClicked, formatDate);
					txt.showTimes(false);
					// make a field to store our parent DOM node
					// it's false b/c we don't have a parent yet.
					txt.getDOM().myParent = null;
					txt.getDOM().killYourself = txt.killYourself;
					txt.getDOM().refresh = txt.refresh;
					txt.getDOM().lighten = txt.lighten;
					txt.getDOM().darken = txt.darken;

					txt = txt.getDOM();

					e_obj[i] = txt;
				}else{
					e_obj[i].refresh();
				}

				i++;
				iter.setDate(iter.getDate() + 1);
			}
			while(e_obj.length > i){
				if($obj(jive.ext.x.xParent(e_obj[i])) && jive.ext.x.xParent(e_obj[i]) != null){
					jive.ext.x.xParent(e_obj[i]).removeChild(e_obj[i]);
					e_obj[i].myParent = null;
				}else if($obj(e_obj[i].myParent) && e_obj[i].myParent != null){
					e_obj[i].myParent.removeChild(e_obj[i]);
					e_obj[i].myParent = null;
				}
				e_obj[i].killYourself();
				e_obj.splice(i,1);
			}

			for(var i=0;i<e_obj.length;i++){
				if(that.getItemVisibility()){
					e_obj[i].style.visibility = "visible";
				}else{
					e_obj[i].style.visibility = "hidden";
				}
			}

			return e_obj;
		}catch(e){
			alert("getting array dom in month: " + e);
		}
	}

	function getTaskDOM(ttask){
		var settings = control.getSettingsManager();

		var holder = that.taskDOMHolders.get(ttask.getID());

		if($obj(holder) && holder != null){
			holder.refresh();
			return holder;
		}



		var taskdom = new jive.gui.TaskDOM(control, ttask, aurora_gui.notifyTaskClicked, aurora_gui.notifyTaskDblClicked);
		var holder = taskdom.getDOM();

		holder.refresh = taskdom.refresh;
		holder.lighten = taskdom.lighten;
		holder.darken = taskdom.darken;
		holder.getTask = taskdom.getTask;

		// we have to forward the killYourself function
		// when this task is unloaded / removed
		holder.killYourself = taskdom.killYourself;

		// make a field to store our parent DOM node
		// it's false b/c we don't have a parent yet.
		holder.myParent = null;

		that.taskDOMHolders.put(ttask.getID(), holder);

		task2cal.put(ttask.getID(), ttask.getProjectID());
		var calHash = taskDOMbyCalHolder.get(ttask.getProjectID());
		if(!$obj(calHash) || calHash == null){
			calHash = new jive.ext.y.HashTable();
			taskDOMbyCalHolder.put(ttask.getProjectID(), calHash);
		}
		calHash.put(ttask.getID(), holder);

		if(that.getItemVisibility()){
			holder.style.visibility = "visible";
		}else{
			holder.style.visibility = "hidden";
		}


		return holder;
	}

	/**
	 * private
	 * retrieves/creates the day cell for the
	 * specified time
	 */
	function getDayCell(dt){
		var dtemp = new Date();
		dtemp.setTime(dt.getTime());

		var hash = dtemp.getDate();
		var dobj = that.dayCells.get(hash);
		if($arr(dobj)){
			for(var i=0;i<dobj.length;i++){
				//
				// we already know the date is the same
				// b/c that's teh hash
				if(jive.model.monthYearEQ(dobj[i].getDate(), dtemp)){
					return dobj[i].getDOM();
				}
			}
		}else{
			dobj = new Array();
			that.dayCells.put(hash, dobj);
		}

		var day = new jive.gui.MonthDayCell(control, aurora_gui, that, dtemp);
		dobj.push(day);
		return day.getDOM();
	}


	/**
	 * shows the month view for
	 * Date d
	 * @param d the date of the month to show
	 */
	var last_month_min = null;
	var last_month_max = null;

	var loading_state = 0;
	this.showMonth = function(dtemp){
		var settings = control.getSettingsManager();
		var start_on = settings.getStartWeekOn();

		loading_state++;

		expanded = true;

		var d = new Date();
		d.setTime(dtemp.getTime());
		// daylight savings time has a problem here...
		// so set the hour to 17, that way daylight savings
		// won't change the date by 2 days if we move 24 hours forward/back
		// temp_panel = this.getDayCellHolder(d);
		d.setHours(17);

		// now set the date to the sunday (before|that) this month starts
		d.setDate(1);
		var sub = d.getDay();
		if(start_on != 0 && sub == 0){
			sub = 7;
		}
		d.setDate(d.getDate() - sub + start_on);

		// this will help us as we iterate through the days
		// it will keep track of what month the sunday for that
		// month is
		// this way, we can bail on the while loop once we've
		// finished showing this month
		var currMonth = new Date();
		currMonth.setTime(dtemp.getTime());

		var lang = control.getLanguageManager().getActiveLanguage();
		var affectedTasks = new Array();
		if(force_month_view || last_month_min == null || !jive.model.dateEQ(last_month_min, d)){
			force_month_view = false;
			/**
			 * set up the minimum date
			 */
			var currMin = new Date();
			currMin.setTime(d.getTime());
			aurora_gui.setMinDate(currMin);

			// add all the day cells
			var cell;
			var td;
			var tr;
			var table = document.createElement('DIV');
			table.setAttribute("class", "month_table");
			table.className = "month_table";
			while(main_panel.childNodes.length > 0) main_panel.removeChild(main_panel.childNodes[0]);

			tr = document.createElement('DIV');
			table.appendChild(tr);
			jive.ext.x.xHeight(tr, 20);

			for(var i=start_on;i-start_on<7;i++){
				td = document.createElement('DIV');
				tr.appendChild(td);
				td.setAttribute("class", "month_table_th");
				td.className = "month_table_th";
				if(jive.ext.x.xWidth(main_panel) > 640){
					td.appendChild(document.createTextNode(lang.longDay(i%7)));
				}else{
					td.appendChild(document.createTextNode(lang.shortDay(i%7)));
				}
				td.style.left = ((i-start_on) * 14.2857) + "%";
			}

			var rows = new Array();
			var weekday_num = 0;
			while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
				if(d.getDay() == start_on){
					tr = document.createElement('DIV');
					tr.setAttribute("class","month_table_row");
					tr.className = "month_table_row";
					rows.push(tr);
					weekday_num = 0;
				}
				td = getDayCell(d);
				td.updateText();
				td.over = false;
				tr.appendChild(td);
				td.style.left = (weekday_num * 14.2857) + "%";
				weekday_num++;


				var foo = new Date();
				foo.setTime(d.getTime());
				d = foo;
				d.setDate(d.getDate() + 1);
				if(d.getDay() == start_on){
					currMonth.setTime(d.getTime());
				}
				if(jive.ext.x.xIE4Up){
					// IE has a bug where it doesn't preserve checkbox's on/off state
					// so we need to fix it here...
					var tasks = that.getTasksOn(d);
					affectedTasks = affectedTasks.concat(tasks);
				}
			}
			for(var i=0;i<rows.length;i++){
				table.appendChild(rows[i]);
			}
			/**
			 * set up the maximum date
			 */
			aurora_gui.setMaxDate(d);

			var currDate = new Date();
			currDate.setTime(dtemp.getTime());
			currDate.setHours(17);
			aurora_gui.setCurrentDate(currDate);

			var lang = control.getLanguageManager().getActiveLanguage();

//			if(!jive.model.dateLTEQ(last_month_min, aurora_gui.getMinDate()) || !jive.model.dateGTEQ(last_month_max, aurora_gui.getMaxDate())){
//				// refresh the shading
//				that.notifyTimesChanged(aurora_gui.getMinDate(), aurora_gui.getMaxDate());
//			}

			main_panel.appendChild(table);

			for(var i=1;i<table.childNodes.length;i++){
				var tr = table.childNodes[i];
//				if($def(tr.style.height)) tr.style.height = (100/(table.childNodes.length-1) - .1) + "%";
				for(var j=0;j<tr.childNodes.length;j++){
					var cell = tr.childNodes[j];
					if(cell.childNodes.length > 0){
						jive.ext.x.xZIndex(cell.childNodes[0], 10 + i);
					}
				}
			}

			last_month_min = new Date();
			last_month_max = new Date();
			last_month_min.setTime(aurora_gui.getMinDate().getTime());
			last_month_max.setTime(aurora_gui.getMaxDate().getTime());
		}else{
			if(jive.ext.x.xIE4Up){
				while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
					var foo = new Date();
					foo.setTime(d.getTime());
					d = foo;
					d.setDate(d.getDate() + 1);
					if(d.getDay() == start_on){
						currMonth.setTime(d.getTime());
					}
					// IE has a bug where it doesn't preserve checkbox's on/off state
					// so we need to fix it here...
					var tasks = that.getTasksOn(d);
					affectedTasks = affectedTasks.concat(tasks);
				}
			}
			aurora_gui.setMinDate(last_month_min);
			aurora_gui.setMaxDate(last_month_max);
		}

		// IE has a bug where it doesn't preserve checkbox's on/off state
		// so we need to fix it here...
		if(jive.ext.x.xIE4Up){
			for(var i=0;i<affectedTasks.length;i++){
				var dom = getTaskDOM(affectedTasks[i]);
				dom.refresh();
			}
		}
		that.refreshShading();
		var t = aurora_gui.getFilterText();
		that.filter(t);
		aurora_gui.fixHeight();
	}


	/**
	 * unselects teh current event in the view
	 */
	function unselectAll(){
		if(jive.model.isEvent(aurora_gui.getSelectedItem())){
			var e_obj = getDOMArray(aurora_gui.getSelectedItem());
			for(var i=0;i<e_obj.length;i++){
				filterNode(e_obj[i], aurora_gui.getFilterText());
			}
		}
		if(jive.model.isTask(aurora_gui.getSelectedItem())){
			var e_obj = getTaskDOM(aurora_gui.getSelectedItem());
			filterNode(e_obj, aurora_gui.getFilterText());
		}
	}


	function ensureStartDates(){
		var start_on = control.getSettingsManager().getStartWeekOn();
		var dtemp = aurora_gui.getCurrentDate();
		var d = new Date();
		d.setTime(dtemp.getTime());
		// daylight savings time has a problem here...
		// so set the hour to 17, that way daylight savings
		// won't change the date by 2 days if we move 24 hours forward/back
		// temp_panel = this.getDayCellHolder(d);
		d.setHours(17);

		// now set the date to the sunday (before|that) this month starts
		d.setDate(1);
		var sub = d.getDay();
		if(start_on != 0 && sub == 0){
			sub = 7;
		}
		d.setDate(d.getDate() - sub + start_on);

		// this will help us as we iterate through the days
		// it will keep track of what month the sunday for that
		// month is
		// this way, we can bail on the while loop once we've
		// finished showing this month
		var currMonth = new Date();
		currMonth.setTime(dtemp.getTime());


		aurora_gui.setMinDate(d);
		while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
			var foo = new Date();
			foo.setTime(d.getTime());
			d = foo;
			d.setDate(d.getDate() + 1);
			if(d.getDay() == start_on){
				currMonth.setTime(d.getTime());
			}
		}
		var currMax = new Date();
		currMax.setTime(d.getTime());
		currMax.setDate(currMax.getDate() + 1);
		aurora_gui.setMaxDate(currMax);

		var currDate = new Date();
		currDate.setTime(dtemp.getTime());
		currDate.setHours(17);
		aurora_gui.setCurrentDate(currDate);

		last_month_min = new Date();
		last_month_max = new Date();
		last_month_min.setTime(aurora_gui.getMinDate().getTime());
		last_month_max.setTime(aurora_gui.getMaxDate().getTime());

		force_month_view = true;
	}

	function updateShowTasks(){
		// show/hide tasks based on preference
		var show_huh = control.getSettingsManager().getShowTasks();
		if(show_huh){
			// loop through calendars,
			// and if the calendar is visible, then
			// get all the task doms in that calendar
			// and displayBlock
			var cals = control.getCalendarCache().getCalendars();
			for(var i=0;i<cals.length;i++){
				if(control.isCalendarVisibleHuh(cals[i].getId())){
					var calDomHash = taskDOMbyCalHolder.get(cals[i].getId());
					if($obj(calDomHash)){
						// displayBlock everything
						var tasks = calDomHash.toArray();
						for(var j=0; j<tasks.length; j++){
							if($def(tasks[j].getTask)){
								jive.ext.x.xDisplayBlock(tasks[j]);
							}
						}
					}
				}
			}
		}else{
			// displayNone everything
			var tasks = that.taskDOMHolders.toArray(jive.gui.isMonthTaskDOM);
			for(var i=0; i<tasks.length; i++){
				if($def(tasks[i].getTask)){
					jive.ext.x.xDisplayNone(tasks[i]);
				}
			}
		}
	}

	/************************************************
	 * listen to stuff
	 ************************************************/

	/**
	 * add a listener to listen for event clicks
	 * when an event is clicked, highlight it in
	 * the main view, and unhighlight (if needed)
	 * the previously highlighted event
	 */
	var list = new Object();
	list.eventClicked = function(tevent){
		unselectAll();
		// select the event
		var e_obj = getDOMArray(tevent);
		if($obj(e_obj)){
			for(var i=0;i<e_obj.length;i++){
				e_obj[i].setAttribute("class","month_day_cell_item_highlight");
				e_obj[i].className = "month_day_cell_item_highlight";
			}
		}
	}
	list.eventDblClicked = function(tevent){ }
	list.taskClicked = function(ttask){
		unselectAll();
		// select the task
		var e_obj = getTaskDOM(ttask);
		if($obj(e_obj)){
			e_obj.setAttribute("class","month_day_cell_item_highlight");
			e_obj.className = "month_day_cell_item_highlight";
		}
	}
	list.taskDblClicked = function(ttask){ }
	list.unselectAll = function(){
		unselectAll();
	}
	aurora_gui.addEventListener(list);

	/**
	 * listen to event cache
	 * when we load an event, tell the month view
	 */

    /**
     * This code is used to manage events
     * for this UI
    var list = new jive.model.EventCacheListener();
	list.loadTask = function(ttask){
		if(ttask.hasDueDate()){
			that.addTask(ttask);
		}
	}
	list.doneLoadingEvents = function(){
		// refresh the shading
		that.refreshShading();
	}
	list.doneSavingEvent = function(tevent){
		loading_state++;
		// refresh the shading
		that.refreshShading();
		// also, udpate it's cache by calendar id
		var old_cal = event2cal.get(tevent.getId());
		if(old_cal != tevent.getCalendarId()){
			var obj = getDOMArray(tevent);

			// clear the event from the old calendar cache
			var calHash = eventDOMbyCalHolder.get(old_cal);
			calHash.clear(tevent.getId());
			// add it to the correct calendar cache
			var calHash = eventDOMbyCalHolder.get(tevent.getCalendarId());
			if(!$obj(calHash) || calHash == null){
				calHash = new jive.ext.y.HashTable();
				eventDOMbyCalHolder.put(tevent.getCalendarId(), calHash);
			}
			calHash.put(tevent.getId(), obj);
		}
	}
	list.savingEventFailed = function(){
		// refresh the shading
		that.refreshShading();
	}
	list.savingTask = function(ttask){
		var obj = getTaskDOM(ttask);
		obj.setDisabled(true);
	}
	list.doneSavingTask = function(ttask){
		// refresh the shading
		loading_state++;
		that.refreshShading();
		if(ttask.hasDueDate()){
			var obj = getTaskDOM(ttask);
			obj.refresh();
			obj.setDisabled(false);
		}
		// also, udpate it's cache by calendar id
		var old_cal = task2cal.get(ttask.getID());
		if(old_cal != ttask.getProjectID()){
			// clear the task from the old calendar cache
			var calHash = taskDOMbyCalHolder.get(old_cal);
			calHash.clear(ttask.getID());
			// add it to the correct calendar cache
			var calHash = taskDOMbyCalHolder.get(ttask.getProjectID());
			if(!$obj(calHash) || calHash == null){
				calHash = new jive.ext.y.HashTable();
				taskDOMbyCalHolder.put(ttask.getProjectID(), calHash);
			}
			calHash.put(ttask.getID(), obj);
		}
	}
	list.savingTaskFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
		var obj = getTaskDOM(ttask);
		obj.setDisabled(false);
		obj.setChecked(ttask.getStatus() == "Complete");
	}
	list.doneDeletingEvent = function(tevent){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingEventFailed = function(tevent){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingEventSeries = function(tevent){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingEventSeries = function(tevent){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingEventSeriesFailed = function(tevent){
		// refresh the shading
		that.refreshShading();
	}
	list.eventChanged = function(tevent){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTask = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingTask = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskSeries = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingTaskSeries = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskSeriesFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.taskChanged = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneAddingEvents = function(){
		// refresh the shading
		that.refreshShading();
	}
	control.getEventCache().addListener(list);
     *
     */
    var list = new jive.model.TaskCacheListener();
	list.loadTask = function(ttask){
		if(ttask.hasDueDate()){
			that.addTask(ttask);
		}
	}
	list.doneLoadingTasks = function(){
		// refresh the shading
		that.refreshShading();
	}
    list.taskChanged = function(ttask){
        // refresh the shading
        that.refreshShading();

    }

	list.savingTask = function(ttask){
		var obj = getTaskDOM(ttask);
		obj.setDisabled(true);
	}
	list.doneSavingTask = function(ttask){
		// refresh the shading
		loading_state++;
		that.refreshShading();
		if(ttask.hasDueDate()){
			var obj = getTaskDOM(ttask);
			obj.refresh();
			obj.setDisabled(false);
		}
		// also, udpate it's cache by calendar id
		var old_cal = task2cal.get(ttask.getID());
		if(old_cal != ttask.getProjectID()){
			// clear the task from the old calendar cache
			var calHash = taskDOMbyCalHolder.get(old_cal);
			calHash.clear(ttask.getID());
			// add it to the correct calendar cache
			var calHash = taskDOMbyCalHolder.get(ttask.getProjectID());
			if(!$obj(calHash) || calHash == null){
				calHash = new jive.ext.y.HashTable();
				taskDOMbyCalHolder.put(ttask.getProjectID(), calHash);
			}
			calHash.put(ttask.getID(), obj);
		}
	}
	list.savingTaskFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
		var obj = getTaskDOM(ttask);
		obj.setDisabled(false);
		obj.setChecked(ttask.getStatus() == "Complete");
	}
	list.deletingTask = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingTask = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskSeries = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingTaskSeries = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskSeriesFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	control.getTaskCache().addListener(list);
	/*************************************************
	 * last bit of initialization
	 *************************************************/
	ensureStartDates();
	jive.ext.x.xDisplayNone(main_panel);
}



jive.gui.isMonthEventDOM = function(item){
	return $def(item) && $def(item.getEvent);
}
jive.gui.isMonthTaskDOM = function(item){
	return $def(item) && $def(item.getTask);
}


/**
 * This is a month view that shows the next 2 weeks of dates only
 */
jive.gui.MiniMonthView = function(control, aurora_gui){


	var that = this;

	var expanded = false;


	var has_add_view = null;

	this.hasAddView = function(){
		if(has_add_view == null){
			has_add_view = $obj(aurora_gui.getView("add_event"));
		}
		return has_add_view;
	}

	/**
	 * holds day cells
	 */
	this.dayCells = new jive.ext.y.HashTable();

	/**
	 * holds the spans that hold the event titles
	 * will return an array for a key
	 * the array will hold all of the title spans for that event
	 *
	 * ie, if an event spans the days, it will have an array of 3 spans
	 * one for each day cell
	 */
	this.eventDOMHolders = new jive.ext.y.HashTable();
	this.taskDOMHolders = new jive.ext.y.HashTable();
    this.cpDOMHolders = new jive.ext.y.HashTable();

    /**
	 * set to true if showMonth() should ignore
	 * optimizing by not showing month if the date is the same
	 * as is currently shown.
	 *
	 * ie, set to true to force showMonth to do something
	 * meaningful
	 */
	var force_month_view = false;

	/**
	 * we're also going to cache
	 * the event and task by its calendar id
	 */
	var taskDOMbyCalHolder = new jive.ext.y.HashTable();
	var eventDOMbyCalHolder = new jive.ext.y.HashTable();
    var cpDOMbyCalHolder = new jive.ext.y.HashTable();

    /**
	 * track which calendar id's we're caching each task/event by
	 */
    var task2cal = new jive.ext.y.HashTable();
    var cp2cal = new jive.ext.y.HashTable();
	var event2cal = new jive.ext.y.HashTable();

	/**
	 * this is the main panel that holds all the day cell holders
	 */
	var main_panel = document.createElement('DIV');
	main_panel.setAttribute("class", "month_view_holder");
	main_panel.className = "month_view_holder";


	var visi = true;
	this.setItemVisibility = function(b){
		visi = b;
	}
	this.getItemVisibility = function(){
		return visi;
	}

    function getTaskDOM(ttask){
        var holder = that.taskDOMHolders.get(ttask.getID());

        if($obj(holder) && holder != null){
            holder.refresh();
            return holder;
        }

        var taskdom = new jive.gui.TaskDOM(control, ttask, aurora_gui.notifyTaskClicked, aurora_gui.notifyTaskDblClicked);
        holder = taskdom.getDOM();

        holder.refresh = taskdom.refresh;
        holder.lighten = taskdom.lighten;
        holder.darken = taskdom.darken;
        holder.getTask = taskdom.getTask;

        // we have to forward the killYourself function
        // when this task is unloaded / removed
        holder.killYourself = taskdom.killYourself;

        // make a field to store our parent DOM node
        // it's false b/c we don't have a parent yet.
        holder.myParent = null;

        that.taskDOMHolders.put(ttask.getID(), holder);

        task2cal.put(ttask.getID(), ttask.getProjectID());
        var calHash = taskDOMbyCalHolder.get(ttask.getProjectID());
        if(!$obj(calHash) || calHash == null){
            calHash = new jive.ext.y.HashTable();
            taskDOMbyCalHolder.put(ttask.getProjectID(), calHash);
        }
        calHash.put(ttask.getID(), holder);

        if(that.getItemVisibility()){
            holder.style.visibility = "visible";
        }else{
            holder.style.visibility = "hidden";
        }


        return holder;
    }

    function getCPDOM(cp){

        var holder = that.cpDOMHolders.get(cp.getID());

        if($obj(holder) && holder != null){
            holder.refresh();
            return holder;
        }

        var cpdom = new jive.gui.CPDOM(control, cp, aurora_gui.notifyCheckPointClicked, aurora_gui.notifyCheckPointDblClicked);
        holder = cpdom.getDOM();

        holder.refresh = cpdom.refresh;
        holder.lighten = cpdom.lighten;
        holder.darken = cpdom.darken;
        holder.getCheckPoint = cpdom.getCheckPoint;

        // we have to forward the killYourself function
        // when this task is unloaded / removed
        holder.killYourself = cpdom.killYourself;

        // make a field to store our parent DOM node
        // it's false b/c we don't have a parent yet.
        holder.myParent = null;

        that.cpDOMHolders.put(cp.getID(), holder);

        cp2cal.put(cp.getID(), cp.getProject().getID());
        var calHash = cpDOMbyCalHolder.get(cp.getProject().getID());
        if(!$obj(calHash) || calHash == null){
            calHash = new jive.ext.y.HashTable();
            cpDOMbyCalHolder.put(cp.getProject().getID(), calHash);
        }
        calHash.put(cp.getID(), holder);

        if(that.getItemVisibility()){
            holder.style.visibility = "visible";
        }else{
            holder.style.visibility = "hidden";
        }

        return holder;
    }

    /**
     * private
     * retrieves/creates the day cell for the
     * specified time
     */
    function getDayCell(dt){
        var dtemp = new Date();
        dtemp.setTime(dt.getTime());

        var hash = dtemp.getDate();
        var dobj = that.dayCells.get(hash);
        if($arr(dobj)){
            for(var i=0;i<dobj.length;i++){
                //
                // we already know the date is the same
                // b/c that's teh hash
                if(jive.model.monthYearEQ(dobj[i].getDate(), dtemp)){
                    return dobj[i].getDOM();
                }
            }
        }else{
            dobj = new Array();
            that.dayCells.put(hash, dobj);
        }

        var day = new jive.gui.MonthDayGroupedCell(control, aurora_gui, that, dtemp);
        dobj.push(day);
        return day.getDOM();
    }



	/**********************************************************************
	***********************************************************************
	***********************************************************************
	**
	** BEGIN VIEW INTERFACE TO AURORA GUI
	**
	***********************************************************************
	***********************************************************************
	***********************************************************************/

	/**
	 * return true if we can print this view, false otherwise
	 */
	this.hasPrintView = function(){
		return true;
	}

	/**
	 * this returns true if week view is the current view, false otherwise
	 */
	this.isExpandedHuh = function(){
		return expanded;
	}

	/**
	 * this function is called when week view comes into view
	 * we have officially 'switched' the view to week view
	 */
	this.expand = function(){
		expanded = true;
		aurora_gui.showArrows();
		jive.ext.x.xDisplayBlock(main_panel);
	}

	/**
	 * this function is called when week view goes out of view
	 * we have officially 'switched out' the view from week view
	 */
	this.collapse = function(){
		expanded = false;
		jive.ext.x.xDisplayNone(main_panel);
	}

	// the function to switch to the previous month
	// (used in the previous button)
	function prevMonthFunc(){
		var d = new Date();
		d.setTime(aurora_gui.getCurrentDate().getTime());
		jive.model.dateMinusMonth(d);
		aurora_gui.setCurrentDate(d);
		aurora_gui.notifyMonthClicked(d);
	}
	// the function to switch to the next month
	// (used in the next button)
	function nextMonthFunc(){
		var d = new Date();
		d.setTime(aurora_gui.getCurrentDate().getTime());
		d.setMonth(d.getMonth()+1);
		while(d.getMonth() > aurora_gui.getCurrentDate().getMonth()+1) jive.model.dateMinusDay(d);
		aurora_gui.setCurrentDate(d);
		aurora_gui.notifyMonthClicked(d);
	}

	/**
	 * return the function to go back a month
	 */
	this.getPrevViewFunc = function(){
		// the function to switch to the previous week
		// (used in the previous button)
		return prevMonthFunc;
	}

	/**
	 * return the function to go forward a month
	 */
	this.getNextViewFunc = function(){
		// the function to switch to the next week
		// (used in the next button)
		return nextMonthFunc;
	}

	/**
	 * when this view is in range, calcualte the min date that should be in range
	 * given month view's getCurrentDate() function
	 */
	this.getMinDate = function(){
		return aurora_gui.getMinDate();
	}

	/**
	 * when this view is in range, calcualte the min date that should be in range
	 * given month view's getCurrentDate() function
	 */
	this.getMaxDate = function(){
		return aurora_gui.getMaxDate();
	}

	/**
	 * returns teh text that should be in month view's header
	 * this is based on month_view's getCurrentDate() function
	 */
	this.getHeaderText = function(){
		var lang = control.getLanguageManager().getActiveLanguage();

		var d = new Date();
		d.setTime(aurora_gui.getCurrentDate().getTime());

		return lang.longMonth(d.getMonth());
	}

	/**
	 * shows the month according to month view's current date
	 */
	this.go = function(d){
		aurora_gui.setCurrentDate(d);
		that.showMonth(aurora_gui.getCurrentDate());
	}

	/**
	 * return the name of this view
	 */
	this.getName = function(){
		return "month";
	}

	/**
	 * return the unique hash for this view
	 */
	this.getHash = function(){
		return "month";
	}


	/**
	 * the language has been updated,
	 */
	this.updateText = function(){
		if(main_panel.childNodes.length > 0){
			var start_on = control.getSettingsManager().getStartWeekOn();
			var lang = control.getLanguageManager().getActiveLanguage();
			for(var i=start_on;i-start_on<7;i++){
				var td = main_panel.childNodes[0].childNodes[0].childNodes[(i-start_on)%7]; // the table cell
				if(td.childNodes.length > 0) td.removeChild(td.childNodes[0]);
				td.appendChild(document.createTextNode(lang.longDay(i%7)));
				td.setAttribute("height", "2");
				td.height = "2";
			}
		}
	}

	/**
	 * return the DOM object that represents this month view
	 */
	this.getDOM = function(){
		return main_panel;
	}


	/**
	 * this flushes a calendar entirely. it removes it from teh views,
	 * and also removes any DOM's we have cached for this calendar's
	 * events
	 */
	var filter_time;
	function filterNode(node, str){
		var title;
		var desc;
		var cal_id;
		if($def(node.getEvent)){
			var event = node.getEvent();
			title = event.getSubject().toLowerCase();
			desc = event.getDescription().toLowerCase();
			cal_id = event.getCalendarId();
		}else
		if($def(node.getTask)){
			var task = node.getTask();
			title = task.getSubject().toLowerCase();
			desc = task.getDescription().toLowerCase();
			cal_id = task.getProjectID();
		}
		if(control.isCalendarVisibleHuh(cal_id)){
			if(str.length == 0  || str.length > 0 && (title.indexOf(str) >= 0 || desc.indexOf(str) >= 0)){
//				alert("showing: " + cell.childNodes[i].innerText);
				node.darken();
			}else{
//				alert("hiding: " + cell.childNodes[i].innerText);
				node.lighten();
			}
		}
	}
	var last_filter = "";
	var last_filter_month = (new Date()).getMonth();
	this.filter = function(str){
		//
		// don't bother filtering if it's
		// the same thing we did last time
		if(last_filter != str || last_filter_month != aurora_gui.getCurrentDate().getMonth()){
			last_filter = str;
			last_filter_month = aurora_gui.getCurrentDate().getMonth()
			if(that.isExpandedHuh()){
				filter_time = "" + (new Date()).getTime() + "" + Math.random();
				var my_time = filter_time;
				var dt = new Date();
				var min = aurora_gui.getMinDate();
				var max = aurora_gui.getMaxDate();
				dt.setTime(min.getTime());
				str = str.toLowerCase();
				while(my_time == filter_time && (dt.getTime() < max.getTime() + 24*60*60*1000)){
					var cell = getDayCell(dt);
					for(var i=1; i < cell.childNodes.length; i++){
						filterNode(cell.childNodes[i], str);
					}
					dt.setTime(dt.getTime() + 24*60*60*1000);
				}
			}
		}
	}


	/**
	 * add (or refresh) an event to display on this month view
	 * i need to update this function. its not handling teh drag listeners
	 * correctly. i need to remove old listeners before i add new ones...
	 */
	this.addEvent = function(tevent){
		try{
			var settings = control.getSettingsManager();

			var e_obj = getDOMArray(tevent);



			var iter = new Date();
			if(tevent.isAllDay()){
				iter.setTime(tevent.getStart().getTime());
			}else{
				iter.setTime(settings.adjustDate(tevent.getStart()).getTime());
			}
			var i=0;
			var end_iter = new Date();
			if(tevent.isAllDay()){
				end_iter.setTime(tevent.getEnd().getTime());
			}else{
				end_iter.setTime(settings.adjustDate(tevent.getEnd()).getTime());
			}

			if(jive.model.dateLT(iter, control.getEventCache().getMinTime())){
				// iter is smaller, so change iter to min date
				iter.setTime(control.getEventCache().getMinTime().getTime());
			}
			if(jive.model.dateGT(end_iter, control.getEventCache().getMaxTime())){
				// end_iter is too large, so change end_iter to max date
				end_iter.setTime(control.getEventCache().getMaxTime().getTime());
			}


			while(jive.model.dateLTEQ(iter, end_iter)){
				var d = getDayCell(iter);
				// update the listener
				if($def(control.getDragManager)){
					var dm = control.getDragManager();
					var dragDate = new Date();
					dragDate.setTime(iter.getTime());
					var dlist = new jive.gui.CellDragListener(control, tevent, dragDate, control.notifyStopDrag, control.notifyDragging);
					if($obj(e_obj[i].monthViewDList) && e_obj[i].monthViewDList != null){
						dm.removeDragListener(e_obj[i], e_obj[i].monthViewDList);
					}
					e_obj[i].monthViewDList = dlist;
					dm.enableDrag(e_obj[i]);
					dm.addDragListener(e_obj[i], dlist);
				}
				/**
				 * hide the event if the calendar is hidden
				 */
				if(!control.isCalendarVisibleHuh(tevent.getCalendarId())){
					jive.ext.x.xDisplayNone(e_obj[i]);
				}else{
					jive.ext.x.xDisplayBlock(e_obj[i]);
				}
				var t = aurora_gui.getFilterText();
				filterNode(e_obj[i], t)
				d.appendEventDOM(e_obj[i]);
				// update our parent
				e_obj[i].myParent = d;

				i++;
				iter.setDate(iter.getDate() + 1);

			}
		}catch(e){
			alert("error adding event to month view: " + e);
		}
	}



    /**
     * add a task to display on this month view
     */
    this.addCheckPoint = function(cp){
        try{
            var e_obj = getCPDOM(cp);

            var d = getDayCell(cp.getDueDate());
            d.appendCheckPointDOM(e_obj);
            // update our parent
            e_obj.myParent = d;

            // update text/checkbox
            e_obj.refresh();

            // update the drag listener
            if($def(control.getDragManager)){
                var dm = control.getDragManager();
                var dragDate = new Date();
                dragDate.setTime(ttask.getDueDate());
                var dlist = new jive.gui.CellDragListener(control, ttask, dragDate, control.notifyStopDrag, control.notifyDragging);
                if($obj(e_obj.monthViewDList) && e_obj.monthViewDList != null){
                    dm.removeDragListener(e_obj, e_obj.monthViewDList);
                }
                e_obj.monthViewDList = dlist;
                dm.enableDrag(e_obj);
                dm.addDragListener(e_obj, dlist);
            }


            /**
             * hide the event if the calendar is hidden
             */
            if(!control.isCalendarVisibleHuh(cp.getProject().getID())){
                jive.ext.x.xDisplayNone(e_obj);
            }else{
                jive.ext.x.xDisplayBlock(e_obj);
            }
        }catch(e){
            alert(e);
        }
    }



    /**
	 * add a task to display on this month view
	 */
	this.addTask = function(ttask){
		try{
			var e_obj = getTaskDOM(ttask);

			var d = getDayCell(ttask.getDueDate());
			d.appendTaskDOM(e_obj);
			// update our parent
			e_obj.myParent = d;

			// update text/checkbox
			e_obj.refresh();

			// update the drag listener
			if($def(control.getDragManager)){
				var dm = control.getDragManager();
				var dragDate = new Date();
				dragDate.setTime(ttask.getDueDate());
				var dlist = new jive.gui.CellDragListener(control, ttask, dragDate, control.notifyStopDrag, control.notifyDragging);
				if($obj(e_obj.monthViewDList) && e_obj.monthViewDList != null){
					dm.removeDragListener(e_obj, e_obj.monthViewDList);
				}
				e_obj.monthViewDList = dlist;
				dm.enableDrag(e_obj);
				dm.addDragListener(e_obj, dlist);
			}


			/**
			 * hide the event if the calendar is hidden
			 */
			if(!control.isCalendarVisibleHuh(ttask.getProjectID())){
				jive.ext.x.xDisplayNone(e_obj);
			}else{
				jive.ext.x.xDisplayBlock(e_obj);
			}
		}catch(e){
			alert(e);
		}
	}

	/**
	 * removes an event from the month view
	 *
	 * this removes any drag listeners from span elements
	 * for that event, and disables the drag
	 */
	this.removeEvent = function(tevent){
		try{
			var settings = control.getSettingsManager();
			// i have completed the drag
			// so remove the listener from
			// all of this events listings in month view
			//
			// i'll be added again by the day cell...
			var arr = getDOMArray(tevent, true);
			// var arr = this.eventDOMHolders.get(event.getId());
			if(jive.ext.y.yArr(arr)){
				for(var j=0;j<arr.length;j++){
					if($def(control.getDragManager())){
						/**
						 * if we don't remove the drag listener
						 * then the event will always think it
						 * started dragging on the wrong day
						 * (the same day really, the day the event
						 *  was on when the page loaded)
						 */
						var dm = control.getDragManager();
						dm.removeDragListener(arr[j], arr[j].monthViewDList);
						dm.disableDrag(arr[j]);
					}
					if($obj(jive.ext.x.xParent(arr[j])) && jive.ext.x.xParent(arr[j]) != null){
						jive.ext.x.xParent(arr[j]).removeChild(arr[j]);
						arr[j].myParent = null;
					}else if($obj(arr[j].myParent) && arr[j].myParent != null){
						arr[j].myParent.removeChild(arr[j]);
						arr[j].myParent = null;
					}
					arr[j].killYourself();
				}
			}
		}catch(e){
			alert("error removing event: " + e);
		}
	}

	/**
	 * removes an event from the month view
	 *
	 * this removes any drag listeners from span elements
	 * for that event, and disables the drag
	 */
	this.removeTask = function(ttask){
		try{
			var settings = control.getSettingsManager();

			var arr = getTaskDOM(ttask);
			// var arr = this.eventDOMHolders.get(event.getId());
			if($obj(arr)){
				/**
				 * if we don't remove the drag listener
				 * then the event will always think it
				 * started dragging on the wrong day
				 * (the same day really, the day the event
				 *  was on when the page loaded)
				 */

				if($def(control.getDragManager)){
					// i have completed the drag
					// so remove the listener from
					// all of this events listings in month view
					//
					// i'll be added again by the day cell...
					var dm = control.getDragManager();
					dm.removeDragListener(arr, arr.monthViewDList);
					dm.disableDrag(arr);
				}

				if($obj(jive.ext.x.xParent(arr)) && jive.ext.x.xParent(arr) != null){
					jive.ext.x.xParent(arr).removeChild(arr);
					arr.myParent = null;
				}else if($obj(arr.myParent) && arr.myParent != null){
					arr.myParent.removeChild(arr);
					arr.myParent = null;
				}
				arr.killYourself();
			}
		}catch(e){
			alert("error removing task: " + ttask.getSubject() + "\nexception: " + e);
		}
	}


	/**
	 * this flushes a calendar entirely. it removes it from teh views,
	 * and also removes any DOM's we have cached for this calendar's
	 * events
	 */
	this.flushCalendar = function(cal){
		var events = that.eventDOMHolders.toArray(jive.gui.isMonthEventDOM);
		for(var i=0;i<events.length;i++){
			if(events[i].getEvent().getCalendarId() == cal.getId()){
				that.flushEvent(events[i].getEvent());
			}
		}
		var tasks = that.taskDOMHolders.toArray(jive.gui.isMonthTaskDOM);
		for(var i=0;i<tasks.length;i++){
			if(tasks[i].getTask().getCalendarId() == cal.getId()){
				that.flushTask(tasks[i].getTask());
			}
		}
	}

	/**
	 * this flushes an event entirely. it removes it from the views,
	 * and also removes any DOM's we have cached for that event
	 */
	this.flushEvent = function(tevent){
		try{
			that.removeEvent(tevent);
			that.eventDOMHolders.clear(tevent.getId());
			// also clear it from teh cache by calendar
			var cal_id = event2cal.get(tevent.getId());
			// clear the task from the old calendar cache
			var calHash = eventDOMbyCalHolder.get(cal_id);
			// we don't check for calHash existing here, b/c it must
			// b/c we added it to the cache in the removeEvent() func
			calHash.clear(tevent.getId());
		}catch(e){
			alert("error flushing event");
		}
	}

	/**
	 * this flushes a task entirely. it removes it from the views,
	 * and also removes any DOM's we have cached for that task
	 */
	this.flushTask = function(ttask){
		try{
			that.removeTask(ttask);
			that.taskDOMHolders.clear(ttask.getID());
			// also clear it from teh cache by calendar
			var cal_id = task2cal.get(ttask.getID());
			// clear the task from the old calendar cache
			var calHash = taskDOMbyCalHolder.get(cal_id);
			// we don't check for calHash existing here, b/c it must
			// b/c we added it to the cache in the getTaskDOM() func
			calHash.clear(ttask.getID());
		}catch(e){
			alert("flushEvent: " + e);
		}
	}


	/**
	 * refresh the text on the bar, possibly because
	 * of a settings change, etc
	 */
	this.refresh = function(){
		var settings = control.getSettingsManager();
		var arr = that.eventDOMHolders.toArray(jive.gui.isMonthEventDOM);
		try{
			for(var ri=0;ri<arr.length;ri++){
				//
				// compare the event against the two timezones,
				// and if teh start/end dates are different,
				// the referesh it
				//
				var tevent = arr[ri].getEvent()
				var old = settings.getOldTimezone();
				if(!jive.model.dateEQ(settings.adjustDate(tevent.getStart()), settings.adjustDate(tevent.getStart(), old)) ||
				   !jive.model.dateEQ(settings.adjustDate(tevent.getEnd()), settings.adjustDate(tevent.getEnd(), old))){
					that.flushEvent(tevent);
					that.addEvent(tevent);
				}
			}
		}catch(e){
			alert(e);
		}

		if(that.isExpandedHuh()){
			that.showMonth(aurora_gui.getCurrentDate());
		}
		that.refreshShading();
	}

	/**
	 * we've just now been placed in our parent div, so
	 * adjust height of our holders etc to fit
	 */
	this.init = function(veryinner){
		veryinner.appendChild(main_panel);
	}

	this.killYourself = function(){
		control = null;
		aurora_gui = null;
	}

	this.refreshWeather = function(){
		// loop through all dates in month view
		// and set weather
		var min = new Date();
		min.setTime(aurora_gui.getMinDate().getTime());

		while(jive.model.dateLTEQ(min, aurora_gui.getMaxDate())){

			var cell = getDayCell(min);
			var image = control.getSettingsManager().getWeatherImage(min);
			var color = cell.style.backgroundColor;
			if(image.length > 0){
				var left = 22;
				if(min.getDate() == 1){
					left = 42;
				}else{
					left = 22;
				}
//				cell.setAttribute("style", "background: url(" + image + ") " + left + "px 2px no-repeat " + cell.style.backgroundColor);
				cell.style.background =  "url(" + image + ") " + left + "px 2px no-repeat " + cell.style.backgroundColor;
			}else{
//				cell.setAttribute("style", "");
				cell.style.background =  "";
			}
			cell.style.backgroundColor = color;

			min.setDate(min.getDate() + 1);
		}

	}



	/**
	 * shows the month view for
	 * Date d
	 * @param d the date of the month to show
	 */
	this.refreshShading = function(){
//		if(loading_state == 1){
			// we've just loaded month view for the first time.
			// so no events/tasks are even in here yet, so don't
			// bother updating shading, when there aren't any
			// events/tasks anyways.
			//
//			return;
//		}
		var settings = control.getSettingsManager();
		var start_on = settings.getStartWeekOn();

		var dtemp = new Date();
		dtemp.setTime(aurora_gui.getMaxDate().getTime());

		var dt = new Date();
		dt.setTime(aurora_gui.getMinDate().getTime());

		// daylight savings time has a problem here...
		// so set the hour to 17, that way daylight savings
		// won't change the date by 2 days if we move 24 hours forward/back
		// temp_panel = this.getDayCellHolder(d);
		dt.setHours(17);
		// now set the date to the sunday (before|that) this month starts
		dt.setDate(1);

		var sub = dt.getDay();
		if(start_on != 0 && sub == 0){
			sub = 7;
		}
		dt.setDate(dt.getDate() - sub + start_on);

		// this will help us as we iterate through the days
		// it will keep track of what month the sunday for that
		// month is
		// this way, we can bail on the while loop once we've
		// finished showing this month
		var currMonth = new Date();
		currMonth.setTime(dt.getTime());

		var backgrounds = new Array();
		backgrounds[0] = "#FFFFFF";
		backgrounds[1] = "#EFEFEF";
		backgrounds[2] = "#DFDFDF";
		backgrounds[3] = "#CFCFCF";
		backgrounds[4] = "#BFBFBF";

		var now = settings.getNOW();

		var smart_shading = settings.getSmartShading();
		var current_month = aurora_gui.getCurrentDate().getMonth();
		// add all the day cells
		while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
			var cell = getDayCell(dt);
			var dt_is_today = jive.model.dateEQ(dt, now);
			if(smart_shading){
                var true_num = cell.countVisibleItems();

				if(true_num > 4){
					true_num = 4;
				}else if(true_num < 0){
					true_num = 0;
				}
				if(dt_is_today){
					cell.style.backgroundColor = "#e4f6e7";
					cell.outColor = "#e4f6e7";
				}else{
					cell.style.backgroundColor = backgrounds[true_num];
					cell.outColor = backgrounds[true_num];
				}
			}else if(cell.getDate().getMonth() != current_month){
				cell.style.backgroundColor = backgrounds[2];
				cell.outColor = backgrounds[2];
			}else{
				cell.style.backgroundColor = backgrounds[0];
				cell.outColor = backgrounds[0];
			}
			if(dt_is_today){
				cell.style.backgroundColor = "#e4f6e7";
				cell.setAttribute("class", "month_cell month_today_cell");
				cell.className = "month_cell month_today_cell";
				cell.overColor = "#ffffda";
			}else{
				cell.setAttribute("class", "month_cell month_day_cell");
				cell.className = "month_cell month_day_cell";
//				cell.overColor = "#f0f6fc";
				cell.overColor = "#ffffda";
			}

			dt.setDate(dt.getDate() + 1);
			if(dt.getDay() == 0){
				currMonth.setTime(dt.getTime());
			}
		}
		that.refreshWeather();
	}



	/**
	 * toggle event visibility if
	 * the calendar visibility is switched (ie, in the sidebar)
	 */
	this.calendarVisible = function(calendar, visibleHuh){
		var show_tasks_huh = control.getSettingsManager().getShowTasks();
		var objs = taskDOMbyCalHolder.get(calendar.getId());
		if($obj(objs) && objs != null){
			objs = objs.toArray();
			for(var i=0;i<objs.length;i++){
				if(visibleHuh && show_tasks_huh){
					jive.ext.x.xDisplayBlock(objs[i]);
				}else{
					jive.ext.x.xDisplayNone(objs[i]);
				}
			}
		}
		var objs = eventDOMbyCalHolder.get(calendar.getId());
		if($obj(objs) && objs != null){
			objs = objs.toArray();
			for(var i=0;i<objs.length;i++){
				for(var j=0; j<objs[i].length; j++){
					if(visibleHuh){
						jive.ext.x.xDisplayBlock(objs[i][j]);
					}else{
						jive.ext.x.xDisplayNone(objs[i][j]);
					}
				}
			}
		}
		that.refreshShading();
	}

	/**
	 * notify everybody else that a drag/drop happened
	 */
	this.stopDrag = function(tevent, dt, left, top){
		var doneHuh = false;
		if(that.isExpandedHuh()){
			/**
			 * loop through table cells
			 * if a point matches, then drop it in it's day cell
			 */
			if(main_panel.childNodes.length > 0){
				var table = main_panel.childNodes[0];
				for(var i=1;i<table.childNodes.length;i++){
					tr = table.childNodes[i];
					for(var j=0;j<tr.childNodes.length;j++){
						if(tr.childNodes[j].childNodes.length > 0){
							var day = tr.childNodes[j];
							if(jive.ext.x.xHasPoint(day, left, top)){
								if($def(day.dropPoint)){
									day.dropPoint(tevent,dt);
									doneHuh = true;
								}
							}
						}
					}
				}
			}
		}
		return doneHuh;
	}

	// this is the last day cell that
	// we've hovered over when dragging
	// an event or task
	var hovered_day_cell = null;
	var threadNum = 0;
	this.dragging = function(tevent, dt, left, tp){
		// this function will be called as they drag around an event
		// which means it'll be called probably as its running
		//
		// the threadNum and myThread variables will make sure that
		// I drop out of execution asap as a new thread starts.
		threadNum++;
		var myThread = threadNum;

		// track if we show a drop zone or not
		var zonedHuh = false;

		// let's check our currently hovered day cell
		// to see if that's what we're still hovered over.
		// if we're not over the same cell anymore, lets
		// just check each cell sequentially.
		//
		// later, we can optimize this to check nearby cells
		// first, instead of just checking /left->bottom/right
		//
		if(hovered_day_cell != null){
			if(jive.ext.x.xHasPoint(hovered_day_cell, left, tp)){
				var w = Math.floor(.95 * jive.ext.x.xWidth(hovered_day_cell));
				control.showHoverOver(jive.ext.x.xPageX(hovered_day_cell), jive.ext.x.xPageY(hovered_day_cell), w, jive.ext.x.xHeight(hovered_day_cell));
				zonedHuh = true;
			}
		}
		// loop through all dates in month view
		// and set weather
		if(main_panel.childNodes.length > 0 && !zonedHuh){
			var table = main_panel.childNodes[0];
			for(var i=1;i<table.childNodes.length && myThread == threadNum && !zonedHuh;i++){
				tr = table.childNodes[i];
				for(var j=0;j<tr.childNodes.length && myThread == threadNum && !zonedHuh;j++){
					var day = tr.childNodes[j];
					if(jive.ext.x.xHasPoint(day, left, tp)){
						if($def(day.dropPoint)){
							// hooray!
							// move the drag area here
							// and save this cell as
							// the last hovered
							var w = Math.floor(.95 * jive.ext.x.xWidth(day));
							control.showHoverOver(jive.ext.x.xPageX(day), jive.ext.x.xPageY(day), w, jive.ext.x.xHeight(day));
							hovered_day_cell = day;
							zonedHuh = true;
						}
					}
				}
			}
			if(myThread != threadNum){
				return;
			}
		}
		if(!zonedHuh){
			control.hideHover();
		}
		return zonedHuh;
	}


	/**********************************************************************
	***********************************************************************
	***********************************************************************
	**
	** END VIEW INTERFACE TO MONTH_VIEW
	**
	***********************************************************************
	***********************************************************************
	***********************************************************************/

	this.fixHeight = function(for_rows){
		try{
//			alert("updating height: " + for_rows);
			jive.ext.x.xHeight(main_panel, for_rows);
			var for_rows = for_rows - 20; // subtract 20 b/c of the header row
			if(main_panel.childNodes.length > 0){
				var table = main_panel.childNodes[0];
				for_rows += for_rows % (table.childNodes.length - 1);
				for(var i=1;i<table.childNodes.length;i++){
					var tr = table.childNodes[i];
					jive.ext.x.xHeight(tr, Math.floor(for_rows / (table.childNodes.length - 1)));
					if(jive.ext.x.xIE4Up){
//						alert("updating foo!");
						for(var j=0;j<tr.childNodes.length;j++){
							jive.ext.x.xDisplayNone(tr.childNodes[j]);
							jive.ext.x.xDisplayBlock(tr.childNodes[j]);
						}
					}
				}
			}
		}catch(e){
			alert(e);
		}
	}

	/**
	 * gets the events for a specific day
	 * @param dt a date object
	 */
	this.getEventsOn = function(dt){
		// we're going to load the day cell for this event
		// and get the events out of that
		var events = new Array();
		var cell = getDayCell(dt);
		for(var i=1; i < cell.childNodes.length; i++){
			if($def(cell.childNodes[i].getEvent)){
				events.push(cell.childNodes[i].getEvent());
			}
		}
		return events;
	}

	/**
	 * gets the tasks for a specific day
	 * @param dt a Date object
	 */
	this.getTasksOn = function(dt){
		// we're going to load the day cell for this event
		// and get the events out of that
		var tasks = new Array();
		var cell = getDayCell(dt);
		for(var i=1; i < cell.childNodes.length; i++){
			if($def(cell.childNodes[i].getTask)){
				tasks.push(cell.childNodes[i].getTask());
			}
		}
		return tasks;
	}


	/**
	 * gets an array of dom objects for the event
	 * there will be enough dom objects to have 1 per day
	 * that the event spans
	 */
	function getDOMArray(tevent, skip){

		try{
			var settings = control.getSettingsManager();

			//
			// we store 1 DOM entry for each day the event is on
			//
			// however, if the dom entry would extend past teh min or max date
			// in month view, then we only cache the necessary dom entries.
			//
			// so if we're already caching the dom array, then lets make sure
			// that we're caching all of it. it could be that we're caching this months,
			// but we just loaded a whole new month and extended max date, so we need
			// to load in more dom's onto the array.
			//
			// if nothing is in the cache yet, then lets load in enough dom's
			// to either take care of the entire event's duration, or extend
			// until min or max date, whichever is shorter
			//
			//

			var iter = new Date();
			var end_iter = new Date();
			if(tevent.isAllDay()){
				iter.setTime(tevent.getStart().getTime());
				end_iter.setTime(tevent.getEnd().getTime());
			}else{
				iter.setTime(settings.adjustDate(tevent.getStart()).getTime());
				end_iter.setTime(settings.adjustDate(tevent.getEnd()).getTime());
			}

			//
			// iter is the start of the event
			// end_iter is the end of the event
			//
			// now lets make sure that iter >= minDate and end_iter <= maxDate
			//

			if(jive.model.dateLT(iter, control.getEventCache().getMinTime())){
				// iter is smaller, so change iter to min date
				iter.setTime(control.getEventCache().getMinTime().getTime());
			}
			if(jive.model.dateGT(end_iter, control.getEventCache().getMaxTime())){
				// end_iter is too large, so change end_iter to max date
				end_iter.setTime(control.getEventCache().getMaxTime().getTime());
			}
		}catch(e){
			alert("top of getdomarray: " + e);
		}

		try{

			var e_obj = that.eventDOMHolders.get(tevent.getId());
			if(!$obj(e_obj)){
				if($def(skip) && skip){
					return null;
				}
				e_obj = new Array();
				e_obj.getEvent = function(){ return tevent; }
				that.eventDOMHolders.put(tevent.getId(), e_obj);

				event2cal.put(tevent.getId(), tevent.getCalendarId());
				var calHash = eventDOMbyCalHolder.get(tevent.getCalendarId());
				if(!$obj(calHash) || calHash == null){
					calHash = new jive.ext.y.HashTable();
					eventDOMbyCalHolder.put(tevent.getCalendarId(), calHash);
				}
				calHash.put(tevent.getId(), e_obj);
			}

			var i=0;
			while(jive.model.dateLTEQ(iter, end_iter)){
				if(e_obj.length <= i){

					var formatDate = function(d){
						return function(d2){
							var dh = new jive.model.DateHelper(control);
							if(jive.model.dateEQ(settings.adjustDate(d2), d)){
								return dh.formatTo12HourTime(settings.adjustDate(d2));
							}else{
								return dh.formatToShortDate(settings.adjustDate(d2));
							}
						}
					}(iter);

					var txt = control.getEventDOMFactory().getEventDOM(tevent, aurora_gui.notifyEventClicked, aurora_gui.notifyEventDblClicked, formatDate);
					txt.showTimes(false);
					// make a field to store our parent DOM node
					// it's false b/c we don't have a parent yet.
					txt.getDOM().myParent = null;
					txt.getDOM().killYourself = txt.killYourself;
					txt.getDOM().refresh = txt.refresh;
					txt.getDOM().lighten = txt.lighten;
					txt.getDOM().darken = txt.darken;

					txt = txt.getDOM();

					e_obj[i] = txt;
				}else{
					e_obj[i].refresh();
				}

				i++;
				iter.setDate(iter.getDate() + 1);
			}
			while(e_obj.length > i){
				if($obj(jive.ext.x.xParent(e_obj[i])) && jive.ext.x.xParent(e_obj[i]) != null){
					jive.ext.x.xParent(e_obj[i]).removeChild(e_obj[i]);
					e_obj[i].myParent = null;
				}else if($obj(e_obj[i].myParent) && e_obj[i].myParent != null){
					e_obj[i].myParent.removeChild(e_obj[i]);
					e_obj[i].myParent = null;
				}
				e_obj[i].killYourself();
				e_obj.splice(i,1);
			}

			for(var i=0;i<e_obj.length;i++){
				if(that.getItemVisibility()){
					e_obj[i].style.visibility = "visible";
				}else{
					e_obj[i].style.visibility = "hidden";
				}
			}

			return e_obj;
		}catch(e){
			alert("getting array dom in month: " + e);
		}
	}

    var num_weeks = 2;
    this.setNumWeeks = function(foo){
        num_weeks = foo;
    }



    /**
	 * shows the month view for
	 * Date d
	 * @param d the date of the month to show
	 */
	var last_month_min = null;
	var last_month_max = null;

	var loading_state = 0;
	this.showMonth = function(dtemp){
		var settings = control.getSettingsManager();
		var start_on = settings.getStartWeekOn();

		loading_state++;

		expanded = true;

		var d = new Date();
		d.setTime(dtemp.getTime());
		// daylight savings time has a problem here...
		// so set the hour to 17, that way daylight savings
		// won't change the date by 2 days if we move 24 hours forward/back
		// temp_panel = this.getDayCellHolder(d);
		d.setHours(17);
        start_on = d.getDay();

        // now set the date to the sunday (before|that) this month starts
//		d.setDate(1);
//		var sub = d.getDay();
//		if(start_on != 0 && sub == 0){
//			sub = 7;
//		}
//		d.setDate(d.getDate() - sub + start_on);

		// this will help us as we iterate through the days
		// it will keep track of what month the sunday for that
		// month is
		// this way, we can bail on the while loop once we've
		// finished showing this month
		var currMonth = new Date();
		currMonth.setTime(dtemp.getTime());

		var lang = control.getLanguageManager().getActiveLanguage();
		var affectedTasks = new Array();
		if(force_month_view || last_month_min == null || !jive.model.dateEQ(last_month_min, d)){
			force_month_view = false;
			/**
			 * set up the minimum date
			 */
			var currMin = new Date();
			currMin.setTime(d.getTime());
			aurora_gui.setMinDate(currMin);

			// add all the day cells
			var cell;
			var td;
			var tr;
			var table = document.createElement('DIV');
			table.setAttribute("class", "month_table");
			table.className = "month_table";
			while(main_panel.childNodes.length > 0) main_panel.removeChild(main_panel.childNodes[0]);

			tr = document.createElement('DIV');
			table.appendChild(tr);
			jive.ext.x.xHeight(tr, 20);

			for(var i=start_on;i-start_on<7;i++){
				td = document.createElement('DIV');
				tr.appendChild(td);
				td.setAttribute("class", "month_table_th");
				td.className = "month_table_th";
				if(jive.ext.x.xWidth(main_panel) > 640){
					td.appendChild(document.createTextNode(lang.longDay(i%7)));
				}else{
					td.appendChild(document.createTextNode(lang.shortDay(i%7)));
				}
				td.style.left = ((i-start_on) * 14.2857) + "%";
			}

			var rows = new Array();
			var weekday_num = 0;
            for(var i=0;i < num_weeks * 7;i++){
//            while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
				if(d.getDay() == start_on){
					tr = document.createElement('DIV');
					tr.setAttribute("class","month_table_row");
					tr.className = "month_table_row";
					rows.push(tr);
					weekday_num = 0;
				}
				td = getDayCell(d);
				td.updateText();
				td.over = false;
				tr.appendChild(td);
				td.style.left = (weekday_num * 14.2857) + "%";
				weekday_num++;


				var foo = new Date();
				foo.setTime(d.getTime());
				d = foo;
				d.setDate(d.getDate() + 1);
				if(d.getDay() == start_on){
					currMonth.setTime(d.getTime());
				}
				if(jive.ext.x.xIE4Up){
					// IE has a bug where it doesn't preserve checkbox's on/off state
					// so we need to fix it here...
					var tasks = that.getTasksOn(d);
					affectedTasks = affectedTasks.concat(tasks);
				}
			}
			for(var i=0;i<rows.length;i++){
				table.appendChild(rows[i]);
			}
			/**
			 * set up the maximum date
			 */
			aurora_gui.setMaxDate(d);

			var currDate = new Date();
			currDate.setTime(dtemp.getTime());
			currDate.setHours(17);
			aurora_gui.setCurrentDate(currDate);

			var lang = control.getLanguageManager().getActiveLanguage();

//			if(!jive.model.dateLTEQ(last_month_min, aurora_gui.getMinDate()) || !jive.model.dateGTEQ(last_month_max, aurora_gui.getMaxDate())){
//				// refresh the shading
//				that.notifyTimesChanged(aurora_gui.getMinDate(), aurora_gui.getMaxDate());
//			}

			main_panel.appendChild(table);

			for(var i=1;i<table.childNodes.length;i++){
				var tr = table.childNodes[i];
//				if($def(tr.style.height)) tr.style.height = (100/(table.childNodes.length-1) - .1) + "%";
				for(var j=0;j<tr.childNodes.length;j++){
					var cell = tr.childNodes[j];
					if(cell.childNodes.length > 0){
						jive.ext.x.xZIndex(cell.childNodes[0], 10 + i);
					}
				}
			}

			last_month_min = new Date();
			last_month_max = new Date();
			last_month_min.setTime(aurora_gui.getMinDate().getTime());
			last_month_max.setTime(aurora_gui.getMaxDate().getTime());
		}else{
			if(jive.ext.x.xIE4Up){
				while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
					var foo = new Date();
					foo.setTime(d.getTime());
					d = foo;
					d.setDate(d.getDate() + 1);
					if(d.getDay() == start_on){
						currMonth.setTime(d.getTime());
					}
					// IE has a bug where it doesn't preserve checkbox's on/off state
					// so we need to fix it here...
					var tasks = that.getTasksOn(d);
					affectedTasks = affectedTasks.concat(tasks);
				}
			}
			aurora_gui.setMinDate(last_month_min);
			aurora_gui.setMaxDate(last_month_max);
		}

		// IE has a bug where it doesn't preserve checkbox's on/off state
		// so we need to fix it here...
		if(jive.ext.x.xIE4Up){
			for(var i=0;i<affectedTasks.length;i++){
				var dom = getTaskDOM(affectedTasks[i]);
				dom.refresh();
			}
		}
		that.refreshShading();
		var t = aurora_gui.getFilterText();
		that.filter(t);
		aurora_gui.fixHeight();
	}


	/**
	 * unselects teh current event in the view
	 */
	function unselectAll(){
		if(jive.model.isEvent(aurora_gui.getSelectedItem())){
			var e_obj = getDOMArray(aurora_gui.getSelectedItem());
			for(var i=0;i<e_obj.length;i++){
				filterNode(e_obj[i], aurora_gui.getFilterText());
			}
		}
		if(jive.model.isTask(aurora_gui.getSelectedItem())){
			var e_obj = getTaskDOM(aurora_gui.getSelectedItem());
			filterNode(e_obj, aurora_gui.getFilterText());
		}
	}


	function ensureStartDates(){
		var start_on = control.getSettingsManager().getStartWeekOn();
		var dtemp = aurora_gui.getCurrentDate();
		var d = new Date();
		d.setTime(dtemp.getTime());
		// daylight savings time has a problem here...
		// so set the hour to 17, that way daylight savings
		// won't change the date by 2 days if we move 24 hours forward/back
		// temp_panel = this.getDayCellHolder(d);
		d.setHours(17);

		// now set the date to the sunday (before|that) this month starts
		d.setDate(1);
		var sub = d.getDay();
		if(start_on != 0 && sub == 0){
			sub = 7;
		}
		d.setDate(d.getDate() - sub + start_on);

		// this will help us as we iterate through the days
		// it will keep track of what month the sunday for that
		// month is
		// this way, we can bail on the while loop once we've
		// finished showing this month
		var currMonth = new Date();
		currMonth.setTime(dtemp.getTime());


		aurora_gui.setMinDate(d);
		while(currMonth.getMonth() <= dtemp.getMonth() && currMonth.getYear() == dtemp.getYear() || currMonth.getYear() < dtemp.getYear()){
			var foo = new Date();
			foo.setTime(d.getTime());
			d = foo;
			d.setDate(d.getDate() + 1);
			if(d.getDay() == start_on){
				currMonth.setTime(d.getTime());
			}
		}
		var currMax = new Date();
		currMax.setTime(d.getTime());
		currMax.setDate(currMax.getDate() + 1);
		aurora_gui.setMaxDate(currMax);

		var currDate = new Date();
		currDate.setTime(dtemp.getTime());
		currDate.setHours(17);
		aurora_gui.setCurrentDate(currDate);

		last_month_min = new Date();
		last_month_max = new Date();
		last_month_min.setTime(aurora_gui.getMinDate().getTime());
		last_month_max.setTime(aurora_gui.getMaxDate().getTime());

		force_month_view = true;
	}

	function updateShowTasks(){
		// show/hide tasks based on preference
		var show_huh = control.getSettingsManager().getShowTasks();
		if(show_huh){
			// loop through calendars,
			// and if the calendar is visible, then
			// get all the task doms in that calendar
			// and displayBlock
			var cals = control.getCalendarCache().getCalendars();
			for(var i=0;i<cals.length;i++){
				if(control.isCalendarVisibleHuh(cals[i].getId())){
					var calDomHash = taskDOMbyCalHolder.get(cals[i].getId());
					if($obj(calDomHash)){
						// displayBlock everything
						var tasks = calDomHash.toArray();
						for(var j=0; j<tasks.length; j++){
							if($def(tasks[j].getTask)){
								jive.ext.x.xDisplayBlock(tasks[j]);
							}
						}
					}
				}
			}
		}else{
			// displayNone everything
			var tasks = that.taskDOMHolders.toArray(jive.gui.isMonthTaskDOM);
			for(var i=0; i<tasks.length; i++){
				if($def(tasks[i].getTask)){
					jive.ext.x.xDisplayNone(tasks[i]);
				}
			}
		}
	}

	/************************************************
	 * listen to stuff
	 ************************************************/

	/**
	 * add a listener to listen for event clicks
	 * when an event is clicked, highlight it in
	 * the main view, and unhighlight (if needed)
	 * the previously highlighted event
	 */
	var list = new Object();
	list.eventClicked = function(tevent){
		unselectAll();
		// select the event
		var e_obj = getDOMArray(tevent);
		if($obj(e_obj)){
			for(var i=0;i<e_obj.length;i++){
				e_obj[i].setAttribute("class","month_day_cell_item_highlight");
				e_obj[i].className = "month_day_cell_item_highlight";
			}
		}
	}
	list.eventDblClicked = function(tevent){ }
	list.taskClicked = function(ttask){
		unselectAll();
		// select the task
		var e_obj = getTaskDOM(ttask);
		if($obj(e_obj)){
			e_obj.setAttribute("class","month_day_cell_item_highlight");
			e_obj.className = "month_day_cell_item_highlight";
		}
	}
	list.taskDblClicked = function(ttask){ }
	list.unselectAll = function(){
		unselectAll();
	}
	aurora_gui.addEventListener(list);

	/**
	 * listen to event cache
	 * when we load an event, tell the month view
	 */
    var list = new jive.model.ProjectCacheListener();
    list.loadProject = function(p){
        var cps = p.getCheckPoints();
        for(var i=0;i<cps.length;i++){
            that.addCheckPoint(cps[i]);
        }
    }
    control.getProjectCache().addListener(list);




    var list = new jive.model.TaskCacheListener();
	list.loadTask = function(ttask){
		if(ttask.hasDueDate()){
			that.addTask(ttask);
		}
	}
	list.doneLoadingTasks = function(){
		// refresh the shading
		that.refreshShading();
	}
    list.taskChanged = function(ttask){
        // refresh the shading
        that.refreshShading();

    }

	list.savingTask = function(ttask){
		var obj = getTaskDOM(ttask);
		obj.setDisabled(true);
	}
	list.doneSavingTask = function(ttask){
		// refresh the shading
		loading_state++;
		that.refreshShading();
		if(ttask.hasDueDate()){
			var obj = getTaskDOM(ttask);
			obj.refresh();
			obj.setDisabled(false);
		}
		// also, udpate it's cache by calendar id
		var old_cal = task2cal.get(ttask.getID());
		if(old_cal != ttask.getProjectID()){
			// clear the task from the old calendar cache
			var calHash = taskDOMbyCalHolder.get(old_cal);
			calHash.clear(ttask.getID());
			// add it to the correct calendar cache
			var calHash = taskDOMbyCalHolder.get(ttask.getProjectID());
			if(!$obj(calHash) || calHash == null){
				calHash = new jive.ext.y.HashTable();
				taskDOMbyCalHolder.put(ttask.getProjectID(), calHash);
			}
			calHash.put(ttask.getID(), obj);
		}
	}
	list.savingTaskFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
		var obj = getTaskDOM(ttask);
		obj.setDisabled(false);
		obj.setChecked(ttask.getStatus() == "Complete");
	}
	list.deletingTask = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingTask = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskSeries = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.doneDeletingTaskSeries = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	list.deletingTaskSeriesFailed = function(ttask){
		// refresh the shading
		that.refreshShading();
	}
	control.getTaskCache().addListener(list);
	/*************************************************
	 * last bit of initialization
	 *************************************************/
	ensureStartDates();
	jive.ext.x.xDisplayNone(main_panel);
}



jive.gui.MonthDayCell = function(control, aurora_gui, month_view, dtemp){

    var cell = new jive.gui.MonthDayGroupedCell(control, aurora_gui, month_view, dtemp);
    var day = cell.getDOM();


    this.getTasks = day.getTasks;

    /**
	 * add a function to the day cell
	 * that lets it add events
	 *
	 * this will add the event into sorted position
	 * into this day cell
	 */
	day.appendTaskDOM = function(txt){
		var txt_title = txt.getTask().getSubject().toLowerCase();
		for(var i=0;i<day.childNodes.length;i++){
			if($def(day.childNodes[i].getEvent)){
				day.insertBefore(txt, day.childNodes[i]);
				break;
			}else if($def(day.childNodes[i].getTask)){
				if(day.childNodes[i].getTask().getSubject().toLowerCase() > txt_title){
					day.insertBefore(txt, day.childNodes[i]);
					break;
				}
			}
		}
		if(i == day.childNodes.length){
			day.appendChild(txt);
		}
	};
    this.appendTaskDOM = day.appendTaskDOM;

    day.removeTaskDOM = function(txt){
       day.removeChild(txt);
    }
    this.removeTaskDOM = day.removeTaskDOM;

    day.getTasks = function(){
        var cell = day;
        var tasks = new Array();
        for(var i=1; i < cell.childNodes.length; i++){
			if($def(cell.childNodes[i].getTask)){
				tasks.push(cell.childNodes[i].getTask());
			}
		}
		return tasks;
    }

	this.getDOM = function(){
		return day;
	}
}


jive.gui.MonthDayGroupedCell = function(control, aurora_gui, month_view, dtemp){

    var that = this;

    var jiveprojecttooltip;

	var tlang = control.getLanguageManager().getActiveLanguage();
	var day = document.createElement('DIV');

	var str = dtemp.getDate();
	if(dtemp.getDate() == 1){
		str = tlang.shortMonth(dtemp.getMonth()) + " " + str;
	}


    var tcache = new jive.ext.y.HashTable();
    var cpcache = new jive.ext.y.HashTable();

    var number = document.createElement('SPAN');
	number.getDate = function(dt){ return function(){ return dt;}; }(dtemp);
	number.setAttribute("class","month_day_cell_number");
	number.className = "month_day_cell_number";

	var add_link = document.createElement('SPAN');
	add_link.id = "add_link";
	add_link.setAttribute("class","month_day_cell_number_link");
	add_link.className = "month_day_cell_number_link";
	add_link.appendChild(document.createTextNode("[ + ]"));

	jive.ext.x.xDisplayNone(add_link);
	number.appendChild(add_link);
	number.appendChild(document.createTextNode(str));


    day.appendChild(number);




    var cp_count = 0;
    var cp_count_div = document.createElement('DIV');
	var cp_divicon = document.createElement('span');
	cp_count_div.appendChild(cp_divicon);
	cp_divicon.className = "jive-icon-med jive-icon-checkpoint";
	cp_divicon.setAttribute("class","jive-icon-med jive-icon-checkpoint");
    cp_count_div.href = "javascript:;"
    cp_count_div.className = "jive-cal-checkpoint jiveTT-hover-tasks";
    cp_count_div.setAttribute("class","jive-cal-checkpoint jiveTT-hover-tasks");
    day.appendChild(cp_count_div);
    jive.ext.x.xDisplayNone(cp_count_div);

    var task_count = 0;
    var task_count_div = document.createElement('DIV');
	var task_divicon = document.createElement('span');
	task_count_div.appendChild(task_divicon);
	task_divicon.className = "jive-icon-med jive-icon-checkpoint";
	task_divicon.setAttribute("class","jive-icon-med jive-icon-task");
    task_count_div.href = "javascript:;"
    task_count_div.className = "jiveTT-hover-tasks";
    task_count_div.setAttribute("class","jiveTT-hover-tasks");
    day.appendChild(task_count_div);
    jive.ext.x.xDisplayNone(task_count_div);


    jive.ext.x.xAddEventListener(task_count_div, "mouseover", function(dtemp){
        return function(e){
//            jiveprojecttooltip.getTasksTooltip(dtemp.getTime());

            var dom = jiveprojecttooltip.getDOM();
            while(dom.childNodes.length > 0) dom.removeChild(dom.childNodes[0]);

            var title = document.createElement("strong");
            var s = (task_count_div != 1) ? "s" : "";
            var dh = new jive.model.DateHelper(control);
            title.appendChild(document.createTextNode(dh.formatToMedDate(that.getDate()) + ", " + that.getDate().getFullYear() + " - " + task_count + " task" + s));
            var ul = document.createElement('UL');

            var tasks = that.getTasks();
            for(var i=0;i<tasks.length;i++){
                var ttask = tasks[i];
                var li = document.createElement('LI');
                var avatar = document.createElement('A');
                avatar.className = "jiveTT-hover-user jive-username-link";
                avatar.href = ttask.getAssignedTo().getURL();
                var img = document.createElement('IMG');
                img.className = "jive-avatar";
                img.setAttribute("class","jive-avatar");
                img.src = CS_BASE_URL + "/people/" + ttask.getAssignedTo().getUsername() + "/avatar/22.png";
                avatar.appendChild(img);
                var span = document.createElement('SPAN');
                var name = document.createElement('A');
                name.href = ttask.getAssignedTo().getURL();
                name.className = "jiveTT-hover-user jive-username-link";
                name.setAttribute("class","jiveTT-hover-user jive-username-link");
                name.appendChild(document.createTextNode(ttask.getAssignedTo().getFullName()));

                jive.ext.x.xAddEventListener(name, "mouseover", function(id){
                    return function(){
                        quickuserprofile.getUserProfileTooltip(id);
                    }
                }(ttask.getAssignedTo().getID()));
                jive.ext.x.xAddEventListener(name, "mouseout", function(){
                        quickuserprofile.cancelTooltip();
                });

                var task = document.createElement('A');
                task.href = ttask.getURL();
                task.appendChild(document.createTextNode(ttask.getSubject()));
                span.appendChild(task);
                span.appendChild(document.createTextNode(" : "));
                span.appendChild(name);
                li.appendChild(avatar);
                li.appendChild(span);
                ul.appendChild(li);
            }


            dom.appendChild(title);
            dom.appendChild(ul);
        }
    }(dtemp));

    jive.ext.x.xAddEventListener(cp_count_div, "mouseover", function(dtemp){
        return function(e){
//            jiveprojecttooltip.getTasksTooltip(dtemp.getTime());

            var dom = jiveprojecttooltip.getDOM();
            while(dom.childNodes.length > 0) dom.removeChild(dom.childNodes[0]);

            var title = document.createElement("strong");
            var s = (cp_count != 1) ? "s" : "";
            var dh = new jive.model.DateHelper(control);
            title.appendChild(document.createTextNode(dh.formatToMedDate(that.getDate()) + ", " + that.getDate().getFullYear() + " - " + cp_count + " checkpoint" + s));
            var ul = document.createElement('UL');
            var cps = that.getCheckPoints();
            for(var i=0;i<cps.length;i++){
                var cp = cps[i];
                var li = document.createElement('LI');
                var span = document.createElement('SPAN');
                span.id = "jive-note-checkpoint-body";
                var name = document.createElement('STRONG');
                var nameicon = document.createElement('span');
                name.insert(nameicon);
                nameicon.className = "jive-icon-med jive-icon-checkpoint";
                nameicon.setAttribute("class","jive-icon-med jive-icon-checkpoint");
                name.appendChild(document.createTextNode(cp.getName()));
                span.appendChild(name);
                if(cp.getProject().isEditable()){
                    var p = document.createElement('P');

                    var edit = document.createElement('A');
                    edit.href = CS_BASE_URL + "/edit-checkpoint!input.jspa?project=" + cp.getProject().getID() + "&checkPointID=" + cp.getID();
                    edit.appendChild(document.createTextNode("Edit"));
                    var del = document.createElement('A');
                    del.href = CS_BASE_URL + "/delete-checkpoint!input.jspa?project=" + cp.getProject().getID() + "&checkPointID=" + cp.getID();
                    del.appendChild(document.createTextNode("Delete"));
                    p.appendChild(edit);
                    p.appendChild(del);
                    span.appendChild(p);
                }
                li.appendChild(span);
                ul.appendChild(li);
            }


            dom.appendChild(title);
            dom.appendChild(ul);
        }
    }(dtemp));

    day.mouseover = function(){
		if(typeof(jive) != "undefined"){
			try{
				if(!day.overed){
					day.outColor = day.style.backgroundColor;
				}
				if(!control.isReadOnly() && month_view.getItemVisibility() && month_view.hasAddView()){
					jive.ext.x.xDisplayBlock(add_link);
				}
				day.style.backgroundColor = day.overColor;
				day.overed = true;
			}catch(e){ }
		}
	};
	day.mouseout = function(){
		if(typeof(jive) != "undefined"){
			try{
				jive.ext.x.xDisplayNone(add_link);
				day.style.backgroundColor = day.outColor;
				day.overed = false;
			}catch(e){ }
		}
	};
	day.updateText = function(){
		var tlang = control.getLanguageManager().getActiveLanguage();
		var str = dtemp.getDate();
		if(dtemp.getDate() == 1){
			var add_link = number.childNodes[0];
			str = tlang.shortMonth(dtemp.getMonth()) + " " + str;
			while(number.childNodes.length > 0) number.removeChild(number.childNodes[0]);
			number.appendChild(add_link);
			number.appendChild(document.createTextNode(str));
		}
	};
	day.getDate = function(d){ return function(){ return d; } }(dtemp);
	day.dropPoint = function(tevent, dt){

		if(dt != null){
			var d = new Date();
			d.setTime(day.getDate().getTime());
			d.setHours(dt.getHours());
			d.setMinutes(dt.getMinutes());
			d.setSeconds(dt.getSeconds());
			d.setMilliseconds(dt.getMilliseconds());
			var distance = d.getTime() - dt.getTime();
		}

		if(dt != null && distance != 0 && jive.model.isEvent(tevent)){
			var s = new Date();
			s.setTime(tevent.getStart().getTime());
			var e = new Date();
			e.setTime(tevent.getEnd().getTime());

			var durr = e.getTime() - s.getTime();
			s.setTime(s.getTime() + distance);
			e.setTime(s.getTime() + durr);
			tevent.setStart(s);
			tevent.setEnd(e);

			// when we edit an event, we need to
			// 'confirm' the changes.
			// this fires the eventChanged notice
			// so that the listeners can actually
			// save the change to the DB
			tevent.confirm();
		}else if((dt == null || distance != 0) && jive.model.isTask(tevent)){
			// now change it's time
			if(dt != null){
				var d = new Date();
				d.setTime(tevent.getDueDate().getTime() + distance);
				tevent.setDueDate(d);
			}else{
				var d = new Date();
				d.setTime(day.getDate().getTime());
				tevent.setDueDate(d);
			}

			// when we edit an task, we need to
			// 'confirm' the changes.
			// this fires the eventChanged notice
			// so that the listeners can actually
			// save the change to the DB
			tevent.confirm();
		}
	};

	/**
	 * add a function to the day cell
	 * that lets it add events
	 *
	 * this will add the event into sorted position
	 * into this day cell
	 */
	day.appendTaskDOM = function(txt){
        // cache it.
        tcache.put(txt.getTask().getID(), txt.getTask())

        jiveprojecttooltip = new JiveProjectTooltip(txt.getTask().getProjectID(), "jive-note-checkpoint-body", "jive-note-tasks-body", "", "", "", "", "");
        jive.ext.x.xAddEventListener(task_count_div, "mouseout", jiveprojecttooltip.cancelTooltip);

        task_count++;
        jive.ext.x.xDisplayNone(txt);
        while(task_count_div.childNodes.length > 1) task_count_div.removeChild(task_count_div.childNodes[0]);
        task_count_div.appendChild(document.createTextNode(task_count + " tasks"));
        jive.ext.x.xDisplayBlock(task_count_div);
    };
    this.appendTaskDOM = day.appendTaskDOM;

    day.removeTaskDOM = function(txt){
        tcache.clear(txt.getTask().getID());

        task_count--;
        if(task_count == 0){
            jive.ext.x.xDisplayNone(task_count_div);
        }
    }
    this.removeTaskDOM = day.removeTaskDOM;

    day.getTasks = function(){
        return tcache.toArray(jive.model.isTask);
    }
    this.getTasks = day.getTasks;
    task_count_div.getTasks = that.getTasks;

    day.getCheckPoints = function(){
        return cpcache.toArray(jive.model.isCheckPoint);
    }
    this.getCheckPoints = day.getCheckPoints;
    cp_count_div.getCheckPoints = day.getCheckPoints;

    /**
	 * add a function to the day cell
	 * that lets it add events
	 *
	 * this will add the event into sorted position
	 * into this day cell
	 */
	day.appendEventDOM = function(txt){
		var tevent = txt.getEvent();
		for(var i=0;i<day.childNodes.length;i++){
			if($def(day.childNodes[i].getEvent)){
				if(day.childNodes[i].getEvent().getStart() > tevent.getStart()){
					day.insertBefore(txt, day.childNodes[i]);
					break;
				}
			}
		}
		if(i == day.childNodes.length){
			day.appendChild(txt);
		}
	};


    /**
	 * add a function to the day cell
	 * that lets it add events
	 *
	 * this will add the event into sorted position
	 * into this day cell
	 */
	day.appendCheckPointDOM = function(txt){
        // cache it.
        cpcache.put(txt.getCheckPoint().getID(), txt.getCheckPoint())

        jiveprojecttooltip = new JiveProjectTooltip(txt.getCheckPoint().getProject().getID(), "jive-note-checkpoint-body", "jive-note-tasks-body", "", "", "", "", "");
        jive.ext.x.xAddEventListener(cp_count_div, "mouseout", jiveprojecttooltip.cancelTooltip);

        cp_count++;
        jive.ext.x.xDisplayNone(txt);
        while(cp_count_div.childNodes.length > 1) cp_count_div.removeChild(cp_count_div.childNodes[0]);
        var s = (cp_count != 1) ? "s" : "";
        cp_count_div.appendChild(document.createTextNode(cp_count + " checkpoint" + s));
        jive.ext.x.xDisplayBlock(cp_count_div);
	};
    

    day.countVisibleItems = function(){
        return that.getTasks().length + that.getCheckPoints().length;
    }
    this.countVisibleItems = day.countVisibleItems;

    // listeners

	jive.ext.x.xAddEventListener(add_link, "click", function(aurora_gui, numDOM){ return function(evt){
		aurora_gui.notifyAddEventClicked(numDOM.getDate());
		jive.ext.x.xStopPropagation(evt);
	} }(aurora_gui, number), true);
	jive.ext.x.xAddEventListener(day, "click", function(aurora_gui, numDOM){ return function(){
		aurora_gui.notifyDayClicked(numDOM.getDate());
	} }(aurora_gui, number), false);

	jive.ext.x.xAddEventListener(day, "mouseover", day.mouseover);
	jive.ext.x.xAddEventListener(day, "mouseout", day.mouseout);

	// functions

	this.getDate = day.getDate;

	this.getDOM = function(){
		return day;
	}

}


/**
 * a month panel is composed of day cell holders, which in turn hold
 * the actual day cells. each holder holds 4 weeks of day cells. these
 * holders can be added to the beginning/end of the month view.
 *
 * to add the month panel to the UI, call it's getDOM() method
 * which returns a dom object
 */
jive.gui.BasicGui = function(control){

	/**
	 * selected item (event or task), or false if none is selected
	 */
	var selected_item = null;

	/**
	 * the list of listeners listening to this month view
	 */
	var listeners = new Array();

	/**
	 * the list of listeners listening to this month view's events
	 * they will recieve info about which events have been clicked etc.
	 */
	var event_listeners = new Array();

	this.addPriorityEventListener = function(list){
		event_listeners.splice(0,0,list);
	}
	this.addEventListener = function(list){
		event_listeners.push(list);
	}

	/**
	 * the current date of this month
	 */
	var currDate = new Date();
	currDate.setHours(17);

	/**
	 * the minimum date currently displayed on month view
	 */
	var currMin = new Date();
	currMin.setHours(17);
	/**
	 * the minimum date currently displayed on month view
	 */
	var currMax = new Date();
	currMax.setHours(17);

	/**
	 * this lets us reference this object from within anonymous objects
	 * inside this class
	 */
	var that = this;


    var has_init = false;
	this.hasInitHuh = function(){
		return has_init;
	}

	var veryinner = document.createElement('DIV');
	veryinner.className = "month_view_very_inner";

	var panel = document.createElement('DIV');
	panel.setAttribute("class", "month_view_main");
	panel.className = "month_view_main";

	var header = new jive.gui.SimpleHeader(control);
	
	var inner = document.createElement('DIV');
	inner.setAttribute("class", "month_view_inner");
	inner.className = "month_view_inner";



	var show_print = true;
	this.showPrintHuh = function(b){
		show_print = b;
		if(b && that.getActiveView().hasPrintView()){
			that.getHeader().showPrintHuh(true);
		}else{
			that.getHeader().showPrintHuh(false);
		}
	}
	this.shouldShowPrintHuh = function(){
		return show_print;
	}

	this.setNavFilter = function(foo){
		that.getHeader().setNavFilter(foo);
	}

	this.showFilterHuh = function(b){
		that.getHeader().showFilterHuh(b);
	}

    this.getFilterText = function(){
		return that.getHeader().getFilterText();
	}

	this.getHeaderFooterHeight = function(){
		return that.getHeader().getHeight();
	}


	/************************************************************************
	**
	** FUNCTIONS TO MANAGE MULTIPLE VIEWS
	**
	*************************************************************************/
	var LEFT_ACTION = function(){ };

	var RIGHT_ACTION = function(){ };

	function getLeftAction(){
		return LEFT_ACTION;
	}
	function getRightAction(){
		return RIGHT_ACTION;
	}

	var views = new jive.ext.y.HashTable();
	// returns true if an object is a view
	this.isViewHuh = function(v){
		return v != null && $obj(v) && $def(v.isExpandedHuh);
	}
	this.addView = function(view){
		var v = that.getView(view.getName());
		if(!$obj(v) || v == null){
			views.put(view.getHash(), view);
			//
			// if month view has already been init'd,
			// then we need to init the view right now.
			//
			// otherwise, the view will be init'd after
			// monthview's init'd
			//
			if(that.hasInitHuh()){
				view.init(veryinner);
				view.collapse();
			}
		}
	}
	this.removeView = function(name){
		views.clear(name);
	}
	this.getAllViews = function(){
		return views.toArray(that.isViewHuh);
	}
	this.getView = function(key){
		return views.get(key);
	}
	/**
	 * show the week view for this date
	 * d: the input to initialize teh view
	 * (it's a date for day/week/etc, adapter for list view, event for event view, etc)
	 * name: the name of the view to show
	 */
	this.showView = function(d, hash){
		var v = that.getAllViews();
		var has_view = false;
		for(var i=0;i<v.length;i++){
			if(v[i].getHash() == hash){
				has_view = true;
			}
		}
		if(!has_view) return that.showView(d, "month");

		for(var i=0;i<v.length;i++){
			if(v[i].getHash() == hash){
				if(!v[i].isExpandedHuh()){
					v[i].expand();
				}
				if(v[i].hasPrintView() && show_print){
					that.getHeader().showPrintHuh(true);
				}else{
					that.getHeader().showPrintHuh(false);
				}
				// show the view
				v[i].go(d);
				// update the header
				that.getHeader().setTitleText(v[i].getHeaderText(d).escapeHTML());
				// now listen for cliks to the next/prev buttons
				LEFT_ACTION = v[i].getPrevViewFunc();
				RIGHT_ACTION = v[i].getNextViewFunc();
				// reset the min/max times that are being shown
				currMin.setTime(v[i].getMinDate());
				currMax.setTime(v[i].getMaxDate());
				that.notifyTimesChanged(v[i].getMinDate(), v[i].getMaxDate());
			}else if(v[i].isExpandedHuh()){
				v[i].collapse();
			}
		}
		that.fixHeight();
	}

	/************************************************************************
	**
	** END FUNCTIONS TO MANAGE MULTIPLE VIEWS
	**
	*************************************************************************/

	/**
	 * return the current date of month view
	 */
	this.getCurrentDate = function(){
		var ret = new Date();
		ret.setTime(currDate.getTime());
		return ret;
	}
	/**
	 * sets the current date of month view
	 */
	this.setCurrentDate = function(d){
		currDate.setTime(d.getTime());
	}

	/**
	 * notify listeners that we're zooming to day view
	 */
	this.notifyPrintClicked = function(d){
		var foo = new Date();
		foo.setTime(d.getTime());
		for(var i=0;i<listeners.length;i++){
			listeners[i].printClicked(foo);
		}
	}


	/**
	 * hide the navigation arrows in the header
	 */
	this.hideArrows = function(){
		that.getHeader().showArrowsHuh(false);
	}

	/**
	 * show the navigation arrows in the header
	 */
	this.showArrows = function(){
		that.getHeader().showArrowsHuh(true);
	}

	/**
	 * make sure that our min date is set to
	 * at least dt
	 */
	this.ensureMinDate = function(dt){
		if(jive.model.dateLT(dt, currMin)){
			currMin.setTime(dt.getTime());
		}
	}
	/**
	 * make sure that our max date is set to
	 * at least dt
	 */
	this.ensureMaxDate = function(dt){
		if(jive.model.dateGT(dt, currMax)){
			currMax.setTime(dt.getTime());
		}
	}

	/**
	 * return the min date of month view
	 */
	this.getMinDate = function(){
		return currMin;
	}
	/**
	 * set the min date
	 */
	this.setMinDate = function(d){
		currMin.setTime(d.getTime());
	}
	/**
	 * return the min date of month view
	 */
	this.getMaxDate = function(){
		return currMax;
	}
	/**
	 * set the max date of month view
	 */
	this.setMaxDate = function(d){
		currMax.setTime(d.getTime());
	}

	/**
	 * the cell that will expand into month view
	 */
	var month_view = new jive.gui.MiniMonthView(control, that);


	this.updateText = function(){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].updateText();
		}

		var tlang = control.getLanguageManager().getActiveLanguage();
		that.getHeader().updateText()
		that.getHeader().setTitleText(that.getActiveView().getHeaderText(that.getCurrentDate()).escapeHTML());
	}

	/**
	 * now tie all the dom objects together
	 */
	panel.appendChild(header.getDOM());
	panel.appendChild(inner);
	inner.appendChild(veryinner);



	/**
	 * return the DOM object that represents this month view
	 */
	this.getDOM = function(){
		return panel;
	}


	/**
	 * gets the events for a specific day
	 * @param dt a date object
	 */
	this.getEventsOn = function(dt){
		return that.getView("month").getEventsOn(dt);
	}

	/**
	 * gets the tasks for a specific day
	 * @param dt a Date object
	 */
	this.getTasksOn = function(dt){
		return that.getView("month").getTasksOn(dt);
	}


	/**
	 * add (or refresh) an event to display on this month view
	 * i need to update this function. its not handling teh drag listeners
	 * correctly. i need to remove old listeners before i add new ones...
	 */
	this.addEvent = function(tevent){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].addEvent(tevent);
		}
	}


	/**
	 * add a task to display on this month view
	 */
	this.addTask = function(ttask){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].addTask(ttask);
		}
	}

	/**
	 * removes an event from the month view
	 *
	 * this removes any drag listeners from span elements
	 * for that event, and disables the drag
	 */
	this.removeEvent = function(tevent){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].removeEvent(tevent);
		}
	}

	/**
	 * removes an event from the month view
	 *
	 * this removes any drag listeners from span elements
	 * for that event, and disables the drag
	 */
	this.removeTask = function(ttask){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].removeTask(ttask);
		}
	}

	/**
	 * this flushes a calendar entirely. it removes it from teh views,
	 * and also removes any DOM's we have cached for this calendar's
	 * events
	 */
	this.flushCalendar = function(cal){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].flushCalendar(cal);
		}
	}

	/**
	 * this flushes an event entirely. it removes it from the views,
	 * and also removes any DOM's we have cached for that event
	 */
	this.flushEvent = function(tevent){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].flushEvent(tevent);
		}

		if(jotlet.model.isEvent(selected_item) && selected_item.getId() == tevent.getId()){
			selected_item = null;
		}
	}

	/**
	 * this flushes a task entirely. it removes it from the views,
	 * and also removes any DOM's we have cached for that task
	 */
	this.flushTask = function(ttask){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].flushTask(ttask);
		}

		if(jotlet.model.isTask(selected_item) && selected_item.getId() == ttask.getId()){
			selected_item = null;
		}
	}


	this.refreshWeather = function(){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if($def(v[i].refreshWeather)){
				v[i].refreshWeather();
			}
		}
	}

	/**
	 * shows the month view for
	 * Date d
	 * @param d the date of the month to show
	 */
	this.refreshShading = function(){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if($def(v[i].refreshShading)){
				v[i].refreshShading();
			}
		}
	}

	this.getActiveView = function(){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if(v[i].isExpandedHuh()){
				return v[i];
			}
		}
		return that.getView("month");
	}


	/**
	 * show the month view for this date
	 */
	this.showMonth = function(dtemp){
		that.showView(dtemp, "month");
	}

	/**
	 * show the week view for this date
	 */
	this.showWeek = function(dtemp){
		that.showView(dtemp, "week");
	}

	/**
	 * show the day view for this date
	 */
	this.showDay = function(dtemp){
		that.showView(dtemp, "day");
	}

	/**
	 * show the list view for this date
	 */
	this.showList = function(dtemp){
		that.showView(dtemp, "list");
	}

	/**
	 * refresh the text on the bar, possibly because
	 * of a settings change, etc
	 */
	this.refresh = function(){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if(v[i].isExpandedHuh()){
				v[i].refresh();
				that.refreshShading();
				return;
			}
		}
		that.showMonth(that.getCurrentDate());
		that.refreshShading();
	}


	this.fixHeight = function(){
		try{
			if(jive.ext.x.xParent(panel) != null){
				var inner_height = jive.ext.x.xHeight(jive.ext.x.xParent(panel)) - that.getHeaderFooterHeight();
				jive.ext.x.xHeight(inner, inner_height);
				that.getView("month").fixHeight(inner_height);
			}
		}catch(e){
			alert(e);
		}
	}

	/**
	 * we've just now been placed in our parent div, so
	 * adjust height of our holders etc to fit
	 */
	this.init = function(){
		has_init = true;
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			v[i].init(veryinner);
		}
	}

	this.isShowingDay = function(){
		return that.getView("day").isExpandedHuh();
	}

	this.isShowingWeek = function(){
		return that.getView("week").isExpandedHuh();
	}


	this.killYourself = function(){
		control = null;
		for(var i=0;i<views.length;i++){
			views[i].killYourself();
		}
	}


	/******************************************
	 * add default views
	 ******************************************/
	that.addView(month_view);


	/******************************************
	 * listener functions
	 ******************************************/
	this.addListener = function(list){
		listeners.push(list);
	}


	/**
	 * notify everybody else that a drag/drop happened
	 */
	this.notifyStopDrag = function(tevent, dt, left, tp){
		var doneHuh = false;
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if(v[i].isExpandedHuh() && $def(v[i].stopDrag)){
				doneHuh = v[i].stopDrag(tevent, dt, left, tp);
			}
		}
		if(!doneHuh){
			for(var i=0;i<listeners.length && !doneHuh;i++){
				doneHuh = listeners[i].stopDrag(tevent, dt, left, tp);
			}
		}
		return doneHuh;
	}

	// this is the last day cell that
	// we've hovered over when dragging
	// an event or task
	var hovered_day_cell = null;
	var threadNum = 0;
	this.notifyDragging = function(tevent, dt, left, tp){
		// this function will be called as they drag around an event
		// which means it'll be called probably as its running
		//
		// the threadNum and myThread variables will make sure that
		// I drop out of execution asap as a new thread starts.
		threadNum++;
		var myThread = threadNum;

		// track if we show a drop zone or not
		var zonedHuh = false;

		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if(v[i].isExpandedHuh()){
				if($def(v[i].dragging)){
					zonedHuh = v[i].dragging(tevent, dt, left, tp);
				}
			}
		}
		if(!zonedHuh){
			for(var i=0;i<listeners.length && !zonedHuh;i++){
				zonedHuh = zonedHuh || listeners[i].dragging(tevent, dt, left, tp);
			}
			if(!zonedHuh){
				control.hideHover();
			}
		}
		return zonedHuh;
	}

	/**
	 * notify listeners that we're zooming to day view
	 */
	this.notifyDayClicked = function(d){
		var foo = new Date();
		foo.setTime(d.getTime());
		for(var i=0;i<listeners.length;i++){
			listeners[i].dayClicked(foo);
		}
	}

	/**
	 * notify listeners that the min/max date for month view
	 * has changed
	 */
	this.notifyTimesChanged = function(mind, maxd){
		for(var i=0;i<listeners.length;i++){
			listeners[i].timesChanged(mind, maxd);
		}
	}


	/**
	 * the user has quick added a task to the calendar/date
	 */
	this.notifyQuickAddTask = function(title, calendar, date){
		for(var i=0;i<listeners.length;i++){
			listeners[i].quickAddTask(title, calendar, date);
		}
	}


	this.filter = function(str){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if(v[i].isExpandedHuh()){
				if($def(v[i].filter)){
					v[i].filter(str);
				}
			}
		}
	}

	/**
	 * toggle event visibility if
	 * the calendar visibility is switched (ie, in the sidebar)
	 */
	this.calendarVisible = function(calendar, visibleHuh){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if($def(v[i].calendarVisible)){
				v[i].calendarVisible(calendar, visibleHuh);
			}
		}
		that.refreshShading();
	}

	/******************************************
	 * end listener functions
	 * begin event listener functions
	 ******************************************/


	/**
	 * notify listeners that the user clicked an event
	 */
	this.notifyEventClicked = function(tevent){
		for(var i=0;i<event_listeners.length;i++){
			event_listeners[i].eventClicked(tevent);
		}
	}
    /**
     * notify listeners that the user clicked a task
     */
    this.notifyTaskClicked = function(ttask){
        try{
            for(var i=0;i<event_listeners.length;i++){
                event_listeners[i].taskClicked(ttask);
            }
        }catch(e){
            alert(e);
        }
    }
    /**
     * notify listeners that the user clicked a checkpoint
     */
    this.notifyCheckPointClicked = function(cp){
        try{
            for(var i=0;i<event_listeners.length;i++){
                event_listeners[i].checkPointClicked(cp);
            }
        }catch(e){
            alert(e);
        }
    }

	/**
	 * notify listeners that the user double clicked an event
	 */
	this.notifyEventDblClicked = function(tevent){
		for(var i=0;i<event_listeners.length;i++){
			event_listeners[i].eventDblClicked(tevent);
		}
	}

    /**
     * notify listeners that the user double clicked a task
     */
    this.notifyTaskDblClicked = function(ttask){
        for(var i=0;i<event_listeners.length;i++){
            event_listeners[i].taskDblClicked(ttask);
        }
    }

    /**
     * notify listeners that the user double clicked a task
     */
    this.notifyCheckPointDblClicked = function(cp){
        for(var i=0;i<event_listeners.length;i++){
            event_listeners[i].checkPointDblClicked(cp);
        }
    }

	/**
	 * notify listeners that we want to unselect all the events
	 */
	this.notifyUnselectAll = function(){
		for(var i=0;i<event_listeners.length;i++){
			event_listeners[i].unselectAll();
		}
	}
	/******************************************
	 * end event listener functions
	 ******************************************/

	/******************************************
	 * navlistener functions
	 ******************************************/
	var nav_listeners = new Array();
	this.addNavListener = function(list){
		nav_listeners.push(list);
	}

	this.removeNavListener = function(list){
		for(var i=0;i<nav_listeners.length;i++){
			if(nav_listeners[i] == list){
				nav_listeners.splice(i, 1);
			}
		}
	}

	this.notifyMonthClicked = function(d){
		for(var i=0;i<nav_listeners.length;i++){
			nav_listeners[i].monthClicked(d);
		}
	}

	this.notifyWeekClicked = function(d){
		for(var i=0;i<nav_listeners.length;i++){
			nav_listeners[i].weekClicked(d);
		}
	}

	this.notifyDayClicked = function(d){
		for(var i=0;i<nav_listeners.length;i++){
			nav_listeners[i].dayClicked(d);
		}
	}

	this.notifyListClicked = function(){
		for(var i=0;i<nav_listeners.length;i++){
			nav_listeners[i].listClicked();
		}
	}

	this.notifyAddEventClicked = function(d){
		for(var i=0;i<nav_listeners.length;i++){
			nav_listeners[i].addEventClicked(d);
		}
	}

	this.notifyBackClicked = function(){
		for(var i=0;i<nav_listeners.length;i++){
			nav_listeners[i].backClicked();
		}
	}
	/******************************************
	 * end nav listener functions
	 ******************************************/

	/**
	 * add a listener to listen for event clicks
	 * when an event is clicked, highlight it in
	 * the main view, and unhighlight (if needed)
	 * the previously highlighted event
	 */
	var list = new Object();
	list.eventClicked = function(tevent){
		selected_item = tevent;
	}
	list.eventDblClicked = function(tevent){ }
    list.taskClicked = function(ttask){
        selected_item = ttask;
    }
    list.taskDblClicked = function(ttask){ }
    list.checkPointClicked = function(cp){
        selected_item = cp;
    }
    list.checkPointDblClicked = function(cp){ }
	list.unselectAll = function(){
		selected_item = null;
	}
	this.addEventListener(list);

	/**
	 * unselects teh current event in the view
	 */
	this.unselectAll = function(){
		that.notifyUnselectAll();
	}


	/**
	 * returns teh currently selected event,
	 * or null if none is selected
	 */
	this.getSelectedItem = function(){
		return selected_item;
	}



	//
	// listen to the header
	//
	var head_list = new Object();
	head_list.printClicked = function(thunk, date_thunk){
		return function(){
			thunk(date_thunk());
		}
	}(that.notifyPrintClicked, that.getCurrentDate);
	head_list.leftClicked = function(foo){
		return function(){
			var func = foo();
			func();
		}
	}(getLeftAction);
	head_list.rightClicked = function(foo){ return function(){ var func = foo(); func(); }}(getRightAction);
	head_list.searchByText = function(str){
		var v = that.getAllViews();
		for(var i=0;i<v.length;i++){
			if($def(v[i].filter)){
				v[i].filter(str);
			}
		}
	}
	header.addListener(head_list);




	this.setHeader = function(h){
		panel.removeChild(header.getDOM());
		header.killYourself();
		header = h;
		header.addListener(head_list);
		panel.insertBefore(header.getDOM(), inner);
	}

	this.getHeader = function(){
		return header;
	}

}



/**
 * the header for jotlet
 */
jive.gui.SimpleHeader = function(control, par){

	var that;
	if($obj(par)){
		that = par;
	}else{
		that = this;
	}

	var button_wrap = document.createElement('DIV');
	button_wrap.setAttribute("class", "month_view_header_button_wrap");
	button_wrap.className = "month_view_header_button_wrap";

	var header = document.createElement('DIV');
	header.setAttribute("class", "month_view_header color_header");
	header.className = "month_view_header color_header";
	header.appendChild(document.createElement('DIV'));
	var l_arrow = document.createElement("span");
	l_arrow.setAttribute("class", "month_view_header_link");
	l_arrow.className = "month_view_header_link";
	button_wrap.appendChild(l_arrow);
	l_arrow.appendChild(document.createTextNode("<"));
	var r_arrow = document.createElement("span");
	r_arrow.setAttribute("class", "month_view_float_r month_view_header_link");
	r_arrow.className = "month_view_float_r month_view_header_link";
	r_arrow.appendChild(document.createTextNode(">"));
	button_wrap.appendChild(r_arrow);
	var monthName = document.createElement('SPAN');
	monthName.setAttribute("class", "month_view_headerc");
	monthName.className = "month_view_headerc";
	button_wrap.appendChild(monthName);
	header.appendChild(button_wrap);

	jive.ext.x.xDisplayBlock(header);

	this.getDOM = function(){
		return header;
	}

	this.showArrowsHuh = function(b){
		if(b){
			jive.ext.x.xDisplayBlock(l_arrow);
			jive.ext.x.xShow(r_arrow);
		}else{
			jive.ext.x.xDisplayNone(l_arrow);
			jive.ext.x.xHide(r_arrow);
		}
	}

	this.showPrintHuh = function(b){ }

	this.showFilterHuh = function(b){ }

	this.getHeight = function(){
		return (jive.ext.x.xDisplay(header) == "block") ? jive.ext.x.xHeight(header) : 0;
	}

	this.setTitleText = function(str){
		while(monthName.childNodes.length > 0) monthName.removeChild(monthName.childNodes[0]);
		monthName.appendChild(document.createTextNode(str));
	}


	this.setNavFilter = function(foo){ }

	this.updateText = function(){ }

	this.getFilterText = function(){
		return "";
	}



	/****************************************
	 **
	 ** listeners
	 **
	 ****************************************/

	var listeners = new Array();

	this.addListener = function(list){
		listeners.push(list);
	}

	/**
	 * notify the listeners that the print button
	 * was clicked
	 */
	this.notifyPrintClicked = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].printClicked();
		}
	}

	/**
	 * the left arrow was clicked
	 */
	this.notifyLeftClicked = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].leftClicked();
		}
	}

	/**
	 * the right arrow was clicked
	 */
	this.notifyRightClicked = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].rightClicked();
		}
	}

	/**
	 * the ui should filter by text
	 */
	this.notifySearchByText = function(str){
		for(var i=0;i<listeners.length;i++){
			listeners[i].searchByText(str);
		}
	}



	/****************************************
	 **
	 ** events
	 **
	 ****************************************/
	jive.ext.x.xAddEventListener(l_arrow, "click", function(that){ return function(){ that.notifyLeftClicked(); } }(that));
	jive.ext.x.xAddEventListener(r_arrow, "click", function(that){ return function(){ that.notifyRightClicked(); } }(that));


	/****************************************
	 **
	 ** destructor
	 **
	 ****************************************/

	this.killYourself = function(){
	}
}




/**
 * the header for jotlet
 */
jive.gui.NullHeader = function(control){

    var that = this;


	var header = document.createElement('DIV');
	jive.ext.x.xDisplayNone(header);

	this.getDOM = function(){
		return header;
	}

	this.showArrowsHuh = function(b){ }

	this.showPrintHuh = function(b){ }

	this.showFilterHuh = function(b){ }

	this.getHeight = function(){
		return 0;
	}

	this.setTitleText = function(str){ }

	this.setNavFilter = function(foo){ }

	this.updateText = function(){ }

	this.getFilterText = function(){
		return "";
	}

	/****************************************
	 **
	 ** listeners
	 **
	 ****************************************/

	var listeners = new Array();

	this.addListener = function(list){
		listeners.push(list);
	}

	/**
	 * notify the listeners that the print button
	 * was clicked
	 */
	this.notifyPrintClicked = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].printClicked();
		}
	}

	/**
	 * the left arrow was clicked
	 */
	this.notifyLeftClicked = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].leftClicked();
		}
	}

	/**
	 * the right arrow was clicked
	 */
	this.notifyRightClicked = function(){
		for(var i=0;i<listeners.length;i++){
			listeners[i].rightClicked();
		}
	}

	/**
	 * the ui should filter by text
	 */
	this.notifySearchByText = function(str){
		for(var i=0;i<listeners.length;i++){
			listeners[i].searchByText(str);
		}
	}

	/****************************************
	 **
	 ** destructor
	 **
	 ****************************************/

	this.killYourself = function(){
	}
}




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");
            }
            else {
                thatTabElem = null;
            }
            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: 100, 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;
            }   
        });
    },

    getRemoteUserProfileTooltip: function(userInfo) {

        var displayAvatarSpan = "<span><img src='" + userInfo.avatarUrl + "' width='48' height='48' class='jive-avatar' /></span>";
        var displayNameSpan = "<h5>" + userInfo.userDisplayName + "</h5>";
        var statusSpan = "<span class='jive-note-user-status'>" + userInfo.userStatus + "</span>";
        var fieldList = "<ul class='jive-profile-tt-fields'>";
        var phoneNumber = "";
        if (userInfo.userPhone) {
            phoneNumber = "<li><strong>Phone Number:</strong> " + userInfo.userPhone + "</li>";
        }
        var email = "";
        if (userInfo.userEmail) {
            email = "<li><strong>Email:</strong> <a href='mailto:" + userInfo.userEmail + "'>" + userInfo.userEmail + "</a></li>";
        }
        var fieldListEnd = "</ul>";
        var profileLink = "<p><a href='" + userInfo.profileUrl + "'>View " + userInfo.userDisplayName + "'s profile</a></p>";        

        $(this.userTT).update("" + displayAvatarSpan + displayNameSpan + statusSpan + fieldList + phoneNumber + email + fieldListEnd + profileLink);
        $(this.userTT).show();
    },

    cancelTooltip: function() {
        if (this.timeoutExecutor) {
            this.timeoutExecutor.cancel();
        }
    }
}

var QuickContainerSummary = Class.create();
QuickContainerSummary.prototype = {

/*
* Initialize the QuickContainerSummary object.
*/
    initialize: function(containerTT, containerTTUrl, textTTLoading, textTTError)
    {
        this.loadingContent = '<strong class="jive-tooltip2-loading">' + textTTLoading + '</strong>';
        this.containerTT = containerTT;
        this.containerTTUrl = containerTTUrl.indexOf('?') < 0? containerTTUrl + '?tooltip=true' : containerTTUrl + '&tooltip=true';
        this.textErrorTT = textTTError;
        this.jiveUserTips = new SuperNote('jivecontainerTT', {showDelay: 700, hideDelay: 100, cssProp: 'visibility', cssVis: 'visible', cssHid: 'hidden'});
    },

    getContainerTooltip: function(containerID, containerType) {
        this.cancelTooltip();
        $(this.containerTT).innerHTML = this.loadingContent;
        this.timeoutExecutor = new TimeoutExecutor(this.getContainerInfo.bind(this, containerID, containerType), 700);
    },

    getContainerInfo: function(containerID, containerType) {
        var instance = this;
        new Ajax.Updater( this.containerTT, this.containerTTUrl, {
            method: 'get',
            parameters: {
                'container': containerID,
                'containerType' : containerType
            },
            onError: function() {
                $(this.containerTT).innerHTML = instance.textTTError;
            }
        });
    },

    cancelTooltip: function() {
        if (this.timeoutExecutor) {
            this.timeoutExecutor.cancel();
        }
    }
}

var QuickViewVideo = Class.create();
QuickViewVideo.prototype = {

/*
* Initialize the QuickUserProfile object.
*/
    initialize: function(videoTT, videoTTURL, textTTLoading, textTTError)
    {
        this.loadingContent = '<strong class="jive-tooltip2-loading">' + textTTLoading + '</strong>';
        this.videoTT = videoTT;
        this.videoTTURL = videoTTURL.indexOf('?') < 0? videoTTURL + '?tooltip=true' : videoTTURL + '&tooltip=true';
        this.textErrorTT = textTTError;
        this.jiveUserTips = new SuperNote('jiveTT', {showDelay: 700, hideDelay: 100, cssProp: 'visibility', cssVis: 'visible', cssHid: 'hidden'});
    },

    getVideoTooltip: function(videoID) {
        this.cancelTooltip();
        $(this.videoTT).innerHTML = this.loadingContent;
        this.timeoutExecutor = new TimeoutExecutor(this.getViewVideo.bind(this, videoID), 700);
    },

    getViewVideo: function(videoID) {
        var instance = this;
        new Ajax.Updater( this.videoTT, this.videoTTURL, {
            method: 'get',
            parameters: {
                'video': videoID
            },
            onError: function() {
                $(this.videoTT).innerHTML = instance.textTTError;
            }
        });
    },

    getRemoteVideoTooltip: function(videoName, url) {
        $(this.videoTT).update("<div class=<p>View <a href=" + url + ">" + videoName + "</a></p>");
        $(this.videoTT).show();
    },

    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(){};

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


var UserPicker = Class.create();
UserPicker.prototype = {

    initialize: function(fieldAutocomplete, fieldAutocompleteChoices, fieldValues, fieldContainer, fieldRowIDPrefix) {
        this.fieldAutocomplete = fieldAutocomplete ? fieldAutocomplete : 'autocomplete';
        this.fieldAutocompleteChoices = fieldAutocompleteChoices ? fieldAutocompleteChoices : 'user_choices';
        this.fieldValues = fieldValues ? fieldValues : 'username';
        this.fieldContainer = fieldContainer ? fieldContainer : 'blog-authors';
        this.fieldRowIDPrefix = fieldRowIDPrefix ? fieldRowIDPrefix : 'jive-user-';
        this.startAutoCompleter(this);
    },

    startAutoCompleter: function(instance) {
        var ac = new Ajax.Autocompleter(this.fieldAutocomplete, this.fieldAutocompleteChoices, "view-people-autocomplete.jspa?resultTypes=COMMUNITY&sort=relevance&start=0&view=search&usernameEnabled=true&nameEnabled=true&emailEnabled=true", {tokens: [',', ' '], updateElement: function(
                selected)
        {
            instance.addUser(Element.collectTextNodesIgnoreClass(selected, 'informal'), instance);
        }, paramName: "query", minChars: 1});
        ac.getToken = function() {
            var bounds = this.getTokenBounds();
            return this.element.value.substring(bounds[0], bounds[1]).strip().toLowerCase();    //compare usernames using lower case
        }
    },

    loadWin: function(elem, multiple) {
        this.win = window.open("user-picker!input.jspa?selectedUsers=" + encodeURIComponent(elem.value) + "&multiple="
                + multiple + "&element=" + elem.getAttributeNode('name').value + "&form="
                + elem.form.getAttributeNode('name').value, "", "menubar=yes,location=no,personalbar=no,scrollbars=yes,width=600,height=800,resizable");
    },

    removeUser: function(user) {
        if (typeof user != 'string') {
            user = Event.element(user).id.gsub('jive-remove-user-link-', '');
        }

        if (user && user.length > 0) {
            Element.remove(this.fieldRowIDPrefix + user);
            var vals = $(this.fieldValues).value.split(",");
            vals[vals.indexOf(user)] = null;    //remove user from array
            vals = vals.compact();  //get rid of any nulls
            $(this.fieldValues).value = vals.join(",");
        }
    },

    addUser: function(user, instance) {
        if (!instance) {
            instance = this;
        }

        if (!user) {
            user = $F(instance.fieldAutocomplete);
        }
        else
        {
            user = user.strip();
        }

        if (user.length > 0) {
            if (user.indexOf(',') > -1) {
                var users = new Array();
                users = user.split(',');
                for (var i = 0; i < users.length; i++) {
                    instance.addUser(users[i]);
                }
            }
            else
            {
                if (!$(instance.fieldRowIDPrefix + user)) {
                    var removeLink = this._addUserElement(user);

                    $(instance.fieldValues).value += "," + user;
                    if (removeLink) {
                        removeLink.observe('click', this.removeUser.bindAsEventListener(this), false);
                    }
                }
                var acAr = $F(instance.fieldAutocomplete).split(",");
                if (acAr.length > 0) acAr.pop();     //last value is the selected value, so drop it
                $(instance.fieldAutocomplete).value = acAr.join(",");
                Field.focus(instance.fieldAutocomplete);
                new Effect.Highlight(instance.fieldRowIDPrefix + user);
            }
        }
    },
    _addUserElement: function(user) {
        var removeLink = new Element('a', {href: 'javascript:;', id: 'jive-remove-user-link-' + user}).update('[x]');
        $(this.fieldContainer).insert(new Element('tr', {id: this.fieldRowIDPrefix + user})
                .insert(new Element('td').insert(new Element('img', {src: '../people/' + encodeURI(user) + '/avatar/16.png'})))
                .insert(new Element('td', {width: 200}).update(user))
                .insert(new Element('td').insert(removeLink)));

        return removeLink;
    }

}

var JiveProjectChooser = Class.create();
JiveProjectChooser.prototype = {
    initialize: function(chooserURL) {
        this.chooserURL = chooserURL;
    },

    handleChange: function(elem) {
        if ($F(elem) == -1) {
            this.openChooser(elem.id);
        }
    },

    openChooser: function(id) {
        this.win = window.open(this.chooserURL + "?id=" + id,"","menubar=yes,location=no,personalbar=no,scrollbars=yes,width=600,height=800,resizable");
    },

    addProject: function(id, value, description) {
        var select = $(id);
        var options = $A(select.getElementsByTagName("option"));
        var opt = options.find(function(option){
            return (option.value == value);
        });
        if (!opt) {
            // option doesn't exist in the list, so create it
            var newOption = new Element("option", {value: value});
            newOption.update(description);
            
            // insert before other, if exists
            var other = $(options.find(function(option) {
                return (option.value == '');
            }));
            other.insert({after: newOption});

            select.selectedIndex = -1;
            newOption.selected = true;
        } else {
            select.selectedIndex = -1;
            opt.selected = true;
        }
        select.focus();
    }
}

var JiveGroupChooser = Class.create();
JiveGroupChooser.prototype = {
    initialize: function(chooserURL) {
        this.chooserURL = chooserURL;
    },

    handleChange: function(elem) {
        if ($F(elem) == -1) {
            this.openChooser(elem.id);
        }
    },

    openChooser: function(id) {
        this.win = window.open(this.chooserURL + "?id=" + id,"","menubar=yes,location=no,personalbar=no,scrollbars=yes,width=600,height=800,resizable");
    },

    addGroup: function(id, value, description) {
        var select = $(id);
        var options = $A(select.getElementsByTagName("option"));
        var opt = options.find(function(option){
            return (option.value == value);
        });
        if (!opt) {
            // option doesn't exist in the list, so create it
            var newOption = new Element("option", {value: value});
            newOption.update(description);
            
            // insert before other, if exists
            var other = $(options.find(function(option) {
                return (option.value == '');
            }));
            other.insert({after: newOption});

            select.selectedIndex = -1;
            newOption.selected = true;
        } else {
            select.selectedIndex = -1;
            opt.selected = true;
        }
        select.focus();
    }
}

// FancyZoomHTML.js - v1.0
// Used to draw necessary HTML elements for FancyZoom
//
// Copyright (c) 2008 Cabel Sasser / Panic Inc
// All rights reserved.

function insertZoomHTML() {

	// All of this junk creates the three <div>'s used to hold the closebox, image, and zoom shadow.
	
	var inBody = document.getElementsByTagName("body").item(0);
	
	// WAIT SPINNER
	
	var inSpinbox = document.createElement("div");
	inSpinbox.setAttribute('id', 'ZoomSpin');
	inSpinbox.style.position = 'absolute';
	inSpinbox.style.left = '10px';
	inSpinbox.style.top = '10px';
	inSpinbox.style.visibility = 'hidden';
	inSpinbox.style.zIndex = '525';
	inBody.insertBefore(inSpinbox, inBody.firstChild);
	
	var inSpinImage = document.createElement("img");
	inSpinImage.setAttribute('id', 'SpinImage');
	inSpinImage.setAttribute('src', zoomImagesURI+'zoom-spin-1.png');
	inSpinbox.appendChild(inSpinImage);
	
	// ZOOM IMAGE
	//
	// <div id="ZoomBox">
	//   <a href="javascript:zoomOut();"><img src="/images/spacer.gif" id="ZoomImage" border="0"></a> <!-- THE IMAGE -->
	//   <div id="ZoomClose">
	//     <a href="javascript:zoomOut();"><img src="/images/closebox.png" width="30" height="30" border="0"></a>
	//   </div>
	// </div>
	
	var inZoombox = document.createElement("div");
	inZoombox.setAttribute('id', 'ZoomBox');
	
	inZoombox.style.position = 'absolute'; 
	inZoombox.style.left = '10px';
	inZoombox.style.top = '10px';
	inZoombox.style.visibility = 'hidden';
	inZoombox.style.zIndex = '499';
	
	inBody.insertBefore(inZoombox, inSpinbox.nextSibling);
	
	var inImage1 = document.createElement("img");
	inImage1.onclick = function (event) { zoomOut(this, event); return false; };	
	inImage1.setAttribute('src',zoomImagesURI+'spacer.gif');
	inImage1.setAttribute('id','ZoomImage');
	inImage1.setAttribute('border', '0');
	// inImage1.setAttribute('onMouseOver', 'zoomMouseOver();')
	// inImage1.setAttribute('onMouseOut', 'zoomMouseOut();')
	
	// This must be set first, so we can later test it using webkitBoxShadow.
	inImage1.setAttribute('style', '-webkit-box-shadow: '+shadowSettings+'0.0)');
	inImage1.style.display = 'block';
	inImage1.style.width = '10px';
	inImage1.style.height = '10px';
	inImage1.style.cursor = 'pointer'; // -webkit-zoom-out?
	inZoombox.appendChild(inImage1);

	var inClosebox = document.createElement("div");
	inClosebox.setAttribute('id', 'ZoomClose');
	inClosebox.style.position = 'absolute';
	
	// In MSIE, we need to put the close box inside the image.
	// It's 2008 and I'm having to do a browser detect? Sigh.
	if (browserIsIE) {
		inClosebox.style.left = '-1px';
		inClosebox.style.top = '0px';	
	} else {
		inClosebox.style.left = '-15px';
		inClosebox.style.top = '-15px';
	}
	
	inClosebox.style.visibility = 'hidden';
	inZoombox.appendChild(inClosebox);
		
	var inImage2 = document.createElement("img");
	inImage2.onclick = function (event) { zoomOut(this, event); return false; };	
	inImage2.setAttribute('src',zoomImagesURI+'closebox.png');		
	inImage2.setAttribute('width','30');
	inImage2.setAttribute('height','30');
	inImage2.setAttribute('border','0');
	inImage2.style.cursor = 'pointer';		
	inClosebox.appendChild(inImage2);
	
	// SHADOW
	// Only draw the table-based shadow if the programatic webkitBoxShadow fails!
	// Also, don't draw it if we're IE -- it wouldn't look quite right anyway.
	
	if (! document.getElementById('ZoomImage').style.webkitBoxShadow && ! browserIsIE) {

		// SHADOW BASE
		
		var inFixedBox = document.createElement("div");
		inFixedBox.setAttribute('id', 'ShadowBox');
		inFixedBox.style.position = 'absolute'; 
		inFixedBox.style.left = '50px';
		inFixedBox.style.top = '50px';
		inFixedBox.style.width = '100px';
		inFixedBox.style.height = '100px';
		inFixedBox.style.visibility = 'hidden';
		inFixedBox.style.zIndex = '498';
		inBody.insertBefore(inFixedBox, inZoombox.nextSibling);	
	
		// SHADOW
		// Now, the shadow table. Skip if not compatible, or irrevelant with -box-shadow.
		
		// <div id="ShadowBox"><table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0"> X
		//   <tr height="25">
		//   <td width="27"><img src="/images/zoom-shadow1.png" width="27" height="25"></td>
		//   <td background="/images/zoom-shadow2.png">&nbsp;</td>
		//   <td width="27"><img src="/images/zoom-shadow3.png" width="27" height="25"></td>
		//   </tr>
		
		var inShadowTable = document.createElement("table");
		inShadowTable.setAttribute('border', '0');
		inShadowTable.setAttribute('width', '100%');
		inShadowTable.setAttribute('height', '100%');
		inShadowTable.setAttribute('cellpadding', '0');
		inShadowTable.setAttribute('cellspacing', '0');
		inFixedBox.appendChild(inShadowTable);

		var inShadowTbody = document.createElement("tbody");	// Needed for IE (for HTML4).
		inShadowTable.appendChild(inShadowTbody);
		
		var inRow1 = document.createElement("tr");
		inRow1.style.height = '25px';
		inShadowTbody.appendChild(inRow1);
		
		var inCol1 = document.createElement("td");
		inCol1.style.width = '27px';
		inRow1.appendChild(inCol1);  
		var inShadowImg1 = document.createElement("img");
		inShadowImg1.setAttribute('src', zoomImagesURI+'zoom-shadow1.png');
		inShadowImg1.setAttribute('width', '27');
		inShadowImg1.setAttribute('height', '25');
		inShadowImg1.style.display = 'block';
		inCol1.appendChild(inShadowImg1);
		
		var inCol2 = document.createElement("td");
		inCol2.setAttribute('background', zoomImagesURI+'zoom-shadow2.png');
        inRow1.appendChild(inCol2);
		// inCol2.innerHTML = '<img src=';
		var inSpacer1 = document.createElement("img");
		inSpacer1.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer1.setAttribute('height', '1');
		inSpacer1.setAttribute('width', '1');
		inSpacer1.style.display = 'block';
		inCol2.appendChild(inSpacer1);
		
		var inCol3 = document.createElement("td");
		inCol3.style.width = '27px';
		inRow1.appendChild(inCol3);  
		var inShadowImg3 = document.createElement("img");
		inShadowImg3.setAttribute('src', zoomImagesURI+'zoom-shadow3.png');
		inShadowImg3.setAttribute('width', '27');
		inShadowImg3.setAttribute('height', '25');
		inShadowImg3.style.display = 'block';
		inCol3.appendChild(inShadowImg3);
		
		//   <tr>
		//   <td background="/images/zoom-shadow4.png">&nbsp;</td>
		//   <td bgcolor="#ffffff">&nbsp;</td>
		//   <td background="/images/zoom-shadow5.png">&nbsp;</td>
		//   </tr>
		
		inRow2 = document.createElement("tr");
		inShadowTbody.appendChild(inRow2);
		
		var inCol4 = document.createElement("td");
		inCol4.setAttribute('background', zoomImagesURI+'zoom-shadow4.png');
		inRow2.appendChild(inCol4);
		// inCol4.innerHTML = '&nbsp;';
		var inSpacer2 = document.createElement("img");
		inSpacer2.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer2.setAttribute('height', '1');
		inSpacer2.setAttribute('width', '1');
		inSpacer2.style.display = 'block';
		inCol4.appendChild(inSpacer2);
		
		var inCol5 = document.createElement("td");
		inCol5.setAttribute('bgcolor', '#ffffff');
		inRow2.appendChild(inCol5);
		// inCol5.innerHTML = '&nbsp;';
		var inSpacer3 = document.createElement("img");
		inSpacer3.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer3.setAttribute('height', '1');
		inSpacer3.setAttribute('width', '1');
		inSpacer3.style.display = 'block';
		inCol5.appendChild(inSpacer3);
		
		var inCol6 = document.createElement("td");
		inCol6.setAttribute('background', zoomImagesURI+'zoom-shadow5.png');
		inRow2.appendChild(inCol6);
		// inCol6.innerHTML = '&nbsp;';
		var inSpacer4 = document.createElement("img");
		inSpacer4.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer4.setAttribute('height', '1');
		inSpacer4.setAttribute('width', '1');
		inSpacer4.style.display = 'block';
		inCol6.appendChild(inSpacer4);
		
		//   <tr height="26">
		//   <td width="27"><img src="/images/zoom-shadow6.png" width="27" height="26"</td>
		//   <td background="/images/zoom-shadow7.png">&nbsp;</td>
		//   <td width="27"><img src="/images/zoom-shadow8.png" width="27" height="26"></td>
		//   </tr>  
		// </table>
		
		var inRow3 = document.createElement("tr");
		inRow3.style.height = '26px';
		inShadowTbody.appendChild(inRow3);
		
		var inCol7 = document.createElement("td");
		inCol7.style.width = '27px';
		inRow3.appendChild(inCol7);
		var inShadowImg7 = document.createElement("img");
		inShadowImg7.setAttribute('src', zoomImagesURI+'zoom-shadow6.png');
		inShadowImg7.setAttribute('width', '27');
		inShadowImg7.setAttribute('height', '26');
		inShadowImg7.style.display = 'block';
		inCol7.appendChild(inShadowImg7);
		
		var inCol8 = document.createElement("td");
		inCol8.setAttribute('background', zoomImagesURI+'zoom-shadow7.png');
		inRow3.appendChild(inCol8);  
		// inCol8.innerHTML = '&nbsp;';
		var inSpacer5 = document.createElement("img");
		inSpacer5.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer5.setAttribute('height', '1');
		inSpacer5.setAttribute('width', '1');
		inSpacer5.style.display = 'block';
		inCol8.appendChild(inSpacer5);
		
		var inCol9 = document.createElement("td");
		inCol9.style.width = '27px';
		inRow3.appendChild(inCol9);  
		var inShadowImg9 = document.createElement("img");
		inShadowImg9.setAttribute('src', zoomImagesURI+'zoom-shadow8.png');
		inShadowImg9.setAttribute('width', '27');
		inShadowImg9.setAttribute('height', '26');
		inShadowImg9.style.display = 'block';
		inCol9.appendChild(inShadowImg9);
	}

	if (includeCaption) {
	
		// CAPTION
		//
		// <div id="ZoomCapDiv" style="margin-left: 13px; margin-right: 13px;">
		// <table border="1" cellpadding="0" cellspacing="0">
		// <tr height="26">
		// <td><img src="zoom-caption-l.png" width="13" height="26"></td>
		// <td rowspan="3" background="zoom-caption-fill.png"><div id="ZoomCaption"></div></td>
		// <td><img src="zoom-caption-r.png" width="13" height="26"></td>
		// </tr>
		// </table>
		// </div>
		
		var inCapDiv = document.createElement("div");
		inCapDiv.setAttribute('id', 'ZoomCapDiv');
		inCapDiv.style.position = 'absolute'; 		
		inCapDiv.style.visibility = 'hidden';
		inCapDiv.style.marginLeft = 'auto';
		inCapDiv.style.marginRight = 'auto';
		inCapDiv.style.zIndex = '501';

		inBody.insertBefore(inCapDiv, inZoombox.nextSibling);
		
		var inCapTable = document.createElement("table");
		inCapTable.setAttribute('border', '0');
		inCapTable.setAttribute('cellPadding', '0');	// Wow. These honestly need to
		inCapTable.setAttribute('cellSpacing', '0');	// be intercapped to work in IE. WTF?
		inCapDiv.appendChild(inCapTable);
		
		var inTbody = document.createElement("tbody");	// Needed for IE (for HTML4).
		inCapTable.appendChild(inTbody);
		
		var inCapRow1 = document.createElement("tr");
		inTbody.appendChild(inCapRow1);
		
		var inCapCol1 = document.createElement("td");
		inCapCol1.setAttribute('align', 'right');
		inCapRow1.appendChild(inCapCol1);
		var inCapImg1 = document.createElement("img");
		inCapImg1.setAttribute('src', zoomImagesURI+'zoom-caption-l.png');
		inCapImg1.setAttribute('width', '13');
		inCapImg1.setAttribute('height', '26');
		inCapImg1.style.display = 'block';
		inCapCol1.appendChild(inCapImg1);
		
		var inCapCol2 = document.createElement("td");
		inCapCol2.setAttribute('background', zoomImagesURI+'zoom-caption-fill.png');
		inCapCol2.setAttribute('id', 'ZoomCaption');
		inCapCol2.setAttribute('valign', 'middle');
		inCapCol2.style.fontSize = '14px';
		inCapCol2.style.fontFamily = 'Helvetica';
		inCapCol2.style.fontWeight = 'bold';
		inCapCol2.style.color = '#ffffff';
		inCapCol2.style.textShadow = '0px 2px 4px #000000';
		inCapCol2.style.whiteSpace = 'nowrap';
		inCapRow1.appendChild(inCapCol2);

		var inCapCol3 = document.createElement("td");
		inCapRow1.appendChild(inCapCol3);
		var inCapImg2 = document.createElement("img");
		inCapImg2.setAttribute('src', zoomImagesURI+'zoom-caption-r.png');
		inCapImg2.setAttribute('width', '13');
		inCapImg2.setAttribute('height', '26');
		inCapImg2.style.display = 'block';
		inCapCol3.appendChild(inCapImg2);
	}
}

// FancyZoom.js - v1.1 - http://www.fancyzoom.com
//
// Copyright (c) 2008 Cabel Sasser / Panic Inc
// All rights reserved.
// 
//     Requires: FancyZoomHTML.js
// Instructions: Include JS files in page, call setupZoom() in onLoad. That's it!
//               Any <a href> links to images will be updated to zoom inline.
//               Add rel="nozoom" to your <a href> to disable zooming for an image.
// 
// Redistribution and use of this effect in source form, with or without modification,
// are permitted provided that the following conditions are met:
// 
// * USE OF SOURCE ON COMMERCIAL (FOR-PROFIT) WEBSITE REQUIRES ONE-TIME LICENSE FEE PER DOMAIN.
//   Reasonably priced! Visit www.fancyzoom.com for licensing instructions. Thanks!
//
// * Non-commercial (personal) website use is permitted without license/payment!
//
// * Redistribution of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistribution of source code and derived works cannot be sold without specific
//   written prior permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var includeCaption = true; // Turn on the "caption" feature, and write out the caption HTML
var zoomTime       = 5;    // Milliseconds between frames of zoom animation
var zoomSteps      = 15;   // Number of zoom animation frames
var includeFade    = 1;    // Set to 1 to fade the image in / out as it zooms
var minBorder      = 90;   // Amount of padding between large, scaled down images, and the window edges
var shadowSettings = '0px 5px 25px rgba(0, 0, 0, '; // Blur, radius, color of shadow for compatible browsers

var zoomImagesURI   = '/images-global/zoom/'; // Location of the zoom and shadow images

// Init. Do not add anything below this line, unless it's something awesome.

var myWidth = 0, myHeight = 0, myScroll = 0; myScrollWidth = 0; myScrollHeight = 0;
var zoomOpen = false, preloadFrame = 1, preloadActive = false, preloadTime = 0, imgPreload = new Image(), preloadFail = false;
var preloadAnimTimer = 0;

var zoomActive = new Array(); var zoomTimer  = new Array(); 
var zoomOrigW  = new Array(); var zoomOrigH  = new Array();
var zoomOrigX  = new Array(); var zoomOrigY  = new Array();

var zoomID         = "ZoomBox";
var theID          = "ZoomImage";
var zoomCaption    = "ZoomCaption";
var zoomCaptionDiv = "ZoomCapDiv";

if (navigator.userAgent.indexOf("MSIE") != -1) {
	var browserIsIE = true;
}

// Zoom: Setup The Page! Called in your <body>'s onLoad handler.

function setupZoom() {
	prepZooms();
	insertZoomHTML();
	zoomdiv = document.getElementById(zoomID);  
	zoomimg = document.getElementById(theID);
}

// Zoom: Inject Javascript functions into hrefs pointing to images, one by one!
// Skip any href that contains a rel="nozoom" tag.
// This is done at page load time via an onLoad() handler.

function prepZooms() {
	if (! document.getElementsByTagName) {
		return;
	}
	var links = document.getElementsByTagName("a");
	for (i = 0; i < links.length; i++) {
		if (links[i].getAttribute("href")) {
			if (links[i].getAttribute("href").search(/(.*)\.(jpg|jpeg|gif|png|bmp|tif|tiff)/gi) != -1) {
				if (links[i].getAttribute("rel") != "nozoom" && links[i].getAttribute("class") != "jive-link-external-small") {
					links[i].onclick = function (event) { return zoomClick(this, event); };
					links[i].onmouseover = function () { zoomPreload(this); };
				}
			}
		}
	}
}

// Zoom: Load an image into an image object. When done loading, function sets preloadActive to false,
// so other bits know that they can proceed with the zoom.
// Preloaded image is stored in imgPreload and swapped out in the zoom function.

function zoomPreload(from) {

	var theimage = from.getAttribute("href");

	// Only preload if we have to, i.e. the image isn't this image already

	if (imgPreload.src.indexOf(from.getAttribute("href").substr(from.getAttribute("href").lastIndexOf("/"))) == -1) {
		preloadActive = true;
		imgPreload = new Image();

		// Set a function to fire when the preload is complete, setting flags along the way.

		imgPreload.onload = function() {
			preloadActive = false;
		}
		
		imgPreload.onerror = function() {
			preloadFail = true; 
			if (preloadActive) {
				document.getElementById("ZoomSpin").style.visibility = "hidden";    
				clearInterval(preloadAnimTimer);
				preloadAnimTimer = 0;
			}
		}
		// Load it!
		imgPreload.src = theimage;
	}
}

// Zoom: Start the preloading animation cycle.

function preloadAnimStart() {
	preloadTime = new Date();
	document.getElementById("ZoomSpin").style.left = (myWidth / 2) + 'px';
	document.getElementById("ZoomSpin").style.top = ((myHeight / 2) + myScroll) + 'px';
	document.getElementById("ZoomSpin").style.visibility = "visible";	
	preloadFrame = 1;
	document.getElementById("SpinImage").src = zoomImagesURI+'zoom-spin-'+preloadFrame+'.png';  
	preloadAnimTimer = setInterval("preloadAnim()", 100);
}

// Zoom: Display and ANIMATE the jibber-jabber widget. Once preloadActive is false, bail and zoom it up!

function preloadAnim(from) {
	if (preloadActive != false && preloadFail == false) {
		document.getElementById("SpinImage").src = zoomImagesURI+'zoom-spin-'+preloadFrame+'.png';
		preloadFrame++;
		if (preloadFrame > 12) preloadFrame = 1;
	} else if (preloadFail == false){
		document.getElementById("ZoomSpin").style.visibility = "hidden";    
		clearInterval(preloadAnimTimer);
		preloadAnimTimer = 0;
		zoomIn(preloadFrom);
	} else {
		document.getElementById("ZoomSpin").style.visibility = "hidden";    
		clearInterval(preloadAnimTimer);
		preloadAnimTimer = 0;
	}
}

// ZOOM CLICK: We got a click! Should we do the zoom? Or wait for the preload to complete?
// todo?: Double check that imgPreload src = clicked src

function zoomClick(from, evt) {

	var shift = getShift(evt);

	// Check for Command / Alt key. If pressed, pass them through -- don't zoom!
	if (! evt && window.event && (window.event.metaKey || window.event.altKey)) {
		return true;
	} else if (evt && (evt.metaKey|| evt.altKey)) {
		return true;
	}

	// Get browser dimensions
	getSize();

	// If preloading still, wait, and display the spinner.
	if (preloadActive == true) {
		// But only display the spinner if it's not already being displayed!
		if (preloadAnimTimer == 0) {
			preloadFrom = from;
			preloadAnimStart();	
		}
	} else {
		// Otherwise, we're loaded: do the zoom!
		zoomIn(from, shift);
	}
	
	return false;
	
}

// Zoom: Move an element in to endH endW, using zoomHost as a starting point.
// "from" is an object reference to the href that spawned the zoom.

function zoomIn(from, shift) {

	zoomimg.src = from.getAttribute("href");

	// Determine the zoom settings from where we came from, the element in the <a>.
	// If there's no element in the <a>, or we can't get the width, make stuff up

	if (from.childNodes[0].width) {
		startW = from.childNodes[0].width;
		startH = from.childNodes[0].height;
		startPos = findElementPos(from.childNodes[0]);
	} else {
		startW = 50;
		startH = 12;
		startPos = findElementPos(from);
	}

	hostX = startPos[0];
	hostY = startPos[1];

	// Make up for a scrolled containing div.
	// TODO: This HAS to move into findElementPos.
	
	if (document.getElementById('scroller')) {
		hostX = hostX - document.getElementById('scroller').scrollLeft;
	}

	// Determine the target zoom settings from the preloaded image object

	endW = imgPreload.width;
	endH = imgPreload.height;

	// Start! But only if we're not zooming already!

	if (zoomActive[theID] != true) {

		// Clear everything out just in case something is already open

		if (document.getElementById("ShadowBox")) {
			document.getElementById("ShadowBox").style.visibility = "hidden";
		} else if (! browserIsIE) {
		
			// Wipe timer if shadow is fading in still
			if (fadeActive["ZoomImage"]) {
				clearInterval(fadeTimer["ZoomImage"]);
				fadeActive["ZoomImage"] = false;
				fadeTimer["ZoomImage"] = false;			
			}
			
			document.getElementById("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';			
		}
		
		document.getElementById("ZoomClose").style.visibility = "hidden";     

		// Setup the CAPTION, if existing. Hide it first, set the text.

		if (includeCaption) {
			document.getElementById(zoomCaptionDiv).style.visibility = "hidden";
			if (from.getAttribute('title') && includeCaption) {
				// Yes, there's a caption, set it up
				document.getElementById(zoomCaption).innerHTML = from.getAttribute('title');
			} else {
				document.getElementById(zoomCaption).innerHTML = "";
			}
		}

		// Store original position in an array for future zoomOut.

		zoomOrigW[theID] = startW;
		zoomOrigH[theID] = startH;
		zoomOrigX[theID] = hostX;
		zoomOrigY[theID] = hostY;

		// Now set the starting dimensions

		zoomimg.style.width = startW + 'px';
		zoomimg.style.height = startH + 'px';
		zoomdiv.style.left = hostX + 'px';
		zoomdiv.style.top = hostY + 'px';

		// Show the zooming image container, make it invisible

		if (includeFade == 1) {
			setOpacity(0, zoomID);
		}
		zoomdiv.style.visibility = "visible";

		// If it's too big to fit in the window, shrink the width and height to fit (with ratio).

		sizeRatio = endW / endH;
		if (endW > myWidth - minBorder) {
			endW = myWidth - minBorder;
			endH = endW / sizeRatio;
		}
		if (endH > myHeight - minBorder) {
			endH = myHeight - minBorder;
			endW = endH * sizeRatio;
		}

		zoomChangeX = ((myWidth / 2) - (endW / 2) - hostX);
		zoomChangeY = (((myHeight / 2) - (endH / 2) - hostY) + myScroll);
		zoomChangeW = (endW - startW);
		zoomChangeH = (endH - startH);
		
		// Shift key?
	
		if (shift) {
			tempSteps = zoomSteps * 7;
		} else {
			tempSteps = zoomSteps;
		}

		// Setup Zoom

		zoomCurrent = 0;

		// Setup Fade with Zoom, If Requested

		if (includeFade == 1) {
			fadeCurrent = 0;
			fadeAmount = (0 - 100) / tempSteps;
		} else {
			fadeAmount = 0;
		}

		// Do It!
		
		zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+hostX+", "+zoomChangeX+", "+hostY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDoneIn(zoomID)')", zoomTime);		
		zoomActive[theID] = true; 
	}
}

// Zoom it back out.

function zoomOut(from, evt) {

	// Get shift key status.
	// IE events don't seem to get passed through the function, so grab it from the window.

	if (getShift(evt)) {
		tempSteps = zoomSteps * 7;
	} else {
		tempSteps = zoomSteps;
	}	

	// Check to see if something is happening/open
  
	if (zoomActive[theID] != true) {

		// First, get rid of the shadow if necessary.

		if (document.getElementById("ShadowBox")) {
			document.getElementById("ShadowBox").style.visibility = "hidden";
		} else if (! browserIsIE) {
		
			// Wipe timer if shadow is fading in still
			if (fadeActive["ZoomImage"]) {
				clearInterval(fadeTimer["ZoomImage"]);
				fadeActive["ZoomImage"] = false;
				fadeTimer["ZoomImage"] = false;			
			}
			
			document.getElementById("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';			
		}

		// ..and the close box...

		document.getElementById("ZoomClose").style.visibility = "hidden";

		// ...and the caption if necessary!

		if (includeCaption && document.getElementById(zoomCaption).innerHTML != "") {
			// fadeElementSetup(zoomCaptionDiv, 100, 0, 5, 1);
			document.getElementById(zoomCaptionDiv).style.visibility = "hidden";
		}

		// Now, figure out where we came from, to get back there

		startX = parseInt(zoomdiv.style.left);
		startY = parseInt(zoomdiv.style.top);
		startW = zoomimg.width;
		startH = zoomimg.height;
		zoomChangeX = zoomOrigX[theID] - startX;
		zoomChangeY = zoomOrigY[theID] - startY;
		zoomChangeW = zoomOrigW[theID] - startW;
		zoomChangeH = zoomOrigH[theID] - startH;

		// Setup Zoom

		zoomCurrent = 0;

		// Setup Fade with Zoom, If Requested

		if (includeFade == 1) {
			fadeCurrent = 0;
			fadeAmount = (100 - 0) / tempSteps;
		} else {
			fadeAmount = 0;
		}

		// Do It!

		zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+startX+", "+zoomChangeX+", "+startY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDone(zoomID, theID)')", zoomTime);	
		zoomActive[theID] = true;
	}
}

// Finished Zooming In

function zoomDoneIn(zoomdiv, theID) {

	// Note that it's open
  
	zoomOpen = true;
	zoomdiv = document.getElementById(zoomdiv);

	// Position the table shadow behind the zoomed in image, and display it

	if (document.getElementById("ShadowBox")) {

		setOpacity(0, "ShadowBox");
		shadowdiv = document.getElementById("ShadowBox");

		shadowLeft = parseInt(zoomdiv.style.left) - 13;
		shadowTop = parseInt(zoomdiv.style.top) - 8;
		shadowWidth = zoomdiv.offsetWidth + 26;
		shadowHeight = zoomdiv.offsetHeight + 26; 
	
		shadowdiv.style.width = shadowWidth + 'px';
		shadowdiv.style.height = shadowHeight + 'px';
		shadowdiv.style.left = shadowLeft + 'px';
		shadowdiv.style.top = shadowTop + 'px';

		document.getElementById("ShadowBox").style.visibility = "visible";
		fadeElementSetup("ShadowBox", 0, 100, 5);
		
	} else if (! browserIsIE) {
		// Or, do a fade of the modern shadow
		fadeElementSetup("ZoomImage", 0, .8, 5, 0, "shadow");
	}
	
	// Position and display the CAPTION, if existing
  
	if (includeCaption && document.getElementById(zoomCaption).innerHTML != "") {
		// setOpacity(0, zoomCaptionDiv);
		zoomcapd = document.getElementById(zoomCaptionDiv);
		zoomcapd.style.top = parseInt(zoomdiv.style.top) + (zoomdiv.offsetHeight + 15) + 'px';
		zoomcapd.style.left = (myWidth / 2) - (zoomcapd.offsetWidth / 2) + 'px';
		zoomcapd.style.visibility = "visible";
		// fadeElementSetup(zoomCaptionDiv, 0, 100, 5);
	}   
	
	// Display Close Box (fade it if it's not IE)

	if (!browserIsIE) setOpacity(0, "ZoomClose");
	document.getElementById("ZoomClose").style.visibility = "visible";
	if (!browserIsIE) fadeElementSetup("ZoomClose", 0, 100, 5);

	// Get keypresses
	document.onkeypress = getKey;
	
}

// Finished Zooming Out

function zoomDone(zoomdiv, theID) {

	// No longer open
  
	zoomOpen = false;

	// Clear stuff out, clean up

	zoomOrigH[theID] = "";
	zoomOrigW[theID] = "";
	document.getElementById(zoomdiv).style.visibility = "hidden";
	zoomActive[theID] == false;

	// Stop getting keypresses

	document.onkeypress = null;

}

// Actually zoom the element

function zoomElement(zoomdiv, theID, zoomCurrent, zoomStartW, zoomChangeW, zoomStartH, zoomChangeH, zoomStartX, zoomChangeX, zoomStartY, zoomChangeY, zoomSteps, includeFade, fadeAmount, execWhenDone) {

	// console.log("Zooming Step #"+zoomCurrent+ " of "+zoomSteps+" (zoom " + zoomStartW + "/" + zoomChangeW + ") (zoom " + zoomStartH + "/" + zoomChangeH + ")  (zoom " + zoomStartX + "/" + zoomChangeX + ")  (zoom " + zoomStartY + "/" + zoomChangeY + ") Fade: "+fadeAmount);
    
	// Test if we're done, or if we continue

	if (zoomCurrent == (zoomSteps + 1)) {
		zoomActive[theID] = false;
		clearInterval(zoomTimer[theID]);

		if (execWhenDone != "") {
			eval(execWhenDone);
		}
	} else {
	
		// Do the Fade!
	  
		if (includeFade == 1) {
			if (fadeAmount < 0) {
				setOpacity(Math.abs(zoomCurrent * fadeAmount), zoomdiv);
			} else {
				setOpacity(100 - (zoomCurrent * fadeAmount), zoomdiv);
			}
		}
	  
		// Calculate this step's difference, and move it!
		
		moveW = cubicInOut(zoomCurrent, zoomStartW, zoomChangeW, zoomSteps);
		moveH = cubicInOut(zoomCurrent, zoomStartH, zoomChangeH, zoomSteps);
		moveX = cubicInOut(zoomCurrent, zoomStartX, zoomChangeX, zoomSteps);
		moveY = cubicInOut(zoomCurrent, zoomStartY, zoomChangeY, zoomSteps);
	
		document.getElementById(zoomdiv).style.left = moveX + 'px';
		document.getElementById(zoomdiv).style.top = moveY + 'px';
		zoomimg.style.width = moveW + 'px';
		zoomimg.style.height = moveH + 'px';
	
		zoomCurrent++;
		
		clearInterval(zoomTimer[theID]);
		zoomTimer[theID] = setInterval("zoomElement('"+zoomdiv+"', '"+theID+"', "+zoomCurrent+", "+zoomStartW+", "+zoomChangeW+", "+zoomStartH+", "+zoomChangeH+", "+zoomStartX+", "+zoomChangeX+", "+zoomStartY+", "+zoomChangeY+", "+zoomSteps+", "+includeFade+", "+fadeAmount+", '"+execWhenDone+"')", zoomTime);
	}
}

// Zoom Utility: Get Key Press when image is open, and act accordingly

function getKey(evt) {
	if (! evt) {
		theKey = event.keyCode;
	} else {
		theKey = evt.keyCode;
	}

	if (theKey == 27) { // ESC
		zoomOut(this, evt);
	}
}

////////////////////////////
//
// FADE Functions
//

function fadeOut(elem) {
	if (elem.id) {
		fadeElementSetup(elem.id, 100, 0, 10);
	}
}

function fadeIn(elem) {
	if (elem.id) {
		fadeElementSetup(elem.id, 0, 100, 10);	
	}
}

// Fade: Initialize the fade function

var fadeActive = new Array();
var fadeQueue  = new Array();
var fadeTimer  = new Array();
var fadeClose  = new Array();
var fadeMode   = new Array();

function fadeElementSetup(theID, fdStart, fdEnd, fdSteps, fdClose, fdMode) {

	// alert("Fading: "+theID+" Steps: "+fdSteps+" Mode: "+fdMode);

	if (fadeActive[theID] == true) {
		// Already animating, queue up this command
		fadeQueue[theID] = new Array(theID, fdStart, fdEnd, fdSteps);
	} else {
		fadeSteps = fdSteps;
		fadeCurrent = 0;
		fadeAmount = (fdStart - fdEnd) / fadeSteps;
		fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
		fadeActive[theID] = true;
		fadeMode[theID] = fdMode;
		
		if (fdClose == 1) {
			fadeClose[theID] = true;
		} else {
			fadeClose[theID] = false;
		}
	}
}

// Fade: Do the fade. This function will call itself, modifying the parameters, so
// many instances can run concurrently. Can fade using opacity, or fade using a box-shadow.

function fadeElement(theID, fadeCurrent, fadeAmount, fadeSteps) {

	if (fadeCurrent == fadeSteps) {

		// We're done, so clear.

		clearInterval(fadeTimer[theID]);
		fadeActive[theID] = false;
		fadeTimer[theID] = false;

		// Should we close it once the fade is complete?

		if (fadeClose[theID] == true) {
			document.getElementById(theID).style.visibility = "hidden";
		}

		// Hang on.. did a command queue while we were working? If so, make it happen now

		if (fadeQueue[theID] && fadeQueue[theID] != false) {
			fadeElementSetup(fadeQueue[theID][0], fadeQueue[theID][1], fadeQueue[theID][2], fadeQueue[theID][3]);
			fadeQueue[theID] = false;
		}
	} else {

		fadeCurrent++;
		
		// Now actually do the fade adjustment.
		
		if (fadeMode[theID] == "shadow") {

			// Do a special fade on the webkit-box-shadow of the object
		
			if (fadeAmount < 0) {
				document.getElementById(theID).style.webkitBoxShadow = shadowSettings + (Math.abs(fadeCurrent * fadeAmount)) + ')';
			} else {
				document.getElementById(theID).style.webkitBoxShadow = shadowSettings + (100 - (fadeCurrent * fadeAmount)) + ')';
			}
			
		} else {
		
			// Set the opacity depending on if we're adding or subtracting (pos or neg)
			
			if (fadeAmount < 0) {
				setOpacity(Math.abs(fadeCurrent * fadeAmount), theID);
			} else {
				setOpacity(100 - (fadeCurrent * fadeAmount), theID);
			}
		}

		// Keep going, and send myself the updated variables
		clearInterval(fadeTimer[theID]);
		fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
	}
}

////////////////////////////
//
// UTILITY functions
//

// Utility: Set the opacity, compatible with a number of browsers. Value from 0 to 100.

function setOpacity(opacity, theID) {

	var object = document.getElementById(theID).style;

	// If it's 100, set it to 99 for Firefox.

	if (navigator.userAgent.indexOf("Firefox") != -1) {
		if (opacity == 100) { opacity = 99.9999; } // This is majorly awkward
	}

	// Multi-browser opacity setting

	object.filter = "alpha(opacity=" + opacity + ")"; // IE/Win
	object.opacity = (opacity / 100);                 // Safari 1.2, Firefox+Mozilla

}

// Utility: Math functions for animation calucations - From http://www.robertpenner.com/easing/
//
// t = time, b = begin, c = change, d = duration
// time = current frame, begin is fixed, change is basically finish - begin, duration is fixed (frames),

function linear(t, b, c, d)
{
	return c*t/d + b;
}

function sineInOut(t, b, c, d)
{
	return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}

function cubicIn(t, b, c, d) {
	return c*(t/=d)*t*t + b;
}

function cubicOut(t, b, c, d) {
	return c*((t=t/d-1)*t*t + 1) + b;
}

function cubicInOut(t, b, c, d)
{
	if ((t/=d/2) < 1) return c/2*t*t*t + b;
	return c/2*((t-=2)*t*t + 2) + b;
}

function bounceOut(t, b, c, d)
{
	if ((t/=d) < (1/2.75)){
		return c*(7.5625*t*t) + b;
	} else if (t < (2/2.75)){
		return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
	} else if (t < (2.5/2.75)){
		return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
	} else {
		return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
	}
}


// Utility: Get the size of the window, and set myWidth and myHeight
// Credit to quirksmode.org

function getSize() {

	// Window Size

	if (self.innerHeight) { // Everyone but IE
		myWidth = window.innerWidth;
		myHeight = window.innerHeight;
		myScroll = window.pageYOffset;
	} else if (document.documentElement && document.documentElement.clientHeight) { // IE6 Strict
		myWidth = document.documentElement.clientWidth;
		myHeight = document.documentElement.clientHeight;
		myScroll = document.documentElement.scrollTop;
	} else if (document.body) { // Other IE, such as IE7
		myWidth = document.body.clientWidth;
		myHeight = document.body.clientHeight;
		myScroll = document.body.scrollTop;
	}

	// Page size w/offscreen areas

	if (window.innerHeight && window.scrollMaxY) {	
		myScrollWidth = document.body.scrollWidth;
		myScrollHeight = window.innerHeight + window.scrollMaxY;
	} else if (document.body.scrollHeight > document.body.offsetHeight) { // All but Explorer Mac
		myScrollWidth = document.body.scrollWidth;
		myScrollHeight = document.body.scrollHeight;
	} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
		myScrollWidth = document.body.offsetWidth;
		myScrollHeight = document.body.offsetHeight;
	}
}

// Utility: Get Shift Key Status
// IE events don't seem to get passed through the function, so grab it from the window.

function getShift(evt) {
	var shift = false;
	if (! evt && window.event) {
		shift = window.event.shiftKey;
	} else if (evt) {
		shift = evt.shiftKey;
		if (shift) evt.stopPropagation(); // Prevents Firefox from doing shifty things
	}
	return shift;
}

// Utility: Find the Y position of an element on a page. Return Y and X as an array

function findElementPos(elemFind)
{
	var elemX = 0;
	var elemY = 0;
	do {
		elemX += elemFind.offsetLeft;
		elemY += elemFind.offsetTop;
	} while ( elemFind = elemFind.offsetParent )

	return Array(elemX, elemY);
}

// wrappers around DWR functions for following a community or project.
// depends on FollowingActionBean, defined in header-javascript.ftl
// currently used by the follow container tooltip, found in jive.js.
// separated into a separate file to avoid additional dependencies on
// jive.js

jive.Follow = function(errorMsg) {

    var that = this;

    var errorMsg = errorMsg;

    this.startFollowingCommunity = function (container) {
        FollowingActionBean.followCommunity(container, true, {
            callback:function() {
                $('jive-link-community-startFollowing').hide();
                $('jive-link-community-stopFollowing').show();
                    $('jive-follow-error').hide();
                var placesCache = jiveControl.getPlacesCache();
                placesCache.reloadPlaces("FOLLOWED_ALL");
                },
                errorHandler:function(msg, e) {
                    $('jive-follow-error').update(errorMsg);
                    $('jive-follow-error').show();
            }
        });
    }

    this.stopFollowingCommunity = function (container) {
        FollowingActionBean.followCommunity(container, false, {
            callback:function() {
                $('jive-link-community-startFollowing').show();
                $('jive-link-community-stopFollowing').hide();
                $('jive-follow-error').hide();
                var placesCache = jiveControl.getPlacesCache();
                placesCache.reloadPlaces("FOLLOWED_ALL");
            },
            errorHandler:function(msg, e) {
                $('jive-follow-error').update(errorMsg);
                $('jive-follow-error').show();
            }
        });
    }

    this.startFollowingProject = function (container) {
        FollowingActionBean.followProject(container, true, {
            callback:function() {
                $('jive-link-project-startFollowing').hide();
                $('jive-link-project-stopFollowing').show();
                $('jive-follow-error').hide();
                var placesCache = jiveControl.getPlacesCache();
                placesCache.reloadPlaces("FOLLOWED_ALL");
            },
            errorHandler:function(msg, e) {
                $('jive-follow-error').update(errorMsg);
                $('jive-follow-error').show();
            }
        });
    }

    this.stopFollowingProject = function (container) {
        FollowingActionBean.followProject(container, false, {
            callback:function() {
                $('jive-link-project-startFollowing').show();
                $('jive-link-project-stopFollowing').hide();
                $('jive-follow-error').hide();
                var placesCache = jiveControl.getPlacesCache();
                placesCache.reloadPlaces("FOLLOWED_ALL");
            },
            errorHandler:function(msg, e) {
                $('jive-follow-error').update(errorMsg);
                $('jive-follow-error').show();
            }
        });
    }

    // functions for widget-props.ftl
    this.enableFollowedPlaces = function() {

        // disable all container radios
        $j('#choose-community-radio').attr('disabled', true);
        $j('#widget-edit-choose-group').attr('disabled', true);
        $j('#choose-project-radio').attr('disabled', true);
        $j('#widget-edit-choose-comm').attr('disabled', true);
        $j('#choose-group-radio').attr('disabled', true);
        $j('#widget-edit-choose-proj').attr('disabled', true);
        $j('#rb-recursive-1').attr('disabled', true);
        $j('#rb-recursive-2').attr('disabled', true);
    }

    this.disableFollowedPlaces = function () {

        // enable all container radios
        $j('#choose-community-radio').removeAttr('disabled');
        $j('#choose-project-radio').removeAttr('disabled');
        $j('#choose-group-radio').removeAttr('disabled');

        if ($j('#choose-community-radio').is(':checked')) {
            $j('#widget-edit-choose-comm').removeAttr('disabled');
        }
        else if ($j('#choose-project-radio').is(':checked')) {
            $j('#widget-edit-choose-proj').removeAttr('disabled');
        }
        else if ($j('#choose-group-radio').is(':checked')) {
            $j('#widget-edit-choose-group').removeAttr('disabled');
        }

        $j('#rb-recursive-1').removeAttr('disabled');
        $j('#rb-recursive-2').removeAttr('disabled');
    }

}

// Wrapper functions around DWR functions for friending or connecting to a user.
// Depends on FriendListAction, included in header-javascript.ftl.
// Currently used by the user tooltip, found in jive.js.
// separated into a separate file to avoid additional dependencies on
// jive.js.  Also separated from view-profile.ftl to avoid conflicting div IDs.

    function addAsFriendTT(targetUserID, friendCount, friendApprovals, hasFriendLists) {
        if (friendApprovals || hasFriendLists){
            Effect.toggle('jive-add-friend-hover','appear');
            if (friendApprovals){
                $('friendReqMsgDiv').show();
            }
            $('jive-adding-friend-link-hover').toggle();
            $('jive-add-friend-link-hover').toggle();
        } else {
            submitFriendRequestTT(targetUserID,'');
        }
    }

    function submitFriendRequestTT(userID, reqMessage, friendCount){
        var relListIDs = [];
        $$('input.relListCB').each(function(name){
            if ($(name).checked){
                relListIDs[relListIDs.length] = $(name).value;
            }
        });
        FriendListAction.addFriend(userID, reqMessage, relListIDs, {
            callback:function(approved) {
                $('friend-add-form-hover').hide();
                if (!approved){
                   new Effect.Appear('friend-pending-hover');
                } else {
                   new Effect.Appear('friend-approved-hover');
                   friendCount = friendCount + 1;
                   if ($('friend-count')){
                        $('friend-count').update(friendCount);
                   }
                   if ($('jive-remove-rel-hover')){
                        $('jive-remove-rel-hover').show();
                   }
                }
            },
            errorHandler:function(msg) {
                $('jive-error-box').show();
                $('jive-error-box').update(msg);
                new Effect.Fade('jive-error-box', {delay: 5});
            }
        });
    }

    function removeAsFriendTT(targetUserID, friendCount){
         FriendListAction.removeFriend(targetUserID, {
                callback:function() {
                    $('friend-pending-hover').hide();
                    $('friend-approved-hover').hide();
                    $('friend-add-form-hover').show();
                    $('jive-add-friend-link-hover').show();
                    $('jive-adding-friend-link-hover').hide();
                    $('jive-add-friend-hover').hide();
                    $('jive-confirm-relationship-removal-hover').hide();
                    friendCount = friendCount - 1;
                    if ($('friend-count')){
                            $('friend-count').update(friendCount);
                       }
                    }
          });
    }

// loads widget content asynchronously via ajax.

jive.model.WidgetLoader = function() {

    var that = this;

    var widgetQueue = new Array();

    this.addWidget = function(widgetArgs) {

        widgetQueue.push(widgetArgs);
    }

    this.renderAll = function () {

        widgetQueue.each(function(widgetArgs) {
            $j('#jive-widgetframe-body_' + widgetArgs.frameID).load(widgetArgs.renderWidgetAction, {
                'frameID':widgetArgs.frameID,
                'containerSize':widgetArgs.containerSize,
                'widgetType':widgetArgs.widgetType,
                'container':widgetArgs.container,
                'containerType':widgetArgs.containerType
            }, function() {
                $('jive-widgetframe-loading_' + widgetArgs.frameID).hide();
                $('jive-widgetframe-body_' + widgetArgs.frameID).show();
                //span for the refresh link
                $('jive-widgetframe-refresh_' + widgetArgs.frameID).show();
                that.refreshLink(widgetArgs);
                // fire an event that this widget frame has loaded
                $j('#jive-widgetframe_' + widgetArgs.frameID).trigger('frameLoaded');
            });
        });
    }

    this.refreshLink = function (widgetArgs) {

        $('jive-widgetframe-refresh-link_' + widgetArgs.frameID).observe('click', function() {
            $('jive-widgetframe-loading_' + widgetArgs.frameID).show();
            $('jive-widgetframe-body_' + widgetArgs.frameID).hide();
            that.refresh(widgetArgs);
        });
    }

    this.refresh = function (widgetArgs) {

        $j('#jive-widgetframe-body_' + widgetArgs.frameID).load(widgetArgs.renderWidgetAction, {
            'frameID':widgetArgs.frameID,
            'containerSize':widgetArgs.containerSize,
            'widgetType':widgetArgs.widgetType,
            'container':widgetArgs.container,
            'containerType':widgetArgs.containerType
        }, function() {
            $('jive-widgetframe-loading_' + widgetArgs.frameID).hide();
            $('jive-widgetframe-body_' + widgetArgs.frameID).show();
            // fire an event that this widget frame has loaded
            $j('#jive-widgetframe_' + widgetArgs.frameID).trigger('frameLoaded');
        });
    }
}

var widgetLoader = new jive.model.WidgetLoader();

Event.observe(window, 'load', function() {
    widgetLoader.renderAll();
});


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 (typeof(o[n]) != "undefined") {
					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 || '';

        console.log(o);

		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
            if(typeof(na) == "undefined") return false;
			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){
                    if(typeof(v) == "object" && n == "style"){
                        var oldv = t.serializeStyle(v);
                        if(v != null && typeof(v["cssText"]) != "undefined"){
                            v = v["cssText"];
                        }
                        v = t.serializeStyle(t.parseStyle(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) {
            v = v.replace(/"/g, "&quot;");
			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;

            v = v.replace(/"/g, "&quot;");

			t.writeRaw(" ")
            t.writeRaw(t.encode(n));
            t.writeRaw('="');
            t.writeRaw(t.encode(v));
            t.writerRaw('"');
		},

		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.writeRaw(this.tags.pop());
                this.writeRaw('>');

				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[');
            this.writeRaw(v);
            this.writeRaw(']]>');
			this.count++;
		},

		writeComment : function(v) {
			this._writeAttributesEnd();
			this.writeRaw('<!-- ');
            this.writeRaw(v);
            this.writeRaw('-->');
			this.count++;
		},

		writeRaw : function(v) {
			this.str.push(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.join("");
		},

		_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) {
                        // we don't need to do any fancy
                        // processing of the title text, so just
                        // set the innerHTML directly
                        e.innerHTML = DOM.encode(o.title);
//						DOM.setHTML(e, DOM.encode(o.title));
						fv = 1;
						return false;
					}
				});

				// If no item was found then present title
				if (!fv) {
                    // we don't need to do any fancy
                    // processing of the title text, so just
                    // set the innerHTML directly
                    e.innerHTML = DOM.encode(t.settings.title);
//						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.settings.onselect(c);
			t.hideMenu();
		},

		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
            //
            // the setContent call to repaint is pretty much only called
            // when we initially load the RTE, so there wouldn't be
            // any ghost selections anyways.
            // i've commented this out since it's a performance hog
            // for large documents
            /*
			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 += "<!--[if IE 7]><style type='text/css'>" +
                            s.ie7_css +
                            "</style><![endif]-->";

			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')});
                // this is for teh isDirty() call, which only the save plugin uses
                // and we don't use the save plugin :)  this is /very/ expensive for large
                // documents
//				t.startContent = t.getContent({format : 'raw'});
                t.startContent = "";
//				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) {
                var theNode = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n;
                if(typeof(t.lastNodeChanged) == "undefined" || t.lastNodeChanged != theNode){
                    // don't call the same node twice in a row
                    t.lastNodeChanged = theNode;
                    t.onNodeChange.dispatch(
                        t,
                        o ? o.controlManager || t.controlManager : t.controlManager,
                        theNode, // 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) {
            return true;

			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', {

        lastList : null,

        lastPrevSib :null,

        fixLists : function(ed){
            var n = ed.selection.getNode();
            ed.plugins.jivemacros.fixBodyParagraphs();
            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]);
                    }
                }
            }

            if(this.lastList != null && this.lastList.parentNode != null){
                // the list is still in the DOM, select the paragraph after it
                var n = this.lastList.nextSibling;
                if(n != null){
                    if(n.nodeType == 1 && n.nodeName.toLowerCase() == "br"){
                        var p = ed.plugins.jivemacros.createEmptyPara(ed.getDoc());
                        n.parentNode.insertBefore(p, n);
                        n.parentNode.removeChild(n);
                        var br = p.childNodes[p.childNodes.length-1];
                        ed.selection.select(br);
                        ed.selection.collapse(true);
                    }
                }
            }else if(this.lastPrevSib != null){
                var n = this.lastPrevSib.nextSibling;
                if(n != null){
                    if(n.nodeType == 1 && n.nodeName.toLowerCase() == "br"){
                        var p = ed.plugins.jivemacros.createEmptyPara(ed.getDoc());
                        n.parentNode.insertBefore(p, n);
                        n.parentNode.removeChild(n);
                        var br = p.childNodes[p.childNodes.length-1];
                        ed.selection.select(br);
                        ed.selection.collapse(true);
                    }
                }
            }
        },

        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);
                }
            }


            var n = ed.selection.getNode();
            var par = null;
            while(n.parentNode != null && (n = ed.dom.getParent(n.parentNode, "ol,ul")) != null){
                par = n;
            };
            n = par;
            if(n != null){
                this.lastList = n;
                this.lastPrevSib = n.previousSibling;
            }else{
                this.lastList = null;
                this.lastPrevSib = null;
            }



            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 && 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, customImage){
            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";
                if (!customImage) {
                    pre.setAttribute('src',src);
                    pre.setAttribute('mce_src',src);
                }
                else {
                    pre.setAttribute('src',customImage);
                    pre.setAttribute('mce_src',customImage);
                }
            }
            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, customImage){
            return this.buildMacro(macro, paramSet, customImage);
        },

        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){
            while(ele != null && $obj(ele)){
                if(ele.nodeName.toLowerCase() == "body") return false;
                // make sure it's an object and not a text node
                if($def(ele.nodeType) && ele.nodeType == 1 && $def(ele.attributes["jivemacro"])){
                    return ele;
                }
                ele = 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();

            var ps = body.getElementsByTagName("p");
            for(var i=0;i<ps.length;i++){
                var p = ps[i];
                var kids = p.getElementsByTagName("p");
                if(kids.length){
                    // move all kids to top level
                    while(p.childNodes.length){
                        p.parentNode.insertBefore(p.childNodes[0], p);
                    }
                    p.parentNode.removeChild(p);
                }
            }

            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){

            if(!tinymce.isIE){
                var nodes = ed.getDoc().getElementsByTagName("td");
                for(var i=0;i<nodes.length;i++){
                    var ele = nodes[i];
                    if(ele.childNodes.length == 0){
                        ele.appendChild(this.createMozBR(ed.getDoc()));
                    }
                }
                var nodes = ed.getDoc().getElementsByTagName("th");
                for(var i=0;i<nodes.length;i++){
                    var ele = nodes[i];
                    if(ele.childNodes.length == 0){
                        ele.appendChild(this.createMozBR(ed.getDoc()));
                    }
                }
            }
            var nodes = ed.getDoc().getElementsByTagName("p");
            for(var i=0;i<nodes.length;i++){
                nodes[i].style.height = "";
            }


            var rte = window.editor.get(ed.id);
            
            if(rte != null && typeof(rte) == "object"){
                var nodes = $j(ed.getBody()).find(".jive_text_macro");
                for(var i=0;i<nodes.length;i++){
                    var ele = nodes[i];
                    if(this.isExactMacro(ele)){
                            var macro = this.getMacroFor(ele);
                            if(macro != null) macro.refresh(rte, ele);
                    }
                }

                var nodes = $j(ed.getBody()).find(".jive_macro");
                for(var i=0;i<nodes.length;i++){
                    var ele = nodes.get(i);
                    if(this.isExactMacro(ele)){
                            var macro = this.getMacroFor(ele);
                            if(macro != null) macro.refresh(rte, ele);
                    }
                }
            }


        },

        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(node.previousSibling != null){
                var n = node.previousSibling;
                var selectionObject = ed.selection;
                var range;
                if (selectionObject.getSel().getRangeAt)
                    range = selectionObject.getSel().getRangeAt(0);
                else { // Safari!
                    range = document.createRange();
                    range.setStart(selectionObject.getSel().anchorNode,selectionObject.getSel().anchorOffset);
                    range.setEnd(selectionObject.getSel().focusNode,selectionObject.getSel().focusOffset);
                }
                var indx = 0;
                while(n.nodeType != 3 && n.nodeType == 1 && n.childNodes && n.childNodes.length != 0){
                    n = n.childNodes[n.childNodes.length -1];
                }

                if(n.nodeType == 3){
                    indx = n.nodeValue.length;
                    console.log("selecting at " + indx);
                    console.log(n);
                    range.setStart(n,indx);
                    range.setEnd(n,indx);
                    ed.selection.setRng(range);
                }else{
                    ed.selection.select(n);
                    ed.selection.collapse(true);
                }
                return;
            }
        },

        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);
        },

        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;

                ed.plugins.jivemacros.removeDuplicateMacros(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){
                // the following is a fix that should
                // only be applied when pressing ctrl+z
                // to undo text
                if(evt.keyCode == 90 && evt.ctrlKey && (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)){
                    while(n.nextSibling != null && n.nextSibling.nodeType == 3 && !tinymce.isIE){
                        n.nodeValue = n.nodeValue + n.nextSibling.nodeValue;
                        n.parentNode.removeChild(n.nextSibling);
                    }
                    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 r3 = new RegExp('(\\xA0' + shorts[i][2] + ')', 'g');
                        var r4 = new RegExp('(\\S\\xA0)', 'g');

                        var index = v.indexOf(shorts[i][0]);
                        if(r1.test(v)){
                            // The emoticon can be contained across multiple text nodes inside of the containing tag
                            // so we need to merge them in order for our tests on whitespace before and after the
                            // emoticon to function properly.
                            if(index >= 0 && index < (v.length-shorts[i][0].length)){
                                var str = v.substr(v.indexOf(shorts[i][0]) + shorts[i][0].length);
                                if(str != v){
                                    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;
                                }
                            }
                            var R2 = r2.test(v);
                            var R3 = r3.test(v);
                            var left = ((!R2) || R3);
                            var prev = null;
                            var temp = null;
                            if(n.previousSibling != null && n.previousSibling.nodeType == 3){
                                temp = n.previousSibling.nodeValue;
                                prev = temp.substr(temp.length-1);
                            }
                            var right = (index > 0 || prev == null || prev != null && r4.test(prev));
                            if(left && right){
                                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);
            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=1;i<3;i++){
                    cell = cells[i];
                    width1 += jive.ext.x.xWidth(cell);
                }
                width1 += 26; // the static width of cells 0, 3 and 4

                toolbar = document.getElementById(edid + "_toolbar2");
                cells = toolbar.rows[0].cells;
                var width2 = 0;
                for(i=6;i<7;i++){
                    cell = cells[i];
                    width2 += jive.ext.x.xWidth(cell);
                }
                width2 += 133; // the static width of cells 0 - 5

                // the 2nd row of buttons has 3 more buttons than the 1st row
                // before the magicspacer. each extra button + 2px padding = 6px
                width2 += 6;

                var width = width1 - width2;

                if(width < -30){
                    // the toolbar has not correctly initialized
                    // if this is the case
                    setTimeout(function(thunk, edid){
                        return function(){ thunk(edid) };
                    }(this.initToolbar, edid), 33);
                    return;
                }else 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);
                    if(width <= 0){
                        jive.ext.x.xDisplayNone(button2);
                    }else{
                        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){
                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: 515 + 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(sel.getNode().nodeName.toLowerCase() == "body" && !before && par.nodeName.toLowerCase() == "html"){
                                par = sel.getNode();
                                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","video"];
                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 + '/upload-video!input.jspa'+_jive_video_picker__url,
                        width : 550 + parseInt(ed.getLang('jivevideo.delta_width', 0)),
                        height : 430 + 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;
            // 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");
            });


			// 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() {
	// 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;
            }

            ed.onInit.add(function() {
/*
                if (!tinymce.isWebKit){
                    try{
                        var headTag = null;
                        for(var i=0;i<ed.getBody().parentNode.childNodes.length;i++){
                            if(ed.getBody().parentNode.childNodes[i].nodeName.toLowerCase() == "head"){
                                headTag = ed.getBody().parentNode.childNodes[i];
                            }
                        }
                        if(headTag == null){
                            headTag = ed.getDoc().getElementsByTagName('head')[0];
                        }
                        var script = ed.getDoc().createElement('style');
                        script.type = 'text/css';
                        script.appendChild(ed.getDoc().createTextNode(ed.settings.non_safari_css));
                        headTag.appendChild(script);
                    }catch(e){  }
                    
                    return;
                }
*/

                t._fixWebKitSpans();

                if (isOldWebKit)
                    t._patchSafari2x(ed);
            });


			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.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');
                            ed.onEndSpelling.dispatch();
						}
					});
				} 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', ' \\t\\r!"#$%&()*+,-./:;<=>?@[\]^_{|}ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½\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,r9, 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;

                        if (r1.test(v) || r2.test(v) || r3.test(v) || r4.test(v)) {
                            v = dom.encode(v);
                            for(var i=0;i<2;i++){
                                v = v.replace(r6, '$1<span class="mceItemHiddenSpellWord">\n$2\n</span>$3');
                                v = v.replace(r7, '$1<span class="mceItemHiddenSpellWord">\n$2\n</span>');
                                v = v.replace(r8, '<span class="mceItemHiddenSpellWord">\n$1\n</span>$2');
                                v = v.replace(r9, '<span class="mceItemHiddenSpellWord">\n$1\n</span>');
                            }
                            v = v.replace(/\n/g, "");

                            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;

			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();
				});

                var vp = dom.getViewPort(ed.getWin());

				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.editor.onEndSpelling.dispatch();
				t.active = 0;
				t._removeWords();

				if (t._menu)
					t._menu.hideMenu();

				if (la)
					t.editor.nodeChanged();
			}
		},

		_sendRPC : function(m, p, cb) {
			var t = this, url = t.editor.getParam("spellchecker_rpc_url", "{backend}");

			if (url == '{backend}') {
				t.editor.setProgressState(0);
				alert('Please specify: spellchecker_rpc_url');
				return;
			}

			JSONRequest.sendRPC({
				url : url,
				method : m,
				params : p,
				success : cb,
				error : function(e, x) {
					t.editor.setProgressState(0);
					t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText));
				}
			});
		}
	});

	// Register plugin
	tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);
})();


/**
 * $Id: editor_plugin_src.js 787 2008-04-10 11:40:57Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	tinymce.create('tinymce.plugins.StylePlugin', {
		init : function(ed, url) {
			// Register commands
			ed.addCommand('mceStyleProps', function() {
				ed.windowManager.open({
					file : url + '/props.htm',
					width : 480 + parseInt(ed.getLang('style.delta_width', 0)),
					height : 320 + parseInt(ed.getLang('style.delta_height', 0)),
					inline : 1
				}, {
					plugin_url : url,
					style_text : ed.selection.getNode().style.cssText
				});
			});

			ed.addCommand('mceSetElementStyle', function(ui, v) {
				if (e = ed.selection.getNode()) {
					ed.dom.setAttrib(e, 'style', v);
					ed.execCommand('mceRepaint');
				}
			});

			ed.onNodeChange.add(function(ed, cm, n) {
				cm.setDisabled('styleprops', n.nodeName === 'BODY');
			});

			// Register buttons
			ed.addButton('styleprops', {title : 'style.desc', cmd : 'mceStyleProps'});
		},

		getInfo : function() {
			return {
				longname : 'Style',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('style', tinymce.plugins.StylePlugin);
})();

/**
 * $Id: editor_plugin_src.js 792 2008-04-10 16:37:29Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	var each = tinymce.each;

	tinymce.create('tinymce.plugins.TablePlugin', {


        putCursorInCell : function(ed, td){
            var remFirst = false;
            if(td.childNodes.length == 0){
                if(td.childNodes.length == 0){
                    td.appendChild(ed.plugins.jivemacros.createEmptyPara(ed.getDoc()));
                    remFirst = true;
                }
            }
            var node = td.childNodes[0];
            var lastEle = td;
            while(node.nodeType == 1 && node.childNodes.length){
                lastEle = node;
                node = node.childNodes[0];
            }
            ed.selection.select(tinymce.isIE ? lastEle : node);
            ed.selection.collapse(true);
            if(remFirst){
                td.removeChild(td.childNodes[0]);
            }
        },

        tempTable : null,

        fixTable : function(){
            if(this.tempTable != null && $def(this.tempTable) && this.tempTable.getAttribute('width')){
                var w = this.tempTable.getAttribute("width");
                if(w.indexOf("%") == -1) w += "px";
                this.tempTable.style.width = w;
                this.tempTable.removeAttribute("width");
                this.tempTable.setAttribute("mce_style", this.tempTable.getAttribute("style"));
            }
            if(this.tempTable.childNodes.length){
                // we need to look for and fix: <table><tbody><tr><tr>....</tr></tr></tbody></table>
                var tbody = this.tempTable.childNodes[0];
                if(tbody.childNodes.length){
                    var tr1 = tbody.childNodes[0];
                    if(tr1.childNodes.length){
                        var tr2 = tr1.childNodes[0];
                        if(tr1.nodeName == tr2.nodeName && tr1.nodeName.toLowerCase() == "tr"){
                            // nested tr's :(
                            while(tr1.childNodes.length){
                                tbody.appendChild(tr1.childNodes[0]);
                            }
                            tbody.removeChild(tr1);
                        }
                    }
                }
            }
        },


        fixInvisibleTableBorders : function(ed, node){
            if(node.nodeType == 1 && node.nodeName.toLowerCase() == "table"){
                if(node.style.borderCollapse == "collapse"){
                    node.style.borderCollapse = "separate";
                    new PeriodicalExecuter(function(node){ return function(pe) {
                        node.style.borderCollapse = "collapse";
                        pe.stop();
                    }}(node), .33);
                }
            }else if(node.nodeType == 1){
                for(var i=0;i<node.childNodes.length;i++){
                    this.fixInvisibleTableBorders(ed, node.childNodes[i]);
                }
            }
        },

        init : function(ed, url) {
			var t = this;

			t.editor = ed;
			t.url = url;
			// Register buttons
			each([
				['table', 'table.props_desc', 'mceInsertTable', true],
				['delete_table', 'table.del', 'mceTableDelete'],
				['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'],
				['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'],
				['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'],
				['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'],
				['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'],
				['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'],
				['row_props', 'table.row_desc', 'mceTableRowProps', true],
				['cell_props', 'table.cell_desc', 'mceTableCellProps', true],
				['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true],
                ['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true],
                ['row_up', 'table.row_up', 'mceTableRowUp', true],
                ['row_down', 'table.row_down', 'mceTableRowDown', true],
                ['col_left', 'table.col_left', 'mceTableColLeft', true],
                ['col_right', 'table.col_right', 'mceTableColRight', true]
			], function(c) {
				ed.addButton(c[0], {title : c[1], cmd : c[2], ui : c[3]});
			});

			ed.onInit.add(function() {

                if(tinymce.isGecko){
                    ed.plugins.table.fixInvisibleTableBorders(ed, ed.getBody());
                }



                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, 'td') || ed.dom.getParent(e, 'th')) {
                            m.removeAll();

                            if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {
//                                m.add({title : 'advanced.link_desc', icon : 'link', cmd : ed.plugins.advlink ? 'mceAdvLink' : 'mceLink', ui : true});
                                m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'});
                                m.addSeparator();
                            }

                            if (el.nodeName == 'IMG' && el.className.indexOf('mceItem') == -1) {
                                m.add({title : 'advanced.image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true});
                                m.addSeparator();
                            }

                            m.add({title : 'table.props_desc', icon : 'table_props', cmd : 'mceInsertTable', ui : true});
                            m.add({title : 'table.del', icon : 'delete_table', cmd : 'mceTableDelete', ui : true});
                            m.addSeparator();

                            // Cell menu
                            sm = m.addMenu({title : 'table.cell'});
                            sm.add({title : 'table.cell_desc', icon : 'cell_props', cmd : 'mceTableCellProps', ui : true});
                            sm.add({title : 'table.split_cells_desc', icon : 'split_cells', cmd : 'mceTableSplitCells', ui : true});
                            sm.add({title : 'table.merge_cells_desc', icon : 'merge_cells', cmd : 'mceTableMergeCells', ui : true});

                            // Row menu
                            sm = m.addMenu({title : 'table.row'});
                            sm.add({title : 'table.row_desc', icon : 'row_props', cmd : 'mceTableRowProps', ui : true});
                            sm.add({title : 'table.row_before_desc', icon : 'row_before', cmd : 'mceTableInsertRowBefore'});
                            sm.add({title : 'table.row_after_desc', icon : 'row_after', cmd : 'mceTableInsertRowAfter'});
                            sm.add({title : 'table.delete_row_desc', icon : 'delete_row', cmd : 'mceTableDeleteRow'});
                            sm.addSeparator();
                            sm.add({title : 'table.cut_row_desc', icon : 'cut', cmd : 'mceTableCutRow'});
                            sm.add({title : 'table.copy_row_desc', icon : 'copy', cmd : 'mceTableCopyRow'});
                            if(ed.tableRowClipboard){
                                sm.add({title : 'table.paste_row_before_desc', icon : 'paste', cmd : 'mceTablePasteRowBefore'});
                                sm.add({title : 'table.paste_row_after_desc', icon : 'paste', cmd : 'mceTablePasteRowAfter'});
                            }

                            // Column menu
                            sm = m.addMenu({title : 'table.col'});
                            sm.add({title : 'table.col_before_desc', icon : 'col_before', cmd : 'mceTableInsertColBefore'});
                            sm.add({title : 'table.col_after_desc', icon : 'col_after', cmd : 'mceTableInsertColAfter'});
                            sm.add({title : 'table.delete_col_desc', icon : 'delete_col', cmd : 'mceTableDeleteCol'});
                        } else if(ed.dom.getParent(e, 'img') == null){
                            m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', ui : true, value : {action : 'insert'}});
                        }
                    });
                }
			});

            ed.onKeyUp.add(function(ed, e) {
                if(e.keyCode == 9 && !e.ctrlKey && !e.altKey){
                    var p = ed.dom.getParent(ed.selection.getNode(), 'td,th,caption');
                    if(p != null){
                        if(e.shiftKey){
                            if(p.previousSibling != null){
                                this.putCursorInCell(ed, p.previousSibling);
                            }else{
                                var row = ed.dom.getParent(p, 'tr');
                                if(row.previousSibling){
                                    this.putCursorInCell(ed, row.previousSibling.cells[row.previousSibling.childNodes.length-1]);
                                }else{
                                    this.putCursorInCell(ed, p);
                                }
                            }
                        }else{
                            if(p.nextSibling != null){
                                this.putCursorInCell(ed, p.nextSibling);
                            }else{
                                var row = ed.dom.getParent(p, 'tr');
                                if(row.nextSibling){
                                    this.putCursorInCell(ed, row.nextSibling.cells[0]);
                                }else{
                                    // insert a row
                                    ed.execCommand("mceTableInsertRowAfter");
                                    this.putCursorInCell(ed, row.nextSibling.cells[0]);
                                }
                            }
                        }
                        ed.nodeChanged();
                        tinymce.dom.Event.cancel(e);
                    }
                }
            }, this);
            // Add undo level when new rows are created using the tab key
            ed.onKeyDown.add(function(ed, e) {
                if (e.keyCode == 9 && !e.ctrlKey && !e.altKey && ed.dom.getParent(ed.selection.getNode(), 'TABLE'))
                    ed.undoManager.add();

                if(e.keyCode == 9 && !e.ctrlKey && !e.altKey){
                    var p = ed.dom.getParent(ed.selection.getNode(), 'td,th,caption');
                    if(p != null){
                        tinymce.dom.Event.cancel(e);
                    }
                }
            });
            ed.onKeyPress.add(function(ed, e) {
                if(e.keyCode == 9 && !e.ctrlKey && !e.altKey){
                    var p = ed.dom.getParent(ed.selection.getNode(), 'td,th,caption');
                    if(p != null){
                        tinymce.dom.Event.cancel(e);
                    }
                }
            });

			// Select whole table is a table border is clicked
			if (!tinymce.isIE) {
				if (ed.getParam('table_selection', true)) {
					ed.onClick.add(function(ed, e) {
						e = e.target;

						if (e.nodeName === 'TABLE')
							ed.selection.select(e);
					});
				}
			}

            ed.onMouseUp.add(function(ed, e){
                var n = ed.selection.getNode();
                var table = ed.dom.getParent(n, 'table');
                if(table){
                    ed.plugins.table.tempTable = table;
                    if(e.target.nodeName.toLowerCase() == "tr" || e.target.nodeName.toLowerCase() == "table"){
                        ed.plugins.table._doExecCommand("fixMissingCells", table);
                    }
                    jive.ext.x.xTimer.set("timeout",ed.plugins.table, "fixTable", 33);
                }
            });

            ed.onNodeChange.add(function(ed, cm, n) {
                var p = ed.dom.getParent(n, 'td,th,caption');
                var cell = ed.dom.getParent(n, 'td,th');
                if(cell && ed.selection.isCollapsed()){
                    var colspan = ed.dom.getAttrib(cell, "colspan");
                    var rowspan = ed.dom.getAttrib(cell, "rowspan");
                    colspan = colspan == "" ? 1 : parseInt(colspan);
                    rowspan = rowspan == "" ? 1 : parseInt(rowspan);

                    cm.setDisabled('split_cells', colspan == 1 && rowspan == 1);
                    cm.setDisabled('merge_cells', !(colspan == 1 && rowspan == 1));
                }else if(cell){
                    cm.setDisabled('split_cells', false);
                    cm.setDisabled('merge_cells', false);
                }else {
                    cm.setDisabled('split_cells', true);
                    cm.setDisabled('merge_cells', true);
                }

                if (p && p.nodeName === 'CAPTION')
					p = null;

				cm.setDisabled('delete_table', !p);
				cm.setDisabled('delete_col', !p);
				cm.setDisabled('delete_table', !p);
				cm.setDisabled('delete_row', !p);
				cm.setDisabled('col_after', !p);
				cm.setDisabled('col_before', !p);
				cm.setDisabled('row_after', !p);
				cm.setDisabled('row_before', !p);
				cm.setDisabled('row_props', !p);
				cm.setDisabled('cell_props', !p);
			});

			// Padd empty table cells
			if (!tinymce.isIE) {
				ed.onBeforeSetContent.add(function(ed, o) {
					if (o.initial)
						o.content = o.content.replace(/<(td|th)([^>]+|)>\s*<\/(td|th)>/g, tinymce.isOpera ? '<$1$2>&nbsp;</$1>' : '<$1$2><br mce_bogus="1" /></$1>');
				});
			}

            ed.onExecCommand.add(function(ed, cmd, ui, val){
                // adam
            });


            // begin edits //
            // https://brewspace.jiveland.com/docs/DOC-10757 //
            //
            // track the node that we are moving from
            ed.onKeyDown.addToTop(function(ed, evt){
                if(ed.getBody().childNodes.length > 0){
                    this.before = ed.selection.getNode();
                }
            }, this);
            //
            // the following code will insert a blank paragraph before
            // the table if the table is the first thing in the <body>
            // also, it will remove a blank paragraph if a <table>
            // immediately follows it at the top of the <body>
            ed.onKeyUp.add(function(ed, evt){
                if(this.before != null && (evt.keyCode == 39 || evt.keyCode == 40)){
                    //
                    // this is for pressing the down or right arrow in the
                    // empty paragraph at the top of the document, to put the
                    // cursor inside a table. in this case, we delete the empty
                    // paragraph above the table
                    var after = ed.selection.getNode();
                    var td = ed.dom.getParent(after, "td");
                    var th = ed.dom.getParent(after, "th");
                    var afterTag = after.tagName.toLowerCase();
                    if(this.before.tagName.toLowerCase() == "p" && (td || th)){ // down or right
                        if(this.before.previousSibling == null){
                            if(this.before.childNodes.length == 1 &&
                               $def(this.before.childNodes[0].tagName) &&
                               this.before.childNodes[0].tagName.toLowerCase() == "br"){
                                var par = ed.dom.getParent(this.before, function(node){ return function(x){ return x == ed.getBody() || x != node; }}(this.before));
                                par.removeChild(this.before);
                                this.before = td ? td : th;
                            }
                        }
                    }
                }
                if(this.before != null && (evt.keyCode == 38)){ // up
                    //
                    // this is when the user presses up or left to try
                    // and move the cursor outside a table when the
                    // table is the very first item in the document.
                    // when they do this, we insert an empty paragraph
                    // tag into the document before the table
                    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 = sel.getNode();
                    var isFirst = this.isFirst(ed.getBody(), node);
                    var isTable = ed.getBody().childNodes[0].tagName.toLowerCase() == "table";
                    var isFrontOfPre = false;
                    if(isTable){
                        var tr = ed.dom.getParent(node, "tr");
                        var tr2 = ed.dom.getParent(this.before, "tr");
                        if(ed.getBody().childNodes[0].rows.length > 0){
                            isFrontOfPre = (tr == ed.getBody().childNodes[0].rows[0]) && tr == tr2;
                        }
                    }
                    if(isFirst && isTable && isFrontOfPre && sel.isCollapsed()){
                        var p = ed.getDoc().createElement('P');
                        var br = this.createMozBR(ed.getDoc());
                        p.appendChild(br);
                        ed.getBody().insertBefore(p, ed.getBody().childNodes[0]);
                        ed.selection.select(br);
                        ed.selection.collapse(true);
                    }
                }
            }, this);
        },
        //
        // this will create the same type of <br>
        // that mozilla automatically generates to
        // fill empty paragraphs. we'll do it
        // explicitly just in case:
        // 1) mozilla doesn't add it sometimes
        // 2) they're not using mozilla
        // to ensure that paragraphs always have
        // some content
        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;
        },
        // checks to see if ele or one of
        // ele's ancestors is the first node
        // in the <body>
        isFirst : function(par, ele){
            if(par.childNodes.length == 0) return false;
            return (par.childNodes[0] == ele || this.isFirst(par.childNodes[0], ele));
        },
        //
        // end edits //

        execCommand : function(cmd, ui, val) {
			var ed = this.editor, b;

			// Is table command
			switch (cmd) {
				case "mceInsertTable":
				case "mceTableRowProps":
				case "mceTableCellProps":
				case "mceTableSplitCells":
				case "mceTableMergeCells":
				case "mceTableInsertRowBefore":
				case "mceTableInsertRowAfter":
				case "mceTableDeleteRow":
				case "mceTableInsertColBefore":
				case "mceTableInsertColAfter":
				case "mceTableDeleteCol":
				case "mceTableCutRow":
				case "mceTableCopyRow":
				case "mceTablePasteRowBefore":
				case "mceTablePasteRowAfter":
				case "mceTableDelete":
                case "JustifyLeft":
                case "JustifyCenter":
                case "JustifyRight":
                case "JustifyFull":
                case "mceTableRowUp":
                case "mceTableRowDown":
                case "mceTableColLeft":
                case "mceTableColRight":
                    ed.execCommand('mceBeginUndoLevel');
					var ret = this._doExecCommand(cmd, ui, val);
					ed.execCommand('mceEndUndoLevel');
					return ret;
			}

			// Pass to next handler in chain
			return false;
		},

		getInfo : function() {
			return {
				longname : 'Tables',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/table',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		},

		// Private plugin internal methods

		/**
		 * Executes the table commands.
		 */
		_doExecCommand : function(command, user_interface, value) {
			var inst = this.editor, ed = inst, url = this.url;
			var focusElm = inst.selection.getNode();
			var trElm = inst.dom.getParent(focusElm, "tr");
			var tdElm = inst.dom.getParent(focusElm, "td,th");
			var tableElm = inst.dom.getParent(focusElm, "table");
			var doc = inst.contentWindow.document;
			var tableBorder = tableElm ? tableElm.getAttribute("border") : "";

			// Get first TD if no TD found
			if (trElm && tdElm == null)
				tdElm = trElm.cells[0];

			function inArray(ar, v) {
				for (var i=0; i<ar.length; i++) {
					// Is array
					if (ar[i].length > 0 && inArray(ar[i], v))
						return true;

					// Found value
					if (ar[i] == v)
						return true;
				}

				return false;
			}

			function select(dx, dy) {
				var td;

				grid = getTableGrid(tableElm);
				dx = dx || 0;
				dy = dy || 0;
				dx = Math.max(cpos.cellindex + dx, 0);
				dy = Math.max(cpos.rowindex + dy, 0);

				// Recalculate grid and select
				inst.execCommand('mceRepaint');
				td = getCell(grid, dy, dx);

				if (td) {
					inst.selection.select(td.firstChild || td);
					inst.selection.collapse(1);
				}
			};

			function makeTD() {
				var newTD = doc.createElement("td");

				if (!tinymce.isIE)
					newTD.innerHTML = '<br mce_bogus="1"/>';
                return newTD;
            }

			function getColRowSpan(td) {
                var colspan = 1, rowspan = 1, v;
				if(v = td.getAttribute("colspan")){
                    if(typeof(v.nodeValue) != "undefined"){
                        colspan = v.nodeValue;
                    }else{
                        colspan = v;
                    }
                }
                if(v = td.getAttribute("rowspan")){
                    if(typeof(v.nodeValue) != "undefined"){
                        rowspan = v.nodeValue;
                    }else{
                        rowspan = v;
                    }
                }
				colspan = parseInt(colspan);
				rowspan = parseInt(rowspan);

				return {colspan : colspan, rowspan : rowspan};
			}

			function getCellPos(grid, td) {
				var x, y;

				for (y=0; y<grid.length; y++) {
					for (x=0; x<grid[y].length; x++) {
						if (grid[y][x] == td)
							return {cellindex : x, rowindex : y};
					}
				}

				return null;
			}

			function getCell(grid, row, col) {
				if (grid[row] && grid[row][col])
					return grid[row][col];

				return null;
			}

            /**
             * returns the youngest node and removes all text nodes
             * @param node
             * @param curr
             */
            function findYoungestAndRemoveText(node, curr){
                if(node.nodeName.toLowerCase() == "br") return curr;
                if(node.childNodes.length == 0) return node;
                for(var i=0;i<node.childNodes.length;i++){
                    if(node.childNodes[i].nodeType == 3){
                        node.removeChild(node.childNodes[i]);
                        i--;
                    }else{
                        curr = findYoungestAndRemoveText(node.childNodes[i], node);
                    }
                }
                if(node.childNodes.length == 0) return node;
                return curr;
            }

            /**
             * replace the text in node with the text parameter
             * @param node
             * @param text
             */
            function fillTextAndSelect(node, text){
                var youngest = findYoungestAndRemoveText(node, node);
                if(youngest.childNodes.length == 0){
                    youngest.appendChild(text);
                }else{
                    youngest.insertBefore(text, youngest.childNodes[youngest.childNodes.length-1]);
                }
                inst.selection.select(text);
                inst.focus();
            }

            function getTableGrid(table) {
				var grid = [], rows = table.rows, x, y, td, sd, xstart, x2, y2;

				for (y=0; y<rows.length; y++) {
					for (x=0; x<rows[y].cells.length; x++) {
						td = rows[y].cells[x];
						sd = getColRowSpan(td);

						// All ready filled
						for (xstart = x; grid[y] && grid[y][xstart]; xstart++) ;

						// Fill box
						for (y2=y; y2<y+sd['rowspan']; y2++) {
							if (!grid[y2])
								grid[y2] = [];

							for (x2=xstart; x2<xstart+sd['colspan']; x2++)
								grid[y2][x2] = td;
						}
					}
				}

				return grid;
			}

			function trimRow(table, tr, td, new_tr) {
				var grid = getTableGrid(table), cpos = getCellPos(grid, td);
				var cells, lastElm;

				// Time to crop away some
				if (new_tr.cells.length != tr.childNodes.length) {
					cells = tr.childNodes;
					lastElm = null;

					for (var x=0; td = getCell(grid, cpos.rowindex, x); x++) {
						var remove = true;
						var sd = getColRowSpan(td);

						// Remove due to rowspan
						if (inArray(cells, td)) {
							new_tr.childNodes[x]._delete = true;
						} else if ((lastElm == null || td != lastElm) && sd.colspan > 1) { // Remove due to colspan
							for (var i=x; i<x+td.colSpan; i++)
								new_tr.childNodes[i]._delete = true;
						}

						if ((lastElm == null || td != lastElm) && sd.rowspan > 1)
							td.rowSpan = sd.rowspan + 1;
                        if(td.rowSpan == 1){
                            td.removeAttribute("rowspan");
                        }
						lastElm = td;
					}

					deleteMarked(tableElm);
				}
			}

			function prevElm(node, name) {
				while ((node = node.previousSibling) != null) {
					if (node.nodeName == name)
						return node;
				}

				return null;
			}

			function nextElm(node, names) {
				var namesAr = names.split(',');

				while ((node = node.nextSibling) != null) {
					for (var i=0; i<namesAr.length; i++) {
						if (node.nodeName.toLowerCase() == namesAr[i].toLowerCase() )
							return node;
					}
				}

				return null;
			}

			function deleteMarked(tbl) {
				if (tbl.rows == 0)
					return;

				var tr = tbl.rows[0];
                for(var i=0;i<tbl.rows.length;i++){
                    var tr = tbl.rows[i];
                    // Delete row
                    if (tr._delete) {
                        tr.parentNode.removeChild(tr);
                        i--;
                        continue;
                    }

                    // Delete cells
                    var td = tr.cells[0];
                    if (td.cells > 1) {
                        do {
                            var nexttd = nextElm(td, "TD,TH");

                            if (td._delete)
                                td.parentNode.removeChild(td);
                        } while ((td = nexttd) != null);
                    }

                }
			}

			function addRows(td_elm, tr_elm, rowspan) {
				// Add rows
                td_elm.rowspan = 1;
                td_elm.setAttribute("rowSpan","1");
                td_elm.removeAttribute("rowSpan");
				var trNext = nextElm(tr_elm, "TR");
				for (var i=1; i<rowspan && trNext; i++) {
					var newTD = doc.createElement("td");
					if (!tinymce.isIE)
						newTD.innerHTML = '<br mce_bogus="1"/>';

					if (tinymce.isIE)
						trNext.insertBefore(newTD, trNext.cells(td_elm.cellIndex));
					else
						trNext.insertBefore(newTD, trNext.cells[td_elm.cellIndex]);

					trNext = nextElm(trNext, "TR");
				}
			}

			function copyRow(doc, table, tr) {
				var grid = getTableGrid(table);
				var newTR = tr.cloneNode(false);
				var cpos = getCellPos(grid, tr.cells[0]);
				var lastCell = null;
				var tableBorder = inst.dom.getAttrib(table, "border");
				var tdElm = null;

				for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
					var newTD = null;

					if (lastCell != tdElm) {
						for (var i=0; i<tr.cells.length; i++) {
							if (tdElm == tr.cells[i]) {
								newTD = tdElm.cloneNode(true);
								break;
							}
						}
					}

					if (newTD == null) {
						newTD = doc.createElement("td");

						if (!tinymce.isIE)
							newTD.innerHTML = '<br mce_bogus="1"/>';
					}

					// Reset col/row span
                    newTD.removeAttribute("colSpan");
                    newTD.removeAttribute("rowspan");

					newTR.appendChild(newTD);

					lastCell = tdElm;
				}

				return newTR;
			}

			// ---- Commands -----

			// Handle commands
			switch (command) {
                case "fixMissingCells":
                    var grid = getTableGrid(user_interface);
                    // first, find the max colspan for the table
                    var cells_per_row = 0;
                    for(var r=0;r<grid.length;r++){
                        if(grid[r].length > cells_per_row) cells_per_row = grid[r].length;
                    }
                    // now add missing cells, if any
                    for(var r=0;r<grid.length;r++){
                        if(grid[r].length < cells_per_row){
                            user_interface.rows[r].appendChild(makeTD());
//                            console.log("added missing cell in row " + (r+1));
                        }
                    }
                    // next, make sure each row is colspan long
                    // now make sure each column is rowspan long
                    return true;
                case "mceTableRowUp" :
                    if (trElm == null)
                        return true;
                    if(trElm.previousSibling != null){
                        var sel = inst.selection.getNode();
                        trElm.parentNode.insertBefore(trElm, trElm.previousSibling);
                        if(sel.childNodes.length && !tinymce.isIE) sel = sel.childNodes[0];
                        inst.selection.select(sel);
                        inst.selection.collapse(true);
                    }
                    return true;
                case "mceTableRowDown" :
                    if (trElm == null)
                        return true;
                    if(trElm.nextSibling != null){
                        var sel = inst.selection.getNode();
                        trElm.parentNode.insertBefore(trElm.nextSibling, trElm);
                        if(sel.childNodes.length && !tinymce.isIE) sel = sel.childNodes[0];
                        inst.selection.select(sel);
                        inst.selection.collapse(true);
                    }
                    return true;
                case "mceTableColRight" :
                    if (!trElm || !tdElm)
                        return true;

                    var grid = getTableGrid(tableElm);
                    var cpos = getCellPos(grid, tdElm);
                    var lastTDElm = null;

                    for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
                        if (tdElm != lastTDElm) {

                            if(tdElm.nextSibling != null){
                                var sel = inst.selection.getNode();
                                tdElm.parentNode.insertBefore(tdElm.nextSibling, tdElm);
                                if(sel.childNodes.length && !tinymce.isIE) sel = sel.childNodes[0];
                                inst.selection.select(sel);
                                inst.selection.collapse(true);
                            }

                            lastTDElm = tdElm;
                        }
                    }
                    return true;
                case "mceTableColLeft" :
                    if (!trElm || !tdElm)
                        return true;

                    var grid = getTableGrid(tableElm);
                    var cpos = getCellPos(grid, tdElm);
                    var lastTDElm = null;

                    for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
                        if (tdElm != lastTDElm) {

                            if(tdElm.previousSibling != null){
                                var sel = inst.selection.getNode();
                                tdElm.parentNode.insertBefore(tdElm, tdElm.previousSibling);
                                if(sel.childNodes.length && !tinymce.isIE) sel = sel.childNodes[0];
                                inst.selection.select(sel);
                                inst.selection.collapse(true);
                            }

                            lastTDElm = tdElm;
                        }
                    }
                    return true;
                case "mceTableRowProps":
					if (trElm == null)
						return true;

					if (user_interface) {
						inst.windowManager.open({
							url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/table/row.htm',
							width : 400 + parseInt(inst.getLang('table.rowprops_delta_width', 0)),
							height : 221 + parseInt(inst.getLang('table.rowprops_delta_height', 0)),
							inline : 1
						}, {
							plugin_url : url
						});
					}

					return true;

				case "mceTableCellProps":
					if (tdElm == null)
						return true;

					if (user_interface) {
						inst.windowManager.open({
							url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/table/cell.htm',
							width : 400 + parseInt(inst.getLang('table.cellprops_delta_width', 0)),
							height : 245 + parseInt(inst.getLang('table.cellprops_delta_height', 0)),
							inline : 1
						}, {
							plugin_url : url
						});
					}

					return true;

				case "mceInsertTable":
					if (user_interface) {
                        if(value && value.action == "insert"){
                            inst.windowManager.open({
                                url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/table/table.htm',
                                width : 420 + parseInt(inst.getLang('table.table_delta_width', 0)),
                                height : 288 + parseInt(inst.getLang('table.table_delta_height', 0)),
                                inline : 1
                            }, {
                                plugin_url : url,
                                action : value ? value.action : 0
                            });
                        }else{
                            inst.windowManager.open({
                                url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/table/table_edit.htm',
                                width : 420 + parseInt(inst.getLang('table.table_delta_width', 0)),
                                height : 258 + parseInt(inst.getLang('table.table_delta_height', 0)),
                                inline : 1
                            }, {
                                plugin_url : url,
                                action : value ? value.action : 0
                            });
                        }
					}

					return true;

				case "mceTableDelete":
					var table = inst.dom.getParent(inst.selection.getNode(), "table");
					if (table) {
						table.parentNode.removeChild(table);
						inst.execCommand('mceRepaint');
					}
					return true;

				case "mceTableSplitCells":
				case "mceTableMergeCells":
				case "mceTableInsertRowBefore":
				case "mceTableInsertRowAfter":
				case "mceTableDeleteRow":
				case "mceTableInsertColBefore":
				case "mceTableInsertColAfter":
				case "mceTableDeleteCol":
				case "mceTableCutRow":
				case "mceTableCopyRow":
				case "mceTablePasteRowBefore":
				case "mceTablePasteRowAfter":
                case "JustifyLeft":
                case "JustifyCenter":
                case "JustifyRight":
                case "JustifyFull":
					// No table just return (invalid command)
					if (!tableElm){
                        switch(command){
                            case "JustifyLeft":
                            case "JustifyCenter":
                            case "JustifyRight":
                            case "JustifyFull":
                                return;
                            default:
                                return true;
                        }
                    }

                    // Table has a tbody use that reference
					// Changed logic by ApTest 2005.07.12 (www.aptest.com)
					// Now lookk at the focused element and take its parentNode.  That will be a tbody or a table.
					if (trElm && tableElm != trElm.parentNode)
						tableElm = ed.dom.getParent(trElm, "table");


                    if (tableElm && (trElm || tinymce.isIE || tinymce.isWebKit)) {
						switch (command) {
                            case "JustifyLeft":
                            case "JustifyCenter":
                            case "JustifyRight":
                            case "JustifyFull":

                            var rows = [];
							var sel = inst.selection.getSel();
							var grid = getTableGrid(tableElm);

                            if (tinymce.isIE || tinymce.isWebKit){
                                var start = ed.dom.getParent(inst.selection.getStart(), "td,th");
                                var end = ed.dom.getParent(inst.selection.getEnd(), "td,th");

                                //console.log("start: " + start);
                                //console.log("end: " + end);
                                if(start && end){
                                    inst.plugins.jiveutil.walkDOMTree(start, end, function(n){
                                        //console.log("walking on: " + n);
                                        if(n.nodeType == 1 && (n.nodeName.toLowerCase() == "td" || n.nodeName.toLowerCase() == "th")){
                                            var td = n;
                                            if(command == "JustifyLeft"){
                                                td.style.textAlign="left";
                                                td.setAttribute("mce_style", td.getAttribute("style"));
                                            }else if(command == "JustifyRight"){
                                                td.style.textAlign="right";
                                                td.setAttribute("mce_style", td.getAttribute("style"));
                                            }else if(command == "JustifyCenter"){
                                                td.style.textAlign="center";
                                                td.setAttribute("mce_style", td.getAttribute("style"));
                                            }else if(command == "JustifyFull"){
                                                td.style.textAlign="justify";
                                                td.setAttribute("mce_style", td.getAttribute("style"));
                                            }
                                        }
                                    });

                                }
                                break;
                            }else if (sel.rangeCount == 1) {
                                var cpos = getCellPos(grid, tdElm);
                                console.log("middle: " + cpos.rowindex + "," + cpos.cellindex);

                                // Get rows and cells
                                var tRows = tableElm.rows;
                                for (var y=cpos.rowindex; y<grid.length; y++) {
                                    var rowCells = [];

                                    for (var x=cpos.cellindex; x<grid[y].length; x++) {
                                        var td = getCell(grid, y, x);

                                        if (td && !inArray(rows, td) && !inArray(rowCells, td)) {
                                            var cp = getCellPos(grid, td);

                                            // Within range
                                            if (cp.cellindex < cpos.cellindex+1 && cp.rowindex < cpos.rowindex+1)
                                                rowCells[rowCells.length] = td;
                                        }
                                    }
                                    if (rowCells.length > 0)
                                        rows[rows.length] = rowCells;
                                }
							} else {
								var cells = [];
								var sel = inst.selection.getSel();
								var lastTR = null;
								var curRow = null;
								var x1 = -1, y1 = -1, x2, y2;

								// Get all selected cells
								for (var i=0; i<sel.rangeCount; i++) {
									var rng = sel.getRangeAt(i);
									var tdElm = rng.startContainer.childNodes[rng.startOffset];

									if (!tdElm)
										break;

									if (tdElm.nodeName == "TD" || tdElm.nodeName == "TH")
										cells[cells.length] = tdElm;
								}

								// Get rows and cells
								var tRows = tableElm.rows;
								for (var y=0; y<tRows.length; y++) {
									var rowCells = [];

									for (var x=0; x<tRows[y].cells.length; x++) {
										var td = tRows[y].cells[x];

										for (var i=0; i<cells.length; i++) {
											if (td == cells[i]) {
												rowCells[rowCells.length] = td;
											}
										}
									}

									if (rowCells.length > 0)
										rows[rows.length] = rowCells;
								}
							}

							// set alignment
							for (var y=0; y<rows.length; y++) {
								for (var x=0; x<rows[y].length; x++) {
									var td = rows[y][x];
									if(command == "JustifyLeft"){
                                        td.style.textAlign="left";
                                        td.setAttribute("mce_style", td.getAttribute("style"));
                                    }else if(command == "JustifyRight"){
                                        td.style.textAlign="right";
                                        td.setAttribute("mce_style", td.getAttribute("style"));
                                    }else if(command == "JustifyCenter"){
                                        td.style.textAlign="center";
                                        td.setAttribute("mce_style", td.getAttribute("style"));
                                    }else if(command == "JustifyFull"){
                                        td.style.textAlign="justify";
                                        td.setAttribute("mce_style", td.getAttribute("style"));
                                    }
                                }
							}
							break;
                            case "mceTableCutRow":
								if (!trElm || !tdElm)
									return true;

								inst.tableRowClipboard = copyRow(doc, tableElm, trElm);
								inst.execCommand("mceTableDeleteRow");
								break;

							case "mceTableCopyRow":
								if (!trElm || !tdElm)
									return true;

								inst.tableRowClipboard = copyRow(doc, tableElm, trElm);
								break;

							case "mceTablePasteRowBefore":
								if (!trElm || !tdElm)
									return true;

								var newTR = inst.tableRowClipboard.cloneNode(true);

								var prevTR = prevElm(trElm, "TR");
								if (prevTR != null)
									trimRow(tableElm, prevTR, prevTR.cells[0], newTR);

								trElm.parentNode.insertBefore(newTR, trElm);
								break;

							case "mceTablePasteRowAfter":
								if (!trElm || !tdElm)
									return true;
								
								var nextTR = nextElm(trElm, "TR");
								var newTR = inst.tableRowClipboard.cloneNode(true);

								trimRow(tableElm, trElm, tdElm, newTR);

								if (nextTR == null)
									trElm.parentNode.appendChild(newTR);
								else
									nextTR.parentNode.insertBefore(newTR, nextTR);

								break;

							case "mceTableInsertRowBefore":
								if (!trElm || !tdElm)
									return true;

								var grid = getTableGrid(tableElm);
								var cpos = getCellPos(grid, tdElm);
								var newTR = doc.createElement("tr");
								var lastTDElm = null;

								cpos.rowindex--;
								if (cpos.rowindex < 0)
									cpos.rowindex = 0;

								// Create cells
								for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
									if (tdElm != lastTDElm) {
										var sd = getColRowSpan(tdElm);

										if (sd['rowspan'] == 1) {
											var newTD = doc.createElement("td");

											if (!tinymce.isIE)
												newTD.innerHTML = '<br mce_bogus="1"/>';

											newTD.colSpan = tdElm.colSpan;

											newTR.appendChild(newTD);
										} else
											tdElm.rowSpan = sd['rowspan'] + 1;
                                        if(tdElm.rowSpan == 1){
                                            tdElm.removeAttribute("rowspan");
                                        }
                                        if(tdElm.colSpan == 1){
                                            tdElm.removeAttribute("colSpan");
                                        }

										lastTDElm = tdElm;
									}
								}

								trElm.parentNode.insertBefore(newTR, trElm);
								select(0, 1);
							break;

							case "mceTableInsertRowAfter":
								if (!trElm || !tdElm)
									return true;

								var grid = getTableGrid(tableElm);
								var cpos = getCellPos(grid, tdElm);
								var newTR = doc.createElement("tr");
								var lastTDElm = null;

								// Create cells
								for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
									if (tdElm != lastTDElm) {
										var sd = getColRowSpan(tdElm);

										if (sd['rowspan'] == 1) {
											var newTD = doc.createElement("td");

											if (!tinymce.isIE)
												newTD.innerHTML = '<br mce_bogus="1"/>';

											newTD.colSpan = tdElm.colSpan;
                                            if(newTD.colSpan == 1){
                                                newTD.removeAttribute("colSpan");
                                            }

											newTR.appendChild(newTD);
										} else
											tdElm.rowSpan = sd['rowspan'] + 1;
                                        if(tdElm.rowSpan == 1){
                                            tdElm.removeAttribute("rowspan");
                                        }

										lastTDElm = tdElm;
									}
								}

								if (newTR.hasChildNodes()) {
									var nextTR = nextElm(trElm, "TR");
									if (nextTR)
										nextTR.parentNode.insertBefore(newTR, nextTR);
									else{
                                        var tbody;
                                        if(tableElm.childNodes.length > 0){
                                            tbody = tableElm.childNodes[0];
                                        }else{
                                            tbody = tableElm;
                                        }
                                        if(tbody.nodeName.toLowerCase() == "tbody"){
                                            tbody.appendChild(newTR);
                                        }else{
                                            tableElm.appendChild(newTR);
                                        }
                                    }
								}

								select(0, 1);
							break;

							case "mceTableDeleteRow":
								if (!trElm || !tdElm)
									return true;

								var grid = getTableGrid(tableElm);
								var cpos = getCellPos(grid, tdElm);

								// Only one row, remove whole table
								if (grid.length == 1) {
									inst.dom.remove(inst.dom.getParent(tableElm, "table"));
									return true;
								}

								// Move down row spanned cells
								var cells = trElm.cells;
								var nextTR = nextElm(trElm, "TR");
								for (var x=0; x<cells.length; x++) {
									if (cells[x].rowSpan > 1) {
										var newTD = cells[x].cloneNode(true);
										var sd = getColRowSpan(cells[x]);

										newTD.rowSpan = sd.rowspan - 1;
                                        if(newTD.rowSpan == 1){
                                            newTD.removeAttribute("rowspan");
                                        }

										var nextTD = nextTR.cells[x];

										if (nextTD == null)
											nextTR.appendChild(newTD);
										else
											nextTR.insertBefore(newTD, nextTD);
									}
								}

								// Delete cells
								var lastTDElm = null;
								for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
									if (tdElm != lastTDElm) {
										var sd = getColRowSpan(tdElm);

										if (sd.rowspan > 1) {
											tdElm.rowSpan = sd.rowspan - 1;
										} else {
											trElm = tdElm.parentNode;

											if (trElm.parentNode)
												trElm._delete = true;
										}
                                        if(trElm.rowSpan == 1){
                                            trElm.removeAttribute("rowspan");
                                        }

										lastTDElm = tdElm;
									}
								}

								deleteMarked(tableElm);

								select(0, -1);
							break;

							case "mceTableInsertColBefore":
								if (!trElm || !tdElm)
									return true;

								var grid = getTableGrid(tableElm);
								var cpos = getCellPos(grid, tdElm);
								var lastTDElm = null;

								for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
									if (tdElm != lastTDElm) {
										var sd = getColRowSpan(tdElm);

										if (sd['colspan'] == 1) {
                                            var newTD = tdElm.cloneNode(true);
//                                            var newTD = doc.createElement(tdElm.nodeName);

                                            if(newTD.nodeName.toLowerCase() == "th"){
                                                // copy the contents of newTD, but change the text to Header #
                                                fillTextAndSelect(newTD, ed.getDoc().createTextNode("Header " + (cpos.cellindex+y+1)));
                                            }else if (!tinymce.isIE){
												newTD.innerHTML = '<br mce_bogus="1"/>';
                                            }else{
                                                newTD.innerHTML = '';
                                            }

                                            newTD.rowSpan = tdElm.rowSpan;
                                            if(newTD.rowSpan == 1){
                                                newTD.removeAttribute("rowspan");
                                            }

											tdElm.parentNode.insertBefore(newTD, tdElm);
										} else
											tdElm.colSpan++;

                                        if(tdElm.colSpan == 1){
                                            tdElm.removeAttribute("colSpan");
                                        }

                                        lastTDElm = tdElm;
									}
								}

								select();
							break;

							case "mceTableInsertColAfter":
								if (!trElm || !tdElm)
									return true;

								var grid = getTableGrid(tableElm);
								var cpos = getCellPos(grid, tdElm);
								var lastTDElm = null;

								for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
									if (tdElm != lastTDElm) {
										var sd = getColRowSpan(tdElm);

										if (sd['colspan'] == 1) {
                                            var newTD = tdElm.cloneNode(true);
//											var newTD = doc.createElement(tdElm.nodeName);
                                            if(newTD.nodeName.toLowerCase() == "th"){
                                                // add 2 to cellindex+y. adding 1 gets the human readable index of the right clicked cell. add 1 more for the next column
                                                fillTextAndSelect(newTD, ed.getDoc().createTextNode("Header " + (cpos.cellindex+y+2)));
                                                console.log("creating header node");
                                            }else if (!tinymce.isIE){
												newTD.innerHTML = '<br mce_bogus="1"/>';
                                            }else{
                                                newTD.innerHTML = '';
                                            }

											newTD.rowSpan = tdElm.rowSpan;
                                            if(newTD.rowSpan == 1){
                                                newTD.removeAttribute("rowspan");
                                            }

											var nextTD = nextElm(tdElm, "TD,TH");
											if (nextTD == null)
												tdElm.parentNode.appendChild(newTD);
											else
												nextTD.parentNode.insertBefore(newTD, nextTD);
										} else
											tdElm.colSpan++;

                                        if(tdElm.colSpan == 1){
                                            tdElm.removeAttribute("colSpan");
                                        }

                                        lastTDElm = tdElm;
									}
								}
                                    

//								select(1);
							break;

							case "mceTableDeleteCol":
								if (!trElm || !tdElm)
									return true;

								var grid = getTableGrid(tableElm);
								var cpos = getCellPos(grid, tdElm);
								var lastTDElm = null;

								// Only one col, remove whole table
								if (grid.length > 1 && grid[0].length <= 1) {
									inst.dom.remove(inst.dom.getParent(tableElm, "table"));
									return true;
								}

								// Delete cells
								for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
									if (tdElm != lastTDElm) {
										var sd = getColRowSpan(tdElm);

										if (sd['colspan'] > 1)
											tdElm.colSpan = sd['colspan'] - 1;
										else {
											if (tdElm.parentNode)
												tdElm.parentNode.removeChild(tdElm);
										}
                                        if(tdElm.colSpan == 1){
                                            tdElm.removeAttribute("colSpan");
                                        }

										lastTDElm = tdElm;
									}
								}

								select(-1);
							break;

						case "mceTableSplitCells":
							if (!trElm || !tdElm)
								return true;

							var spandata = getColRowSpan(tdElm);

							var colspan = spandata["colspan"];
							var rowspan = spandata["rowspan"];

							// Needs splitting
							if (colspan > 1 || rowspan > 1) {
								// Generate cols
								tdElm.colSpan = 1;
								for (var i=1; i<colspan; i++) {
									var newTD = doc.createElement("td");

									if (!tinymce.isIE)
										newTD.innerHTML = '<br mce_bogus="1"/>';

									trElm.insertBefore(newTD, nextElm(tdElm, "TD,TH"));

									if (rowspan > 1)
										addRows(newTD, trElm, rowspan);
								}
                                if(tdElm.colSpan == 1){
                                    tdElm.removeAttribute("colSpan");
                                }

								addRows(tdElm, trElm, rowspan);
                            }
                            if(tdElm.childNodes.length == 0) tdElm.innerHTML = '<br mce_bogus="1"/>';
                            inst.selection.select(tdElm.childNodes[0]);
                            inst.selection.collapse(true);

                            // Apply visual aids
							tableElm = inst.dom.getParent(inst.selection.getNode(), "table");
							break;

						case "mceTableMergeCells":
							var rows = [];
							var sel = inst.selection.getSel();
							var grid = getTableGrid(tableElm);

							if (tinymce.isIE || sel.rangeCount == 1) {
								if (user_interface) {
									// Setup template
									var sp = getColRowSpan(tdElm);

                                    // find out how many cells are allowed to be merged to the right/down
                                    var max_cols = 0; // relative to current colspan
                                    var max_rows = 0; // relative to current rowspan
                                    var temp = tdElm;
                                    while(temp && temp.nextSibling){
                                        var crs = getColRowSpan(tdElm);
                                        max_cols += crs.colspan;
                                        temp = temp.nextSibling;
                                    }
                                    var temp = trElm;
                                    while(temp && temp.nextSibling){
                                        max_rows += 1;
                                        temp = temp.nextSibling;
                                    }

                                    inst.windowManager.open({
										url : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/table/merge_cells.htm',
										width : 360 + parseInt(inst.getLang('table.merge_cells_delta_width', 0)),
										height : 105 + parseInt(inst.getLang('table.merge_cells_delta_height', 0)),
										inline : 1
									}, {
										action : "update",
                                        numcols : sp.colspan,
                                        numrows : sp.rowspan,
                                        maxcols : max_cols,
                                        maxrows : max_rows,
										plugin_url : url
									});

									return true;
								} else {
									var numRows = parseInt(value['numrows']);
									var numCols = parseInt(value['numcols']);

                                    var cpos = getCellPos(grid, tdElm);

									if (("" + numRows) == "NaN")
										numRows = 1;

									if (("" + numCols) == "NaN")
										numCols = 1;

									// Get rows and cells
									var tRows = tableElm.rows;
									for (var y=cpos.rowindex; y<grid.length; y++) {
										var rowCells = [];

										for (var x=cpos.cellindex; x<grid[y].length; x++) {
											var td = getCell(grid, y, x);

											if (td && !inArray(rows, td) && !inArray(rowCells, td)) {
												var cp = getCellPos(grid, td);

												// Within range
												if (cp.cellindex < cpos.cellindex+numCols && cp.rowindex < cpos.rowindex+numRows)
													rowCells[rowCells.length] = td;
											}
										}

										if (rowCells.length > 0)
											rows[rows.length] = rowCells;

										var td = getCell(grid, cpos.rowindex, cpos.cellindex);
										each(ed.dom.select('br', td), function(e, i) {
											if (i > 0 && ed.dom.getAttrib('mce_bogus'))
												ed.dom.remove(e);
										});
									}

									//return true;
								}
							} else {
								var cells = [];
								var sel = inst.selection.getSel();
								var lastTR = null;
								var curRow = null;
								var x1 = -1, y1 = -1, x2, y2;

								// Only one cell selected, whats the point?
								if (sel.rangeCount < 2)
									return true;

								// Get all selected cells
								for (var i=0; i<sel.rangeCount; i++) {
									var rng = sel.getRangeAt(i);
									var tdElm = rng.startContainer.childNodes[rng.startOffset];

									if (!tdElm)
										break;

									if (tdElm.nodeName == "TD" || tdElm.nodeName == "TH")
										cells[cells.length] = tdElm;
								}

								// Get rows and cells
								var tRows = tableElm.rows;
								for (var y=0; y<tRows.length; y++) {
									var rowCells = [];

									for (var x=0; x<tRows[y].cells.length; x++) {
										var td = tRows[y].cells[x];

										for (var i=0; i<cells.length; i++) {
											if (td == cells[i]) {
												rowCells[rowCells.length] = td;
											}
										}
									}

									if (rowCells.length > 0)
										rows[rows.length] = rowCells;
								}

								// Find selected cells in grid and box
								var curRow = [];
								var lastTR = null;
								for (var y=0; y<grid.length; y++) {
									for (var x=0; x<grid[y].length; x++) {
										grid[y][x]._selected = false;

										for (var i=0; i<cells.length; i++) {
											if (grid[y][x] == cells[i]) {
												// Get start pos
												if (x1 == -1) {
													x1 = x;
													y1 = y;
												}

												// Get end pos
												x2 = x;
												y2 = y;

												grid[y][x]._selected = true;
											}
										}
									}
								}

								// Is there gaps, if so deny
								for (var y=y1; y<=y2; y++) {
									for (var x=x1; x<=x2; x++) {
										if (!grid[y][x]._selected) {
											alert("Invalid selection for merge.");
											return true;
										}
									}
								}
							}

							// Validate selection and get total rowspan and colspan
							var rowSpan = 1, colSpan = 1;

							// Validate horizontal and get total colspan
							var lastRowSpan = -1;
							for (var y=0; y<rows.length; y++) {
								var rowColSpan = 0;

								for (var x=0; x<rows[y].length; x++) {
									var sd = getColRowSpan(rows[y][x]);

									rowColSpan += sd['colspan'];

									if (lastRowSpan != -1 && sd['rowspan'] != lastRowSpan) {
										alert("Invalid selection for merge.");
										return true;
									}

									lastRowSpan = sd['rowspan'];
								}

								if (rowColSpan > colSpan)
									colSpan = rowColSpan;

								lastRowSpan = -1;
							}

							// Validate vertical and get total rowspan
							var lastColSpan = -1;
							for (var x=0; x<rows[0].length; x++) {
								var colRowSpan = 0;

								for (var y=0; y<rows.length; y++) {
									var sd = getColRowSpan(rows[y][x]);

									colRowSpan += sd['rowspan'];

									if (lastColSpan != -1 && sd['colspan'] != lastColSpan) {
										alert("Invalid selection for merge.");
										return true;
									}

									lastColSpan = sd['colspan'];
								}

								if (colRowSpan > rowSpan)
									rowSpan = colRowSpan;

								lastColSpan = -1;
							}

							// Setup td
							tdElm = rows[0][0];
							tdElm.rowSpan = rowSpan;
							tdElm.colSpan = colSpan;
                            if(tdElm.rowSpan == 1){
                                tdElm.removeAttribute("rowspan");
                            }
                            if(tdElm.colSpan == 1){
                                tdElm.removeAttribute("colSpan");
                            }

							// Merge cells
							for (var y=0; y<rows.length; y++) {
								for (var x=0; x<rows[y].length; x++) {
									var html = rows[y][x].innerHTML;
									var chk = html.replace(/[ \t\r\n]/g, "");

									if (chk != "<br/>" && chk != "<br>" && chk != '<br mce_bogus="1"/>' && (x+y > 0))
										tdElm.innerHTML += html;

									// Not current cell
									if (rows[y][x] != tdElm && !rows[y][x]._deleted) {
										var cpos = getCellPos(grid, rows[y][x]);
										var tr = rows[y][x].parentNode;

										tr.removeChild(rows[y][x]);
										rows[y][x]._deleted = true;

										// Empty TR, remove it
										if (!tr.hasChildNodes()) {
											tr.parentNode.removeChild(tr);

											var lastCell = null;
											for (var x=0; cellElm = getCell(grid, cpos.rowindex, x); x++) {
												if (cellElm != lastCell && cellElm.rowSpan > 1)
													cellElm.rowSpan--;

												lastCell = cellElm;
											}

											if (tdElm.rowSpan > 1)
												tdElm.rowSpan--;
                                            if(tdElm.rowSpan == 1){
                                                tdElm.removeAttribute("rowspan");
                                            }
										}
									}
								}
							}

							// Remove all but one bogus br
							each(ed.dom.select('br', tdElm), function(e, i) {
								if (i > 0 && ed.dom.getAttrib(e, 'mce_bogus'))
									ed.dom.remove(e);
							});

							break;
						}

						tableElm = inst.dom.getParent(inst.selection.getNode(), "table");
						inst.addVisual(tableElm);
						inst.nodeChanged();
					}

				return true;
			}

			// Pass to next handler in chain
			return false;
		}
	});

	// Register plugin
	tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin);
})();


tinyMCE.addI18n('en.table_dlg',{
general_tab:"General",
advanced_tab:"Advanced",
general_props:"General properties",
advanced_props:"Advanced properties",
rowtype:"Row in table part",
title_new:"Insert table",
title_edit:"Modify table",
width:"Width",
height:"Height",
cols:"Cols",
rows:"Rows",
cellspacing:"Cellspacing",
cellpadding:"Cellpadding",
border:"Border",
align:"Alignment",
align_default:"Default",
align_left:"Left",
align_right:"Right",
align_middle:"Center",
row_title:"Table row properties",
cell_title:"Table cell properties",
cell_type:"Cell type",
valign:"Vertical alignment",
align_top:"Top",
align_bottom:"Bottom",
bordercolor:"Border color",
bgcolor:"Background color",
merge_cells_title:"Merge table cells",
merge_cells_description: "Choose the number of cells to the right and below the current cell that you would like to merge.",
id:"Id",
style:"Style",
langdir:"Language direction",
langcode:"Language code",
mime:"Target MIME type",
ltr:"Left to right",
rtl:"Right to left",
bgimage:"Background image",
summary:"Summary",
td:"Data",
th:"Header",
cell_cell:"Update current cell",
cell_row:"Update all cells in row",
cell_all:"Update all cells in table",
row_row:"Update current row",
row_odd:"Update odd rows in table",
row_even:"Update even rows in table",
row_all:"Update all rows in table",
thead:"Table Head",
tbody:"Table Body",
tfoot:"Table Foot",
scope:"Scope",
rowgroup:"Row Group",
colgroup:"Col Group",
col_limit:"You've exceeded the maximum number of columns of {$cols}.",
row_limit:"You've exceeded the maximum number of rows of {$rows}.",
cell_limit:"You've exceeded the maximum number of cells of {$cells}.",
missing_scope:"Are you sure you want to continue without specifying a scope for this table header cell. Without it, it may be difficult for some users with disabilities to understand the content or data displayed of the table.",
caption:"Table caption",
frame:"Frame",
frame_none:"none",
frame_groups:"groups",
frame_rows:"rows",
frame_cols:"cols",
frame_all:"all",
rules:"Rules",
rules_void:"void",
rules_above:"above",
rules_below:"below",
rules_hsides:"hsides",
rules_lhs:"lhs",
rules_rhs:"rhs",
rules_vsides:"vsides",
rules_box:"box",
rules_border:"border"
});

/**
 * $Id: editor_plugin_src.js 593 2008-02-13 13:00:12Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	tinymce.create('tinymce.plugins.HTML', {

        thunks : new Array(),

        registerToggleFunction : function(thunk){
            this.thunks.push(thunk);
        },

        toggleHTML : function(ed){
            var thunks = ed.plugins.html.thunks;
            for(var i=0;i<thunks.length;i++){
                thunks[i](ed.id);
            }
        },

        prepForTab : false,
        prepForDel :false,
        prepForLargeDel : false,

        init : function(ed, url) {
			var t = this;

			t.editor = ed;
            // Register commands
			ed.addCommand('mceToggleHTML', function(ed, t){
                    return function(){ t.toggleHTML(ed) };
                }(ed, t));
            ed.onBeforeSetContent.add(function(ed, o) {
                o.content = o.content.replace(/  /g, '&nbsp; ');
                o.content = o.content;
            });
            ed.onGetContent.add(function(ed, o) {
                o.content = o.content.replace(/&nbsp;/g, ' ');
                o.content = o.content;
            });
            ed.onKeyDown.add(function(ed, evt){
                this.prepForTab = false;
                this.prepForDel = false;
                this.prepForLargeDel = false;
                if(evt.keyCode == 9 && !evt.ctrlKey && !evt.altKey && !ed.dom.getParent(ed.selection.getNode(), "table") && !ed.dom.getParent(ed.selection.getNode(), "ul,ol")){
                    tinymce.dom.Event.cancel(evt);
                    this.prepForTab = true;
                }
                if(tinymce.isGecko && (evt.keyCode == 8 || evt.keyCode == 46 && !ed.selection.isCollapsed())){
                    // check to see if i'm in an empty node
                    if(ed.selection.isCollapsed()){
                        var n = ed.selection.getNode();
                        if(n.nodeName.toLowerCase() == "img"){
                            // noop, let the browser handle it
                        }else if(n.childNodes.length == 0 || n.childNodes.length == 1 && n.childNodes[0].nodeName.toLowerCase() == "br"){
                            tinymce.dom.Event.cancel(evt);
                            this.prepForDel = true;
                        }
                    }else{
                        this.prepForLargeDel = ed.selection.getStart().previousSibling;
                    }
                    return false;
                }
            }, this);
            ed.onKeyPress.add(function(ed, evt){
                if(this.prepForDel){
                    tinymce.dom.Event.cancel(evt);
                }
                if(evt.keyCode == 9 && !evt.ctrlKey && !evt.altKey && !ed.dom.getParent(ed.selection.getNode(), "table") && !ed.dom.getParent(ed.selection.getNode(), "ul,ol")){
                    tinymce.dom.Event.cancel(evt);
                }
            }, this);
            ed.onKeyUp.add(function(ed, evt){
                if(this.prepForDel){
                    tinymce.dom.Event.cancel(evt);
                    var n = ed.selection.getStart();
                    var newN = n.previousSibling;
                    if(newN){
                        n.parentNode.removeChild(n);
                        while(newN.nodeName.toLowerCase() == "ol" || newN.nodeName.toLowerCase() == "ul"){
                            newN = newN.childNodes[newN.childNodes.length-1];
                        }
                        if(newN.childNodes.length > 0){
                            ed.selection.select(newN.childNodes[newN.childNodes.length-1]);
                            ed.selection.collapse();
                        }else{
                            ed.selection.select(newN);
                            ed.selection.collapse();
                        }
                    }
                }else if(this.prepForLargeDel){
                    tinymce.dom.Event.cancel(evt);
                    window.setTimeout(function(n){ return function() {
                        if(n.childNodes.length > 1){
                            n = n.childNodes[n.childNodes.length-2];
                        }else if(n.childNodes.length == 1) n = n.childNodes[n.childNodes.length-1];{
                            n = n.childNodes[n.childNodes.length-1];
                        }
                        ed.selection.select(n);
                        var collapseToFront = n.nodeType == 1 && n.nodeName.toLowerCase() == "br";
                        ed.selection.collapse(collapseToFront);
                    }}(this.prepForLargeDel), 33);
                }
                if(evt.keyCode == 9 && !evt.ctrlKey && !evt.altKey && this.prepForTab && !ed.dom.getParent(ed.selection.getNode(), "table") && !ed.dom.getParent(ed.selection.getNode(), "ul,ol")){
                    tinymce.dom.Event.cancel(evt);
                    ed.selection.setContent("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
                }
            }, this);


            // Register buttons
			ed.addButton('html', {title : 'html.desc', cmd : 'mceToggleHTML'});

		},

		getInfo : function() {
			return {
				longname : 'HTML mode',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullpage',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}

    });

	// Register plugin
	tinymce.PluginManager.add('html', tinymce.plugins.HTML);
})();

tinyMCE.addI18n('en.html',{
desc:"Toggle HTML mode"
});

/**
 * $Id: editor_plugin_src.js 609 2008-02-18 16:19:27Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	tinymce.create('tinymce.plugins.Save', {
		init : function(ed, url) {
			var t = this;

			t.editor = ed;

			// Register commands
			ed.addCommand('mceSave', t._save, t);
			ed.addCommand('mceCancel', t._cancel, t);

			// Register buttons
			ed.addButton('save', {title : 'save.save_desc', cmd : 'mceSave'});
			ed.addButton('cancel', {title : 'save.cancel_desc', cmd : 'mceCancel'});

			ed.onNodeChange.add(t._nodeChange, t);
			ed.addShortcut('ctrl+s', ed.getLang('save.save_desc'), 'mceSave');
		},

		getInfo : function() {
			return {
				longname : 'Save',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/save',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		},

		// Private methods

		_nodeChange : function(ed, cm, n) {
			var ed = this.editor;

			if (ed.getParam('save_enablewhendirty')) {
				cm.setDisabled('save', !ed.isDirty());
				cm.setDisabled('cancel', !ed.isDirty());
			}
		},

		// Private methods

		_save : function() {
			var ed = this.editor, formObj, os, i, elementId;

			formObj = tinymce.DOM.get(ed.id).form || tinymce.DOM.getParent(ed.id, 'form');

			if (ed.getParam("save_enablewhendirty") && !ed.isDirty())
				return true;

			tinyMCE.triggerSave();

			// Use callback instead
			if (os = ed.getParam("save_onsavecallback")) {
				if (ed.execCallback('save_onsavecallback', ed)) {
					ed.startContent = tinymce.trim(ed.getContent({format : 'raw'}));
					ed.nodeChanged();
				}

				return;
			}

			if (formObj) {
				ed.isNotDirty = true;

				if (formObj.onsubmit == null || formObj.onsubmit() != false)
					formObj.submit();

				ed.nodeChanged();
			} else
				ed.windowManager.alert("Error: No form element found.");

			return true;
		},

		_cancel : function() {
			var ed = this.editor, os, h = tinymce.trim(ed.startContent);

			// Use callback instead
			if (os = ed.getParam("save_oncancelcallback")) {
				ed.execCallback('save_oncancelcallback', ed);
				return;
			}

			ed.setContent(h);
			ed.undoManager.clear();
			ed.nodeChanged();
		}
	});

	// Register plugin
	tinymce.PluginManager.add('save', tinymce.plugins.Save);
})();

/**
 * $Id: editor_plugin_src.js 677 2008-03-07 13:52:41Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	tinymce.create('tinymce.plugins.AdvancedImagePlugin', {
		init : function(ed, url) {
			// Register commands
			ed.addCommand('mceAdvImage', function() {
				// Internal image object like a flash placeholder
				if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1)
					return;

				ed.windowManager.open({
					file : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/advimage/image.htm',
					width : 480 + parseInt(ed.getLang('advimage.delta_width', 0)),
					height : 225 + parseInt(ed.getLang('advimage.delta_height', 0)),
					inline : 1
				}, {
					plugin_url : url
				});
			});

			// Register buttons
			ed.addButton('image', {
				title : 'advimage.image_desc',
				cmd : 'mceAdvImage'
			});
		},

		getInfo : function() {
			return {
				longname : 'Advanced image',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advimage',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('advimage', tinymce.plugins.AdvancedImagePlugin);
})();

tinyMCE.addI18n('en.advimage_dlg',{
tab_general:"General",
tab_appearance:"Appearance",
tab_advanced:"Advanced",
general:"General",
title:"Title",
preview:"Preview",
constrain_proportions:"Constrain proportions",
langdir:"Language direction",
langcode:"Language code",
long_desc:"Long description link",
style:"Style",
classes:"Classes",
ltr:"Left to right",
rtl:"Right to left",
id:"Id",
map:"Image map",
swap_image:"Swap image",
alt_image:"Alternative image",
mouseover:"for mouse over",
mouseout:"for mouse out",
misc:"Miscellaneous",
example_img:"Appearance preview image",
missing_alt:"Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.",
dialog_title:"Insert Image",
src:"URL",
alt:"Image description",
list:"Image list",
border:"Border",
dimensions:"Dimensions",
vspace:"Vertical space",
hspace:"Horizontal space",
align:"Alignment",
align_baseline:"Baseline",
align_top:"Top",
align_middle:"Middle",
align_bottom:"Bottom",
align_texttop:"Text top",
align_textbottom:"Text bottom",
align_inline:"Normal",
align_nowrap:"No Wrap",
align_left:"Left",
align_right:"Right",
image_list:"Image list",
insert_desc : "Use the form below to insert a remote image from a web page into your discussion, document, blog post or comment.",
from_the_web: "From the Web",
example : "example"
});

/**
 * $Id: editor_plugin_src.js 539 2008-01-14 19:08:58Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	tinymce.create('tinymce.plugins.AdvancedLinkPlugin', {
		init : function(ed, url) {
			this.editor = ed;

			// Register commands
			ed.addCommand('mceAdvLink', function() {
				var se = ed.selection;

				// No selection and not in link
				if (se.isCollapsed() && !ed.dom.getParent(se.getNode(), 'A'))
					return;

				ed.windowManager.open({
					file : CS_BASE_URL + '/resources/scripts/tiny_mce3/plugins/advlink/link.htm',
					width : 480 + parseInt(ed.getLang('advlink.delta_width', 0)),
					height : 400 + parseInt(ed.getLang('advlink.delta_height', 0)),
					inline : 1
				}, {
					plugin_url : url
				});
			});

			// Register buttons
			ed.addButton('link', {
				title : 'advlink.link_desc',
				cmd : 'mceAdvLink'
			});

			ed.addShortcut('ctrl+k', 'advlink.advlink_desc', 'mceAdvLink');

			ed.onNodeChange.add(function(ed, cm, n, co) {
				cm.setDisabled('link', co && n.nodeName != 'A');
				cm.setActive('link', n.nodeName == 'A' && !n.name);
			});
		},

		getInfo : function() {
			return {
				longname : 'Advanced link',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlink',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('advlink', tinymce.plugins.AdvancedLinkPlugin);
})();

tinyMCE.addI18n('en.advlink_dlg',{
title:"Insert/edit link",
url:"Link URL",
target:"Target",
titlefield:"Title",
is_email:"The URL you entered seems to be an email address, do you want to add the required mailto: prefix?",
is_external:"The URL you entered seems to external link, do you want to add the required http:// prefix?",
list:"Link list",
general_tab:"General",
popup_tab:"Popup",
events_tab:"Events",
advanced_tab:"Advanced",
general_props:"General properties",
popup_props:"Popup properties",
event_props:"Events",
advanced_props:"Advanced properties",
popup_opts:"Options",
anchor_names:"Anchors",
target_same:"Open in this window / frame",
target_parent:"Open in parent window / frame",
target_top:"Open in top frame (replaces all frames)",
target_blank:"Open in new window",
popup:"Javascript popup",
popup_url:"Popup URL",
popup_name:"Window name",
popup_return:"Insert 'return false'",
popup_scrollbars:"Show scrollbars",
popup_statusbar:"Show status bar",
popup_toolbar:"Show toolbars",
popup_menubar:"Show menu bar",
popup_location:"Show location bar",
popup_resizable:"Make window resizable",
popup_dependent:"Dependent (Mozilla/Firefox only)",
popup_size:"Size",
popup_position:"Position (X/Y)",
id:"Id",
style:"Style",
classes:"Classes",
target_name:"Target name",
langdir:"Language direction",
target_langcode:"Target language",
langcode:"Language code",
encoding:"Target character encoding",
mime:"Target MIME type",
rel:"Relationship page to target",
rev:"Relationship target to page",
tabindex:"Tabindex",
accesskey:"Accesskey",
ltr:"Left to right",
rtl:"Right to left",
link_list:"Link list"
});

/**
 * $Id: editor_plugin_src.js 520 2008-01-07 16:30:32Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	tinymce.create('tinymce.plugins.IESpell', {
		init : function(ed, url) {
			var t = this, sp;

			if (!tinymce.isIE)
				return;

			t.editor = ed;

			// Register commands
			ed.addCommand('mceIESpell', function() {
				try {
					sp = new ActiveXObject("ieSpell.ieSpellExtension");
					sp.CheckDocumentNode(ed.getDoc().documentElement);
				} catch (e) {
					if (e.number == -2146827859) {
						ed.windowManager.confirm(ed.getLang("iespell.download"), function(s) {
							if (s)
								window.open('http://www.iespell.com/download.php', 'ieSpellDownload', '');
						});
					} else
						ed.windowManager.alert("Error Loading ieSpell: Exception " + e.number);
				}
			});

			// Register buttons
			ed.addButton('iespell', {title : 'iespell.iespell_desc', cmd : 'mceIESpell'});
		},

		getInfo : function() {
			return {
				longname : 'IESpell (IE Only)',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/iespell',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}
	});

	// Register plugin
	tinymce.PluginManager.add('iespell', tinymce.plugins.IESpell);
})();

/**
 * $Id: editor_plugin_src.js 776 2008-04-08 17:00:39Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	var DOM = tinymce.DOM, Element = tinymce.dom.Element, Event = tinymce.dom.Event, each = tinymce.each, is = tinymce.is;

	tinymce.create('tinymce.plugins.InlinePopups', {
		init : function(ed, url) {
			// Replace window manager
			ed.onBeforeRenderUI.add(function() {
				ed.windowManager = new tinymce.InlineWindowManager(ed);
                DOM.loadCSS(ed.baseURI.toAbsolute('plugins/inlinepopups/skins/' + (ed.settings.inlinepopups_skin || 'clearlooks2') + "/window.css"));
			});
		},

		getInfo : function() {
			return {
				longname : 'InlinePopups',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/inlinepopups',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}
	});

	tinymce.create('tinymce.InlineWindowManager:tinymce.WindowManager', {
		InlineWindowManager : function(ed) {
			var t = this;

			t.parent(ed);
			t.zIndex = 300000;
			t.count = 0;
		},

        doc : null,

        open : function(f, p) {
			var t = this, id, opt = '', ed = t.editor, dw = 0, dh = 0, vp, po, mdf, clf, we, w, u;
            var offsetLeft=0, offsetTop = 0;
            var w = window;
            try{
                while(w.parent && w != w.parent && typeof(w.parent.jive) != "undefined"){
                    if(w.frameElement){
                        offsetLeft += jive.ext.x.xPageX(w.frameElement);
                        offsetTop += jive.ext.x.xPageY(w.frameElement);
                    }
                    w = w.parent;
                    w.tinymce = window.tinymce;
                    w.tinyMCE = window.tinyMCE;
                }
            }catch(e){ /* permission denied b/c clearspace is in an iframe probably */ }
            ed.plugins.inlinepopups.doc = w.document;

            var doc = ed.plugins.inlinepopups.doc;
            var dom = new tinymce.dom.DOMUtils(doc);

            if(!$def(ed.plugins.inlinepopups.overlay)){
                var d = doc.createElement('DIV');
                d.className = "overlay";
                d.setAttribute("class","overlay");
                doc.body.appendChild(d);
                ed.plugins.inlinepopups.overlay = d;
                jive.ext.x.xAddEventListener(d, "click", function(){
                    var editors = window.editor.toArray(function(ed){ return $def(ed.closeAllDialogs); });
                    for(var i=0;i<editors.length;i++){
                        editors[i].closeAllDialogs();
                    }
                })

            }


            f = f || {};
			p = p || {};

			// Run native windows
			if (!f.inline)
				return t.parent(f, p);

			// Only store selection if the type is a normal window
			if (!f.type)
				t.bookmark = ed.selection.getBookmark('simple');

			id = dom.uniqueId();
			vp = dom.getViewPort();
			f.width = parseInt(f.width || 320);
			f.height = parseInt(f.height || 240);
			f.min_width = parseInt(f.min_width || 150);
			f.min_height = parseInt(f.min_height || 100);
			f.max_width = parseInt(f.max_width || 2000);
			f.max_height = parseInt(f.max_height || 2000);
			f.left = f.left || Math.round(Math.max(vp.x, vp.x + (vp.w / 2.0) - (f.width / 2.0)));
			f.top = f.top || Math.round(Math.max(vp.y, vp.y + (vp.h / 2.0) - (f.height / 2.0)));
            f.left += offsetLeft;
            f.top += offsetTop;
            f.movable = f.resizable = true;
			p.mce_width = f.width;
			p.mce_height = f.height;
			p.mce_inline = true;
			p.mce_window_id = id;
			p.mce_auto_focus = f.auto_focus;

			// Transpose
//			po = DOM.getPos(ed.getContainer());
//			f.left -= po.x;
//			f.top -= po.y;

			t.features = f;
			t.params = p;
			t.onOpen.dispatch(t, f, p);

			if (f.type) {
				opt += ' mceModal';

				if (f.type)
					opt += ' mce' + f.type.substring(0, 1).toUpperCase() + f.type.substring(1);

				f.resizable = false;
			}

			if (f.statusbar)
				opt += ' mceStatusbar';

			if (f.resizable)
				opt += ' mceResizable';

			if (f.minimizable)
				opt += ' mceMinimizable';

			if (f.maximizable)
				opt += ' mceMaximizable';

			if (f.movable)
				opt += ' mceMovable';

			// Create DOM objects
			t._addAll(doc.body,
				['div', {id : id, 'class' : ed.settings.inlinepopups_skin || 'clearlooks2', style : 'width:100px;height:100px'},
					['div', {id : id + '_wrapper', 'class' : 'mceWrapper' + opt},
						['div', {id : id + '_top', 'class' : 'mceTop'}, 
							['div', {'class' : 'mceLeft'}],
							['div', {'class' : 'mceCenter'}],
							['div', {'class' : 'mceRight'}],
							['span', {id : id + '_title'}, f.title || '']
						],

						['div', {id : id + '_middle', 'class' : 'mceMiddle'}, 
							['div', {id : id + '_left', 'class' : 'mceLeft'}],
							['span', {id : id + '_content'}],
							['div', {id : id + '_right', 'class' : 'mceRight'}]
						],

						['div', {id : id + '_bottom', 'class' : 'mceBottom'},
							['div', {'class' : 'mceLeft'}],
							['div', {'class' : 'mceCenter'}],
							['div', {'class' : 'mceRight'}],
							['span', {id : id + '_status'}, 'Content']
						],

						['a', {'class' : 'mceMove', tabindex : '-1', href : 'javascript:;'}],
						['a', {'class' : 'mceMin', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
						['a', {'class' : 'mceMax', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
						['a', {'class' : 'mceMed', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
						['a', {'class' : 'mceClose', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
						['a', {id : id + '_resize_n', 'class' : 'mceResize mceResizeN', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_s', 'class' : 'mceResize mceResizeS', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_w', 'class' : 'mceResize mceResizeW', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_e', 'class' : 'mceResize mceResizeE', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_nw', 'class' : 'mceResize mceResizeNW', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_ne', 'class' : 'mceResize mceResizeNE', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_sw', 'class' : 'mceResize mceResizeSW', tabindex : '-1', href : 'javascript:;'}],
						['a', {id : id + '_resize_se', 'class' : 'mceResize mceResizeSE', tabindex : '-1', href : 'javascript:;'}]
					]
				]
			);

			dom.setStyles(id, {top : -10000, left : -10000});

			// Fix gecko rendering bug, where the editors iframe messed with window contents
			if (tinymce.isGecko)
				dom.setStyle(id, 'overflow', 'auto');

			// Measure borders
			if (!f.type) {
				dw += dom.get(id + '_left').clientWidth;
				dw += dom.get(id + '_right').clientWidth;
				dh += dom.get(id + '_top').clientHeight;
				dh += dom.get(id + '_bottom').clientHeight;
			}

			// Resize window
			dom.setStyles(id, {top : f.top, left : f.left, width : f.width + dw, height : f.height + dh});

			u = f.url || f.file;
			if (u) {
				if (tinymce.relaxedDomain)
					u += (u.indexOf('?') == -1 ? '?' : '&') + 'mce_rdomain=' + tinymce.relaxedDomain;

				u = tinymce._addVer(u);
			}

			if (!f.type) {
				dom.add(id + '_content', 'iframe', {id : id + '_ifr', src : 'javascript:""', frameBorder : 0, style : 'border:0;width:10px;height:10px'});
				dom.setStyles(id + '_ifr', {width : f.width, height : f.height});
				dom.setAttrib(id + '_ifr', 'src', u);
			} else {
                try{
                    t._addAll($(id + '_wrapper'), ['input', {id : id + '_ok', 'class' : 'mceOk', value : 'Ok', type :'button'}, 'Ok']);
                }catch(e){
                    dom.add(id + '_wrapper', 'input', {id : id + '_ok', 'class' : 'mceOk', value : 'Ok', type :'button'}, 'Ok');
                }

				if (f.type == 'confirm')
					dom.add(id + '_wrapper', 'input', {'class' : 'mceCancel', value : 'Cancel', type :'button'}, 'Cancel');

				dom.add(id + '_middle', 'div', {'class' : 'mceIcon'});
				dom.setHTML(id + '_content', f.content.replace('\n', '<br />'));
			}

			// Register events
			mdf = Event.add(id, 'mousedown', function(e) {
				var n = e.target, w, vp;

				w = t.windows[id];
				t.focus(id);

				if (n.nodeName == 'A' || n.nodeName == 'a') {
                    if (n.className == 'mceClose') {
                        t.close(null, id);
                        return Event.cancel(e);

                    }else if (n.className == 'mceMax') {
						w.oldPos = w.element.getXY();
						w.oldSize = w.element.getSize();

						vp = dom.getViewPort();

						// Reduce viewport size to avoid scrollbars
						vp.w -= 2;
						vp.h -= 2;

						w.element.moveTo(vp.x, vp.y);
						w.element.resizeTo(vp.w, vp.h);
						dom.setStyles(id + '_ifr', {width : vp.w - w.deltaWidth, height : vp.h - w.deltaHeight});
						dom.addClass(id + '_wrapper', 'mceMaximized');
					} else if (n.className == 'mceMed') {
						// Reset to old size
						w.element.moveTo(w.oldPos.x, w.oldPos.y);
						w.element.resizeTo(w.oldSize.w, w.oldSize.h);
						w.iframeElement.resizeTo(w.oldSize.w - w.deltaWidth, w.oldSize.h - w.deltaHeight);

						dom.removeClass(id + '_wrapper', 'mceMaximized');
					} else if (n.className == 'mceMove')
						return t._startDrag(id, e, n.className);
					else if (dom.hasClass(n, 'mceResize'))
						return t._startDrag(id, e, n.className.substring(13));
				}
			});

            clf = function(e){
                var n = e.target || e.srcElement;
                if($def(n) && n){
                    t.focus(id);
                    if (n.nodeName == 'INPUT' || n.nodeName == 'input' || n.nodeName == 'A' || n.nodeName == 'a') {
                        switch (n.className) {
                            case 'mceClose':
                                    var editors = window.editor.toArray(function(ed){ return $def(ed.closeAllDialogs); });
                                    for(var i=0;i<editors.length;i++){
                                        editors[i].closeAllDialogs();
                                    }
//                                t.close(null, id);
                                return Event.cancel(e);

                            case 'mceOk':
                            case 'mceCancel':
                                f.button_func(n.className == 'mceOk');
                                return Event.cancel(e);
                        }
                    }
                }
            }

            jive.ext.x.xAddEventListener(doc.getElementById(id), "click", clf)
            Event.add(id, 'click', clf);

			// Add window
			t.windows = t.windows || {};
			w = t.windows[id] = {
				id : id,
				mousedown_func : mdf,
				click_func : clf,
				element : new Element(id, {blocker : 1, container : ed.getContainer()}),
				iframeElement : new Element(id + '_ifr'),
				features : f,
				deltaWidth : dw,
				deltaHeight : dh
			};

			w.iframeElement.on('focus', function() {
				t.focus(id);
			});

			// Setup blocker
			if (t.count == 0 && t.editor.getParam('dialog_type') == 'modal') {
				dom.add(dom.doc.body, 'div', {
					id : 'mceModalBlocker',
					'class' : (t.editor.settings.inlinepopups_skin || 'clearlooks2') + '_modalBlocker',
					style : {left : vp.x, top : vp.y, zIndex : t.zIndex - 1}
				});

				dom.show('mceModalBlocker'); // Reduces flicker in IE
			} else
				dom.setStyle('mceModalBlocker', 'z-index', t.zIndex - 1);

			t.focus(id);
			t._fixIELayout(id, 1);

			// Focus ok button
			if (dom.get(id + '_ok'))
				dom.get(id + '_ok').focus();

			t.count++;

            ed.plugins.inlinepopups.overlay.style.height = jive.ext.x.xDocHeight(doc) + "px";
            ed.plugins.inlinepopups.overlay.style.display = "block";

            return w;
		},

		focus : function(id) {
            var ed = tinyMCE.activeEditor;
            if(ed.plugins.inlinepopups.doc == null){
                var w = window;
                while(w.parent && w != w.parent){
                    w = w.parent;
                    if(!$def(w.tinymce)){
                        w.tinymce = window.tinymce;
                        w.tinyMCE = window.tinyMCE;
                    }
                }
                ed.plugins.inlinepopups.doc = w.document;
            }

            var t = this, w = t.windows[id], doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);

            if($def(w) && w){
                w.zIndex = this.zIndex++;
                w.element.setStyle('zIndex', w.zIndex);
                w.element.update();
            }

			id = id + '_wrapper';
			dom.removeClass(t.lastId, 'mceFocus');
			dom.addClass(id, 'mceFocus');
			t.lastId = id;
		},

		_addAll : function(te, ne) {
			var i, n, t = this, doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);

			if (is(ne, 'string'))
				te.appendChild(dom.doc.createTextNode(ne));
			else if (ne.length) {
				te = te.appendChild(dom.create(ne[0], ne[1]));

				for (i=2; i<ne.length; i++)
					t._addAll(te, ne[i]);
			}
		},

		_startDrag : function(id, se, ac) {
			var t = this, mu, mm, doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
            var eb, w = t.windows[id], we = w.element, sp = we.getXY(), p, sz, ph, cp, vp, sx, sy, sex, sey, dx, dy, dw, dh;

			// Get positons and sizes
//			cp = DOM.getPos(t.editor.getContainer());
			cp = {x : 0, y : 0};
			vp = dom.getViewPort();

			// Reduce viewport size to avoid scrollbars while dragging
			vp.w -= 2;
			vp.h -= 2;

			sex = se.screenX;
			sey = se.screenY;
			dx = dy = dw = dh = 0;

			// Handle mouse up
			mu = Event.add(doc, 'mouseup', function(e) {
				Event.remove(doc, 'mouseup', mu);
				Event.remove(doc, 'mousemove', mm);

				if (eb)
					eb.remove();

				we.moveBy(dx, dy);
				we.resizeBy(dw, dh);
				sz = we.getSize();
				dom.setStyles(id + '_ifr', {width : sz.w - w.deltaWidth, height : sz.h - w.deltaHeight});
				t._fixIELayout(id, 1);

				return Event.cancel(e);
			});

			if (ac != 'Move')
				startMove();

			function startMove() {
                var doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
                if (eb)
					return;

				t._fixIELayout(id, 0);

				// Setup event blocker
				dom.add(d.body, 'div', {
					id : 'mceEventBlocker',
					'class' : 'mceEventBlocker ' + (t.editor.settings.inlinepopups_skin || 'clearlooks2'),
					style : {left : vp.x, top : vp.y, zIndex : t.zIndex + 1}
				});
				eb = new Element('mceEventBlocker');
				eb.update();

				// Setup placeholder
				p = we.getXY();
				sz = we.getSize();
				sx = cp.x + p.x - vp.x;
				sy = cp.y + p.y - vp.y;
				dom.add(eb.get(), 'div', {id : 'mcePlaceHolder', 'class' : 'mcePlaceHolder', style : {left : sx, top : sy, width : sz.w, height : sz.h}});
				ph = new Element('mcePlaceHolder');
			};

			// Handle mouse move/drag
			mm = Event.add(d, 'mousemove', function(e) {
				var x, y, v;

				startMove();

				x = e.screenX - sex;
				y = e.screenY - sey;

				switch (ac) {
					case 'ResizeW':
						dx = x;
						dw = 0 - x;
						break;

					case 'ResizeE':
						dw = x;
						break;

					case 'ResizeN':
					case 'ResizeNW':
					case 'ResizeNE':
						if (ac == "ResizeNW") {
							dx = x;
							dw = 0 - x;
						} else if (ac == "ResizeNE")
							dw = x;

						dy = y;
						dh = 0 - y;
						break;

					case 'ResizeS':
					case 'ResizeSW':
					case 'ResizeSE':
						if (ac == "ResizeSW") {
							dx = x;
							dw = 0 - x;
						} else if (ac == "ResizeSE")
							dw = x;

						dh = y;
						break;

					case 'mceMove':
						dx = x;
						dy = y;
						break;
				}

				// Boundary check
				if (dw < (v = w.features.min_width - sz.w)) {
					if (dx !== 0)
						dx += dw - v;

					dw = v;
				}
	
				if (dh < (v = w.features.min_height - sz.h)) {
					if (dy !== 0)
						dy += dh - v;

					dh = v;
				}

				dw = Math.min(dw, w.features.max_width - sz.w);
				dh = Math.min(dh, w.features.max_height - sz.h);
				dx = Math.max(dx, vp.x - (sx + vp.x));
				dy = Math.max(dy, vp.y - (sy + vp.y));
				dx = Math.min(dx, (vp.w + vp.x) - (sx + sz.w + vp.x));
				dy = Math.min(dy, (vp.h + vp.y) - (sy + sz.h + vp.y));

				// Move if needed
				if (dx + dy !== 0) {
					if (sx + dx < 0)
						dx = 0;
	
					if (sy + dy < 0)
						dy = 0;

					ph.moveTo(sx + dx, sy + dy);
				}

				// Resize if needed
				if (dw + dh !== 0)
					ph.resizeTo(sz.w + dw, sz.h + dh);

				return Event.cancel(e);
			});

			return Event.cancel(se);
		},

		resizeBy : function(dw, dh, id) {
			var w = this.windows[id];

			if (w) {
				w.element.resizeBy(dw, dh);
				w.iframeElement.resizeBy(dw, dh);
			}
		},

		close : function(win, id) {
            var doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
			var t = this, w, ix = 0, fw;

			t.count--;

			if (t.count == 0)
				dom.remove('mceModalBlocker');

			// Probably not inline
			if (!id && win) {
				t.parent(win);
				return;
			}

            if (w = t.windows[id]) {
                tinyMCE.activeEditor.plugins.inlinepopups.overlay.style.display = "none";
                t.onClose.dispatch(t);
				Event.remove(doc, 'mousedown', w.mousedownFunc);
				Event.remove(doc, 'click', w.clickFunc);
				Event.clear(id);
				Event.clear(id + '_ifr');

				dom.setAttrib(id + '_ifr', 'src', 'javascript:""'); // Prevent leak
                var ele = doc.getElementById(w.id);
                try{
                    w.element.remove();
                    ele.parentNode.removeChild(ele);
                }catch(e){ }

                delete t.windows[id];
            }else{
                // console.log("can't close the window.")
            }
            tinyMCE.activeEditor.focus();
		},

		setTitle : function(ti, id) {
            var doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
			var e;

			if (e = dom.get(id + '_title'))
				e.innerHTML = dom.encode(ti);
		},

		alert : function(txt, cb, s) {
            var ed = tinyMCE.activeEditor;
            if(ed.plugins.inlinepopups.doc == null){
                var w = window;
                while(w.parent && w != w.parent){
                    w = w.parent;
                    if(!$def(w.tinymce)){
                        w.tinymce = window.tinymce;
                        w.tinyMCE = window.tinyMCE;
                    }
                }
                ed.plugins.inlinepopups.doc = w.document;
            }


            var doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
			var t = this, w;

			w = t.open({
				title : t,
				type : 'alert',
				button_func : function(s) {
					if (cb)
						cb.call(s || t, s);

					t.close(null, w.id);
				},
				content : dom.encode(t.editor.getLang(txt, txt)),
				inline : 1,
				width : 400,
				height : 130
			});
		},

		confirm : function(txt, cb, s) {
            var ed = tinyMCE.activeEditor;
            if(ed.plugins.inlinepopups.doc == null){
                var w = window;
                while(w.parent && w != w.parent){
                    w = w.parent;
                    if(!$def(w.tinymce)){
                        w.tinymce = window.tinymce;
                        w.tinyMCE = window.tinyMCE;
                    }
                }
                ed.plugins.inlinepopups.doc = w.document;
            }


            var doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
			var t = this, w;

			w = t.open({
				title : t,
				type : 'confirm',
				button_func : function(s) {
					if (cb)
						cb.call(s || t, s);

					t.close(null, w.id);
				},
				content : dom.encode(t.editor.getLang(txt, txt)),
				inline : 1,
				width : 400,
				height : 130
			});
		},

		// Internal functions

		_fixIELayout : function(id, s) {
            var doc = tinyMCE.activeEditor.plugins.inlinepopups.doc, dom = new tinymce.dom.DOMUtils(doc);
			var w, img;

			if (!tinymce.isIE6)
				return;

			// Fixes the bug where hover flickers and does odd things in IE6
			each(['n','s','w','e','nw','ne','sw','se'], function(v) {
				var e = dom.get(id + '_resize_' + v);

				dom.setStyles(e, {
					width : s ? e.clientWidth : '',
					height : s ? e.clientHeight : '',
					cursor : dom.getStyle(e, 'cursor', 1)
				});

				dom.setStyle(id + "_bottom", 'bottom', '-1px');

				e = 0;
			});

			// Fixes graphics glitch
			if (w = this.windows[id]) {
				// Fixes rendering bug after resize
				w.element.hide();
				w.element.show();

				// Forced a repaint of the window
				//DOM.get(id).style.filter = '';

				// IE has a bug where images used in CSS won't get loaded
				// sometimes when the cache in the browser is disabled
				// This fix tries to solve it by loading the images using the image object
				each(dom.select('div,a', id), function(e, i) {
					if (e.currentStyle.backgroundImage != 'none') {
						img = new Image();
						img.src = e.currentStyle.backgroundImage.replace(/url\(\"(.+)\"\)/, '$1');
					}
				});

				dom.get(id).style.filter = '';
			}
		}
	});

	// Register plugin
	tinymce.PluginManager.add('inlinepopups', tinymce.plugins.InlinePopups);
})();



/**
 * $Id: editor_plugin_src.js 755 2008-03-29 19:14:42Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright ï¿½ 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

(function() {
	var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM;

	tinymce.create('tinymce.plugins.ContextMenu', {
		init : function(ed) {
			var t = this;

			t.editor = ed;
			t.onContextMenu = new tinymce.util.Dispatcher(this);

			ed.onContextMenu.add(function(ed, e) {
				if (!e.ctrlKey) {
					t._getMenu(ed).showMenu(e.clientX, e.clientY);
					Event.add(document, 'click', hide);
					Event.cancel(e);
				}
			});

			function hide() {
				if (t._menu) {
					t._menu.removeAll();
					t._menu.destroy();
					Event.remove(document, 'click', hide);
				}
			};

			ed.onMouseDown.add(hide);
			ed.onKeyDown.add(hide);
		},

		getInfo : function() {
			return {
				longname : 'Contextmenu',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/contextmenu',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		},

		_getMenu : function(ed) {
			var t = this, m = t._menu, se = ed.selection, col = se.isCollapsed(), el = se.getNode() || ed.getBody(), am, p1, p2;

			if (m) {
				m.removeAll();
				m.destroy();
			}

			p1 = DOM.getPos(ed.getContentAreaContainer());
			p2 = DOM.getPos(ed.getContainer());

			m = ed.controlManager.createDropMenu('contextmenu', {
				offset_x : p1.x,
				offset_y : p1.y,
/*				vp_offset_x : p2.x,
				vp_offset_y : p2.y,*/
				constrain : 1
			});

			t._menu = m;

            // begin edits //
            // https://brewspace.jiveland.com/docs/DOC-10757 //
//            m.add({title : 'advanced.cut_desc', icon : 'cut', cmd : 'Cut'}).setDisabled(col);
//            m.add({title : 'advanced.copy_desc', icon : 'copy', cmd : 'Copy'}).setDisabled(col);
//            m.add({title : 'advanced.paste_desc', icon : 'paste', cmd : 'Paste'});


            if(el.nodeName != 'A'){
                m.add({title : 'jivelink.link_desc', icon : 'link', cmd : 'mceJiveLink', ui : true});
                m.addSeparator();
            }
            if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {
                m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'});
                m.addSeparator();
            }

            if (ed.dom.getParent(el, 'img')) {
//                m.add({title : 'advanced.edit_image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true});
//                m.addSeparator();
                am = m.addMenu({title : 'contextmenu.align', icon : "align"});
                // end edits //
                am.add({title : 'advimage_dlg.align_inline', cmd : 'mceNoFloatImage'});
                am.add({title : 'advimage_dlg.align_left', icon : 'justifyleft', cmd : 'JustifyLeft'});
                am.add({title : 'advimage_dlg.align_right', icon : 'justifyright', cmd : 'JustifyRight'});
            }else{
                if (ed.settings.images_enabled) {
                    m.add({title : 'advanced.insert_image_desc', icon : 'image', cmd : ed.plugins.jiveimage
                            ? 'mceJiveImage' : 'mceImage', ui : true});
                    m.addSeparator();
                }
                am = m.addMenu({title : 'contextmenu.align', icon : "align"});
                // end edits //
                am.add({title : 'contextmenu.left', icon : 'justifyleft', cmd : 'JustifyLeft'});
                am.add({title : 'contextmenu.center', icon : 'justifycenter', cmd : 'JustifyCenter'});
                am.add({title : 'contextmenu.right', icon : 'justifyright', cmd : 'JustifyRight'});
                am.add({title : 'contextmenu.full', icon : 'justifyfull', cmd : 'JustifyFull'});
            }


			t.onContextMenu.dispatch(t, m, el, col);

			return m;
		}
	});

	// Register plugin
	tinymce.PluginManager.add('contextmenu', tinymce.plugins.ContextMenu);
})();

/**
 * $Id: editor_template_src.js 793 2008-04-10 17:32:40Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright ï¿½ 2004-2008, Moxiecode Systems AB, All rights reserved.
 */

var foo = (function() {
	var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, each = tinymce.each, Cookie = tinymce.util.Cookie, lastExtID, explode = tinymce.explode;

	// Tell it to load theme specific language pack(s)
	tinymce.ThemeManager.requireLangPack('advanced');

	tinymce.create('tinymce.themes.AdvancedTheme', {
		// Control name lookup, format: title, command
		controls : {
			bold : ['bold_desc', 'Bold'],
			italic : ['italic_desc', 'Italic'],
			underline : ['underline_desc', 'Underline'],
			strikethrough : ['striketrough_desc', 'Strikethrough'],
			justifyleft : ['justifyleft_desc', 'JustifyLeft'],
			justifycenter : ['justifycenter_desc', 'JustifyCenter'],
			justifyright : ['justifyright_desc', 'JustifyRight'],
			justifyfull : ['justifyfull_desc', 'JustifyFull'],
			bullist : ['bullist_desc', 'mceInsertUnorderedList'],
			numlist : ['numlist_desc', 'mceInsertOrderedList'],
			outdent : ['outdent_desc', 'mceOutdent'],
			indent : ['indent_desc', 'mceIndent'],
			cut : ['cut_desc', 'Cut'],
			copy : ['copy_desc', 'Copy'],
			paste : ['paste_desc', 'Paste'],
			link : ['link_desc', 'mceLink'],
			unlink : ['unlink_desc', 'unlink'],
			image : ['image_desc', 'mceImage'],
            undo : ['undo_desc', 'Undo'],
            redo : ['redo_desc', 'Redo'],
            sub : ['sub_desc', 'subscript'],
            sup : ['sup_desc', 'superscript'],
//			cleanup : ['cleanup_desc', 'mceCleanup'],
			code : ['code_desc', 'mceCodeEditor'],
			hr : ['hr_desc', 'InsertHorizontalRule'],
			removeformat : ['removeformat_desc', 'mceRemoveFullFormat'],
			forecolor : ['forecolor_desc', 'ForeColor'],
			forecolorpicker : ['forecolor_desc', 'mceForeColor'],
			backcolor : ['backcolor_desc', 'HiliteColor'],
			backcolorpicker : ['backcolor_desc', 'mceBackColor'],
			blockquote : ['blockquote_desc', 'mceBlockQuote']
		},

		stateControls : ['bold', 'italic', 'underline', 'strikethrough', 'bullist', 'numlist', 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull', 'blockquote'],

        removeListsFromHelper : 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 || currNode.nodeName.toLowerCase() != "ol" && currNode.nodeName.toLowerCase() != "ul"){ // text node
                pre.appendChild(currNode);
                return false;
            }
            // add all my children
            while(currNode.childNodes.length > 0){
                var node = currNode.childNodes[0];
                if(this.removeListsFromHelper(ed, pre, node, lastNode)){
                    if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul") currNode.removeChild(node);
                    return true;
                }
                if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul") currNode.removeChild(node);
            }
            if(this.shouldEndLine(currNode)){
                pre.appendChild(doc.createTextNode("\n"));
            }

            if(ret) return ret;

            // add my next sibling, if any
            if(currNode.nextSibling != null){
                if(this.removeListsFromHelper(ed, pre, currNode.nextSibling, lastNode)){
                    return true;
                }
            }
            return false;
        },

        getParentNode : function(ed, node){
            return (new tinymce.dom.DOMUtils(ed.getDoc())).getParent(node, function(x){ return x == ed.getBody() || x != node; });
        },

        removeListsFrom : function(ed, pre, currNode, lastNode){
            // if it's the final node, quit
            var ret = false;
            if(currNode == null) return true;
            if(currNode == lastNode){
                ret = true;
            }
            var doc = ed.getDoc();
            if(currNode.nodeType == 3 || currNode.nodeName.toLowerCase() != "ol" && currNode.nodeName.toLowerCase() != "ul"){ // text node
                pre.appendChild(currNode);
                return false;
            }
            // add all my children
            while(currNode.childNodes.length > 0){
                var node = currNode.childNodes[0];
                if(this.removeListsFromHelper(ed, pre, node, lastNode)){
                    if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul") currNode.removeChild(node);
                    return true;
                }
                if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul") currNode.removeChild(node);
            }
            if(this.shouldEndLine(currNode)){
                pre.appendChild(doc.createTextNode("\n"));
            }

            if(ret){
                if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul"){
                    var par = this.getParentNode(ed, currNode);
                    par.removeChild(currNode);
                }
                return ret;
            }

            // add my next sibling, if any
            if(currNode.nextSibling != null){
                var next = currNode.nextSibling;
                if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul"){
                    var par = this.getParentNode(ed, currNode);
                    par.removeChild(currNode);
                }
                if(this.removeListsFromHelper(ed, pre, next, lastNode)){
                    return true;
                }
            }
            // go up to my parent, and start adding again with my parent's next sibling
            var next = this.getNextSibling(ed, currNode);
            if(currNode.nodeName.toLowerCase() == "ol" || currNode.nodeName.toLowerCase() == "ul"){
                var par = this.getParentNode(ed, currNode);
                par.removeChild(currNode);
            }
            if(this.removeListsFrom(ed, pre, next, lastNode)){
                return true;
            }

            return false;
        },

        init : function(ed, url) {
			var t = this, s, v;

            // Register commands
			ed.addCommand('mceRemoveFullFormat', function() {
                if(!ed.selection.isCollapsed()){

                    /**
                     * code to remove list formatting - in process...
                    var sel = ed.selection;
                    var start = sel.getStart();
                    start = tinyMCE.activeEditor.plugins.jivemacros.getOldestNode(ed, sel.getNode(), start);

                    var par = tinyMCE.activeEditor.plugins.jivemacros.getParentNode(ed, start);
                    var end = sel.getEnd();
                    var pre = ed.getDoc().createElement('DIV');
                    par.insertBefore(pre, start);

                    ed.theme.removeListsFrom(ed, pre, start,end);

                    // now remove the div
                    for(var i=0;i<pre.childNodes.length;i++){
                        par.insertBefore(pre.childNodes[0], pre);
                    }
                    par.removeChild(pre);
                    */

                    ed.execCommand("RemoveFormat");
                    var n = ed.selection.getNode();
                    var p = ed.dom.getParent(n, "table");
                    if(p == null){
                        ed.execCommand("FormatBlock",false,"<p>");
                    }
                }
            });


            t.editor = ed;
			t.url = url;
            t.onResolveName = new tinymce.util.Dispatcher(this);
            t.onResize = new tinymce.util.Dispatcher(this);

			// Default settings
			t.settings = s = extend({
				theme_advanced_path : true,
				theme_advanced_toolbar_location : 'bottom',
//                jivestyle,formatselect,
//                theme_advanced_buttons1 : "bold,italic,strikethrough,forecolor,backcolor,|,formatselect,|,bullist,numlist,|,indent,outdent,alignment,|,jiveemoticons,link,unlink,|,tabletoolbar,mediatoolbar,extratoolbar",
//                theme_advanced_buttons2 : "image,media,jivemacros",
//                theme_advanced_buttons3 : "sub,sup,|,undo,redo,|,removeformat,blockquote,charmap,|,spellchecker,jivespell",
//                theme_advanced_buttons4 : "tablecontrols",
				theme_advanced_blockformats : "p,address,pre,h1,h2,h3,h4,h5,h6",
				theme_advanced_toolbar_align : "center",
				theme_advanced_fonts : "Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",
				theme_advanced_font_sizes : "1,2,3,4,5,6,7",
				theme_advanced_more_colors : 1,
				theme_advanced_row_height : 23,
				theme_advanced_resize_horizontal : 1,
				theme_advanced_resizing_use_cookie : 1
			}, ed.settings);

			if ((v = s.theme_advanced_path_location) && v != 'none')
				s.theme_advanced_statusbar_location = s.theme_advanced_path_location;

			if (s.theme_advanced_statusbar_location == 'none')
				s.theme_advanced_statusbar_location = 0;

			// Init editor
			ed.onInit.add(function() {
                ed.onNodeChange.add(t._nodeChanged, t);
                ed.onKeyDown.add(t._keyDown, t);
				jive.ext.x.xTimer.set("timeout",t, "_refreshFont", 33);
                jive.ext.x.xTimer.set("timeout",t, "_refreshFont", 500);
                jive.ext.x.xTimer.set("timeout",t, "_refreshFontColor", 33);
                jive.ext.x.xTimer.set("timeout",t, "_refreshFontColor", 500);
                jive.ext.x.xTimer.set("timeout",t, "_refreshFontSize", 33);
                jive.ext.x.xTimer.set("timeout",t, "_refreshFontSize", 500);

				if (ed.settings.content_css !== false){
                    ed.dom.loadCSS(ed.baseURI.toAbsolute("themes/advanced/skins/" + ed.settings.skin + "/content.css"));
                }
            });

            ed.onInit.add(function(){
                var c = ed.controlManager.get('fontsizeselect');
                if (c) {
                    var div = document.createElement('DIV');
                    var lo = [
                        "8 pt",
                        "10 pt",
                        "12 pt",
                        "14 pt",
                        "18 pt",
                        "24 pt",
                        "36 pt"
                    ], fz = [8, 10, 12, 14, 18, 24, 36];
                    c.computed = new Array();
                    document.body.appendChild(div);
                    each(explode(t.settings.theme_advanced_font_sizes), function(v) {
                        div.style.fontSize = fz[v - 1] + 'pt';
                        var computedValue = jive.ext.x.xGetCS(div, "font-size");
                        c.computed.push(computedValue);
                        c.add(lo[parseInt(v) - 1], v, {'style' : 'font-size:' + fz[v - 1] + 'pt', 'class' : 'mceFontSize' + v});
                    });
                    document.body.removeChild(div);

                    ed.onExecCommand.add(function(ed, cmd, ui, val) {
                        if(cmd == "FontSize"){
                            // remove inline style size on all nodes in selection
                            // http://jira.jiveland.com/browse/CS-8010
                            if(!ed.selection.isCollapsed()){
                                var startN = ed.selection.getStart();
                                var endN = ed.selection.getEnd();
                                startN = ed.plugins.jiveutil.getNodeAt(startN, ed.selection.getRng().startOffset);
                                ed.plugins.jiveutil.walkDOMTree(startN, endN, function(node){
                                    // safari only works with inline style, not font tags...
                                    // so if we're safari, ignore this line
                                    if(!tinymce.isWebKit && node.style){
                                        node.style.fontSize = "";
                                    }
                                    if(node.nodeType == 1 && node.nodeName.toLowerCase() == "font" && node.getAttribute("size") != val){
                                        node.setAttribute("size", val);
                                    }
                                });
                            }
                        }
                    });
                }
            });

			ed.onSetProgressState.add(function(ed, b, ti) {
				var co, id = ed.id, tb;

				if (b) {
					t.progressTimer = setTimeout(function() {
						co = ed.getContainer();
						co = co.insertBefore(DOM.create('DIV', {style : 'position:relative'}), co.firstChild);
						tb = DOM.get(ed.id + '_tbl');

						DOM.add(co, 'div', {id : id + '_blocker', 'class' : 'mceBlocker', style : {width : tb.clientWidth + 2, height : tb.clientHeight + 2}});
						DOM.add(co, 'div', {id : id + '_progress', 'class' : 'mceProgress', style : {left : tb.clientWidth / 2, top : tb.clientHeight / 2}});
					}, ti || 0);
				} else {
					DOM.remove(id + '_blocker');
					DOM.remove(id + '_progress');
					clearTimeout(t.progressTimer);
				}
			});

			DOM.loadCSS(ed.baseURI.toAbsolute(s.editor_css || "themes/advanced/skins/" + ed.settings.skin + "/ui.css"));

			if (s.skin_variant)
				DOM.loadCSS(ed.baseURI.toAbsolute(s.editor_css || "themes/advanced/skins/" + ed.settings.skin + "/ui_" + s.skin_variant + ".css"));
		},

		createControl : function(n, cf) {
			var cd, c;

			if (c = cf.createControl(n))
				return c;

			switch (n) {
				case "styleselect":
					return this._createStyleSelect();

				case "formatselect":
					return this._createBlockFormats();

				case "fontselect":
					return this._createFontSelect();

				case "fontsizeselect":
					return this._createFontSizeSelect();

				case "forecolor":
					return this._createForeColorMenu();

				case "backcolor":
					return this._createBackColorMenu();
			}

			if ((cd = this.controls[n]))
				return cf.createButton(n, {title : "advanced." + cd[0], cmd : cd[1], ui : cd[2], value : cd[3]});
		},

		execCommand : function(cmd, ui, val) {
			var f = this['_' + cmd];

			if (f) {
				f.call(this, ui, val);
				return true;
			}

			return false;
		},

		_importClasses : function() {
			var ed = this.editor, c = ed.controlManager.get('styleselect');

			if (c.getLength() == 0) {
				each(ed.dom.getClasses(), function(o) {
					c.add(o['class'], o['class']);
				});
			}
		},

		_createStyleSelect : function(n) {
			var t = this, ed = t.editor, cf = ed.controlManager, c = cf.createListBox('styleselect', {
				title : 'advanced.style_select',
				onselect : function(v) {
					if (c.selectedValue === v) {
						ed.execCommand('mceSetStyleInfo', 0, {command : 'mceRemoveFullFormat'});
						c.select();
						return false;
					} else
						ed.execCommand('mceSetCSSClass', 0, v);
				}
			});

			if (c) {
				each(ed.getParam('theme_advanced_styles', '', 'hash'), function(v, k) {
					if (v)
						c.add(t.editor.translate(k), v);
				});

				c.onPostRender.add(function(ed, n) {
					Event.add(n, 'focus', t._importClasses, t);
					Event.add(n, 'mousedown', t._importClasses, t);
				});
			}

			return c;
		},

		_createFontSelect : function() {
			var c, t = this, ed = t.editor;

			c = ed.controlManager.createListBox('fontselect', {title : 'advanced.fontdefault', cmd : 'FontName'});
			if (c) {
				each(ed.getParam('theme_advanced_fonts', t.settings.theme_advanced_fonts, 'hash'), function(v, k) {
                    c.add(ed.translate(k), v.toLowerCase(), {style : v.indexOf('dings') == -1 ? 'font-family:' + v : ''});
				});
			}

			return c;
		},

		_createFontSizeSelect : function() {
            var  t = this, ed = t.editor;
			var c = ed.controlManager.createListBox('fontsizeselect', {title : 'advanced.font_size', cmd : 'FontSize'});

			return c;
		},

		_createBlockFormats : function() {
			var c, fmts = {
				p : 'advanced.paragraph',
				address : 'advanced.address',
				pre : 'advanced.pre',
				h1 : 'advanced.h1',
				h2 : 'advanced.h2',
				h3 : 'advanced.h3',
				h4 : 'advanced.h4',
				h5 : 'advanced.h5',
				h6 : 'advanced.h6',
				div : 'advanced.div',
				blockquote : 'advanced.blockquote',
				code : 'advanced.code',
				dt : 'advanced.dt',
				dd : 'advanced.dd',
				samp : 'advanced.samp'
			}, t = this;

			c = t.editor.controlManager.createListBox('formatselect', {title : 'advanced.block', cmd : 'FormatBlock'});
			if (c) {
				each(t.editor.getParam('theme_advanced_blockformats', t.settings.theme_advanced_blockformats, 'hash'), function(v, k) {
					c.add(t.editor.translate(k != v ? k : fmts[v]), v, {'class' : 'mce_formatPreview mce_' + v});
				});
			}

			return c;
		},

		_createForeColorMenu : function() {
			var c, _theme = this, s = _theme.settings, o = {}, v;

			if (s.theme_advanced_more_colors) {
				o.more_colors_func = function() {
					_theme._mceColorPicker(0, {
						color : c.value,
						func : function(co) {
                            c.setColor(co);
						}
					});
				};
			}

			if (v = s.theme_advanced_text_colors)
				o.colors = v;

			o.title = 'advanced.forecolor_desc';
			o.cmd = 'ForeColor';
			o.scope = this;

			c = _theme.editor.controlManager.createColorSplitButton('forecolor', o);
            c.oldSetColor = c.setColor;
            c.setColor = function(co){
                _theme.ignoreRefreshCommand = true;
                this.oldSetColor(co);
                _theme.ignoreRefreshCommand = false;
            }
            return c;
		},

		_createBackColorMenu : function() {
			var c, _theme = this, s = _theme.settings, o = {}, v;

			if (s.theme_advanced_more_colors) {
				o.more_colors_func = function() {
					_theme._mceColorPicker(0, {
						color : c.value,
						func : function(co) {
							c.setColor(co);
						}
					});
				};
			}

			if (v = s.theme_advanced_background_colors)
				o.colors = v;

			o.title = 'advanced.backcolor_desc';
			o.cmd = 'HiliteColor';
			o.scope = this;

			c = _theme.editor.controlManager.createColorSplitButton('backcolor', o);
            c.oldSetColor = c.setColor;
            c.setColor = function(co){
                _theme.ignoreRefreshCommand = true;
                this.oldSetColor(co);
                _theme.ignoreRefreshCommand = false;
            }

			return c;
		},

		renderUI : function(o) {
			var n, ic, tb, t = this, ed = t.editor, s = t.settings, sc, p, nl;

			n = p = DOM.create('span', {id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '')});

			if (!DOM.boxModel)
				n = DOM.add(n, 'div', {'class' : 'mceOldBoxModel'});

			n = sc = DOM.add(n, 'table', {id : ed.id + '_tbl', 'class' : 'mceLayout', cellSpacing : 0, cellPadding : 0});
			n = tb = DOM.add(n, 'tbody');

			switch ((s.theme_advanced_layout_manager || '').toLowerCase()) {
				case "rowlayout":
					ic = t._rowLayout(s, tb, o);
					break;

				case "customlayout":
					ic = ed.execCallback("theme_advanced_custom_layout", s, tb, o, p);
					break;

				default:
					ic = t._simpleLayout(s, tb, o, p);
			}

			n = o.targetNode;

			// Add classes to first and last TRs
			nl = DOM.stdMode ? sc.getElementsByTagName('tr') : sc.rows; // Quick fix for IE 8
			DOM.addClass(nl[0], 'mceFirst');
			DOM.addClass(nl[nl.length - 1], 'mceLast');

			// Add classes to first and last TDs
			each(DOM.select('tr', tb), function(n) {
				DOM.addClass(n.firstChild, 'mceFirst');
				DOM.addClass(n.childNodes[n.childNodes.length - 1], 'mceLast');
			});

			if (DOM.get(s.theme_advanced_toolbar_container))
				DOM.get(s.theme_advanced_toolbar_container).appendChild(p);
			else
				DOM.insertAfter(p, n);

			Event.add(ed.id + '_path_row', 'click', function(e) {
				e = e.target;

				if (e.nodeName == 'A') {
					t._sel(e.className.replace(/^.*mcePath_([0-9]+).*$/, '$1'));

					return Event.cancel(e);
				}
			});
/*
			if (DOM.get(ed.id + '_path_row')) {
				Event.add(ed.id + '_tbl', 'mouseover', function(e) {
					var re;

					e = e.target;

					if (e.nodeName == 'SPAN' && DOM.hasClass(e.parentNode, 'mceButton')) {
						re = DOM.get(ed.id + '_path_row');
						t.lastPath = re.innerHTML;
						DOM.setHTML(re, e.parentNode.title);
					}
				});

				Event.add(ed.id + '_tbl', 'mouseout', function(e) {
					if (t.lastPath) {
						DOM.setHTML(ed.id + '_path_row', t.lastPath);
						t.lastPath = 0;
					}
				});
			}
*/

			if (!ed.getParam('accessibility_focus') || ed.getParam('tab_focus'))
				Event.add(DOM.add(p, 'a', {href : '#'}, '<!-- IE -->'), 'focus', function() {tinyMCE.get(ed.id).focus();});

			if (s.theme_advanced_toolbar_location == 'external')
				o.deltaHeight = 0;

			t.deltaHeight = o.deltaHeight;
			o.targetNode = null;

			return {
				iframeContainer : ic,
				editorContainer : ed.id + '_parent',
				sizeContainer : sc,
				deltaHeight : o.deltaHeight
			};
		},

		getInfo : function() {
			return {
				longname : 'Advanced theme',
				author : 'Moxiecode Systems AB',
				authorurl : 'http://tinymce.moxiecode.com',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			}
		},

		resizeBy : function(dw, dh) {
			var e = DOM.get(this.editor.id + '_tbl');

			this.resizeTo(e.clientWidth + dw, e.clientHeight + dh);
		},

		resizeTo : function(w, h) {
			var ed = this.editor, s = ed.settings, e = DOM.get(ed.id + '_tbl'), ifr = DOM.get(ed.id + '_ifr'), dh;

			// Boundery fix box
			w = Math.max(s.theme_advanced_resizing_min_width || 100, w);
			h = Math.max(s.theme_advanced_resizing_min_height || 100, h);
			w = Math.min(s.theme_advanced_resizing_max_width || 0xFFFF, w);
			h = Math.min(s.theme_advanced_resizing_max_height || 0xFFFF, h);

			// Calc difference between iframe and container
			dh = e.clientHeight - ifr.clientHeight;

			// Resize iframe and container
			DOM.setStyle(ifr, 'height', h - dh);
			DOM.setStyles(e, {width : w, height : h});
		},

		destroy : function() {
			var id = this.editor.id;

			Event.clear(id + '_resize');
			Event.clear(id + '_path_row');
			Event.clear(id + '_external_close');
		},

		// Internal functions

		_simpleLayout : function(s, tb, o, p) {
			var t = this, ed = t.editor, lo = s.theme_advanced_toolbar_location, sl = s.theme_advanced_statusbar_location, n, ic, etb, c;

			// Create toolbar container at top
			if (lo == 'top')
				t._addToolbars(tb, o);

			// Create external toolbar
			if (lo == 'external') {
				n = c = DOM.create('div', {style : 'position:relative'});
				n = DOM.add(n, 'div', {id : ed.id + '_external', 'class' : 'mceExternalToolbar'});
				DOM.add(n, 'a', {id : ed.id + '_external_close', href : 'javascript:;', 'class' : 'mceExternalClose'});
				n = DOM.add(n, 'table', {id : ed.id + '_tblext', cellSpacing : 0, cellPadding : 0});
				etb = DOM.add(n, 'tbody');

				if (p.firstChild.className == 'mceOldBoxModel')
					p.firstChild.appendChild(c);
				else
					p.insertBefore(c, p.firstChild);

				t._addToolbars(etb, o);

				ed.onMouseUp.add(function() {
					var e = DOM.get(ed.id + '_external');
					DOM.show(e);

					DOM.hide(lastExtID);

					var f = Event.add(ed.id + '_external_close', 'click', function() {
						DOM.hide(ed.id + '_external');
						Event.remove(ed.id + '_external_close', 'click', f);
					});

					DOM.show(e);
					DOM.setStyle(e, 'top', 0 - DOM.getRect(ed.id + '_tblext').h - 1);

					// Fixes IE rendering bug
					DOM.hide(e);
					DOM.show(e);
					e.style.filter = '';

					lastExtID = ed.id + '_external';

					e = null;
				});
			}

			if (sl == 'top')
				t._addStatusBar(tb, o);

			// Create iframe container
			if (!s.theme_advanced_toolbar_container) {
				n = DOM.add(tb, 'tr');
				n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'});
			}

			// Create toolbar container at bottom
			if (lo == 'bottom')
				t._addToolbars(tb, o);

			if (sl == 'bottom')
				t._addStatusBar(tb, o);

			return ic;
		},

		_rowLayout : function(s, tb, o) {
			var t = this, ed = t.editor, dc, da, cf = ed.controlManager, n, ic, to, a;

			dc = s.theme_advanced_containers_default_class || '';
			da = s.theme_advanced_containers_default_align || 'center';

			each(explode(s.theme_advanced_containers || ''), function(c, i) {
				var v = s['theme_advanced_container_' + c] || '';

				switch (c.toLowerCase()) {
					case 'mceeditor':
						n = DOM.add(tb, 'tr');
						n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'});
						break;

					case 'mceelementpath':
						t._addStatusBar(tb, o);
						break;

					default:
						a = s['theme_advanced_container_' + c + '_align'].toLowerCase();
						a = 'mce' + t._ufirst(a);

						n = DOM.add(DOM.add(tb, 'tr'), 'td', {
							'class' : 'mceToolbar ' + (s['theme_advanced_container_' + c + '_class'] || dc) + ' ' + a || da
						});

						to = cf.createToolbar("toolbar" + i);
						t._addControls(v, to);
						DOM.setHTML(n, to.renderHTML());
						o.deltaHeight -= s.theme_advanced_row_height;
				}
			});

			return ic;
		},

		_addControls : function(v, tb) {
			var t = this, s = t.settings, di, cf = t.editor.controlManager;

			if (s.theme_advanced_disable && !t._disabled) {
				di = {};

				each(explode(s.theme_advanced_disable), function(v) {
					di[v] = 1;
				});

				t._disabled = di;
			} else
				di = t._disabled;

			each(explode(v), function(n) {
				var c;

				if (di && di[n])
					return;

				// Compatiblity with 2.x
				if (n == 'tablecontrols') {
					each(["table","delete_table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","row_up","row_down","col_left","col_right","|","split_cells","merge_cells"], function(n) {
						n = t.createControl(n, cf);

						if (n)
							tb.add(n);
					});

					return;
				}

				c = t.createControl(n, cf);

				if (c)
					tb.add(c);
			});
		},

		_addToolbars : function(c, o) {
			var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a;

			a = s.theme_advanced_toolbar_align.toLowerCase();
			a = 'mce' + t._ufirst(a);

			n = DOM.add(DOM.add(c, 'tr'), 'td', {'class' : 'mceToolbar ' + a});

			if (!ed.getParam('accessibility_focus') || ed.getParam('tab_focus'))
				h.push(DOM.createHTML('a', {href : '#', onfocus : 'tinyMCE.get(\'' + ed.id + '\').focus();'}, '<!-- IE -->'));

			h.push(DOM.createHTML('a', {href : '#', accesskey : 'q', title : ed.getLang("advanced.toolbar_focus")}, '<!-- IE -->'));

			// Create toolbar and add the controls
			for (i=1; (v = s['theme_advanced_buttons' + i]); i++) {
				tb = cf.createToolbar("toolbar" + i, {'class' : 'mceToolbarRow' + i});

				if (s['theme_advanced_buttons' + i + '_add'])
					v += ',' + s['theme_advanced_buttons' + i + '_add'];

				if (s['theme_advanced_buttons' + i + '_add_before'])
					v = s['theme_advanced_buttons' + i + '_add_before'] + ',' + v;

				t._addControls(v, tb);

				//n.appendChild(n = tb.render());
				h.push(tb.renderHTML());

				o.deltaHeight -= s.theme_advanced_row_height;
			}

			h.push(DOM.createHTML('a', {href : '#', accesskey : 'z', title : ed.getLang("advanced.toolbar_focus"), onfocus : 'tinyMCE.getInstanceById(\'' + ed.id + '\').focus();'}, '<!-- IE -->'));

            // DOM.setHTML runs the html through all kinds of cleanup
            // and since we know our HTMLis safe, just set it outright
            n.innerHTML = h.join('');
//			DOM.setHTML(n, h.join(''));
		},

		_addStatusBar : function(tb, o) {
			var n, t = this, ed = t.editor, s = t.settings, r, mf, me, td;

			n = DOM.add(tb, 'tr');
			n = td = DOM.add(n, 'td', {'class' : 'mceStatusbar'});
			n = DOM.add(n, 'div', {id : ed.id + '_path_row'});
			DOM.add(n, 'a', {href : '#', accesskey : 'x'});

			if (s.theme_advanced_resizing && !tinymce.isOldWebKit) {
				DOM.add(td, 'a', {id : ed.id + '_resize', href : 'javascript:;', onclick : "return false;", 'class' : 'mceResize'});

				if (s.theme_advanced_resizing_use_cookie) {
					ed.onPostRender.add(function() {
						var o = Cookie.getHash("TinyMCE_" + ed.id + "_size"), c = DOM.get(ed.id + '_tbl');

						if (!o)
							return;

						if (s.theme_advanced_resize_horizontal)
							c.style.width = Math.max(10, o.cw) + 'px';

						c.style.height = Math.max(10, o.ch) + 'px';
						DOM.get(ed.id + '_ifr').style.height = Math.max(10, parseInt(o.ch) + t.deltaHeight) + 'px';
					});
				}

				ed.onPostRender.add(function() {
					Event.add(ed.id + '_resize', 'mousedown', function(e) {
						var c, p, w, h, n, pa;

						// Measure container
						c = DOM.get(ed.id + '_tbl');
						w = c.clientWidth;
						h = c.clientHeight;

						miw = s.theme_advanced_resizing_min_width || 100;
						mih = s.theme_advanced_resizing_min_height || 100;
						maw = s.theme_advanced_resizing_max_width || 0xFFFF;
						mah = s.theme_advanced_resizing_max_height || 0xFFFF;

						// Setup placeholder
						p = DOM.add(DOM.get(ed.id + '_parent'), 'div', {'class' : 'mcePlaceHolder'});
						DOM.setStyles(p, {width : w, height : h});

						// Replace with placeholder
						DOM.hide(c);
						DOM.show(p);

						// Create internal resize obj
						r = {
							x : e.screenX,
							y : e.screenY,
							w : w,
							h : h,
							dx : null,
							dy : null
						};

						// Start listening
						mf = Event.add(DOM.doc, 'mousemove', function(e) {
							var w, h;

							// Calc delta values
							r.dx = e.screenX - r.x;
							r.dy = e.screenY - r.y;

							// Boundery fix box
							w = Math.max(miw, r.w + r.dx);
							h = Math.max(mih, r.h + r.dy);
							w = Math.min(maw, w);
							h = Math.min(mah, h);

							// Resize placeholder
							if (s.theme_advanced_resize_horizontal)
								p.style.width = w + 'px';

							p.style.height = h + 'px';

							return Event.cancel(e);
						});

						me = Event.add(DOM.doc, 'mouseup', function(e) {
							var ifr;

							// Stop listening
							Event.remove(DOM.doc, 'mousemove', mf);
							Event.remove(DOM.doc, 'mouseup', me);

							c.style.display = '';
							DOM.remove(p);

							if (r.dx === null)
								return;

							ifr = DOM.get(ed.id + '_ifr');

							if (s.theme_advanced_resize_horizontal)
								c.style.width = Math.max(10, r.w + r.dx) + 'px';

							c.style.height = Math.max(10, r.h + r.dy) + 'px';
							ifr.style.height = Math.max(10, ifr.clientHeight + r.dy) + 'px';

							if (s.theme_advanced_resizing_use_cookie) {
								Cookie.setHash("TinyMCE_" + ed.id + "_size", {
									cw : r.w + r.dx,
									ch : r.h + r.dy
								});
							}

                            ed.theme.onResize.dispatch(ed.theme);
                        });

						return Event.cancel(e);
					});
				});
			}

			o.deltaHeight -= 21;
			n = tb = null;
		},

        _getCurrentNode : function(ed){
            var node = ed.selection.getNode();
            if(node == null){
                if(ed.getBody().childNodes.length > 0){
                    return ed.getBody().childNodes[0];
                }else{
                    return null;
                }
            }
            return node;
        },

        ignoreRefreshCommand : false,

        _refreshFontColor : function(computedStyle){
            if(!this.ignoreRefreshCommand){
                var ed = tinyMCE.activeEditor;
                var preview = $(ed.id + "_forecolor_preview");
                var color;
                if(preview){
                    if(typeof(computedStyle) != "function"){
                        var node = this._getCurrentNode(ed);
                        color = jive.ext.x.xGetCS(node, "color");
                    }else{
                        color = computedStyle("color");
                    }
                    preview.style.backgroundColor = color;
                    var cm = ed.controlManager;
                    cm.get('forecolor').value = color;
                }
            }
        },

        _refreshFont : function(computedStyle){
            var ed = tinyMCE.activeEditor;
            var cm = ed.controlManager;
            var c = cm.get('fontselect');
            var font;
            if(c){
                if(typeof(computedStyle) != "function"){
                    var node = this._getCurrentNode(ed);
                    font = jive.ext.x.xGetCS(node, "font-family");
                }else{
                    font = computedStyle("font-family");
                }
                if(font.indexOf(",") >= 0){
                    // the value is a list of fonts, like "arial,helvetica,sans-serif" which is what we want
                    c.select(font.toLowerCase());
                }else{
                    // the value is 1 font, like "arial", which is not what we want
                    var done = false;

                    // check to see if we have an exact match
                    each(ed.getParam('theme_advanced_fonts', this.settings.theme_advanced_fonts, 'hash'), function(v, k) {
                        if(!done){
                            var values = v.split(',');
                            if(values.indexOf(font) == 0){
                                done = true;
                                c.select(v.toLowerCase());
                            }
                        }
                    });
                    if(done) return;                    

                    // it doesn't match an exact font name,
                    // check to see if it matches any of the secondary font
                    // choices
                    each(ed.getParam('theme_advanced_fonts', this.settings.theme_advanced_fonts, 'hash'), function(v, k) {
                        if(!done){
                            var values = v.split(',');
                            if(values.indexOf(font) >= 0){
                                done = true;
                                c.select(v.toLowerCase());
                            }
                        }
                    });
                }
            }
        },

        _refreshFontSize : function(computedStyle){
            var ed = tinyMCE.activeEditor;
            var cm = ed.controlManager;
            var c = cm.get('fontsizeselect');
            var size;
            if(c){
                if(typeof(computedStyle) != "function"){
                    var node = this._getCurrentNode(ed);
                    size = jive.ext.x.xGetCS(node, "font-size");
                }else{
                    size = computedStyle("font-size");
                }
                var index;
                if(size && size.indexOf && size.indexOf("pt") >= 0){
                    index = c.computed.indexOf(size);
                }else{
                    index = parseInt(size) - 1;
                }
                c.select(index+1);
            }
        },

        _keyDown :function(ed, evt){
            if(evt.keyCode == 13){
                // user hit return key
            }
        },

        _nodeChanged : function(ed, cm, n, co) {
			var t = this, p, de = 0, v, c, s = t.settings;

			tinymce.each(t.stateControls, function(c) {
				cm.setActive(c, ed.queryCommandState(t.controls[c][1]));
			});

			cm.setActive('visualaid', ed.hasVisual);
			cm.setDisabled('undo', !ed.undoManager.hasUndo() && !ed.typing);
			cm.setDisabled('redo', !ed.undoManager.hasRedo());
			cm.setDisabled('outdent', !ed.queryCommandState('Outdent'));

			p = DOM.getParent(n, 'A');
			if (c = cm.get('link')) {
				if (!p || !p.name) {
					c.setDisabled(!p && co);
					c.setActive(!!p);
				}
			}

			if (c = cm.get('unlink')) {
				c.setDisabled(!p && co);
				c.setActive(!!p && !p.name);
			}

			if (c = cm.get('anchor')) {
				c.setActive(!!p && p.name);

				if (tinymce.isWebKit) {
					p = DOM.getParent(n, 'IMG');
					c.setActive(!!p && DOM.getAttrib(p, 'mce_name') == 'a');
				}
			}

			p = DOM.getParent(n, 'IMG');
			if (c = cm.get('image'))
				c.setActive(!!p && n.className.indexOf('mceItem') == -1);

			if (c = cm.get('styleselect')) {
				if (n.className) {
					t._importClasses();
					c.select(n.className);
				} else
					c.select();
			}

			if (c = cm.get('formatselect')) {
				p = DOM.getParent(n, DOM.isBlock);

				if (p)
					c.select(p.nodeName.toLowerCase());
			}

            var node = this._getCurrentNode(ed);
            var computedStyle = jive.ext.x.xGetCSFunc(node);

			if (c = cm.get('fontselect')){
				c.select(ed.queryCommandValue('FontName'));
                this._refreshFont(computedStyle);
            }

            if(c = cm.get('forecolor')){
                this._refreshFontColor(computedStyle);
            }

            if (c = cm.get('fontsizeselect')){
                this._refreshFontSize(computedStyle);
            }

            if (s.theme_advanced_path && s.theme_advanced_statusbar_location) {
				p = DOM.get(ed.id + '_path') || DOM.add(ed.id + '_path_row', 'span', {id : ed.id + '_path'});
				DOM.setHTML(p, '');

				ed.dom.getParent(n, function(n) {
					var na = n.nodeName.toLowerCase(), u, pi, ti = '';

					// Ignore non element and hidden elements
					if (n.nodeType != 1 || (DOM.hasClass(n, 'mceItemHidden') || DOM.hasClass(n, 'mceItemRemoved')))
						return;

					// Fake name
					if (v = DOM.getAttrib(n, 'mce_name'))
						na = v;

					// Handle prefix
					if (tinymce.isIE && n.scopeName !== 'HTML')
						na = n.scopeName + ':' + na;

					// Remove internal prefix
					na = na.replace(/mce\:/g, '');

					// Handle node name
					switch (na) {
						case 'b':
							na = 'strong';
							break;

						case 'i':
							na = 'em';
							break;

						case 'img':
							if (v = DOM.getAttrib(n, 'src'))
								ti += 'src: ' + v + ' ';

							break;

						case 'a':
							if (v = DOM.getAttrib(n, 'name')) {
								ti += 'name: ' + v + ' ';
								na += '#' + v;
							}

							if (v = DOM.getAttrib(n, 'href'))
								ti += 'href: ' + v + ' ';

							break;

						case 'font':
							if (s.convert_fonts_to_spans)
								na = 'span';

							if (v = DOM.getAttrib(n, 'face'))
								ti += 'font: ' + v + ' ';

							if (v = DOM.getAttrib(n, 'size'))
								ti += 'size: ' + v + ' ';

							if (v = DOM.getAttrib(n, 'color'))
								ti += 'color: ' + v + ' ';

							break;

						case 'span':
							if (v = DOM.getAttrib(n, 'style'))
								ti += 'style: ' + v + ' ';

							break;
					}

					if (v = DOM.getAttrib(n, 'id'))
						ti += 'id: ' + v + ' ';

					if (v = n.className) {
						v = v.replace(/(webkit-[\w\-]+|Apple-[\w\-]+|mceItem\w+|mceVisualAid)/g, '');

						if (v && v.indexOf('mceItem') == -1) {
							ti += 'class: ' + v + ' ';

							if (DOM.isBlock(n) || na == 'img' || na == 'span')
								na += '.' + v;
						}
					}

					na = na.replace(/(html:)/g, '');
					na = {name : na, node : n, title : ti};
					t.onResolveName.dispatch(t, na);
					ti = na.title;
					na = na.name;

					//u = "javascript:tinymce.EditorManager.get('" + ed.id + "').theme._sel('" + (de++) + "');";
					pi = DOM.create('a', {'href' : "javascript:;", onmousedown : "return false;", title : ti, 'class' : 'mcePath_' + (de++)}, na);

					if (p.hasChildNodes()) {
						p.insertBefore(DOM.doc.createTextNode(' \u00bb '), p.firstChild);
						p.insertBefore(pi, p.firstChild);
					} else
						p.appendChild(pi);
				}, ed.getBody());
			}

            this.prepForReturn = false;
        },

		// Commands gets called by execCommand

		_sel : function(v) {
			this.editor.execCommand('mceSelectNodeDepth', false, v);
		},

		_mceInsertAnchor : function(ui, v) {
			var ed = this.editor;

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/anchor.htm',
				width : 320 + parseInt(ed.getLang('advanced.anchor_delta_width', 0)),
				height : 90 + parseInt(ed.getLang('advanced.anchor_delta_height', 0)),
				inline : true
			}, {
				theme_url : this.url
			});
		},

		_mceCharMap : function() {
			var ed = this.editor;

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/charmap.htm',
				width : 550 + parseInt(ed.getLang('advanced.charmap_delta_width', 0)),
				height : 250 + parseInt(ed.getLang('advanced.charmap_delta_height', 0)),
				inline : true
			}, {
				theme_url : this.url
			});
		},

		_mceHelp : function() {
			var ed = this.editor;

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/about.htm',
				width : 480,
				height : 380,
				inline : true
			}, {
				theme_url : this.url
			});
		},

		_mceColorPicker : function(u, v) {
			var ed = this.editor;

			v = v || {};

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/color_picker.htm',
				width : 375 + parseInt(ed.getLang('advanced.colorpicker_delta_width', 0)),
				height : 250 + parseInt(ed.getLang('advanced.colorpicker_delta_height', 0)),
				close_previous : false,
				inline : true
			}, {
				input_color : v.color,
				func : v.func,
				theme_url : this.url
			});
		},

		_mceCodeEditor : function(ui, val) {
			var ed = this.editor;

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/source_editor.htm',
				width : parseInt(ed.getParam("theme_advanced_source_editor_width", 720)),
				height : parseInt(ed.getParam("theme_advanced_source_editor_height", 580)),
				inline : true,
				resizable : true,
				maximizable : true
			}, {
				theme_url : this.url
			});
		},

		_mceImage : function(ui, val) {
			var ed = this.editor;

			// Internal image object like a flash placeholder
			if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1)
				return;

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/image.htm',
				width : 355 + parseInt(ed.getLang('advanced.image_delta_width', 0)),
				height : 275 + parseInt(ed.getLang('advanced.image_delta_height', 0)),
				inline : true
			}, {
				theme_url : this.url
			});
		},

		_mceLink : function(ui, val) {
			var ed = this.editor;

			ed.windowManager.open({
				url : tinymce.baseURL + '/themes/advanced/link.htm',
				width : 310 + parseInt(ed.getLang('advanced.link_delta_width', 0)),
				height : 200 + parseInt(ed.getLang('advanced.link_delta_height', 0)),
				inline : true
			}, {
				theme_url : this.url
			});
		},

		_mceNewDocument : function() {
			var ed = this.editor;

			ed.windowManager.confirm('advanced.newdocument', function(s) {
				if (s)
					ed.execCommand('mceSetContent', false, '');
			});
		},

		_mceForeColor : function() {
			var t = this;

			this._mceColorPicker(0, {
				color: t.fgColor,
				func : function(co) {
					t.fgColor = co;
					t.editor.execCommand('ForeColor', false, co);
				}
			});
		},

		_mceBackColor : function() {
			var t = this;

			this._mceColorPicker(0, {
				color: t.bgColor,
				func : function(co) {
					t.bgColor = co;
					t.editor.execCommand('HiliteColor', false, co);
				}
			});
		},

		_ufirst : function(s) {
			return s.substring(0, 1).toUpperCase() + s.substring(1);
		}
	});

	tinymce.ThemeManager.add('advanced', tinymce.themes.AdvancedTheme);
}());


tinyMCE.addI18n('en.advanced',{
style_select:"Styles",
font_size:"Size",
fontdefault:"Font family",
block:"Format",
paragraph:"Paragraph",
div:"Div",
address:"Address",
pre:"Preformatted",
h1:"Heading 1",
h2:"Heading 2",
h3:"Heading 3",
h4:"Heading 4",
h5:"Heading 5",
h6:"Heading 6",
blockquote:"Blockquote",
code:"Code",
samp:"Code sample",
dt:"Definition term ",
dd:"Definition description",
bold_desc:"Bold (Ctrl+B)",
italic_desc:"Italic (Ctrl+I)",
underline_desc:"Underline (Ctrl+U)",
striketrough_desc:"Strikethrough",
justifyleft_desc:"Align left",
justifycenter_desc:"Align center",
justifyright_desc:"Align right",
justifyfull_desc:"Align full",
bullist_desc:"Unordered list",
numlist_desc:"Ordered list",
outdent_desc:"Outdent",
indent_desc:"Indent",
undo_desc:"Undo (Ctrl+Z)",
redo_desc:"Redo (Ctrl+Y)",
link_desc:"Insert/edit link",
unlink_desc:"Unlink",
insert_image_desc:"Insert image",
edit_image_desc:"Edit image",
cleanup_desc:"Cleanup messy code",
code_desc:"Edit HTML Source",
sub_desc:"Subscript",
sup_desc:"Superscript",
hr_desc:"Insert horizontal ruler",
removeformat_desc:"Remove formatting",
custom1_desc:"Your custom description here",
forecolor_desc:"Select text color",
backcolor_desc:"Select background color",
charmap_desc:"Insert custom character",
visualaid_desc:"Toggle guidelines/invisible elements",
anchor_desc:"Insert/edit anchor",
cut_desc:"Cut",
copy_desc:"Copy",
paste_desc:"Paste",
image_props_desc:"Image properties",
newdocument_desc:"New document",
help_desc:"Help",
blockquote_desc:"Blockquote",
clipboard_msg:"Copy/Cut/Paste is not available in Mozilla and Firefox.\r\nDo you want more information about this issue?",
path:"Path",
newdocument:"Are you sure you want clear all contents?",
toolbar_focus:"Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X",
more_colors:"More colors"
});

tinyMCE.addI18n('en.advanced_dlg',{
about_title:"About TinyMCE",
about_general:"About",
about_help:"Help",
about_license:"License",
about_plugins:"Plugins",
about_plugin:"Plugin",
about_author:"Author",
about_version:"Version",
about_loaded:"Loaded plugins",
anchor_title:"Insert/edit anchor",
anchor_name:"Anchor name",
code_title:"HTML Source Editor",
code_wordwrap:"Word wrap",
colorpicker_title:"Select a color",
colorpicker_picker_tab:"Picker",
colorpicker_picker_title:"Color picker",
colorpicker_palette_tab:"Palette",
colorpicker_palette_title:"Palette colors",
colorpicker_named_tab:"Named",
colorpicker_named_title:"Named colors",
colorpicker_color:"Color:",
colorpicker_name:"Name:",
charmap_title:"Select custom character",
image_title:"Insert/edit image",
image_src:"Image URL",
image_alt:"Image description",
image_list:"Image list",
image_border:"Border",
image_dimensions:"Dimensions",
image_vspace:"Vertical space",
image_hspace:"Horizontal space",
image_align:"Alignment",
image_align_baseline:"Baseline",
image_align_top:"Top",
image_align_middle:"Middle",
image_align_bottom:"Bottom",
image_align_texttop:"Text top",
image_align_textbottom:"Text bottom",
image_align_left:"Left",
image_align_right:"Right",
link_title:"Insert/edit link",
link_url:"Link URL",
link_target:"Target",
link_target_same:"Open link in the same window",
link_target_blank:"Open link in a new window",
link_titlefield:"Title",
link_is_email:"The URL you entered seems to be an email address, do you want to add the required mailto: prefix?",
link_is_external:"The URL you entered seems to external link, do you want to add the required http:// prefix?",
link_list:"Link list"
});


// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (WikiTextConverter == null) var WikiTextConverter = {};
WikiTextConverter._path = '/dwr';
WikiTextConverter.getPreferredEditorMode = function(callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'getPreferredEditorMode', false, callback);
}
WikiTextConverter.cleanUpHtml = function(p0, p1, callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'cleanUpHtml', p0, p1, false, callback);
}
WikiTextConverter.convertFromWiki = function(p0, p1, callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'convertFromWiki', p0, p1, false, callback);
}
WikiTextConverter.convertToWiki = function(p0, p1, callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'convertToWiki', p0, p1, false, callback);
}
WikiTextConverter.convertToPreviewFromXhtml = function(p0, p1, callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'convertToPreviewFromXhtml', p0, p1, false, callback);
}
WikiTextConverter.convertToPreviewFromWiki = function(p0, p1, callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'convertToPreviewFromWiki', p0, p1, false, callback);
}
WikiTextConverter.setPreferredEditorMode = function(p0, callback) {
  dwr.engine._execute(WikiTextConverter._path, 'WikiTextConverter', 'setPreferredEditorMode', p0, false, false, callback);
}



// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (Watches == null) var Watches = {};
Watches._path = '/dwr';
Watches.isThreadWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isThreadWatched', p0, callback);
}
Watches.isDocumentWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isDocumentWatched', p0, callback);
}
Watches.isBlogWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isBlogWatched', p0, callback);
}
Watches.isBlogPostWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isBlogPostWatched', p0, callback);
}
Watches.isUserWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isUserWatched', p0, callback);
}
Watches.isCommunityWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isCommunityWatched', p0, callback);
}
Watches.isProjectWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isProjectWatched', p0, callback);
}
Watches.isTaskWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isTaskWatched', p0, callback);
}
Watches.isSocialGroupWatched = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'isSocialGroupWatched', p0, callback);
}
Watches.watchThread = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchThread', p0, callback);
}
Watches.watchDocument = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchDocument', p0, callback);
}
Watches.watchBlog = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchBlog', p0, callback);
}
Watches.watchBlogPost = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchBlogPost', p0, callback);
}
Watches.watchUser = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchUser', p0, callback);
}
Watches.watchCommunity = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchCommunity', p0, callback);
}
Watches.watchProject = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchProject', p0, callback);
}
Watches.watchTask = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchTask', p0, callback);
}
Watches.watchSocialGroup = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchSocialGroup', p0, callback);
}
Watches.watchVideo = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'watchVideo', p0, callback);
}
Watches.removeThreadWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeThreadWatch', p0, callback);
}
Watches.removeDocumentWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeDocumentWatch', p0, callback);
}
Watches.removeBlogWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeBlogWatch', p0, callback);
}
Watches.removeBlogPostWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeBlogPostWatch', p0, callback);
}
Watches.removeUserWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeUserWatch', p0, callback);
}
Watches.removeCommunityWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeCommunityWatch', p0, callback);
}
Watches.removeProjectWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeProjectWatch', p0, callback);
}
Watches.removeTaskWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeTaskWatch', p0, callback);
}
Watches.removeSocialGroupWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeSocialGroupWatch', p0, callback);
}
Watches.removeVideoWatch = function(p0, callback) {
  dwr.engine._execute(Watches._path, 'Watches', 'removeVideoWatch', p0, callback);
}



// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (Comment == null) var Comment = {};
Comment._path = '/dwr';
Comment.previewComment = function(p0, p1, p2, callback) {
  dwr.engine._execute(Comment._path, 'Comment', 'previewComment', p0, p1, p2, false, callback);
}
Comment.updateComment = function(p0, callback) {
  dwr.engine._execute(Comment._path, 'Comment', 'updateComment', p0, false, callback);
}
Comment.deleteComment = function(p0, callback) {
  dwr.engine._execute(Comment._path, 'Comment', 'deleteComment', p0, false, callback);
}



/* bookmark Mgr holds the cache for all people bookmarking a particular piece of data.
    Basic cache in -> cache out stuff */
function bookmarkMgr() {
	
	var cache = new jive.ext.y.HashTable();
	
	this.addUser = function(obj){
		cache.put(obj.userId, obj);
	}
			
	this.getUser = function(userId) {
		return cache.get(userId);
	}
	
}

var bookmarkInfo = new bookmarkMgr();


$j(function() {
    var $mainContainer = $j("#jive-bookmark-list");
    var $mainForm = $j("#view-favorite-activity-form");

    /* handle select box change */
    /* update the class on the container, set page value to first page, load in html for the correct page */
	$j("#jive-view-picker").change(function() {    
        var $page = $j("#view-favorite-activity-form").find(":hidden[name=page]");
        $page.val(1);
        hideElement($j(".view-previous"), true);
        var view = $j(this).val();
        $j("#jive-bookmark-list").html("");
        
        if (view == "everyone" || view == "friends") {
            $j("#jive-bookmark-list").removeClass("jive-notes-list").addClass("jive-avatar-list");;
        } else {
            $j("#jive-bookmark-list").removeClass("jive-avatar-list").addClass("jive-notes-list");
        }
        $mainContainer.data("knownPages", 1);
        goToPage(1, false, true);
        
    })

    $j(".jive-more").click(function() {
        /* increment the page, go to the page */
        var $page = $j("#view-favorite-activity-form").find(":hidden[name=page]");
        $page.val(parseInt($page.val()) + 1);

        goToPage($page.val(), true);

        return false;
    });
    $j(".jive-less").click(function() {
        /* decrement the page, go to the page */
        var $page = $j("#view-favorite-activity-form").find(":hidden[name=page]");
        $page.val(parseInt($page.val()) - 1);

        goToPage($page.val(), true);

        return false;
    });

    
    function goToPage(pageTarget, animate, initialLoad) {
        /* check if page exists */
        if ($mainContainer.find("ul.page-" + pageTarget).size() <= 0) {
            /* page does not exist. load it in */
            $mainForm.ajaxSubmit({
                success: function(result) {
                    if (initialLoad) {
                        $mainContainer.html(result);
                        $mainContainer.find("ul:first").css("margin-top", "20px");
                    } else {
                        $mainContainer.append(result);
                    }
                    goToPagePart2(pageTarget, animate)
                }
            })
        } else {
            goToPagePart2(pageTarget, animate);
        }

    }
    
    function goToPagePart2(pageTarget, animate) {
        var currentPage = parseInt($mainForm.find(":hidden[name=page]").val());
        
        renderPagination(animate);
        
        /* set height on the container */
        var newHeight = $j("ul.page-" + pageTarget).height();
     
        if (animate) {
            $mainContainer.animate({"height": newHeight}, 100);
            $mainContainer.scrollTo("ul.page-" + pageTarget + " li:first", 400,  {margin: true, onAfter: function() { }});
        } else {
            $mainContainer.scrollTo("ul.page-" + pageTarget + " li:first",  {margin:true});
            $mainContainer.height(newHeight);
        }
    }

    /* hide / show the view previous / next buttons as necessary. Also remove the straggler and update "knownPages" */
    function renderPagination(animate) {
        var currentPage = parseInt($mainForm.find(":hidden[name=page]").val());
        var $straggler = $mainContainer.find("ul.page-" + currentPage).find("li.page-" + (currentPage + 1));
        
        /* if the previous page exists */
        if ($mainContainer.find("ul.page-" + (currentPage - 1)).size() > 0) {
            showElement($mainContainer.siblings(".view-previous"), animate);
        } else {
            hideElement($mainContainer.siblings(".view-previous"), animate);
        }
        
        /* if the next page exists already */
        if (($mainContainer.find("ul.page-" + (currentPage + 1)).size() > 0) || (currentPage < parseInt($mainContainer.data("knownPages")))) {
            showElement($mainContainer.siblings(".view-next"), animate);
        } else if ($straggler.size() > 0) {
            showElement($mainContainer.siblings(".view-next"), animate);
            $mainContainer.data("knownPages", $mainContainer.data("knownPages") + 1);
            $straggler.remove();
        } else {
            hideElement($mainContainer.siblings(".view-next"), animate);
        }
    }

    function showElement ($element, animate) {
		if (animate) {
			$element.slideDown(500);
		} else {
			$element.show();
		}
	}
	
	function hideElement ($element, animate) {
		if (animate) {
			$element.slideUp(500);
		} else {
			$element.hide();
		}
	}
});

/**
 * jQuery.ScrollTo - Easy element scrolling using jQuery.
 * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 9/11/2008
 * @author Ariel Flesler
 * @version 1.4
 *
 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
 */
;(function(h){var m=h.scrollTo=function(b,c,g){h(window).scrollTo(b,c,g)};m.defaults={axis:'y',duration:1};m.window=function(b){return h(window).scrollable()};h.fn.scrollable=function(){return this.map(function(){var b=this.parentWindow||this.defaultView,c=this.nodeName=='#document'?b.frameElement||b:this,g=c.contentDocument||(c.contentWindow||c).document,i=c.setInterval;return c.nodeName=='IFRAME'||i&&h.browser.safari?g.body:i?g.documentElement:this})};h.fn.scrollTo=function(r,j,a){if(typeof j=='object'){a=j;j=0}if(typeof a=='function')a={onAfter:a};a=h.extend({},m.defaults,a);j=j||a.speed||a.duration;a.queue=a.queue&&a.axis.length>1;if(a.queue)j/=2;a.offset=n(a.offset);a.over=n(a.over);return this.scrollable().each(function(){var k=this,o=h(k),d=r,l,e={},p=o.is('html,body');switch(typeof d){case'number':case'string':if(/^([+-]=)?\d+(px)?$/.test(d)){d=n(d);break}d=h(d,this);case'object':if(d.is||d.style)l=(d=h(d)).offset()}h.each(a.axis.split(''),function(b,c){var g=c=='x'?'Left':'Top',i=g.toLowerCase(),f='scroll'+g,s=k[f],t=c=='x'?'Width':'Height',v=t.toLowerCase();if(l){e[f]=l[i]+(p?0:s-o.offset()[i]);if(a.margin){e[f]-=parseInt(d.css('margin'+g))||0;e[f]-=parseInt(d.css('border'+g+'Width'))||0}e[f]+=a.offset[i]||0;if(a.over[i])e[f]+=d[v]()*a.over[i]}else e[f]=d[i];if(/^\d+$/.test(e[f]))e[f]=e[f]<=0?0:Math.min(e[f],u(t));if(!b&&a.queue){if(s!=e[f])q(a.onAfterFirst);delete e[f]}});q(a.onAfter);function q(b){o.animate(e,j,a.easing,b&&function(){b.call(this,r,a)})};function u(b){var c='scroll'+b,g=k.ownerDocument;return p?Math.max(g.documentElement[c],g.body[c]):k[c]}}).end()};function n(b){return typeof b=='object'?b:{top:b,left:b}}})(jQuery);

/*
 * jQuery EasIng v1.1.2 - http://gsgd.co.uk/sandbox/jquery.easIng.php
 *
 * Uses the built In easIng capabilities added In jQuery 1.1
 * to offer multiple easIng options
 *
 * Copyright (c) 2007 George Smith
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 */

// t: current time, b: begInnIng value, c: change In value, d: duration

jQuery.extend( jQuery.easing,
{
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

;(function($) {
    var tooltips = {};

    $.fn.tooltip = function(id, url, options) {
        var superNoteId;
        if (!tooltips[id]) {
            superNoteId = createTooltip(id);
        }
        else {
            superNoteId = tooltips[id];
        }
        $(this).addClass(superNoteId + "-hover-" + id);
        $(this).mouseover(function() {
            $("#" + "jive-note-" + id + "-body").html('<span class="jive-tooltip2-loading" />');
            $("#" + "jive-note-" + id + "-body").load(url);
        });
        console.log(url);
    };

    function createTooltip(id) {
        var superNoteId = randomString(4);
        var ttId = superNoteId + "-note-" + id;
        tooltips[id] = superNoteId;
        new SuperNote(superNoteId, {showDelay: 700, hideDelay: 100, cssProp: 'visibility', cssVis: 'visible',
            cssHid: 'hidden'});
        $("body").append("<div id=\"" + ttId + "\"/>");
        var bookmarkTT = $("#" + ttId);

        bookmarkTT.addClass("jive-tooltip2").addClass("notedefault").addClass("snp-mouseoffset");
        bookmarkTT.append("<div class=\"jive-tooltip2-top\"></div>");
        bookmarkTT.append("<div class=\"jive-tooltip2-mid\"></div>");
        var bookmarkTTMid = bookmarkTT.children(".jive-tooltip2-mid");

        var noteId = "jive-note-" + id + "-body";
        bookmarkTTMid.append("<div id=\"" + noteId + "\" class=\"jive-tooltip2-mid-padding note-bookmark-body\">AWESOME</div>");
        bookmarkTT.append("<div class=\"jive-tooltip2-btm\"/>");
        return superNoteId;
    }

    function randomInt(intFrom, intTo, intSeed) {
        // Make sure that we have integers.
        intFrom = Math.floor(intFrom);
        intTo = Math.floor(intTo);

        // Return the random number.
        return(
                Math.floor(intFrom + (
                        (intTo - intFrom + 1) *

                            // Seed the random number if a value was passed.
                        Math.random((intSeed != null) ? intSeed : 0)
                        ))
                );
    }

    function randomString(len) {
        // Define local variables.
        var strLargeText = "";
        var intValue = 0;
        var arrCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;

        // Loop over number of characters in string.
        for (var intI = 0; intI < len; intI++) {

            // Get a random value between 0 and the length of the
            // character list.
            intValue = randomInt(0, (arrCharacters.length - 1), intI);

            // Append a character that is randomly chosen
            strLargeText += arrCharacters.charAt(intValue);

        }
        return strLargeText;
    }
})(jQuery);


// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (MoreLikeThis == null) var MoreLikeThis = {};
MoreLikeThis._path = '/dwr';
MoreLikeThis.getMoreLikeThisResults = function(p0, p1, p2, callback) {
  dwr.engine._execute(MoreLikeThis._path, 'MoreLikeThis', 'getMoreLikeThisResults', p0, p1, p2, callback);
}
MoreLikeThis.getMoreLikeThisResultsByType = function(p0, p1, p2, p3, callback) {
  dwr.engine._execute(MoreLikeThis._path, 'MoreLikeThis', 'getMoreLikeThisResultsByType', p0, p1, p2, p3, callback);
}



// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (FollowingActionBean == null) var FollowingActionBean = {};
FollowingActionBean._path = '/dwr';
FollowingActionBean.followCommunity = function(p0, p1, callback) {
  dwr.engine._execute(FollowingActionBean._path, 'FollowingActionBean', 'followCommunity', p0, p1, callback);
}
FollowingActionBean.followProject = function(p0, p1, callback) {
  dwr.engine._execute(FollowingActionBean._path, 'FollowingActionBean', 'followProject', p0, p1, callback);
}



// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (FriendListAction == null) var FriendListAction = {};
FriendListAction._path = '/dwr';
FriendListAction.getFriends = function(callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'getFriends', callback);
}
FriendListAction.getFriends = function(p0, p1, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'getFriends', p0, p1, callback);
}
FriendListAction.createList = function(p0, p1, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'createList', p0, p1, callback);
}
FriendListAction.updateList = function(p0, p1, p2, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'updateList', p0, p1, p2, callback);
}
FriendListAction.deleteList = function(p0, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'deleteList', p0, callback);
}
FriendListAction.getListsForUser = function(p0, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'getListsForUser', p0, callback);
}
FriendListAction.addRelationshipsToList = function(p0, p1, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'addRelationshipsToList', p0, p1, callback);
}
FriendListAction.removeRelationshipsFromList = function(p0, p1, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'removeRelationshipsFromList', p0, p1, callback);
}
FriendListAction.emailFriends = function(p0, p1, p2, p3, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'emailFriends', p0, p1, p2, p3, callback);
}
FriendListAction.watchFriends = function(p0, p1, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'watchFriends', p0, p1, callback);
}
FriendListAction.removeFriend = function(p0, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'removeFriend', p0, callback);
}
FriendListAction.removeFriends = function(p0, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'removeFriends', p0, callback);
}
FriendListAction.addFriend = function(p0, p1, p2, callback) {
  dwr.engine._execute(FriendListAction._path, 'FriendListAction', 'addFriend', p0, p1, p2, callback);
}


