// Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
//
// This file is auto-generated from mwbuild - DO NOT EDIT
//
var USE_INCLUDE = false;
var AUTOCOMPLETE_PAGE_SIZE = 10;
var AUTOCOMPLETE_MIN_LEN = 1;
var MQL_COLLAPSE = true;
var MQL_BATCH = true;
var CHECK_CYCLIC_INHERITANCE = false;
var MW_SERVER_NAME = "www.metaweb.com";
var VERSION = "39";
var LOGANZRWEB_VIEW_TXN_LOG_URL = "";
viewTransactionIdLogURL = "";
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * ///////////////////
 * global variables //
 * ///////////////////
 */
/////////////////////
// Cookie handling //
/////////////////////
//------------------------------------------------------------------ readCookie
// returns the value of a cookie given the cookie name.
function readCookie(name){
  var cookieValue = "";
  name += "=";
  if(document.cookie.length > 0){ 
    var offset = document.cookie.indexOf(name);
    if(offset != -1){ 
      offset += name.length;
      var end = document.cookie.indexOf(";", offset);
      if(end == -1) end = document.cookie.length;
      cookieValue = document.cookie.substring(offset, end);
    }
  }
  return cookieValue;
}

//----------------------------------------------------------------- writeCookie
// a simple function to write a cookie. The "hours" parameter is 
// optional - if it is left out, the cookie value will expire when 
// the user's browser session ends
function writeCookie(name, value, hours, path){
  var expire = "";
  if(typeof hours != "undefined" && hours != null){
    // Safari does not understand ver1.1 cookies that support max-age as it always does browser expiration.
    // The example on Safari FAQ uses expires - http://developer.apple.com/internet/safari/faq.html#anchor6
    // expire set as the GMT date the cookie will expire
    expire = hours * 3600 * 1000;  // milliseconds
    var expireDate = new Date();
    expireDate.setTime(expireDate.getTime() + expire);
    expire = "; expires=" +  expireDate.toGMTString();  
  }
  if (typeof path != "undefined" && path != null) {
    path = "; path=" + path;
  }
  else
    path = "";
  // clear out legacy cookie, got installed without an existing path
  // in a previous client release. Not specifying the path to get the
  // 'default' which was wrong.
  document.cookie = name + "=" + String(value) + "; max-age=0";

  // now actually set the cookie
  document.cookie = name + "=" + String(value) + expire + path;
}

(function() {
browserVersion = -1;
browserChecked = "1" == readCookie("mwbrowserchecked");
isFirefox = false;
isIE = false;
isIE6 = false;
isIE7 = false;
isSafari = false;
userAgent = navigator.userAgent.toLowerCase();
if (/firefox\/(\d+(?:\.\d+))/.test(userAgent)) { 
  isFirefox = true;
  browserVersion = Number(RegExp.$1);
}
else if (/safari\/(\d+(?:\.\d+))/.test(userAgent)) {  
  isSafari = true;
  browserVersion = Number(RegExp.$1);
}
else if (/msie (\d+(?:\.\d+))/.test(userAgent)) { 
  isIE = true;
  browserVersion = Number(RegExp.$1);
  isIE6 = browserVersion >= 6 && browserVersion < 7;
  isIE7 = browserVersion >= 7;
}
 })();

///////////////////
// check for XHR //
///////////////////
if(typeof XMLHttpRequest == "undefined" && typeof ActiveXObject != "undefined"){
  XMLHttpRequest = function(){return (new ActiveXObject("MSXML2.XmlHttp"));};
}
if(!browserChecked && typeof XMLHttpRequest == "undefined"){
  // TODO: handle non AJAX capable browsers
  alert(bmsg);
  writeCookie("mwbrowserchecked", "1", 336, "/");
}

////////////////////
// empty function //
////////////////////
function NO_OP() {};

/////////////////////
// generate pageID //
/////////////////////
function pageID () {
  // PageID = location.href + 8 hexes;
  var NUM_CHARS = 8;
  var st="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  var stLen = st.length;
  var key = "";
  for (var i = 0; i < NUM_CHARS; i++) {
    // Pick a random character from the 'st' string and add it to the key
    var r = Math.floor (stLen * Math.random ());
    key += st.charAt(r);
  }
  
  var uri = window.location.pathname + window.location.search;
  //var uri = window.location.href;
  return key+uri;
}

// Generate a fresh pageID on each page HTML page load and put it out as a cookie
// so that all resource fetches and XHR requests can be reconciled to this original
// pageload. Store in global variable BrowserPID in case other JS want to reference it.
writeCookie("BrowserPID", pageID(), null, "/");

var g_transactionIdList = new Array();
if ('PAGE_TID' in window)
  g_transactionIdList.push(PAGE_TID)
if(typeof viewTransactionIdLogURL=="undefined") viewTransactionIdLogURL = "";
var g_windowLoaded = false;

///////////////////////////////////////////////////////////////
// Utility to get arguments from window.location querystring //
///////////////////////////////////////////////////////////////
var WINDOW_ARGS = {};  // map of name:value in the query string
(function() {
var q = window.location.search;
if(q && q.length > 0){
  q = q.substring(1);
  var vars = q.split("&");
  for(var i=0, len=vars.length ; i<len; i++){
    var index = vars[i].indexOf("=");
    if(index != -1){
      var name = vars[i].substring(0, index);
      var value = vars[i].substring(index+1);
      if (! (name in WINDOW_ARGS))
          WINDOW_ARGS[name] = [];
      // decodeURIComponent doesn't convert "+" to space
      var value_esc = value.replace(/\+/g," ");
      WINDOW_ARGS[name].push(decodeURIComponent(value_esc));
    }
  }
 }})();

//------------------------------------------------------------- arg
// @param multiple:Boolean - whether or not to return an array of all
//                           arguments, or return a single argument
function arg(name, multiple) {
    if (name in WINDOW_ARGS)
        if (multiple)
            return WINDOW_ARGS[name];
        else
            return WINDOW_ARGS[name][0];
    else
        if (multiple)
            return [];
        else
            return null;
};

//////////////////////////////////////////////////////////////////////////////////////
// A wrapper around firebug's console, enabled by cookie or debug=true in url query //
//////////////////////////////////////////////////////////////////////////////////////
function Debug(){};
Debug.METHODS = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
 "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
Debug.LEVEL = [
  "none",
  "error",
  "all"
];

//----------------------------------------------------- enable
Debug.enable = function(debugLevel) {
  for(var i=0, len=Debug.METHODS.length; i<len; i++) {
    var m = Debug.METHODS[i]; 
    switch(debugLevel) {
      case "error":
        if (m != "error") Debug[m] = NO_OP;
        break;
      case "all":       
        if (m != "error") Debug[m] = ("console" in window && m in window.console ? window.console[m] : NO_OP);
        break;
      default:
        Debug[m] = NO_OP;
        break;
    };
    // _method always invokes console
    if ("console" in window && m in window.console) {
      Debug["_" + m] = window.console[m];
    }
    else {
      Debug["_" + m] = NO_OP;
    }
  }
};

//----------------------------------------------------- error
Debug.error = function() {
  if ("console" in window) {
    if ("open" in window.console)
      window.console.open();
    
    if ("error" in window.console)
      window.console.error.apply(window.console, arguments);
  }
};

var debugLevel = readCookie("debugLevel");
if (!debugLevel) debugLevel = "none";
Debug.enable(debugLevel);

// dae: Please make this work in all browsers so Debug.trace can call this
//
//------------------------------------------------------------- formatStack
function formatStack(start, ex) {
  if (ex != undefined)
    start = 0;
  else if (start === undefined)
    start = 1;
  
  if (ex==undefined)
    try {
      // garbage
      xyz[pdq] = lakjsdflkj;
    } catch (e) {
      ex = e;
    }
  
  // only works on mozilla!
  if (typeof(ex) == "object" && 'stack' in ex) {
    var stack = ex.stack.split('\n');
    var formattedStack = [];
    for (var i=start; i<stack.length; i++) {
      var frame = stack[i].split('@');
      // sourceFile:lineNumber: declaration....
      formattedStack.push(frame[1] + ": " + frame[0].slice(0,50));
    }
    
    formattedStack = formattedStack.join("\n");
    return formattedStack;
  } else if (typeof ex == "string") {
    return "(no stack, ex was a " + typeof(ex) + "!!)";
  }
  
  return "";
}
//------------------------------------------------------------- dumpStack
function dumpStack(ex) {
  Debug.log(formatStack(2, ex));
}


//////////////////////
// include          //
//////////////////////
var DEFAULT_USE_INCLUDE = false;
if(typeof USE_INCLUDE == "undefined"){
  USE_INCLUDE = DEFAULT_USE_INCLUDE;
}
var DEFAULT_INCLUDE_PATH_PREFIX = "/library/";
if(typeof INCLUDE_PATH_PREFIX == "undefined"){
  INCLUDE_PATH_PREFIX = DEFAULT_INCLUDE_PATH_PREFIX;
}
var INCLUDE = {}; // keep track of includes
//--------------------------------------------------------------------- include
// @param file:String
// @returns Box
if(USE_INCLUDE){
  include = function(file){
    if(INCLUDE[file] == undefined){
      INCLUDE[file] = 0;
      file = INCLUDE_PATH_PREFIX + file;    
      var includeXHR = new XMLHttpRequest();
      includeXHR.open("GET", file, false);
      includeXHR.send(null);
      if(includeXHR.status == 200 || includeXHR.status == 0){
        try {      
            if(isIE){      
              window.execScript(includeXHR.responseText);
            }
            else {
              window.eval(includeXHR.responseText);
            }
        }
        catch (ex) {
            Debug.error("[include] error evaluating", file, ":", ex);
        }
      }
      // http error:
      else{
        Debug.error( "Problem retrieving the HTTPRequest (", file, "):",
                      includeXHR.status, includeXHR.statusText, 
                      "\n\nHere is the full response text:\n\n",
                      includeXHR.responseText);
      }
    }
    INCLUDE[file] = INCLUDE[file] + 1; // tally up total calls to includes    
  }
}
else {
  include = NO_OP;
}

//////////////////////
// inheritance      //
//////////////////////

//------------------------------------------------------------ applyNew
// @param classFunction:Function
// @param firstArg:Object
// @param args:Array
function applyNew (classFunction, args) {
  // create a dummy class with the same prototype as classFunction, so
  // we start with a valid empty class
  function K() {};
  K.prototype = classFunction.prototype;
  var result = new K();
  
  // now call the constructor with the arguments
  classFunction.apply(result, args);
  return result;
}

//------------------------------------------------------------ superConstructor
// @param instance:Object
// @param superClass:Function
// @param args:Array
function superConstructor(superClass, instance, args){
  if(args == undefined) {
    args = [];
  }
  superClass.apply(instance, args);
}
//----------------------------------------------------------------- superMethod
// @param superClass:Function
// @param instance:Object
// @param methodName:String
// @param args:Array
function superMethod(superClass, instance, methodName, args){
  if(args == undefined) {
    args = [];
  }
  return (superClass.prototype[methodName].apply(instance, args));
}
//------------------------------------------------------------- InheritanceNode
// @param clazz:Function
function InheritanceNode(clazz){
  this._clazz = clazz;
  this._children = new Array();
}
InheritanceNode.prototype.getClass = function(){ 
  return (this._clazz); 
}
InheritanceNode.prototype.getChildren = function(){ 
  return (this._children); 
}
InheritanceNode.prototype.addChild = function(c){ 
  this._children.push(c); 
}
InheritanceNode.prototype.toString = function(){
  if ('_inheritanceClassName' in this)
      return this._inheritanceClassName;
  
  var name = [this._clazz];
  name.push(" <[ ");
  var children = this._children;
  for(var i=0, l=children.length; i<l; i++){
      name.push(children[i].toString());
  }
  name.push(" ]> ");
  
  var className = this._inheritanceClassName = name.join("");
  
  return className;
}
//------------------------------------------------------------- InheritanceTree
// @param clazz:Function
function InheritanceTree(clazz){
  this._root = new InheritanceNode(clazz);
  InheritanceTree.createTree(this._root);
}
var ObjectMap = [];
InheritanceTree.createTree = function(node){
  var clazz = node.getClass();
  if(clazz.superClass != undefined){
    for(var i=0, l=clazz.superClass.length; i<l; i++){
      var superClass = clazz.superClass[i];
      if (!superClass) {
          console.log("Couldn't find superClass for " + clazz.superClass[i]);
      }
      var childNode = new InheritanceNode(superClass);
      node.addChild(childNode);
      InheritanceTree.createTree(childNode);
    }
  }
}
InheritanceTree.prototype.getRoot = function(){ 
  return (this._root); 
}
InheritanceTree.prototype.isCyclic = function(){
  var visitor = new GraphVisitor();
  this.dfs(this.getRoot(), visitor, [], []);
  return (visitor.detectedCycle());
}
InheritanceTree.prototype.dfs = function(node, visitor, preOrder, postOrder){
  // mark node as gray
  visitor.startVisit(node);
  preOrder.push(node);
  var children = node.getChildren();
  for(var i=0, l=children.length; i<l; i++){
    var child = children[i];
    if(visitor.isWhite(child)){
      this.dfs(child, visitor, preOrder, postOrder);
    }
    else if(visitor.isGray(child)){
      visitor.addCycle(child);
    }
  }
  postOrder.push(node);
  // mark node as black
  visitor.finishVisit(node);
}
//---------------------------------------------------------------- GraphVisitor
/**
 * As commonly practiced, the dfs and bfs algorithms color the
 * verices white, gray, and black - white means the vertex has not
 * been visited by the algorithm, gray means the vertex has been
 * discovered and in the queue for processing, and black means the
 * algorithm has finished visiting the vertex (dequeued and
 * expanded).
 */
function GraphVisitor(){
  this._visitStarted = {};
  this._visitFinished = {};
  this._cycles = new Array();
}

GraphVisitor.visitorKey = 0;
GraphVisitor.getKey = function(node) {
    // annotate the node with a key
    var key;
    if ('_visitorKey' in node)
        key = node._visitorKey;
    else
        key = node._visitorKey = GraphVisitor.visitorKey++;
    return key;
};

GraphVisitor.prototype.startVisit = function(node){
  var key = GraphVisitor.getKey(node);
  this._visitStarted[key] = true;
}
GraphVisitor.prototype.finishVisit = function(node){
  var key = GraphVisitor.getKey(node);
  this._visitFinished[key] = true;
}
GraphVisitor.prototype.isWhite = function(node){
    var key = GraphVisitor.getKey(node);
    return (!((key in this._visitStarted) ||
              (key in this._visitFinished)));
}
GraphVisitor.prototype.isGray = function(node){
    var key = GraphVisitor.getKey(node);
    return ((key in this._visitStarted) &&
            !(key in this._visitFinished));
}
GraphVisitor.prototype.isBlack = function(node){
    var key = GraphVisitor.getKey(node);
    return ((key in this._visitStarted) &&
            (key in this._visitFinished));
}
GraphVisitor.prototype.addCycle = function(node){
  this._cycles.push(node);
}
GraphVisitor.prototype.detectedCycle = function(){
  return (this._cycles.length > 0);
}
GraphVisitor.prototype.arrayContains = function(arr, item){
  for(var i=0, len=arr.length; i<len; i++){
  	if(arr[i] == item){
  	  return true;
  	}
  }
  return false;
};
//------------------------------------------------------------ setupInheritance
function setupInheritance(clazz){
  if(clazz._inheritInitialized == undefined){
    var tree = new InheritanceTree(clazz);
    if(CHECK_CYCLIC_INHERITANCE && tree.isCyclic()){
      Debug.error("Cannot initialize inheritance tree for ",
            clazz, " because it contains cycle");
      return;
    }
    setupInheritanceRecursive(tree.getRoot());
  }
}

//--------------------------------------------------- setupInheritanceRecursive
function registerClass(clazz, className) {
    ObjectMap[className] = clazz;
    clazz._className = className;
}

//--------------------------------------------------- setupInheritanceRecursive
function setupInheritanceRecursive(inode){
  var clazz = inode.getClass();
  if(clazz._inheritInitialized == undefined){
    var parent = inode;
    var children = parent.getChildren();
    for(var i=0, l=children.length; i<l; i++){
      var child = children[i];
      setupInheritanceRecursive(child);
      inherit(parent.getClass(), child.getClass());
    }
    clazz._inheritInitialized = true;
  }
}
//-------------------------------------------------------------- inheritClasses
// @param superClass:Function
// @param classList:Array
function inheritClasses(superClass, classList){
  classList.push(superClass);
  if(typeof superClass._superClasses == "object"){
    for(var i=0; i < superClass._superClasses.length; i++){
      inheritClasses(superClass._superClasses[i], classList);
    }
  }
}
//--------------------------------------------------------------------- inherit
// @param subClass
// @param superClass
function inherit(subClass, superClass){
  if(subClass.prototype.constructor._superClasses == undefined){
    subClass.prototype.constructor._superClasses = new Array();
  }
  inheritClasses(superClass, subClass.prototype.constructor._superClasses);
  for(var property in superClass.prototype){
    if(subClass.prototype[property] == undefined){
      subClass.prototype[property] = superClass.prototype[property];
    }
  }
}


///////////////////////
// globals functions //
///////////////////////
 
//------------------------------------------------------------- getClassName
// @param c:Function or Function instance
function getClassName(c){
  if ('_className' in c)
      return c._className;
  var className = this.CLASS_NAME;
  if (typeof this.CLASS_NAME == "undefined" || this.CLASS_NAME == null){
    if(typeof c == "function"){
      var m = c.prototype.constructor.toString().match(/function\s*([^\(\)\s]+)/);
      className = (m && m.length == 2 ? m[1] : undefined);
    }
    else if(typeof c == "object"){
      var m = c.constructor.toString().match(/function\s*([^\(\)\s]+)/);
      className = (m && m.length == 2 ? m[1] : undefined);
    }
//    Debug.warn("Please set the CLASS_NAME variable for", className);
  }
  this._className = className;
  return className;
}

//------------------------------------------------------------- cloneObject
function cloneObject(obj) {
  if (obj === null)
    return null;
  if (typeof obj == "object") {
    var result;
    if (obj instanceof Array) {
      result = [];
      for (var i=0; i<obj.length; i++)
        result.push(cloneObject(obj[i]));
    } else {
      result = {}
      for (var k in obj)
        result[k] = cloneObject(obj[k]);
    }
    return result;
  }

  // all other data types should be immutable
  return obj;
};

//------------------------------------------------------------- doLater
// delay is optional, default=0 meaning as soon as
// possible but still async
function doLater(owner, method, delay, params){
  if(delay==undefined) delay = 0;
  if(params==undefined) params = [];
  if(owner==null) owner = arguments.caller;
  var handler = function(){
    method.apply(owner, params);
  }
  return setTimeout(handler, delay);
}

//------------------------------------------------------------- wait
// for debug, TODO: this should be removed for deploy
function wait(ms){
  var start = new Date().getTime();
  var current = start;
  while((current-start) < ms){
    current = new Date().getTime();
  }
}

//------------------------------------------------------------- addWindowOnloadHandler
function addWindowOnloadHandler(handler){
  if(typeof window.addEventListener != "undefined")
    window.addEventListener("load", handler, false);
  else if(typeof window.attachEvent != "undefined"){
    window.attachEvent( "onload", handler);
  }
}

//------------------------------------------------------------- addWindowOnunloadHandler
function addWindowOnunloadHandler(handler){
  if(typeof window.addEventListener != "undefined")
    window.addEventListener("unload", handler, false);
  else if(typeof window.attachEvent != "undefined"){
    window.attachEvent( "onunload", handler);
  }
}

//------------------------------------------------------------- openLocation
function openLocation(loc) {
  if (loc) {
    window.location = loc;
  }
  return false;
};

//------------------------------------------------------------- reload
function reload() {
  window.location.reload();
};

//--------------------------------------------------------------- $id
// shorthand for 
// document.getElementById(id)
function $id(id) {
  return document.getElementById(id);
};

//--------------------------------------------------------------- $elt
// shorthand for 
// var elt = document.createElement(tagName)
// elt.className = className
// parent.appendChild(elt)
function $elt(tagName, parent, className) {
  var elt = document.createElement(tagName);
  if (className) {
    elt.className = className;
  }
  if (parent) {
    parent.appendChild(elt);
  }
  return elt;
};

//-------------------------------------------------------------- discardElement
// Sometimes Internet Explorer keeps items in memory after the page is done using them. 
// Although these pseudo-leaks are freed after the user leaves the page, 
// some web pages may be open for long periods of time.
// To avoid pseudo-leaks, do not use removeChild to remove elements. 
// Instead, set the parent's innerHTML to "" or use a function like this:
function discardElement(element) {
  if (!element) return;
  var garbageBin = $id('IELeakGarbageBin');
  if (!garbageBin) {
    garbageBin = $elt('DIV');
    garbageBin.id = 'IELeakGarbageBin';
    garbageBin.style.display = 'none';
    document.body.appendChild(garbageBin);
  }
  // move the element to the garbage bin
  garbageBin.appendChild(element);
  garbageBin.innerHTML = '';
};

//------------------------------------------------------------- createElementWithName
// This fixes IE bug with ÒSetting the ÒnameÓ attribute in Internet Explorer
// See original blog and particular comments at end where this solution was derived from
//   http://www.thunderguy.com/semicolon/2005/05/23/setting-the-name-attribute-in-internet-explorer/
// Since we add DOM events, innerHTML's problems with using innerHTML necessitated this approach
// See also problem with read/write-once with type for input elements - hence createTypedElement
//   http://alt-tag.com/blog/archives/2006/02/ie-dom-bugs/
function createNamedTypedElement(){};

(function() {
try {
  var el=$elt( '<div name="foo">' );
  if( 'div'!=el.tagName.toLowerCase() ||
      'foo'!=el.name ){
    throw 'create element error';
  }
  createNamedTypedElement = function( tag, nameValue, typeValue, parent, attrs ){
    var nameString = "";
    if (typeof nameValue != "undefined" && nameValue)
      nameString = 'name="' + nameValue + '"';
    var typeString = "";
    if (typeof typeValue != "undefined" && typeValue)
      typeString = 'type="' + typeValue + '"';
    var el = $elt( '<'+tag + " " +nameString + " " + typeString+'></'+tag+'>', parent );
    for (var attr in attrs) 
      if (attr != "name" && attr != "type")
        el.setAttribute(attr, attrs[attr]);
    return el;
  }
}catch( e ){
  createNamedTypedElement = function( tag, nameValue, typeValue, parent, attrs ){
    var el = $elt( tag , parent);
    if (typeof nameValue != "undefined" && nameValue)
      el.setAttribute('name', nameValue);
    if (typeof typeValue != "undefined" && typeValue)
      el.setAttribute('type', typeValue);
    for (var attr in attrs) 
      if (attr != "name" && attr != "type")
        el.setAttribute(attr, attrs[attr]);
    return el;
  }
 }
 })();

// ---------------------------------------------------- sprintf
// sprintf("%s foo %s", "hello", "world") == "hello foo world"
// @return String
function sprintf(str) {  
  // this is state used in the replacement closure - starts with the
  // first argument after 'str' and advances through all arguments
  var argIndex = 1;
  var localArgs = arguments;
  
  // this gets called once for each match
  function replace() {
      return localArgs[argIndex++];
  }
  
  var res = str.replace(/%s/g, replace)
  
  return (res);
}

// ---------------------------------------------------- createHiddenInput
function createHiddenInput(form, name, values) {  
  var frag = document.createDocumentFragment();
  var input;
  for(var i=0, len=values.length; i<len; i++){
    input = createNamedTypedElement("input", name, "hidden", frag, {value:values[i], "class":"hidden"});
  }
  form.appendChild(frag);
}

// ---------------------------------------------------- autoclean
AUTOCLEAN_HEAP = [];

function autoclean(obj, finalizer) {
  AUTOCLEAN_HEAP.push([obj, finalizer]);
}

function autoclean_gc() {
  Debug.log("Cleaning..")
  for (var i=AUTOCLEAN_HEAP.length-1; i>=0; i--) {
    var heap_entry = AUTOCLEAN_HEAP[i];
    var obj = heap_entry[0];
    var finalizer = heap_entry[1];
    if (obj)
      finalizer(obj);
  }
  AUTOCLEAN_HEAP = [];
  Debug.log("Done.");
}

function finalize(obj) {
  // would be nice if this weren't O(n)
  for (var i=AUTOCLEAN_HEAP.length-1; i>=0; i--) {
    if (AUTOCLEAN_HEAP[i][0] === obj) {
      var finalizer = AUTOCLEAN_HEAP[i][1];
      finalizer(obj);
      
      // remove from the array
      AUTOCLEAN_HEAP.splice(i, 1);
    }
  }
}
addWindowOnunloadHandler(autoclean_gc);
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * Base helper routines
 * alecf@metaweb.com
 */
function recursive_filter(data, f) {
  // return a flattened list of all objects where f() is true
  results = [];
  function recurse(obj) {
    if (f(obj))
      results.push(obj);
    if (typeof obj == "object") {
      if (obj instanceof Array)
        for (var i=0; i<obj.length; i++)
          recurse(obj[i]);
        
      else
        for (var k in obj)
          recurse(obj[k]);
    }
  }
  recurse(data);
  
  // all other types are immutable
  return results;
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * JSONUtils class
 * mark@metaweb.com
 */
function JSONUtils(){}
JSONUtils.format = function(obj, plain, _indent){
  var str = [];                 // array of strings to be joined later
  if(_indent==undefined) _indent = "";
  var newIndent = _indent + "  ";
  var newLine = "\n";
  if (plain) {
    newIndent = "";
    newLine = "";
  }  
  switch(typeof obj){
    case "boolean":
      str.push(obj);
      break;
    case "number":
      str.push(obj);
      break;
    case "string":
      obj=obj.replace(/([\"\\])/g, '\\$1').replace(/\r/g, '').replace(/\n/g, '\\n');
      str.push('"'+obj+'"');
      break;
    case "object":
      if(obj instanceof Array){
        str.push("[");
        var first=true;
        for(var i=0, len=obj.length; i<len; i++){
          if (!first) {
            str.push(",");
          }
          first=false;
          str.push(JSONUtils.format(obj[i],plain,_indent));
        }
        str.push("]");
      }
      else{
        if(obj==null){
          str.push("null");
        }
        else{
          // make sure to sort the keys for proper caching
          str.push("{");
          var keys = [];
          for (var item in obj) {
            keys.push(item);
          }
          keys.sort();
          var first=true;
          for(var i=0, len=keys.length; i<len; i++) {
            if (!first) {
              str.push(",");
            }
            first=false;
            var item=keys[i];
            str.push(newLine+newIndent+'"'+item+'":');
            str.push(JSONUtils.format(obj[item],plain,newIndent));
          }
          if(item) str.push(newLine+_indent);
          str.push("}");
        }
      }
      break;
  }
  return str.join("");
};
JSONUtils.is_valid = function(s) {
  return /^(\"(\\.|[^\"\\\n\r])*?\"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(s);
};
JSONUtils._encoding_dict = { '[': 'A',
                             ']': 'B',
                             '{': 'C',
                             '}': 'D',
                             '<': 'E',
                             '>': 'F',
                             '.': 'G',
                             ',': 'H',
                             ':': 'I',
                             '"': 'J',
                             '\\': 'K',
                             '/': 'L',
                             '#': 'M',
                             '$': 'N',
                             '=': 'O',
                             '+': 'P',
                             '-': 'Q',
                             '*': 'R',
                             '(': 'S',
                             ')': 'T',
                             '|': 'U',
                             '_': 'V',
                             ' ': 'W',
                             '`': 'Y0',
                             '!': 'Y1',
                             '@': 'Y2',
                             '^': 'Y3',
                             '&': 'Y4',
                             ';': 'Y5',
                             '?': 'Y6',
                             '%': 'Y7',
                             "'": 'Y8',
                             '~': 'Y9'};
JSONUtils._init = function() {
  JSONUtils._decoding_dict = {};
  var encoding_dict = JSONUtils._encoding_dict;
  for (var k in encoding_dict) {
    JSONUtils._decoding_dict[encoding_dict[k]] = k;
  }
};
JSONUtils._init();
JSONUtils.encode = function(query) {
  return JSONUtils.encode_string(JSONUtils.format(query, true));
}
JSONUtils.encode_string = function(query_string) {
  // Given a JSON structure already encoded as a string, compress it
  var result = []
  for (var i=0; i<query_string.length; i++) {
    var c = query_string.charAt(i);
      
    if (c >= 'a' && c <= 'z')
      result.push(c);
    else if (c >= '0' && c <= '9')
      result.push(c);
    else if (c in JSONUtils._encoding_dict)
      result.push(JSONUtils._encoding_dict[c]);
    else if (c >= 'A' && c <= 'Z') {
      result.push('Y');
      result.push(c);
    } else {
      result.push('X');
      //var charcode = c.charCodeAt(0);
      var charcode = query_string.charCodeAt(i);
      if (charcode < 16)
        result.push("0" + charcode.toString(16));
      else
        result.push(charcode.toString(16));
    }
  }
    
  return result.join("");
};
JSONUtils.decode = function(encoded_string) {
  var json_string = JSONUtils.decode_string(encoded_string);
  
  // this is a little scary. is it really good to eval stuff from the url?
  // we should think about using a json parser
  if (!JSONUtils.is_valid(json_string))
    throw "Invalid JSON string: " + json_string;
  
  return eval("("+json_string+")");
}
  
JSONUtils.decode_string = function(encoded_string) {
  // Given an encoded string, decode it to a string representing a
  // serialized JSON structure
  result = [];
  last = null;
  for (var i=0; i<encoded_string.length; i++) {
    var c = encoded_string.charAt(i);
    if (last) {
      if (last == 'Y' && (c >= 'A' && c <= 'Z')) {
        result.push(c);
        last = null;
      } else if (last == 'Y' && (c >= '0' && c <= '9')) {
        result.push(JSONUtils._decoding_dict['Y' + c]);
        last = null;
      } else if (last == 'X' && ((c >= '0' && c <= '9') ||
                                 (c >= 'A' && c <= 'F'))) {
        last = c;
      } else if (((last >= '0' && last <= '9') ||
                  (last >= 'A' && last <= 'F')) &&
                 ((c >= '0' && c <= '9') ||
                  (c >= 'A' && c <= 'F'))) {
        result.push(unescape("%" + last + c));
        last = null;
      } else {
        throw "Character '" + c + "' was not expected here in " + encoded_string;
      }
    }
      
    else {
      if (c >= 'A' && c <= 'W')
        result.push(JSONUtils._decoding_dict[c]);
      else if (c >= 'a' && c <= 'z')
        result.push(c);
      else if (c >= '0' && c <= '9')
        result.push(c);
      else if (c == 'X' || c == 'Y')
        last = c;
      else
        throw "Character '" + c + "' was not expected here in " + encoded_string;
    }
  }
  return result.join("");
}
function KeyQuote() {};
KeyQuote._unencoded = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
KeyQuote._hex = "0123456789ABCDEF";
KeyQuote.encode = function(s) {
  var output = "";
  for (var i = 0; i < s.length ; i++) {
    var c = s.charAt(i);
    if (KeyQuote._unencoded.indexOf(c) != -1) {
      output += c;
    } else if ((c == '-' || c == '_') && (i != 0) && (i + 1 != s.length)) {
      output += c;
    } else {
      var cc = s.charCodeAt(i);
      output += "$";
      output += KeyQuote._hex.charAt(Math.floor(cc / 4096) % 16);
      output += KeyQuote._hex.charAt(Math.floor(cc / 256) % 16);
      output += KeyQuote._hex.charAt(Math.floor(cc / 16) % 16);
      output += KeyQuote._hex.charAt(cc % 16);
    }    
  }
  
  return output;
};
KeyQuote.decode = function(s) {
  // remove invalid characters from the key
  s = s.replace(/(?:[^A-Za-z0-9\_\-\$]|\$(?![0-9A-F]{4}))/g, "");
  var output = "";
  var i = 0;
  while (i < s.length) {
    var c = s.charAt(i);
    if (c != '$') {
      output += c;
      i++;
    } else {
      var cc = KeyQuote._hex.indexOf(s.charAt(i+1)) * 4096;
      cc +=  KeyQuote._hex.indexOf(s.charAt(i+2)) * 256;
      cc +=  KeyQuote._hex.indexOf(s.charAt(i+3)) * 16;
      cc +=  KeyQuote._hex.indexOf(s.charAt(i+4));
      output += String.fromCharCode(cc);
      i += 5;
    }  
  }
  return output;
};
KeyQuote.fixUnderscore = function(s) {
  // replace underscore with space
  s = s.replace(/_/g, " ");
  return s;
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * DOMUtils class
 * alee@metaweb.com
 * mark@metaweb.com
 * STATIC utilities for accessing DOM information
 */
function DOMUtils() {}
DOMUtils.getElementDimension = function(element) {
  return {w:element.offsetWidth, h:element.offsetHeight};
}
  DOMUtils.getElementLocation = function(element) {
    var x = 0;
    var y = 0;
    var parent = element;
    while ( parent ) {
      
      // <IE6_HACK>
      if (isIE6 && parent.getAttribute("id") == "rightGutterContent") {
        // hack to get position calculation working in our templates for IE6
        // until we fix our pages to not use margin percentage (margin-left: 68%)
        parent = parent.offsetParent;
        continue;
      }
      // </IE6_HACK>
  
      var borderXOffset = 0;
      var borderYOffset = 0;
      if ( parent != element ) {
        var borderXOffset = parseInt(parent.style.borderLeftWidth, 10);
        var borderYOffset = parseInt(parent.style.borderTopWidth, 10);
        borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
        borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
      }
      
      //Debug.log("parent:", parent.tagName, "class:", parent.className, "id:", parent.getAttribute("id"), "margin:", DOMUtils.getElementStyle(parent, "marginLeft"), "offset:", parent.offsetLeft, parent.offsetTop);
      x += parent.offsetLeft  + borderXOffset;
      y += parent.offsetTop  + borderYOffset;
      parent = parent.offsetParent;
    }    
    parent = element;
    while ( parent &&
          parent != document.body &&
          parent != document.documentElement ) {
      if ( parent.scrollLeft  )
        x -= parent.scrollLeft;
      if ( parent.scrollTop )
        y -= parent.scrollTop;
      parent = parent.parentNode;
    }    
    return {x:x, y:y};
}
DOMUtils.docScrollLeft = function() {
  if ( window.pageXOffset )
    return window.pageXOffset;
  else if ( document.documentElement && document.documentElement.scrollLeft )
    return document.documentElement.scrollLeft;
  else if ( document.body )
    return document.body.scrollLeft;
  else
    return 0;
}
DOMUtils.docScrollTop = function() {
  if ( window.pageYOffset )
    return window.pageYOffset;
  else if ( document.documentElement && document.documentElement.scrollTop )
    return document.documentElement.scrollTop;
  else if ( document.body )
    return document.body.scrollTop;
  else
    return 0;
}
DOMUtils.getElementStyle = function(elem, style){
  var computedStyle;
  if (typeof elem.currentStyle != 'undefined') { 
    computedStyle = elem.currentStyle; 
  }
  else if (document.defaultView) { 
    computedStyle = document.defaultView.getComputedStyle(elem, null); 
  }
  else return null;
  if (computedStyle == null) {return null;}
  return computedStyle[style];
}
DOMUtils.setElementStyle = function(element, styleString){
  if(isIE) element.style.setAttribute("cssText", styleString);
  else element.setAttribute("style", styleString);
}
DOMUtils.getWindowSize = function() 
{
  var myWidth = 0;
  var myHeight = 0;
  if(typeof(window.innerWidth) == 'number' ) 
  {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } 
  else if(document.documentElement &&
          (document.documentElement.clientWidth || 
           document.documentElement.clientHeight)) 
  {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } 
  else if(document.body && 
          (document.body.clientWidth || document.body.clientHeight)) 
  {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }
  
  return ({w:myWidth, h:myHeight});
}
DOMUtils.getWindowScrollPos = function() 
{
  var scrOfX = 0;
  var scrOfY = 0;
  if(typeof(window.pageYOffset) == 'number') 
  {
    //Netscape compliant
    scrOfY = window.pageYOffset;
    scrOfX = window.pageXOffset;
  }
  else if(document.body && 
          (document.body.scrollLeft || document.body.scrollTop)) 
  {
    //DOM compliant
    scrOfY = document.body.scrollTop;
    scrOfX = document.body.scrollLeft;
  }
  else if(document.documentElement &&
          (document.documentElement.scrollLeft || 
           document.documentElement.scrollTop)) 
  {
    //IE6 standards compliant mode
    scrOfY = document.documentElement.scrollTop;
    scrOfX = document.documentElement.scrollLeft;
  }
  return ({x:scrOfX, y:scrOfY});
}
DOMUtils.scrollIntoView = function(element, parent) {
  if (element && parent) {
    var parentPos = DOMUtils.getElementLocation(parent);
    var parentDim = DOMUtils.getElementDimension(parent);
    var scrollTop = parent.scrollTop;
    var itemPos = DOMUtils.getElementLocation(element);
    var itemDim = DOMUtils.getElementDimension(element);
    if (itemPos.y < (parentPos.y + scrollTop)) {
      element.scrollIntoView(false);
    }
    else if ((itemPos.y + itemDim.h) > (parentPos.y + scrollTop + parentDim.h)) {
      element.scrollIntoView(false);
    }
  }
};
DOMUtils.getChildNode = function(node, nodeName) {
  var result = $(node).find(nodeName + ":first")[0];
  if (!result)
    Debug.error("[DOMUtils.getChildNode] node not found:", nodeName);
  return result;
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * rights reserved under the copyright laws of the United States.
 *
 * <b>Delegate</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** Delegate
 * @constructor
 */
function Delegate()
{
}
/******************************************************** create
 * Creates a functions wrapper for the original function so that it runs 
 * in the provided context.
 *
 * @param obj:Object - Context in which to run the function
 * @param func:Function - Function to run.
 */
Delegate.create = function(obj, func, args) 
{
  if (typeof args == "undefined") {
    args = [];
  }
  
  var f = function(){
    // 'arguments' isn't technically an array, so we can't just use concat
    var myArgs = [];
    for(var i=0, len=arguments.length; i<len; i++) {
      	myArgs.push(arguments[i]);
    }
    if (arguments.callee && arguments.callee.func) {
      return (arguments.callee.func.apply(arguments.callee.target, arguments.callee.args.concat(myArgs)));
    }
    return undefined;
  };
  f.target = obj;
  f.func = func;
  f.args = args;
  
  Delegate._heap.push(f);
  return (f);
};
/******************************************************** destroy
 * Properly cleanup a delegate created from Delegate.create
 *
 * @param f:Function - Function created from Delegate.create
 */
Delegate.destroy = function(f) {
  if (f) {
    f.target = null;
    f.func = null;
    f.args = null;
  }
};
Delegate._heap = [];
Delegate._gc = function() {
  if (!Delegate._heap) return;
  for(var i=0, len=Delegate._heap.length; i<len; i++) {
    Delegate.destroy(Delegate._heap[i]);
  	delete Delegate._heap[i];
  }
  delete Delegate._heap;
};
addWindowOnunloadHandler(Delegate._gc);
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>Params</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** Params
 * @constructor
 * works one of three ways:
 * new Params() - creates new, empty object
 * new Params({"x": "y", "z": ["foo", "bar"]}) - creates parameters as
 *            specified in the object
 * new Params(arguments, ["typeId", "conceptId"]) - creates named
 *            parameters from an javascript arguments builtin
 */
function Params(args, names)
{
  setupInheritance(Params);
  this._keys = new Array();
  this._params = new Object();
  if (args === undefined)
    return;
      
  // args might be a map { x: "x", y: "y" }
  var initialValues = args;
  // or wait, maybe the user passed arguments, argnames
  // automatically make: { names[0]: args[0], names[1]: args[1], etc.. }
  if (names !== undefined &&
      names.length &&
      args.length) {
    initialValues = {}
    if (names.length < args.length)
      Debug.warn("Ignoring " + args.length - names.length + " arguments");
    
    for (var i=0; i<args.length; i++)
      if (names[i])
        initialValues[names[i]] = args[i];
  }
  // now transform that map into a map we can understand
  for (key in initialValues) {
    var value = initialValues[key];
    
    // automatically ignore unspecified arguments
    if (value !== undefined &&
        value !== null) {
      
      if (value instanceof Array) {
        for (var i=0; i<value.length; i++)
          this.add(key, value[i]);
      }
      else
        this.add(key, value);
    }
  }
}
Params.superClass = [];
registerClass(Params, "Params");
/******************************************************** add
 * @param key:String
 * @param value:String
 */
Params.prototype.add = function(key, value)
{
  var valueArray = this._params[key];
  if (!valueArray)
  {
    valueArray = new Array();
    this._params[key] = valueArray;
    this._keys.push(key);
  }
  
  valueArray.push(value);
};
/******************************************************** add
 * @param key:String
 * @param arr:Array
 * add the elements of arr to the given key
 */
Params.prototype.extend = function(key, arr)
{
    var valueArray = this._params[key];
    if (!valueArray) {
        this._params[key] = valueArray = [];
        this._keys.push(key);
    }
    for (var i=0; i<arr.length; i++) {
        valueArray.push(arr[i]);
    }
}
    
/******************************************************** toQueryString
 * @return String
 */
Params.prototype.toQueryString = function()
{
  return this.toString(true);
}
Params.prototype.toString = function(doEscape)
{
  var buf = [];
  
  for (var i=0, len=this._keys.length; i<len; i++)
  {
    var key = this._keys[i];
    var valueArray = this._params[key];
    
    for (var j=0, len2=valueArray.length; j<len2; j++)
    {
      var value = valueArray[j];
      if (doEscape != undefined && doEscape == true)
        value = encodeURIComponent(value);
      buf.push(key + "=" + value);
    }
  }
  
  return buf.join("&");
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ImageFactory</b>
 * 
 * When you use the native javascript Image object, there is a potential memory
 * leak especially in IE when you overwrite the Image objects onload handler.
 * This factory ensures proper clean up of all Image object instantiations
 * that it's created on window unload.
 *
 * @author daepark@apmindsf.com
 */
function ImageFactory(){}
ImageFactory.create = function(src, onload, alt, width, height) {
  var image = new Image();
  if (onload) image.onload = onload;
  if (src) image.src = src;
  if (alt != null) image.alt = alt;
  if (width != null) image.width = width;
  if (height != null) image.height = height;
  ImageFactory._heap.push(image);
  return image;
};
ImageFactory._heap = [];
ImageFactory._gc = function() {
  for(var i=0, len=ImageFactory._heap.length; i<len; i++) {
    if (ImageFactory._heap[i]) {
      ImageFactory._heap[i].onload = null;
    }
  }
  delete ImageFactory._heap;
};
addWindowOnunloadHandler(ImageFactory._gc);
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>Iso8601Date</b>
 *
 * @author nick@metaweb.com
 *
 *
 * Converts a string to IS08601 format
 *
 * Has three accesors accessors:
 * getDateObject()  returns a javascript Date object set to the date or time of this object
 * getIsoString()   return a string of the date or time in ISO format, such as 2010-12-15T05:55
 * getDateFormat()  returns the format that the IsoString is in, such as YYYY-MM-DDThh:mm
 * 
 *  Constructor takes a single string and converts it to ISO8601 format
 *  String may be a date, a time, or both
 *  String may be already formated such as "1985-04-12T10:15:30" or human readable such as "14:00 May 2 2005" or "today" or "2:00pm" etc.
 *  If no timezone is given, input is assumed to be UTC
 *
 * Allowed input formats:
 * -Date
 * -Time
 * -Date and time
 * -Date and time can be combined in any order; but if a time is given the date must specify the year month and day.
 * -Time zones given in ISO format
 * 
 * Time formats:
 * 8am
 * 8:00am
 * 20:00
 * 20:00:01
 * 20:00:02.003
 * 8h
 * 8h 30m
 * 8 o'clock
 *
 * Date Formats: (case insensitive)
 * today
 * tomorrow
 * Monday
 * 2006       (years must be four digits)
 * 0800 BCE   (years must be four digits)
 * -0800      (years must be four digits)
 * March 2006
 * 03/28/2006
 * 03-28-2006
 * 28th of March 2006
 *
 * ISO with timezone:
 * 1985-04-12T10:15:30Z        (UTC time)
 * 1985-04-12T10:15:30+0400    (+4 hours)
 * 1985-04-12T10:15:30-04:00   (-4 hours)
 * 1985-04-12T10:15:30+04      (+4 hours)
 * 
 */
 
function Iso8601Date(str) {
  this._dateObject = new Date(0,0);
  this._dateFormat = "";  
  this._validIsoString = "";
  var timeZoneString = '';
  var tzRegExp = /(?:(?:(?:\:|T)[0-9]{2})|(?:\:[0-9]{2}\.[0-9]+))(Z|(?:(?:[-+])(?:[0-9]{2})(?::?(?:[0-9]{2}))?))$/;
  var valid_with_tz = tzRegExp.exec(str);
  if (valid_with_tz) { 
    timeZoneString = valid_with_tz[1];
    str = str.substr(0, str.length - timeZoneString.length);
  }
  if (str.match(/am|pm|[0-9]\s*h|o\'clock/i)) {
    str = this._forceTimeFormat (str);
  }
  var myRegExp = /^(.*?)([0-9]+\:[0-9]+(?:\:[0-9]+(?:\.[0-9]*)?)?)?([^\:]*)$/;
  var comps = myRegExp.exec(str);
  if (comps[1].indexOf(":") >= 0) { comps[1] = ""; }
  if (comps[3].indexOf(":") >= 0) { comps[3] = ""; }
  var dateString = comps[1] + " " + comps[3];
  var timeString = comps[2];
  if (dateString.length > 1) {
    this._validIsoString = this._setDate(dateString);
  }
  
  if (timeString) {
    if (dateString.length > 1) {
      if (this._dateFormat == 'Invalid') {
        this._dateFormat = '';
        this._validIsoString = '';
      } else {
        this._dateFormat = this._dateFormat + "T";
        this._validIsoString = this._validIsoString + "T";
      }
    }      
    this._validIsoString = this._validIsoString + this._setTime(timeString, timeZoneString) + "Z";
  }
};
Iso8601Date.prototype.getDateObject = function() {
  return (this._dateObject);
};
Iso8601Date.prototype.getDateFormat = function() {
  return (this._dateFormat);
};
Iso8601Date.prototype.getIsoString = function() {
  return (this._validIsoString);
};
Iso8601Date.monthMap = {
      'january': '01',
      'jan': '01',
      'february': '02',
      'feb': '02',
      'march': '03',
      'mar': '03', 
      'april': '04',
      'apr': '04',
      'may':'05',
      'june':'06',
      'jun':'06',
      'july': '07',
      'jul': '07',
      'august': '08',
      'aug': '08',
      'september': '09',
      'sep': '09',
      'sept': '09',
      'october': '10',
      'oct': '10',
      'november': '11',
      'nov': '11',
      'december': '12',
      'dec': '12'
};
Iso8601Date.formatMap = [[/^(-?[0-9]{4})$/, "YYYY"],
                         [/^(-?[0-9]{4})-?([0-9]{2})$/, "YYYY-MM"],
                         [/^(-?[0-9]{4})-?([0-9]{2})-?([0-9]{2})$/, "YYYY-MM-DD"]];
Iso8601Date.prototype._setDate = function(str) {
  var fullRegexp = /^(-?[0-9]{4})(?:-?([0-9]{2})(?:-?([0-9]{2}))?)?$/;
  var dd = str.match(fullRegexp);
  if (!(dd)) {
   str = this._forceDateFormat(str);
  }
  //now parse the date
  this._dateFormat = "Invalid";
  for (var i=0; i<Iso8601Date.formatMap.length; i++) {
    var re = Iso8601Date.formatMap[i][0];
    var d = str.match(re);
    if (d) {
      this._dateFormat = Iso8601Date.formatMap[i][1];
      var year = d[1];
      var month = d[2];
      var date = d[3];
    }
  }
  if ((month > 12) || (date > 31)) {
    this._dateFormat= 'Invalid';
    return;
  }
  this._dateObject.setUTCFullYear(year);
  if (month) {
    this._dateObject.setUTCDate(1);
    this._dateObject.setUTCMonth(month - 1);
    if (date) {
      this._dateObject.setUTCDate(date);
    }
  }
  //Need to set str based on dateObject, for cases like 2006-10-35
  var thisDate = this._dateObject.getUTCDate() + ""; 
  var thisMonth = this._dateObject.getUTCMonth() + 1; 
  thisMonth = thisMonth + ""; // convert to string
  if (thisMonth.length ==1) {thisMonth = "0" + thisMonth;}
  if (thisDate.length ==1) {thisDate = "0" + thisDate;}
  var fullYear = this._dateObject.getUTCFullYear(); // getFullYear doesn't show leading zeros.
  if ((fullYear > 1000) || (fullYear < -1000)) {
    fullYear = String(fullYear);
  } else {
    fullYear = String(fullYear);
    if (fullYear >= 0) { 
      while (fullYear.length < 4) { fullYear = "0" + fullYear + "";}
    } 
    else {
      fullYear = 0 - fullYear;
      fullYear = String(fullYear);
      while (fullYear.length < 4) { fullYear = "0" + fullYear;}
      fullYear = String("-" + fullYear);
    }
  }
  if (this._dateFormat == 'YYYY') {
    str = fullYear;
  } else if (this._dateFormat == 'YYYY-MM') {
    str = fullYear + "-" + thisMonth;
  } else if (this._dateFormat == 'YYYY-MM-DD') {
    str = fullYear + "-" + thisMonth + "-" + thisDate;
  }
  return (str);
};
Iso8601Date.prototype._forceTimeFormat = function(str) {
  if (str.match(/o\'clock|am|pm/)) {
    var hoursMins = new Array();  
    var strA = " " + str;
     hoursMins = strA.match(/[^0-9\.\:]([0-2]?[0-9])\s*(\:[0-5][0-9])?\s*(o\'clock|am|pm)/);
    if (!(hoursMins)) {return (str);} 
    // Cannot say 08:08:08.08 pm, that's asinine. If you specify secs or millisecs, must use 24 hour clock. (for now)
    var hours = hoursMins[1];
    if (hoursMins[2]) {
      var mins = hoursMins[2];
      if (mins.indexOf(":") == 0) {
        mins = mins.substring(1);
      }
    } else {
      var mins = "00" + "";
    }
    if ((hoursMins[3] == "am") && (hours == 12)) {
     hours = "00" + "";
    }
    if ((hoursMins[3] == "pm") && (hours < 12)) {
         // The following doesn't work:   hours = hours.valueOf() + 12;
      hours = ++hours + 11;  // A hack! (but works perfectly)  
    } else { 
      if (hours.length ==1) {hours = "0" + hours + "";}
    }
    str=str.replace(/([0-2]?[0-9])\s*\:?([0-5][0-9])?\s*(o\'clock|am|pm)/, hours + ":" + mins);
    return(str);
  } else {  //Not am|pm|o\'clock, so must be [0-9]h
    var hoursMins = new Array();  
    hoursMins = str.match(/([0-2]?[0-9])\s*h\s*([0-5][0-9])?\s*(m)?/);
    var hours = hoursMins[1];
    if (hours.length ==1) {hours = "0" + hours + "";}
    var mins = "00" + ""; 
    if (hoursMins[3] == 'm') {
      mins = hoursMins[2];
    }
    str = str.replace(/([0-2]?[0-9])\s*h\s*([0-5][0-9])?\s*(m)?/, hours + ":" + mins);
    return(str);
  }
};
Iso8601Date.prototype._forceDateFormat = function(str) {
  var hasAlpha = str.match(/[a-zA-Z]/);
  if (hasAlpha) {
    if (str.match(/today/i)) {
      var nowObj = new Date();
      var thisMonth = nowObj.getMonth() + 1; // Numeric addition
      thisMonth = thisMonth + ""; // Now convert to a string.
      var thisDate =  nowObj.getDate() + "";
      if (thisMonth.length ==1) {thisMonth = "0" + thisMonth;}
      if (thisDate.length ==1) {thisDate = "0" + thisDate;}
      str = nowObj.getFullYear() + "-" + thisMonth + "-" + thisDate;
      return (str);
    }
    if (str.match(/yesterday/i)) {
      var nowObj = new Date();
      var nowTime = nowObj.getTime();
      var yesterday = nowTime - (24*3600000);
      yesObj = new Date(yesterday);
      var thisMonth = yesObj.getMonth() + 1; // Numeric addition
      thisMonth = thisMonth + ""; // Now convert to a string.
      var thisDate =  yesObj.getDate() + "";
      if (thisMonth.length ==1) {thisMonth = "0" + thisMonth;}
      if (thisDate.length ==1) {thisDate = "0" + thisDate;}
      str = yesObj.getFullYear() + "-" + thisMonth + "-" + thisDate;
      return (str);
    } 
    if (str.match(/tomorrow/i)) {
      var nowObj = new Date();
      var nowTime = nowObj.getTime();
      var tomorrow = nowTime + (24*3600000);
      yesObj = new Date(tomorrow);
      var thisMonth = yesObj.getMonth() + 1; // Numeric addition
      thisMonth = thisMonth + ""; // Now convert to a string.
      var thisDate =  yesObj.getDate() + "";
      if (thisMonth.length ==1) {thisMonth = "0" + thisMonth;}
      if (thisDate.length ==1) {thisDate = "0" + thisDate;}
      str = yesObj.getFullYear() + "-" + thisMonth + "-" + thisDate;
      return (str);
    } 
    var dayOfWeek =/sunday|monday|tuesday|wednesday|thursday|friday|saturday/i; 
    var regexRes = dayOfWeek.exec(str);
    if (regexRes) {
        //assume they mean the following one, ie "tuesday" == "this tuesday" == "next tuesday"
        var givenDOWstring = regexRes[0].toLowerCase();
        var DOWstrings = {"sunday":0,
                          "monday":1,
                          "tuesday":2,
                          "wednesday":3,
                          "thursday":4,
                          "friday":5,
                          "saturday":6};
        var givenDOW = DOWStrings[givenDOWstring];
        var nowObj = new Date();
        var nowTime = nowObj.getTime();
        var dayDiff = givenDOW - nowObj.getDay();
        if (dayDiff < 1) {dayDiff = dayDiff + 7;}
        var dowTime = nowTime + (dayDiff * 24 * 3600000);
        var dowObj = new Date(dowTime);
        var dowMonth = dowObj.getMonth() + 1; // Numeric addition
        dowMonth = String(dowMonth); // Now convert to a string.
        var dowDate =  String(dowObj.getDate());
        if (dowMonth.length ==1) {dowMonth = "0" + dowMonth;}
        if (dowDate.length ==1) {dowDate = "0" + dowDate;}
        str = dowObj.getFullYear() + "-" + dowMonth + "-" + dowDate;
        return (str);        
      }
    var givenMonth = false;
    str = str.replace(/(January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)(\s|\/|\,|-|$)/i, function (p1) {
	
        p1 = String(p1);
        p1 = p1.replace(/\s|\/|\,|-/, '');
        var monthNum = Iso8601Date.monthMap[p1.toLowerCase()];
        if (monthNum) {
          givenMonth = monthNum;
        }
        return ' ';
      }
      );
  }
  str = [",", str, ","].join("");
  str = str.replace(/([^0-9])([0-9])([^0-9])/g, "$1" + 0+ "$2" + "$3" +"");
  str = str.replace(/([^0-9])([0-9])([^0-9])/g, "$1" + 0+ "$2" + "$3" +"");
  //Now extract dddd as year.
  var yearA = /[^0-9](-)?([0-9]{4})[^0-9]/.exec(str);
  if (!(yearA)) {return ("Invalid Date Format");}
  var yearVal = yearA[1] ? ("-" + yearA[2] + "") : yearA[2];
  if (str.match(/bc/i)) {
    yearVal--;
    yearVal = yearVal + "";
    while (yearVal.length < 4) { yearVal = "0" + yearVal + "";}
    yearVal = "-" + yearVal;
  }
  /* //Taking this out, must specify year.
  //If no year is given, assume the current year.
  var givenYear=1;
  if (yearA) {
  var yearVal = yearA[1] ? ("-" + yearA[2] + "") : yearA[2];
  } else {
  givenYear=0;
  var nowObj = new Date();
  var yearVal = nowObj.getFullYear();
  }
  */
  if (givenMonth) {
    var dayArray  = /[^0-9]([0-9]{2})[^0-9]+/.exec(str);
    if (dayArray) {
      return (yearVal + "-" + givenMonth + "-" + dayArray[1].substring(0,2));
    } else {
      return (yearVal + "-" + givenMonth);
    }
  } else {
    var monDayArray = /[^0-9]([0-9]{2})[^0-9]+([0-9]{2}[^0-9])?/.exec(str);
    if (!(monDayArray)) {
      //if (givenYear) {
      return (yearVal);
      //} else {
      //  return ("");
      //}
    } else if (monDayArray[2]) {
      return (yearVal + "-" + monDayArray[1] + "-" + monDayArray[2].substring(0,2));
    } else {
      //if (givenYear) {
      return (yearVal + "-" + monDayArray[1]);
      //}
    }
  }
}
Iso8601Date.prototype._setTime = function(str, tzStr) {
  var offset = 0; 
  if (tzStr) {
    var d = tzStr.match(/Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$/);
    if (d[0] != 'Z') {
      offset = (Number(d[3]) * 60);
      if (d[5]) {
        offset += Number(d[5]);
      } 
      offset *= ((d[2] == '-') ? 1 : -1);
    }
  }
  
  if (str.indexOf(":") == 1) { str = "0" + str; }
  var d = str.match(/^([0-9]{2})(?::([0-9]{2})(?::([0-9]{2})(?:\.([0-9]+))?)?)?/);
  if(!d) {
    return('');
  }
  var hours = d[1];
  if (hours > 23) {
    return('');
  }
  var timeFormat = "hh";
  var mins=0;
  if (d[2]) {
    mins = Number(d[2]);
    if (mins > 59) {
      return ('');
    }
    timeFormat = timeFormat + ":mm";
  }
  var secs=0;
  if (d[3]) {
    secs = Number(d[3]);
    if (secs > 59) {
      return ('');
    }
    timeFormat = timeFormat + ":ss";
   
      if (d[4]) { //fractions of a second
      secs = secs + "." + d[4] + "";
      secs = Number(secs);
      timeFormat = timeFormat + ".s";
    }
  } 
  
  this._dateFormat = this._dateFormat + timeFormat;
  this._dateObject.setUTCHours(hours);
  this._dateObject.setUTCMinutes(mins);
  this._dateObject.setUTCSeconds(secs);
    
  var msec = parseFloat(secs - parseInt(secs, 10)) * 1000;
  msec = (Math.round(msec*10000000)) / 10000000; 
  this._dateObject.setUTCMilliseconds(msec);
  if (offset != 0) {
    this._dateObject.setTime(this._dateObject.getTime() + offset * 60000);
  }
  hours = hours + "";
  if (hours.length == 1) {hours = "0" + hours;}
  mins = mins + "";
  if (mins.length == 1) {mins = "0" + mins;}
  secs = secs + "";
  if (secs.length == 1) {secs = "0" + secs;}
  if (timeFormat == "hh") {
    return (hours);
  } else if (timeFormat == "hh:mm") {
    return (hours + ":" + mins);
  } else {
    return (hours + ":" + mins  + ":" + secs);  //secs includes as needed
  } 
};
Iso8601Date.makeUTCString = function(dateValue) {
    if (!dateValue)
        return dateValue;
            
    // there must be a 3rd party library to do this?
    var result =
        dateValue.getUTCFullYear() + "-" +
        this._addLeading(dateValue.getUTCMonth() + 1) + "-" +
        this._addLeading(dateValue.getUTCDate()) + "T" +
        this._addLeading(dateValue.getUTCHours()) + ":" +
        this._addLeading(dateValue.getUTCMinutes()) + ":" +
        this._addLeading(dateValue.getUTCSeconds());
        
    return result;
}
Iso8601Date._addLeading = function(i) {
    // return a 2+ digit number
    if (i<10)
        return "0" + i;
    else
        return "" + i;
}
/*
data = {
  this._dateObject: new Date(0,0),  // A javascript date object.
  this._dateFormat: 'YYYY-MM-DDThh:mm:ss',
  this._validIsoString: '2012-03-04T12:34:56'
}
Iso8601Date.VALID_FORMATS = [
  "YYYY-MM-DDThh:mm:ss.s[Z]",  
  "YYYY-MM-DDThh:mm:ss[Z]",
  "YYYY-MM-DDThh:mm[Z]",
  "YYYY-MM-DDThh[Z]",
  "YYYY-MM-DD",
  "YYYY-MM",
  "YYYY",
  "hh:mm:ss.s[Z]",
  "hh:mm:ss[Z]",
  "hh:mm[Z]"
];
*/
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 * 
 * <b>MQLDocumentItemQuery</b>
 * 
 * Original code from http://www.webtoolkit.info/ by Justas Vinevicius.
 * 
 * Reframed in style consistent with Metaweb code.
 * 
 */
/****************************************************** Base64
 */
function Base64() {};
Base64._keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
Base64.encode = function (input) {
  // public method for encoding
  var output = "";
  var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  var i = 0;
  input = Base64._utf8_encode(input);
  while (i < input.length) {
    chr1 = input.charCodeAt(i++);
    chr2 = input.charCodeAt(i++);
    chr3 = input.charCodeAt(i++);
    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;
    if (isNaN(chr2))
      enc3 = enc4 = 64;
    else if (isNaN(chr3))
      enc4 = 64;
    output = output +
              Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
              Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);
  }
  return output;
};
Base64.decode = function (input) {
  // public method for decoding
  var output = "";
  var chr1, chr2, chr3;
  var enc1, enc2, enc3, enc4;
  var i = 0;
  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  while (i < input.length){
    enc1 = Base64._keyStr.indexOf(input.charAt(i++));
    enc2 = Base64._keyStr.indexOf(input.charAt(i++));
    enc3 = Base64._keyStr.indexOf(input.charAt(i++));
    enc4 = Base64._keyStr.indexOf(input.charAt(i++));
    chr1 = (enc1 << 2) | (enc2 >> 4);
    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    chr3 = ((enc3 & 3) << 6) | enc4;
    output = output + String.fromCharCode(chr1);
    if (enc3 != 64)
        output = output + String.fromCharCode(chr2);
    if (enc4 != 64)
        output = output + String.fromCharCode(chr3);
  }
  return Base64._utf8_decode(output);
};
Base64._utf8_encode = function (string) {
  // private method for UTF-8 encoding
  string = string.replace(/\r\n/g,"\n");
  var utftext = "";
  for (var n = 0; n < string.length; n++) {
    var c = string.charCodeAt(n);
    if (c < 128)
        utftext += String.fromCharCode(c);
    else if((c > 127) && (c < 2048)) {
      utftext += String.fromCharCode((c >> 6) | 192);
      utftext += String.fromCharCode((c & 63) | 128);
    }
    else {
      utftext += String.fromCharCode((c >> 12) | 224);
      utftext += String.fromCharCode(((c >> 6) & 63) | 128);
      utftext += String.fromCharCode((c & 63) | 128);
    }
  }
  return utftext;
};
Base64._utf8_decode = function (utftext) {
  // private method for UTF-8 decoding
  var string = "";
  var i = 0;
  var c = c1 = c2 = 0;
  while ( i < utftext.length ) {
    c = utftext.charCodeAt(i);
  
    if (c < 128) {
      string += String.fromCharCode(c);
      i++;
    }
    else if((c > 191) && (c < 224)) {
      c2 = utftext.charCodeAt(i+1);
      string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
      i += 2;
    }
    else {
      c2 = utftext.charCodeAt(i+1);
      c3 = utftext.charCodeAt(i+2);
      string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
      i += 3;
    }
  }
  return string;
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * StringUtils class
 * daepark@apmindsf.com
 *
 * rights reserved under the copyright laws of the United States.
 */
function StringUtils()
{
}
/******************************************************* isEmpty
 * @param s:String
 * @return TRUE if undefined, null or the empty string
 */
StringUtils.isEmpty = function(s)
{
  return (s == null || (s === ""));
}
/******************************************************* trim
 * @param s:String
 * @return TRUE if undefined, null or the empty string
 */
StringUtils.trim = function(s)
{
  return (s ? s.replace(/^\s*(.*?)\s*$/g, "$1") : s);
}
/****************************************************** toUpperWords
 * make the first letter of every word upper case.
 * i.e. "abc def" to "Abc Def"
 */
StringUtils.toUpperWords = function(s) {
	for(var exp = StringUtils.UPPERWORDS_REGEX; 
	     exp.test(s); 
	     s = s.replace(exp, RegExp.$1 + RegExp.$2.toUpperCase()) );
	if (s.length > 0) {
	  s = s.substring(0,1).toUpperCase() + s.substring(1);
	}
	return s;  
}
StringUtils.UPPERWORDS_REGEX = /(\s)([a-z])/;
/******************************************************* trimUrl
 * @param urlStr:String
 * @return shortened URL string
 */
StringUtils.trimUrl = function(urlStr, maxLength)
{
  var trimmed = urlStr;
  if (/^(\w+:\/{2,3}[^\/]+)(.*)/.test(urlStr)) {
    trimmed = RegExp.$1;
    
    var path = RegExp.$2;
    if (path && path.length > 10) {
      trimmed += '\u2026';
    }
  }
  return trimmed;
}
StringUtils.startsWith = function(s, prefix) {
  return s.slice(0,prefix.length) == prefix;
}
/****************************************************** toValidUrl
 * prepend http:// if needed
 * 
 */
StringUtils.toValidUrl = function(s) {
  if (s.match(/^[a-z]+:\/\//i)) {
    return (s);
  } else {
    return ("http://" + s);
  }
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AutocompleteManager</b>
 *
 * @author daepark@apmindsf.com
 */
function State()
{
  setupInheritance(State);
  superConstructor(EventDispatcher, this);
  this._stateMachine = null;
};
State.superClass = [EventDispatcher];
registerClass(State, "State");
State.prototype.CLASS_NAME = "State";
/********************************************************************** enter
 */
State.prototype.enter = function()
{
};
/********************************************************************** exit
 */
State.prototype.exit = function()
{
};
/********************************************************************** handle
 */
State.prototype.handle = function(signal)
{
  // handle null/undefined signals
  if (typeof signal == "undefined")
  {
    return;
  }
};
/************************************************************** setStateMachine
 */
State.prototype.setStateMachine = function(stateMachine)
{
  this._stateMachine = stateMachine;
};
/************************************************************** getStateMachine
 */
State.prototype.getStateMachine = function()
{
  return (this._stateMachine);
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>StateMachine</b>
 *
 * @author daepark@apmindsf.com
 */
/***************************************************************** StateMachine
 *
 * @param states: [["STATE_NAME_1", state_1], ["STATE_NAME_2", state_2],...,["STATE_NAME_n", state_n]]
 **/
function StateMachine(states)
{
  setupInheritance(StateMachine);
  superConstructor(EventDispatcher, this);
  this._currentState = null;
  this._states = {};
  var len = states.length;
  for (var i=0; i<len; i++)
  {
    var name = states[i][0];
    var state = states[i][1];
    state.setStateMachine(this);
    this._states[name] = state;
    if (i ==0)
    {
      this._currentState = name;
    }
  }
  if (this._currentState == null)
  {
    Debug.error("The StateMachine must be initialized with at least one state");
  }
  this._states[this._currentState].enter();
};
StateMachine.superClass = [EventDispatcher];
registerClass(StateMachine, "StateMachine");
StateMachine.prototype.CLASS_NAME = "StateMachine";
/************************************************************** getCurrentState
 * @return String - the name of the current state.
 */
StateMachine.prototype.getCurrentState = function() 
{
  return (this._currentState);
};
/******************************************************************* transition
 * @param toState:String - The name of the state to transition to.
 * @param signal:Object - The signal to be handled by the toState.
 */
StateMachine.prototype.transition = function(toState, signal) 
{
  var target = this._states[toState];
  if (!target) 
  {
    Debug.error("Unrecongized state:", toState);
  }
  var source = this._states[this._currentState];
  // exit current state
  source.exit();
  // enter target state
  target.enter();
  this._currentState = toState;
  // handle signal
  target.handle(signal);
};
/*********************************************************************** handle
 * @param signal:Object - The signal to be handled by the currentState
 */
StateMachine.prototype.handle = function(signal) 
{
  var state = this._states[this._currentState];
  state.handle(signal);
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * DataProvider class
 * mark@metaweb.com
 */
function DataProvider(url, postData, dataString, headers){
  this._data;
  this._dataRequesters = new Array();
  this._dataLoaded = false;
  this._headers = headers;
  this._url = url;
  // switch to post for long urls:
  // Microsoft states that the maximum length of a URL in Internet Explorer 
  // is 2,083 characters, with no more than 2,048 characters in the path
  // portion of the URL.
  // Metaweb cache chokes on anything >= 4096
  if(url && postData==undefined){
    var index = url.indexOf("?");
    if(index==-1) index = url.length;
    var urlPath = url.substring(0,index);
    //  (ben) And now it occurs to me that the length of the public
    //  URL prefix known to the client will typically be different
    //  than the length of the proxied URL prefix known to the
    //  cache. I think the only solution is to include a fudge factor
    //  = fudge = max(len("http://www.metaweb.com"),
    //  len("http://255.255.255.255:FFFF")) + 1 max_url_len = 4096 -
    //  fudge
    if (url.length + DataProvider.FUDGE_FACTOR >=
        DataProvider.POST_SIZE_THRESHOLD) {
      this._url = urlPath;
      postData = url.substring(index+1);
    }
  }
  if(dataString) this.onDataLoaded(dataString, 0);// preload
  if(this._url){
    var owner = this;
    this._handler = {
      object: owner,
      callback: Delegate.create(this, this.onDataLoaded),
      errback: Delegate.create(this, this.onError)
    };      
    this._httpRequest = new HTTPRequest(this._url, this._handler, postData, this._headers);
  }
  this.serial_number = DataProvider.current_serial_number++;
  DataProvider._heap[this.serial_number] = this;
}
DataProvider.prototype.CLASS_NAME = "DataProvider";
DataProvider.current_serial_number = 0;
DataProvider.prototype.onError = function(httpRequest){
  // override to do any error handling   
};
DataProvider.prototype.onDataLoaded = function(httpRequest){
  this._dataLoaded = true;
  if(!this._dataRequesters) this._dataRequesters = new Array();
  var data = httpRequest.responseJSON();
  try {
    this._data = this.checkData(data);
  }
  catch (ex) {
    return;
  }
  try {
    this._metadata = this.getMetaData(data);
    for(var i=0, len=this._dataRequesters.length; i<len; i++){
      if(this._dataRequesters[i])
        this.setSelectedData(this._dataRequesters[i]);
    }
  }
  finally {
    DataProvider._cleanupHandler(this);
  }
}
DataProvider.prototype.setSelectedData = function(requester){
  var data;
  // transform data:
  if(typeof requester.select == "function"){
    data = requester.select(this._data, this._metadata);
  }
  else data = this._data;
  // set data:
  if (typeof requester.dataHandler == "object" && 
      requester.dataHandler.receiveData ){///setData
    requester.dataHandler.receiveData(data, this._metadata);///setData
    // Dispatch onDataLoaded event - temporary for now until we rework DataProvider to be ignorant of component.
    requester.dataHandler.onDataLoaded();
  }
  else if (typeof requester.dataHandler == "function") {
    requester.dataHandler.call(null, data, this._metadata);
  }
}
DataProvider.prototype.requestData = function(dataHandler, select){
  if(!dataHandler) {
    slkdfjlksjdfxyz[ldksja] = lkjlkjlkj;
    //throw "MQLDataProvider.requestData: dataHandler must not be null";
    throw "DataProvider.requestData: dataHandler must not be null";
  }
  var requester = {dataHandler:dataHandler,select:select};
  // already received data:
  if(this._data || this._dataLoaded) {
    doLater(this, this.setSelectedData, 0, [requester]);
  }
  // data still loading:
  else this._dataRequesters.push(requester);
}
/****************************************************** DataProvider.checkData
 * 
 * Those who want to process the eval'ed json object should do so here by
 * overwriting checkData and returning the appropriate data object that will 
 * be assigned to this._data. By default, this just returns the passed in data.
 * 
 * @param data:{Object}
 * @return Object - Default to returning the passed in data.
 */
DataProvider.prototype.checkData = function(data){
  return (data);
};
/****************************************************** DataProvider.checkData
 *
 * Sometimes there is extra data that goes alongside the data returned
 * by checkData. This could be state information from the data
 * provider, or other data from a JSON envelope. This should return an
 * object with attributes.
 * 
 * @param data:{Object}
 * @return Object - Default to returning the passed in data.
 */
DataProvider.prototype.getMetaData = function(data) {
    return {};
}
DataProvider.FUDGE_URL = "https://255.255.255.255:FFFF";
DataProvider.FUDGE_FACTOR = DataProvider.FUDGE_URL.length;
if (isIE)
  DataProvider.POST_SIZE_THRESHOLD = 2083;
else
  DataProvider.POST_SIZE_THRESHOLD = 4096;
/*
How this works:
As soon as a DataProvider is instantiated, it HTTPRequests data from the _url.
Each component that wants data from this DataProvider can then immediately requestData()
from it. When the DataProvider eventually gets its data back from the HTTPRequest,
it will go through the list of components that requested data from it and hand them
their select(ed) data.
*/
DataProvider._heap = {};
DataProvider._gc = function() {
  // make sure we cleanup all referenced handlers here, because they
  // may have a reference back to the DataProvider instance
  for (var k in DataProvider._heap) {
    var dataProvider = DataProvider._heap[k];
    delete dataProvider._dataRequesters;
  }
  DataProvider._heap = {};
};
DataProvider._cleanupHandler = function(handler) {
  delete handler._dataRequesters;
  delete DataProvider._heap[handler.serial_number];
}
addWindowOnunloadHandler(DataProvider._gc);
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * ArticleBlurb class
 * mark@metaweb.com
 */
function ArticleBlurb(div) {
  this._request;
  this._batch = true;
  setupInheritance(ArticleBlurb);
  superConstructor(BaseComponent, this, [div]);
}
ArticleBlurb.superClass = [BaseComponent];
registerClass(ArticleBlurb, "ArticleBlurb");
ArticleBlurb.prototype.CLASS_NAME = "ArticleBlurb";
ArticleBlurb.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  var articleId = this._body.getAttribute("articleId");
  if (articleId)
    this._article = { "id": articleId };
  
  this._length = this._body.getAttribute("blurbLength");
  if (this._length)
    this._length = parseInt(this._length, 10);
  this._quotes = (this._body.getAttribute("showQuotes") == "true");
  this._text = "";
  if (this._article)
    this.load();
};
ArticleBlurb.prototype.batch = function(b){
  this._batch = (true == b);
};
ArticleBlurb.prototype.setData = function(data) {
  this._article = data.article;
  this._length = data.blurbLength;
  this._quotes = data.quotes;
  this._text = "";
  this.load();
};
ArticleBlurb.prototype.load = function() {
    
  if (!this._article) {
    this._text = "No description available.";
    this.addCSSClass("emptyMessage");
  } 
  else if ('blurbText' in this._article) {
      this._text = this._article.blurbText;
  } 
  else if ('id' in this._article) {
  	var url = Globals.lobUrl(this._article.id, true, Globals.BLURB);
  	if (this._length) {
  		url += "?maxlength=" + this._length
  	}
  	var callback = {
  	  callback: Delegate.create(this, this.onRequestLoaded, [this._quotes]),
  	  errback: Delegate.create(this, this.onRequestError)
  	};
  	if (this._batch) {
      this._request = batch_request(url, callback.callback, callback.errback);
  	}
  	else {
  	  this._request = new HTTPRequest(url, callback.callback);
  	}
  }
  this.setText(this._quotes, this._text)
};
ArticleBlurb.prototype.destroy = function() {
  // cancel pending requests
  if (this._request) {
    this._request.abort();
  }
  superMethod(BaseComponent, this, "destroy");
};
ArticleBlurb.prototype.onRequestLoaded = function(quotes, text, xml, status) {
  this.setText(quotes, text);
  this._request = null;
  this.dispatchEvent({type:"onArticleBlurbLoaded"});
}
ArticleBlurb.prototype.onRequestError = function(error) {
  var errorMessage = sprintf("We're sorry, there was a problem loading the article blurb for this \"%s\".", g_application.conceptName);
  this.onError(errorMessage, null, error);
  this._request = null;
}
ArticleBlurb.prototype.setText = function(quote, text) {
  if (!this._body)              // div has been destroyed
    return;
  if(quote)
   this._body.innerHTML = '"'+text+'"';
  else
   this._body.innerHTML = text;
}
/*
_data: {
  article: {
    id: "#9237912213431017747",
    blurbtext: "This is a snippit of the text"
  },
  blurbLength: 100,     # optional
  quotes: "'"           # optional
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>Button</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** Button
 * @constructor
 */
function Button(div, acceptHTML)
{
  this._disabled = false;
  this._focus = false;
  this._label;
  this._mouseEventHandler;
  this._inputEventHandler;
  this.acceptHTML = (typeof acceptHTML != "undefined" && acceptHTML != null)?true:false;
  
  // DOM event handlers
  this._clickHandler;
  this._focusHandler;   
  this._blurHandler;
  
  setupInheritance(Button);
  superConstructor(BaseComponent, this, [div]);
}
Button.superClass = [BaseComponent];
registerClass(Button, "Button");
Button.prototype.CLASS_NAME = "Button";
/******************************************************** init
 */
Button.prototype.init = function()
{
  this.changeTag("button");
  
  var d = this._body.getAttribute("disabled");
  if (typeof d == "boolean") {
    this._disabled = d;
  }
  else {
    if (typeof d == "undefined" || d == null) {
      this._disabled = false;
    }
    else {
      this._disabled = true;
    }
  }
  if (this._disabled) {
    this._body.setAttribute("disabled", "true");
  }
  else {
    this._body.removeAttribute("disabled");
  }
  
  this.addCSSClass(this.CLASS_NAME);
  this.initInteractive();
  var frag = document.createDocumentFragment();
  // This span is created to house the label so that text can be hidden and an image use in place of button text.
  this._label = this.createTag("span", frag, null, null, "ButtonLabel");
  var label = this._body.getAttribute("label");
  if (!label)
    label = "button";
  this.setLabel(label);
  
  this._body.appendChild(frag);
}
Button.prototype.initInteractive = function(){
  this._mouseEventHandler = new MouseEventHandler(this, this._body, 
    MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT | MouseEventHandler.MOUSECLICK);
    
  if (isSafari) {
    this._inputEventHandler = new InputEventHandler(this, this._body, InputEventHandler.ENTERKEY);
  }
  
  DOMEvent.addEventHandler(this._body, "click", Delegate.create(this, this._onClick));
  DOMEvent.addEventHandler(this._body, "focus", Delegate.create(this, this._onFocus));
  DOMEvent.addEventHandler(this._body, "blur", Delegate.create(this, this._onBlur));
};
Button.prototype.setText = function(text)
{
  if (!this.acceptHTML)
    this.replaceText(text, this._label);
  else
    this._label.innerHTML = text;
  this._mouseEventHandler.setInteractive(true);
  if (isSafari) {
    this._inputEventHandler.setInteractive(true);
  }
}
Button.prototype.getText = function()
{
  return this._label.innerHTML;
}
Button.prototype.setLabel = Button.prototype.setText;
Button.prototype.getLabel = Button.prototype.getText;
Button.prototype.setDisabled = function(disable, dontReplaceCSS){
  if (this.getDisabled() == disable) return;
  if (typeof dontReplaceCSS == "undefined" || dontReplaceCSS == null)
    dontReplaceCSS = false;
  this._disabled = disable;
  if (dontReplaceCSS)
    return;
  if(disable) {
    if (this._focus)
      this.setBlur();
    this.addCSSClass("disabled");
  }
  else {
    this.removeCSSClass("disabled");
  }
  if (this._disabled) {
    this._body.setAttribute("disabled", "true");
  }
  else {
    this._body.removeAttribute("disabled");
  }
}
Button.prototype.getDisabled = function(){
  return (this._disabled);
}
Button.prototype.onMouseOver = function(e){
  if (isIE6) this.addCSSClass('buttonHover');
}
Button.prototype.onMouseOut = function(e){
  if (isIE6) this.removeCSSClass('buttonHover');
}
Button.prototype.onEnterKey = function(e) 
{
  if (!this._disabled)
    this.dispatchEvent({type:"onClick", target: this});
}
Button.prototype._onClick = function(e) 
{
  if (!this._disabled)
    this.dispatchEvent({type:"onClick", target: this});
}
Button.prototype._onFocus = function(e) 
{
  this._focus = true;
  this.dispatchEvent({type:"onFocus", target: this});
}
Button.prototype._onBlur = function(e) 
{
  this._focus = false;
  this.dispatchEvent({type:"onBlur", target:this});
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ArticleCollection</b>
 *
 * @author alee@metaweb.com
 */
function ArticleCollection(div){
  // member vars:
  this._tabControl;
  
  setupInheritance(ArticleCollection);
  superConstructor(AbstractCollection, this, [div]);
}
ArticleCollection.superClass = [AbstractCollection];
registerClass(ArticleCollection, "ArticleCollection");
ArticleCollection.prototype.CLASS_NAME = "ArticleCollection";
ArticleCollection.prototype.setData = function(data){
  // Draw order ensures least amount of popping effect in UI
  if (!data) {
    this.clearData();
    return;
  }
  this._data = data;
  var dataItems = this._data.list.listItems;
  // check for single article and mark accordingly
  if(dataItems.length<=1) this.addCSSClass("singleArticle");
  else this.removeCSSClass("singleArticle");
  // draw tab control
  this._tabControl = this._createTabControl(this._header);
  // Set the data and selected for tabControl and the tabContent
  for(var i=0, len=dataItems.length; i<len; i++){
    this._tabControl.addListItem({html:"<span><em>"+dataItems[i].text+"</em></span>"});
  }
  this._list.setData(this._data.list);
  
  var selected = this._tabControl.getSelectionIndex();
  if (selected < 0 || selected > this._tabControl.getLength())
    // set _tabControl's selection if none set.
    this._tabControl.setSelection(null,0);
}
ArticleCollection.prototype._isEmpty = function(){
  // if currentSelection's data does not contain html or html==""
  var listItem = this._list.getSelection();
  if (listItem) {
    // check current selection's tabContent.
    var listItemData = listItem.getData();
    if (!listItemData.html || listItemData.html == "")
      return true;
    else 
      return false;
  }
  else
    // always one tab selected (default - 1st); so if none selected, then isEmpty
    return true;
};
ArticleCollection.prototype._createTitle = function(){
}
ArticleCollection.prototype._createTabControl = function(parentDiv) {
  // _tabControl - itself a list with data on what the tab labels are.
  // it's listItem was formerly HTMLListItem - but now is EditableHTMLList's ListItem
  var tabControlElement = this.createTag("div", parentDiv, 1, null, "tabs");
  var shimElement = this.createTag("div", parentDiv, 2, null, "shim");
  var tabPadderElement = this.createTag("div", tabControlElement, 1, null, "tabPadder");
  var tabControl = this.addChildComponent(EditableHTMLList, tabPadderElement, "span");
  tabControl.addEventListener("onSelectionChange", Delegate.create(this, this.onTabSelectionChange));
  return tabControl;
}
ArticleCollection.prototype._createEditableList = function(div) {
  // this EditableList is single item list housing the currently selected tab's content.
  var list = new EditableHTMLList(div);
  list.addEventListener("onLobLoaded", Delegate.create(this, this.onContentLobLoaded));
  return list;
};
ArticleCollection.prototype.onTabSelectionChange = function(eventObject){
  var targetIndex = eventObject.targetIndex;
  var listItem = this._list.getItemByIndex(targetIndex);
  this._list.setSelection(null,targetIndex);
  this.dispatchEvent({type:"onTabSelectionChange",
        target: eventObject.target,
        listItem:listItem,
        listItemIndex: targetIndex,
        collectionComponent: this});
}
ArticleCollection.prototype.onContentLobLoaded = function(eventObject) {
  this._updateEmptyMessage(false);
  this.dispatchEvent(eventObject);
}
/*
note: setData sets _data.html first, then requests url and 
      sets _data.html again when the result comes back
_data = {
  uid:"1234",
  list:{
    listItems:[
      {
        url:"dweller.html",
        src:"dweller.html",
        name:"Dweller",
        html:"<p><b>This is the <a href='#'>Dweller</a> article.</b></p>"
      },
      {
        url:"mechanism.html",
        src:"mechanism.html",
        name:"Mechanism",
        html:"<p><b>This is the <a href='#'>Mechanism</a> article.</b></p>"
      },
      {
        url:"homunculus.html",
        src:"homunculus.html",
        name:"Homunculus",
        html:"<p><b>This is the <a href='#'>Homunculus</a> article.</b></p>"
      }
    ]
  }
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>OverlayError</b>
 *
 * @author daepark@apmindsf.com
 */
/****************************************************************** constructor
 * @param overlayElement - the html element that we are overlaying over. 
 * If null, overlay over entire document body 
 */
function OverlayError(msg, callback, overlayElement) {
  var buttons = [{
    text: "OK",
    event: "onOk",
    css: "okButton", 
    callback: callback
  }];
  setupInheritance(OverlayError);
  superConstructor(OverlayPrompt, this, [overlayElement, (msg)?{text:msg}:null, buttons]);
};
OverlayError.superClass = [OverlayPrompt];
registerClass(OverlayError, "OverlayError");
OverlayError.prototype.CLASS_NAME = "OverlayError";
OverlayError.ERR_MSG = "An error has occurred. Press 'Ctrl+F12' to view details. Bugs can reported from the 'report a bug' link on the homepage."
OverlayError.prototype.createPromptContent = function(frag) {
  this._iconDiv = this.createTag("span", frag, null, null, "icon");
  var label = this.addChildComponent(Label, frag);
  label.setText(OverlayError.ERR_MSG);
  var textMessageArea = this.addChildComponent(TextAreaInput, frag);
  Debug.log("OverlayError.createPrompt: this._msgData:", this._msgData);
  if (this._msgData)
    textMessageArea.setData(this._msgData);
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>UserStatusComponent</b>
 *
 * @author tim@metaweb.com
 * Based on SearchBox.js
 */
/******************************************************** UserStatusComponent
 * @constructor
 */
function UserStatusComponent(div)
{
  setupInheritance(UserStatusComponent);
  superConstructor(BaseComponent, this, [div]);
  
  this.signoutUrl = "";
  
  var owner = this;
  g_application.addEventListener("login", function(ev){owner.onLoginEvent(ev)});
  this.onLoginEvent({target: g_application});
}
UserStatusComponent.superClass = [BaseComponent];
registerClass(UserStatusComponent, "UserStatusComponent");
UserStatusComponent.prototype.CLASS_NAME = "UserStatusComponent";
/******************************************************** init
 */
UserStatusComponent.prototype._init = function(){}
UserStatusComponent.prototype.onLoginEvent = function(event) {
  // Get the user object
  var user = g_application.getUser();
  // Generate the HTML for the display based on the user.
  if (user && user.name) {
    	// Erase ourselves
    	this._body.innerHTML="Welcome back, ";
    	// Add the user's name, as a link if we can
    	var addNameTo = this._body;
    	try {
      // Try to get a user URL and link to the user page.
      // Ask for user.url() first so we can fall back to the catch
  	    // below if it's unavailable or unimplemented.
  	    var userUrl = user.url();
  	    addNameTo = this.addChildTag("A");
  	    addNameTo.href = userUrl;
    	} catch(e) {
  	    // There's no user URL, so leave addNameTo == this._body and just
  	    // add the name as plain text.
    	}
    	// If there's a display name, show it, else the username.
    	var name;
    	if (user.displayName)
  	    name = user.displayName;
    	else
  	    name = user.name;
    	this.createText(name, addNameTo);
    	// Finish the "Welcome" sentence.
    	this.createText(".  Not you? ", this._body);
    	// Add a signout link.
    	var signOutLink = this.addChildTag("A");
    	this.createText("Sign out.", signOutLink);
    	var signoutUrl = Globals.signoutUrl();
    	signOutLink.href=signoutUrl;
  
    // Warn user, if need be, about unsupported browser.
    if (!browserChecked) this.browserWarn();
  
  } else {
    var signinUrl = Globals.signinUrl(window.location);
    	var registerUrl = signinUrl;
    	var signupMarkup = "Please <a href='" + signinUrl + "'>sign in</a> or <a href='" + registerUrl + "'>register</a> to contribute.";
    	this._body.innerHTML=signupMarkup;
  }
};
UserStatusComponent.prototype.browserWarn = function() {
  if (browserChecked || (isFirefox && browserVersion >= 1.5) || (isIE && browserVersion >= 6)) 
    // FF1.5+ || IE6.0+
    return;
  
  // Anything else is currently not supported
  g_application.warnFeedback('We recommend one of the following web browsers: Firefox 1.5+ (Mac or PC), Internet Explorer 6.0+.' +
    '\n\nYou may be using a web browser that is not fully supported at this time.' +
    '\n\nFor more information, see the "Browser Support" article in our "Help" section.'
  );
  writeCookie("mwbrowserchecked", "1", 336, "/");
  browserChecked = true;
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>TextInput</b>
 *
 * Refactored by alee@metaweb.com to implement TextInput from AbstractInput
 *
 * @author mark@metaweb.com
 */
function TextInput(div, name){
  // member vars:
  this._label;
  if (typeof name != "undefined")
    this._name = name;
  else {
    name = div.getAttribute("name");
    if (name)
      this._name = name;
    else
      this._name = null;
  }
  setupInheritance(TextInput);
  superConstructor(AbstractInput, this, [div]);
}
TextInput.superClass = [AbstractInput];
registerClass(TextInput, "TextInput");
TextInput.prototype.CLASS_NAME = "TextInput";
TextInput.prototype._createInput = function(div) {
  this._input = createNamedTypedElement("input", this._name, "text");
  this._body.appendChild(this._input);
  
  // default - disable browser autocomplete feature
  this.setBrowserAutoComplete(false);
  var prompt = this._body.getAttribute("prompt");
  if(prompt) this.setPrompt(prompt);
  this._input.className = getClassName(this) + "-input";
  return this._input;  
};
TextInput.prototype.setBrowserAutoComplete = function(state){
  if(state) this._input.setAttribute("autocomplete", "on");
  else this._input.setAttribute("autocomplete", "off");
}
TextInput.prototype.getPrompt = function(){
  if(this._label) this._label.getText();
  else null;
}
TextInput.prototype.setPrompt = function(prompt){
  if(!this._label) this._label = this.addChildComponent(Label, null, "div", 0);
  this._label.setText(prompt);
}
/*
DATA:
_data.text
_data.name
HTML:
<div class="TextInput">
  <div class="Label">description</div>
  <input name="description" class="TextInput" value="default text here"></input>
</div>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * Popup class
 * mark@metaweb.com
 */
function Popup(targetElement){
  // member vaars
  this._container;
  this._targetElement;
  
  this._targetElement = targetElement;
  var contentDiv = $elt("div");
  setupInheritance(Popup);
  superConstructor(BaseComponent, this, [contentDiv]); // contentDiv is the this._body of Popup
}
Popup.superClass = [BaseComponent];
registerClass(Popup, "Popup");
Popup.DEFAULTSTYLE = "display:block;z-index:1000;position:absolute;";
Popup.prototype.CLASS_NAME = "Popup";
Popup.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  this._createShadow();
}
Popup.prototype._createShadow = function(){
  this._container = $elt("div", null, this.CLASS_NAME + "TopShadow");
  DOMUtils.setElementStyle(this._container, Popup.DEFAULTSTYLE);
  var shadow2 = $elt("div", this._container, this.CLASS_NAME + "BottomShadow");
  shadow2.appendChild(this._body);
  this._container.style.display = "none";
  document.body.appendChild(this._container);
}
Popup.prototype.show = function(anchorElement, immediate){
  if(immediate) this._showLater(anchorElement);
  else{
    this._container.style.display = "none"; // avoid async flicker
    doLater(this, this._showLater, 0, [anchorElement]);
  }
}
Popup.prototype._showLater = function(anchorElement){
  if(anchorElement==undefined) anchorElement = this._targetElement;
  else this._targetElement = anchorElement;
  this._setLocation(anchorElement);
  if(!this.visible){
    this._container.style.display = "block";
    this._windowResizeHandler = Delegate.create(this, this._onWindowResize)
    DOMEvent.addEventHandler(window, "resize", this._windowResizeHandler);
    this._documentClickHandler = Delegate.create(this, this._onDocumentClick);
    DOMEvent.addEventHandler(document, "click", this._documentClickHandler);
    this.visible = true;
  }
  this.dispatchEvent("onShow");
}
Popup.prototype.hide = function(){
  this._container.style.display = "none";
  this.visible = false;
  DOMEvent.removeEventHandler(window, "resize", this._windowResizeHandler);
  Delegate.destroy(this._windowResizeHandler);
  this._windowResizeHandler = null;
  DOMEvent.removeEventHandler(document, "click", this._documentClickHandler);
  Delegate.destroy(this._documentClickHandler);
  this._documentClickHandler = null;
  this.dispatchEvent("onHide");
}
Popup.prototype._setLocation = function(){
  anchorElement = this._targetElement;
  var loc = DOMUtils.getElementLocation(anchorElement);
  loc.y += anchorElement.offsetHeight;
  
  var sz = DOMUtils.getWindowSize();
  var left = loc.x - 5;
  var right = loc.x + this._container.offsetWidth - 5;
  var docScrollTop = DOMUtils.docScrollTop();
  if(docScrollTop>0 && isFirefox) sz.w -= 20;
  if(right+6 > sz.w){
    left = loc.x + anchorElement.offsetWidth-this._container.offsetWidth;
  }
  right = left + this._container.offsetWidth;
  if(right > sz.w){
    left = sz.w - this._container.offsetWidth;
    if(!this._noShadow) left -= 6;
  }
  var top = loc.y;
  this._container.style.left = left+"px";
  this._container.style.top = top+"px";
}
Popup.prototype._onWindowResize = function(){
  this._setLocation();
}
Popup.prototype._onDocumentClick = function(e){
  if(this.visible) this.hide();
}
/*
usage example:
  var menu = new Popup(someElement);
  menu.show();
  // ...
  menu.show(someOtherElement);
  menu.hide();
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>HTMLCollection</b>
 *
 * @author alee@metaweb.com
 */
function HTMLCollection(div){
  setupInheritance(HTMLCollection);
  superConstructor(AbstractCollection, this, [div]);
};
HTMLCollection.superClass = [AbstractCollection];
registerClass(HTMLCollection, "HTMLCollection");
HTMLCollection.prototype.CLASS_NAME = "HTMLCollection";
HTMLCollection.prototype._createEditableList = function(div) {
  var list = new EditableHTMLList(div);  
  list.addEventListener("onLobLoaded", this);
  return list;
};
HTMLCollection.prototype.onLobLoaded = function(eventObject) {
  // TODO: ArticleCollection.setData sets this._updateEmptyMessage(false); 
  // we should override setData and set to this._updateEmptyMessage(true) 
  // so the following is reasonable.
  var listItem = this._list.getSelection();
  if (eventObject.target == listItem)
    this._updateEmptyMessage(false);
  this.dispatchEvent(eventObject);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>RadioButton</b>
 *
 * @author alee@metaweb.com
 */
function RadioButton(div){
  // member vars
  this._disabled;
  
  setupInheritance(RadioButton);
  superConstructor(AbstractOption, this, [div]);
}
RadioButton.superClass = [AbstractOption];
registerClass(RadioButton, "RadioButton");
RadioButton.prototype.CLASS_NAME = "RadioButton";
RadioButton.prototype._createOption = function(id){
  // can't use createTag because we need to set the
  // type attibute before appending to the DOM:
  var name;
  if(typeof this._data.name!= "undefined")
    name = this._data.name;
  else {
    name = this._body.getAttribute("name");
    if (!name)
      name="";
  }
  var option = createNamedTypedElement("input", name, "radio");
  var value;
  if(typeof this._data.value!= "undefined")
    value = this._data.value;
  else {
    value = this._body.getAttribute("value");
    if (!value)
      value="";
  }
  option.setAttribute("value", value);
  option.setAttribute("id", id);
  option.className = "RadioButton-input";
  return option;
}
RadioButton.prototype.clearData = function(){
  this._data = { label: "", name:"", value: "", checked: false };
}
RadioButton.prototype.getData = function(){
  return {
    label: this._label.getText(),
    name: this._data.name,
    value: this._data.value,
    checked: this.getState()
  };
}
/*
data = {
  label:"yes",
  name:"answer",
  value: 1,
  checked: false
}
<span class="RadioButton">
  <input id="RB1356" type="radio" name="answer" value="1">
  <label id="RB1356" class="label">yes</label>
</span>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SimpleList</b>
 *
 * @author daepark@apmindsf.com
 */
function SimpleList(div, listItemComponentClass){
  // member vars
  this.listItemComponentClass = (listItemComponentClass)?listItemComponentClass:SimpleListItem;
  
  setupInheritance(SimpleList);
  superConstructor(AbstractList, this, [div]);
};
SimpleList.superClass = [AbstractList];
registerClass(SimpleList, "SimpleList");
SimpleList.prototype.CLASS_NAME = "SimpleList";
SimpleList.prototype._createListItem = function(div) {
  return new this.listItemComponentClass(div);
};
SimpleList.prototype.onMouseOver = function(e){
  var listItem = e.target;
  this.setSelection(listItem);
}
/*
  this._data = {
    id:"1234",
    list:{
      listItems:[
        {
          text:"Metaweb",
          url:"dweller.html",
          value: "dweller"
        },
        {
          text:"Wikipedia",
          url:"mechanism.html",
          value:"mechanism"
        },
        {
          text:"Homunculus",
          url:"homunculus.html",
          value:"homunculus"
        },
        {
          text:"Ooze",
          url:"ooze.html",
          value:"ooze"
        }
      ]
    }
  }
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ButtonListItem</b>
 *
 * @author alee@metaweb.com
 */
function ButtonListItem(div){
  // member vars
  this._button;
  
  setupInheritance(ButtonListItem);
  superConstructor(AbstractListItem, this, [div]);
};
ButtonListItem.superClass = [AbstractListItem];
registerClass(ButtonListItem, "ButtonListItem");
ButtonListItem.prototype.CLASS_NAME = "ButtonListItem";
ButtonListItem.prototype._destroyDisplayContent = function()
{
  if (this._button) {
    this._button.destroy();
    this._button = null;
  }
};
  
ButtonListItem.prototype._createDisplayContent = function()
{
  // Components that use ButtonListItem should rely on onListItemClick dispatched by ButtonListItem
  // Button dispatches onClick but should be the same.  The event payload in onListItemClick has more info. 
  this._button = this.addChildComponent(Button, null, "button", null, null, null, [true]);
  
  // Don't need a buttonClickHandler because Button itself is passing along onClick events.
  
  // set the button's data
  this._button.setLabel(this._data.text);
  this._button.setToolTip(this._data.tip);
  if (typeof this._data.disabled != "undefined" && this._data.disabled != null)
    this._button.setDisabled(this._data.disabled);
};
ButtonListItem.prototype.getTip = function(){
  return this._data.tip;
};
ButtonListItem.prototype.getValue = function(){
  return this._data.value;
};
ButtonListItem.prototype.setDisabled = function(state){
  if (this._disabled == state)
    return;
  this._disabled = state;
  if(this._disabled)
    this.addCSSClass("disabled");
  else
    this.removeCSSClass("disabled");
  // set the actual RadioButton disabled.
  this._button.setDisabled(this._disabled);
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * PopupMenu class
 * mark@metaweb.com
 */
function PopupMenu(targetElement, menuData, noShadow){
  // member vars:
  this._list;
  this._menuData = menuData;
  setupInheritance(PopupMenu);
  superConstructor(Popup, this, [targetElement,noShadow]);
}
PopupMenu.superClass = [Popup];
registerClass(PopupMenu, "PopupMenu");
PopupMenu.prototype.CLASS_NAME = "PopupMenu";
PopupMenu.prototype.init = function(){
  superMethod(Popup, this, "init");
  this._list = this.addChildComponent(SimpleList, null, "ul");
  this._list.addEventListener("onListItemClick", Delegate.create(this, this.onMenuSelect));
  if(this._menuData) this.setData(this._menuData);
}
PopupMenu.prototype.setData = function(data){
  if (!data || data.length<=0) {
    this._data = [];
    return;
  }
  this._menuData = data;
  var others = [];
  var listData = {listItems:[]};
  this._list.setData(listData);
  for(var i=0, len=this._menuData.length; i<len; i++){
    if(this._menuData[i].text==null) others.push({position: i, data: null});
    else {
      if (typeof this._menuData[i].disabled == "undefined")
        // make sure disabled is set to false as this is default of PopupMenu
        this._menuData[i].disabled = false;
      var listItem = this._list.addListItem(this._menuData[i]);
      if (this._menuData[i].disabled == true) {
        listItem.setInteractive(false);
        listItem.addCSSClass("disabled");
      }
      if (this._menuData[i].css) 
        listItem.addCSSClass(this._menuData[i].css);
    }
  }
  for(var i=0, len=others.length; i<len; i++){
    this._list.addListItemComponent(Separator, others[i].data, others[i].position);
  }
}
PopupMenu.prototype.getList = function(){
  return this._list;
}
PopupMenu.prototype.getData = function(){
   return this._menuData;
}
PopupMenu.prototype.onMenuSelect = function(eventObject){
    // do this so that menu does not "stick" when handlers use confirmation dialogs
  doLater(this, this._onMenuSelectDelay, 1, [eventObject]);
};
PopupMenu.prototype._onMenuSelectDelay = function(eventObject){
  var index = eventObject.targetIndex;
  var menuItem = eventObject.targetItem;
  this.dispatchEvent({type:"onMenuSelect", target:this, index:index, menuItem:menuItem});
}
PopupMenu.prototype.setMenuItemDisabled = function(menuItemIndex, state){
  // menuItemIndex is wrt _menuData which includes separators.
  if (!this._menuData || menuItemIndex<0 || menuItemIndex>=this._menuData.length)
    return;
  if (this._menuData[menuItemIndex].disabled == state)
    return;
  this._menuData[menuItemIndex].disabled = state;
  if (state)
    this._list._listItems[menuItemIndex].addCSSClass("disabled");
  else
    this._list._listItems[menuItemIndex].removeCSSClass("disabled");
}
/*
var menuData = [
  {text:"item zero", css:"zero", value:0},
  {text:"item one", css:"one", value:1, disabled: false},
  {}, // separator
  {text:"item two", css:"two",   value:2},
  {text:"item three", css:"three", value:3, disabled: false},
  {value:"separator"}, // separator
  {text:"item four", css:"four",  value:4}
]
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractList</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractList.prototype._createListItem]
 *
 * @author daepark@apmindsf.com
 */
function AbstractList(div, elementTagName){
  // member vars:
  this._limit;
  this._selection;
  this._prevSelection;
  this._listItems;
  this._elementTagName = "ul";
  if (typeof elementTagName != "undefined") {
    if (elementTagName.toLowerCase() == "ol") {
      this._elementTagName = "ol";      
    }    
  }  
  setupInheritance(AbstractList);
  superConstructor(BaseComponent, this, [div]);
};
AbstractList.superClass = [BaseComponent];
registerClass(AbstractList, "AbstractList");
AbstractList.prototype.CLASS_NAME = "AbstractList";
AbstractList.prototype.init = function() {
  this.addCSSClass(this.CLASS_NAME);
  this.clearData();
  this.changeTag(this._elementTagName);
};
AbstractList.prototype.setData = function(data){
  this.clearData();
  
  this._data = data;
  
  // create all listitem before adding them to the document
  // using DocumentFragment
  var frag = document.createDocumentFragment();
  this._addListItems(frag);  
  this._body.appendChild(frag);
  
  // mark the first and last item
  this._cssUpdate();
};
AbstractList.prototype.setLimit = function(n){
  this._limit = n;
};
AbstractList.prototype.getLimit = function(){
  return this._limit;
};
AbstractList.prototype.clearData = function(){
  this._clearList();
  this._data = {listItems:new Array()};
};
AbstractList.prototype._clearList = function(){
  if(this._listItems){
    for(var i=0, len=this._listItems.length; i<len; i++){
      this.removeItemEventListeners(this._listItems[i]);
      this._destroyListItem(this._listItems[i]);
      delete this._listItems[i];
    }
  }
  this._listItems = new Array();
};
AbstractList.prototype._addListItems = function(parent){
  if (!this._data || !this._data.listItems)
    return;
  var len = this._data.listItems.length;
  var limit = this._limit;
  if (limit == undefined || limit == null) {
    limit = len;
  }
  this._listItems = new Array();
  var itemCount = 0;
  for(var i=0; i<len && itemCount<limit;i++){
    itemCount++;
    var itemData = this._data.listItems[i];
    // make list item
    var listItem = this._addListItem(itemData, null, parent);
  }
}
AbstractList.prototype._addListItem = function(itemData, beforeIndex, parent){
  if (typeof parent == "undefined" || parent == null)
    parent = this._body;
  var listItem;
  var div = this.createTag('li', parent, beforeIndex);
  var listItem = this._createListItem(div, itemData);
  this._addEventListeners(listItem);
  if(typeof beforeIndex == "undefined" || beforeIndex == null)
    this._listItems.push(listItem);
  else
    this._listItems.splice(beforeIndex, 0, listItem);
  listItem.setData(itemData);  	
  return listItem;
};
/******************************************************************* _cssUpdate
 * mark first and last list items
 */
AbstractList.prototype._cssUpdate = function(){
  for(var i=0, len=this._listItems.length; i<len; i++) {
  	this._listItems[i].removeCSSClass("FirstListItem");
  	this._listItems[i].removeCSSClass("LastListItem");
  	if (i == 0)
  	  this._listItems[i].addCSSClass("FirstListItem");
  	if (i == len - 1)
  	  this._listItems[i].addCSSClass("LastListItem");
  }
};
/************************************************************** _createListItem
 */
AbstractList.prototype._createListItem = function(div) {
  Debug.error("Subclasses must overwrite [AbstractList.prototype._createListItem]");
  return null;
};
/************************************************************* _destroyListItem
 */
AbstractList.prototype._destroyListItem = function(listItem) {
  if (listItem)
    listItem.destroy();
};
      
AbstractList.prototype.addListItemComponent = function(liClass, data, index, args)
{
  // check if index is within bounds
  if (index < 0 || index > this.getLength()) {
    Debug.warn("Index out of bounds:", index, ", length:", this.getLength());
    return;
  }
  
  var itemElement = this.addChildTag("li", index, null); 
  var listItem = applyNew(liClass, [itemElement].concat(args));
  this._addEventListeners(listItem);
  
  // add data:
  this._data.listItems.splice(index, 0, data);
  // add and draw component:
  this._listItems.splice(index, 0, listItem);
  listItem.setData(data); // setData only after adding the listItem to list as setData may inquire about item's location in list.
  // mark the first and last item
  this._cssUpdate();
  return (listItem);
}
AbstractList.prototype.getOrdered = function(){
  return (this._elementTagName == "ol");
};
AbstractList.prototype.setOrdered = function(b){
  b = true == b;
  var currentTagName = this._elementTagName;
  var newTagName = "ol";
  if (!b)
    newTagName = "ul";
  if (newTagName == currentTagName)
    return;
  this._elementTagName = newTagName;
  
  var oldTag = this._body;
  var newTag = this.createTag(this._elementTagName);
  // this is a fix for IE, which doesn't copy 
  // class attribute correctly:
  var cssClassName = oldTag.className;
  // copy attributes over to the new tag
  for(var i=0,len=oldTag.attributes.length; i<len; i++){
    var attrName = oldTag.attributes[i].nodeName;
    var attrValue = oldTag.attributes[i].nodeValue;
    if(attrValue != undefined)
      newTag.setAttribute(attrName, attrValue);
  }
  var childNodes = [];
  while(oldTag.childNodes.length > 0) {
    var childNode = oldTag.childNodes[0];
    childNodes.push(childNode);
    oldTag.removeChild(childNode);
  }
  for(var i=0, len=childNodes.length; i<len; i++)
  	newTag.appendChild(childNodes[i]);
  
  var parent = this._body.parentNode;
  parent.replaceChild(newTag, oldTag);  
  this._body = newTag;
  this._body._component = this;
  this._body.className = cssClassName;  
};
AbstractList.prototype.addListItem = function(data, beforeIndex){
  // this is the public interface for adding new items after the
  // setData has already been called
  if(beforeIndex==undefined)
    this._data.listItems.push(data);
  else
    this._data.listItems.splice(beforeIndex, 0, data);
  
  var listItem = this._addListItem(data, beforeIndex);
  // mark the first and last item
  this._cssUpdate();  
  
  return listItem;
};
AbstractList.prototype.removeListItem = function(item){
  if(!item)
    return;
  
  var index = -1;
  var selection = this.getSelection();
  if (selection == item)
    // Manipulate selection through item rather than index because index affected by remove
    this.setSelection(null);
    
  // remove component:
  for(var i=0, len=this._listItems.length; i<len; i++){
    if(this._listItems[i]==item){
      this.removeItemEventListeners(item);
      item.destroy();
      this._listItems.splice(i,1);
      this._data.listItems.splice(i,1);
      break;
    }
  }
  // mark the first and last item
  this._cssUpdate();  
  
  item = null;
};
AbstractList.prototype.moveItem = function(itemIndex, newIndex) {
  if (itemIndex == null)
    itemIndex = this.getSelectionIndex();
  
  var len = this.getLength();
  if (typeof itemIndex == "undefined" || itemIndex<0 || itemIndex>=len) {
    Debug.warn("[AbstractList.prototype.moveItem] IndexOutOfBoundsException: itemIndex:", itemIndex);
    return;
  }
  if (typeof newIndex == "undefined" || newIndex<0 || newIndex>=len) {
    Debug.warn("[AbstractList.prototype.moveItem] IndexOutOfBoundsException: newIndex:", newIndex);
    return;
  }
  if (itemIndex == newIndex)
    // no need to move
    return;
  
  var listItem = this._listItems[itemIndex];
  var listItemData = this._data.listItems[itemIndex];
  this._listItems.splice(itemIndex, 1);
  this._data.listItems.splice(itemIndex, 1);
  
  this._listItems.splice(newIndex, 0, listItem);
  this._data.listItems.splice(newIndex, 0, listItemData);
  
  var elt = this._body.removeChild(listItem._body);
  if (newIndex == len - 1)
    this._body.appendChild(elt);
  else {
    var beforeItem = this.getItemByIndex(newIndex + 1);
    this._body.insertBefore(elt, beforeItem._body);
  }
  // listItem.onMouseOut();
  //doLater(listItem, listItem.onMouseOut, 1000);
  listItem.onMouseOut();
  
  // mark the first and last item
  this._cssUpdate();
};
AbstractList.prototype.getItemByIndex = function(index){
  var item = null;
  if (index >=0 && index<this._listItems.length)
    item = this._listItems[index];
  return (item);
};
AbstractList.prototype.getItemIndex = function(item){
  for(var i=0, len=this._listItems.length; i<len; i++)
    if(item == this._listItems[i]) return i;
  return -1;
};
AbstractList.prototype.getLength = function(){
  return this._listItems.length;
};
AbstractList.prototype.getListItems = function(){
  return this._listItems;
};
AbstractList.prototype._addEventListeners = function(item){
  item.addEventListeners(["onListItemClick",
                          "onListItemDblClick",
                          "onMouseOver",
                          "onMouseOut"], this);
}
AbstractList.prototype.removeItemEventListeners = function(item){
  item.removeEventListeners([
    "onListItemClick", 
    "onListItemDblClick", 
    "onMouseOver", 
    "onMouseOut"
    ], this); 
}
AbstractList.prototype.selectNext = function(){
  var newIndex = this.getSelectionIndex() + 1;
  this.setSelection(null,newIndex);
  // todo: scroll into view if not already
};
AbstractList.prototype.selectPrevious = function(){
  var newIndex;
  if(this.getSelectionIndex()<0) 
    newIndex = this._listItems.length - 1;
  else 
    newIndex = this.getSelectionIndex() - 1;
  this.setSelection(null,newIndex);
};
AbstractList.prototype.setSelection = function(listItem, index){
  if(this._listItems.length<1){
    this._dispatchSelectionChangeEvent(-1, null);
    return;
  }
  if(this._selection) {
    this._selection.setSelected(false);
    this._prevSelection = this._selection;
  }
  if (index!=undefined) 
    listItem = this.getItemByIndex(index);
  if (listItem){
    this._selection = listItem;
    this._selection.setSelected(true);
  }
  else this._selection = null;
  if(this._prevSelection != this._selection)
    this._dispatchSelectionChangeEvent(this.getSelectionIndex(), listItem);
  return listItem;
}
AbstractList.prototype._dispatchSelectionChangeEvent = function(index, listItem){
  this.dispatchEvent({type:"onSelectionChange", 
                      targetIndex:index, 
                      targetItem:listItem});
};
AbstractList.prototype.getSelection = function(){
  return this._selection;
}
AbstractList.prototype.getPrevSelection = function(){
  return this._prevSelection;
}
AbstractList.prototype.getSelectionIndex = function(){
  return this.getItemIndex(this._selection);
}
AbstractList.prototype.onListItemClick = function(eventObject){
  var listItem = eventObject.target;
  this.setSelection(listItem);
  this.dispatchEvent({type:"onListItemClick", 
                         targetIndex:this.getItemIndex(listItem), 
                         targetItem:listItem,
                         domEventObject:eventObject.domEventObject});
}
AbstractList.prototype.onListItemDblClick = function(eventObject){
  var listItem = eventObject.target;
  this.dispatchEvent({type:"onListItemDblClick", 
                         targetIndex:this.getItemIndex(listItem), 
                         targetItem:listItem,
                         domEventObject:eventObject.domEventObject});
}
AbstractList.prototype.onMouseOver = function(eventObject){
  // For IE that does not support multiple CSS classname
  var listItem = eventObject.target;
  if (isIE6) {
    if (listItem == this.getSelection())
      listItem.addCSSClass("listItemSelectedHover");
  }
  this.dispatchEvent({type:"onListItemMouseOver", targetItem:listItem, 
                      clientX: eventObject.clientX,  clientY: eventObject.clientY});
}
AbstractList.prototype.onMouseOut = function(eventObject){
  // For IE that does not support multiple CSS classname
  var listItem = eventObject.target;
  if (isIE6) {
    if (listItem == this.getSelection())
      listItem.removeCSSClass("listItemSelectedHover");
  }
  this.dispatchEvent({type:"onListItemMouseOut", targetItem:listItem, 
                      clientX: eventObject.clientX,  clientY: eventObject.clientY});
}
/* 
DATA example:
data = {
  listItems:[
    {
    },
    {
    }
  ]
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractEditableItem</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractEditableItem.prototype._createEditComponent]
 *
 * @author alee@metaweb.com
 */
function AbstractEditableItem(div){
  // member vars
  this._editFormDiv;
  this._editComponent;
  this._leftShadow;
  this._rightShadow;
  this._editFormBackground;
  this._saveButton = null;
  this._cancelButton = null;
  this._deleteConfirmationMessage;
  
  // additional empty message stuff - event handlers
  this._emptyMessageDiv;
  this._emptyMessageLabelHandler;
  this._emptyMessageAddLabel;
  this._emptyMessageAddLabelHandler;
  this._emptyMessageAddText;
  setupInheritance(AbstractEditableItem);
  superConstructor(AbstractItem, this, [div]);
  superConstructor(Editable, this);
}
AbstractEditableItem.superClass = [AbstractItem, Editable];
registerClass(AbstractEditableItem, "AbstractEditableItem");
AbstractEditableItem.prototype.CLASS_NAME = "AbstractEditableItem";
AbstractEditableItem.prototype.EMPTY_COMPONENT_CSSCLASS = "empty";
AbstractEditableItem.prototype.EMPTY_MESSAGE_CSSCLASS = "emptyMessage";
AbstractEditableItem.prototype.PROMPT_TEXT = "double-click to edit text";
AbstractEditableItem.prototype.init = function(){
  superMethod(AbstractItem, this, "init");
  
  var emptyMessageAddText = this._body.getAttribute("emptyMessageAddText");
  this._emptyMessageAddText = (emptyMessageAddText)?emptyMessageAddText:"You can add it.";
  var deleteConfirmationMessage = this._body.getAttribute("deleteConfirmationMessage");
  this._deleteConfirmationMessage = 
    (deleteConfirmationMessage)?deleteConfirmationMessage : "Are you sure you want to delete this item?"
  
  this.setEditable(false);
}
AbstractEditableItem.prototype._isEditable = function() {
  // subclasses may overwrite to provide more specific rules on what makes this editable
  return true;
};
AbstractEditableItem.prototype._createEditComponent = function(div) {
  Debug.error("Subclasses must overwrite [AbstractEditableItem.prototype._createEditComponent]");
  return null;
};
AbstractEditableItem.prototype._createEditContent = function()
{
  this._editFormDiv = this.addChildTag("div", null, null, "editFormDiv");
  this._leftShadow = this.createTag("div", this._editFormDiv, null, null, "editFormTopLeftShadow");
  this._rightShadow = this.createTag("div", this._leftShadow, null, null, "editFormBottomRightShadow");
  this._editFormBackground = this.createTag("div", this._rightShadow, null, null, "editFormBackground");
  var fieldsDiv = this.createTag("div", this._editFormBackground, null, null, "editFieldsDiv");
  var submitDiv = this.createTag("div", this._editFormBackground, null, null, "editSubmitDiv");
  var div = this.createTag("div", fieldsDiv, null, null);
  
  this._editComponent = this._createEditComponent(div);
  this._editComponent.addEventListeners([
    "onSubmitEnable",
    "onSubmitFocus",
    "onSubmit",
    "onSubmitCancel"], this);
  var editData = cloneObject(this._data);
  this._editComponent.setData(editData);
  
  this._saveButton = this.addChildComponent(Button, submitDiv, "button");
  this._saveButton.setDisabled(true);
  this._saveButton.setText("Save");
  this._saveButton.addEventListener("onClick", Delegate.create(this, this.onSave));
  
  this._cancelButton = this.addChildComponent(Button, submitDiv, "button");
  this._cancelButton.setText("Cancel");
  this._cancelButton.addEventListener("onClick", Delegate.create(this, this.onCancel));
  // Set focus into the editComponent.
  this._editComponent.setFocus();
};
AbstractEditableItem.prototype._destroyEditContent = function()
{
  if (this._saveButton) {
    this._saveButton.destroy();
    delete this._saveButton;
  }
  
  if (this._cancelButton) {
    this._cancelButton.destroy();
    delete this._cancelButton;
  }
  
  if (this._editComponent) {
    this._editComponent.destroy();
    delete this._editComponent;
  }
  
  if (this._editFormDiv) {
    this._body.removeChild(this._editFormDiv);
    this._editFormDiv = null;
  }
};
AbstractEditableItem.prototype._enterState_editable = function(){
  this._destroyDisplayContent();
  this._destroyEmptyMessage();
  
  this._createEditContent();
};
AbstractEditableItem.prototype._exitState_editable = function(){
  this._destroyEditContent();
  this._createDisplayContent();
};
AbstractEditableItem.prototype.onSubmitEnable = function(eventObject) {
  if (this._saveButton) {
    var enable = true == eventObject.enable;
    //
    // caution: enabling/disabling buttons grabs focus
    // so don't go from enabld to disabled because you might lose focus
    //
    if (this._saveButton.getDisabled() && enable) {    
      this._saveButton.setDisabled(!enable);
    }
  }
};
AbstractEditableItem.prototype.onSubmitFocus = function(eventObject) {
  if (this._saveButton) {
    this._saveButton.setFocus();
  }
};
AbstractEditableItem.prototype.onSubmit = function(event){
  this.onSave(event);
};
AbstractEditableItem.prototype.onSubmitCancel = function(event){
  if (this._cancelButton) {
    this._cancelButton.setFocus();
  }
  this.onCancel();
};
AbstractEditableItem.prototype.onSave = function(event) {
  // Disable the Save button so that it cannot be clicked until operation is done.
  this._disableButtons();
  var submitData = this._editComponent.getData();
  this.setEditable(false);
  this.edit_submit(submitData);
  this.dispatchEvent({type:"onSave", target: this, submitData:submitData});
};
AbstractEditableItem.prototype.onCancel = function(event) {
  this.setEditable(false);
  this.setData(this._data);  // setEditable(false) will use this._data?
  this.dispatchEvent({type:"onCancel", target: this});
};
AbstractEditableItem.prototype._disableButtons = function() {
  if (this._saveButton) {
    this._saveButton.setDisabled(true);
  }  
  if (this._cancelButton) {
    this._cancelButton.setDisabled(true);
  }
};
AbstractEditableItem.prototype._buildEmptyMessageDiv = function(loading){
  // empty message div
  this._emptyMessageDiv = this.createTag("span", this._content, 0, null, "EmptyMessageContainer");
  // Empty message
  this._emptyMessageLabel = this.addChildComponent(Label, this._emptyMessageDiv, "span", 0, null, this.EMPTY_MESSAGE_CSSCLASS, [true]);
  if (this._isEditable()) {
    // double click handler if this can be edited
    DOMEvent.addEventHandler(this._emptyMessageLabel._body, "dblclick", Delegate.create(this, this.onAdd));
  }
  if (loading) 
    // Loading message
    this._emptyMessageLabel.setText(this._loadingMessage);
  else {
    // Empty message add LinkButton
    if (this._isEditable() && (typeof this._emptyMessageAddText != "undefined" && this._emptyMessageAddText!= null)) {
      // Add LinkButton if this can be edited
      this._emptyMessageAddLabel = this.createTag("a", this._emptyMessageDiv, null, null, "emptyMessageAdd");
      this.createText(this._emptyMessageAddText, this._emptyMessageAddLabel);
      this._emptyMessageAddLabel.setAttribute("href", "javascript:void(0);");
      DOMEvent.addEventHandler(this._emptyMessageAddLabel, "click", Delegate.create(this, this.onAdd));
    }  
    // Empty message text
    this._emptyMessageLabel.setText(this._emptyMessage);
  }
}
AbstractEditableItem.prototype._destroyEmptyMessage = function(){
  this._destroyEmptyMessageDiv();
}
AbstractEditableItem.prototype._destroyEmptyMessageDiv = function(){
  // onDblClick - empty message add LinkButton - might not have this if emptyMessage was 'loading' message.
  if (this._emptyMessageAddLabel){
    this._emptyMessageAddLabel.parentNode.removeChild(this._emptyMessageAddLabel);
    this._emptyMessageAddLabel = null;
  }
  if (this._emptyMessageLabel){
    this._emptyMessageLabel.destroy();
    this._emptyMessageLabel = null;
  }
  // empty message div wrapping message and LinkButton
  if (this._emptyMessageDiv) {
    this._emptyMessageDiv.parentNode.removeChild(this._emptyMessageDiv);
    this._emptyMessageDiv = null;
  }
  
  // Remove the EMPTY_COMPONENT_CSSCLASS;
  this.removeCSSClass(this.EMPTY_COMPONENT_CSSCLASS);
}
AbstractEditableItem.prototype.onAdd = function(eventObject){
  this.add_checkPrivileges();
};
AbstractEditableItem.prototype.add_checkPrivileges = function(){
  this.add_begin();
};
AbstractEditableItem.prototype.add_begin = function(){
  this.setEditable(true);
};
AbstractEditableItem.prototype.add_submit = function(data){
  this.add_finish(data);
};
AbstractEditableItem.prototype.add_finish = function(data){
  this.setData(data);
};
AbstractEditableItem.prototype.delete_checkPrivileges = function(){
  this.delete_begin();
};
AbstractEditableItem.prototype.delete_begin = function(){
  this.delete_submit();
};
AbstractEditableItem.prototype.delete_submit = function(){
  this.delete_finish();
};
AbstractEditableItem.prototype.delete_finish = function(){
  // TODO: item.destroy?
  //item.destroy();
  this._updateEmptyMessage(false);
};
AbstractEditableItem.prototype.edit_checkPrivileges = function(){
  this.edit_begin();
}
AbstractEditableItem.prototype.edit_begin = function(){
  this.setEditable(true);
}
AbstractEditableItem.prototype.edit_submit = function(data){
  this.edit_finish(data);
}
AbstractEditableItem.prototype.edit_finish = function(data){
  // this.setEditable(false);   // should not need to do since earlier _destroyEditContent removes item after it's been setEditable(true)
  this.setData(data);
}
AbstractEditableItem.prototype.flag_checkPrivileges = function(flag){
  this.flag_begin(flag);
}
AbstractEditableItem.prototype.flag_begin = function(flag){
  this.flag_submit(flag);
}
AbstractEditableItem.prototype.flag_submit = function(flag){
  this.flag_finish();
}
AbstractEditableItem.prototype.flag_finish = function(message){
  this.onEditSuccess(message);
}
AbstractEditableItem.prototype.onEditError = function(synopsis, elaboration, details, siblingDiv) {
  var errorComponentDiv;
  if (siblingDiv == "undefined" || siblingDiv == null) {
    errorComponentDiv = this.addChildTag("div", null, this._body.nextSibling);
  } else {
    errorComponentDiv = document.createElement("div");
    siblingDiv.parentNode.insertBefore(errorComponentDiv, siblingDiv.nextSibling);
  }
  var errorComponent = MakeErrorFeedback(errorComponentDiv,
                                         {synopsis:synopsis,
                                          elaboration:elaboration,
                                          details:details});
  errorComponent.addEventListener("onClose", Delegate.create(this, this.onEditErrorClose));
  return errorComponent;
};
AbstractEditableItem.prototype.onEditErrorClose = function() {  
  this.onCancel({target:this});
};
AbstractEditableItem.prototype.onEditSuccess = function(message, siblingDiv) {  
  var confirmationDiv;
  if (siblingDiv == "undefined" || siblingDiv == null) 
    confirmationDiv = this.addChildTag("div", null, this._body.nextSibling);
  else {
    confirmationDiv = document.createElement("div");
    siblingDiv.parentNode.insertBefore(confirmationDiv, siblingDiv.nextSibling);
  } 
  var confirmComponent = ConfirmationFeedback.MakeConfirmationFeedback(confirmationDiv, {synopsis:message});
  confirmComponent.addEventListener("onClose", Delegate.create(this, this.onEditSuccessClose));
}
AbstractEditableItem.prototype.onEditSuccessClose = function() {  
  // Do nothing but subclasses can override
};
/* 
data = {}
HTML:
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SearchBox</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** SearchBox
 * @constructor
 */
function SearchBox(div)
{
  this._searchUrl;
  this._input;
  this._searchButton;
  this._emptyMessage;
  //this._typeSelector;
  
  setupInheritance(SearchBox);
  superConstructor(BaseComponent, this, [div]);
}
SearchBox.superClass = [BaseComponent];
registerClass(SearchBox, "SearchBox");
SearchBox.DEFAULT = "Keyword search Freebase";
SearchBox.prototype.CLASS_NAME = "SearchBox";
/******************************************************** init
 */
SearchBox.prototype.init = function()
{
  this.addCSSClass("SearchBox");
  this._searchUrl = this._body.getAttribute("searchUrl");
  
  var frag = document.createDocumentFragment();
  
  this.form = createNamedTypedElement("form", null, null, frag, {action:this._searchUrl, method:"get"});
  createNamedTypedElement("input", "limit", "hidden", this.form, {value:"30"});
  createNamedTypedElement("input", "start", "hidden", this.form, {value:"0"});
  this._input = this.addChildComponent(TextInput, this.form, null, null, null, null, ["query"]);
  this._input.setPrompt("");
  this.emptyMessage = this._body.getAttribute("prompt");
  if (!this._emptyMessage) this._emptyMessage = SearchBox.DEFAULT;
  
  var prompt = arg("query");
  if (!prompt) {
    prompt = this._emptyMessage;
  }
  this._input.setText(prompt);
  this._input.addEventListeners([
    "onFocus", 
    "onBlur", 
    "onEnterKey"
    ], this);
  this._input.setBrowserAutoComplete(false);
  this._searchButton = this.addChildComponent(Button, this.form, "button");
  this._searchButton.setLabel("Search");
  this._searchButton.addEventListener("onClick", Delegate.create(this, this._onSearch));
  this._body.appendChild(frag);
}
/******************************************************** 
 */
SearchBox.prototype.setData = function(data)
{
  superMethod(BaseComponent, this, "setData", [data]);
  this._searchUrl = data.searchUrl;
  this.form.setAttribute("action", this._searchUrl);
}
/******************************************************** 
 */
SearchBox.prototype.onFocus = function(eventObject)
{
  if (this._input.getText() == this._emptyMessage)
  {
    this._input.setText("");
  }
}
/******************************************************** 
 */
SearchBox.prototype.onBlur = function(eventObject)
{
  if (StringUtils.isEmpty(this._input.getText()))
  {
    this._input.setText(this._emptyMessage);
  }
}
/******************************************************** 
 */
SearchBox.prototype.onEnterKey = function(eventObject)
{
  this._onSearch();
}
/******************************************************** 
 */
SearchBox.prototype._onSearch = function() 
{
  if (this._searchUrl) this.form.submit();
}
/******************************************************** setText
 */
SearchBox.prototype.setText = function(text){
  this._input.setText(text);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractEditableListItem</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractEditableListItem.prototype._createEditComponent]
 * If applicable, subclasses can overwrite [AbstractEditableListItem.prototype._createHelpComponent]
 *
 * @author daepark@apmindsf.com
 */
function AbstractEditableListItem(div){
  // member vars
  this._editFormDiv;  // html elt
  this._leftShadow;   // html elt
  this._rightShadow;  // html elt
  this._editFormBackground;  // html elt
  
  this._editComponent;  
  this._helpComponent;  // optional and may not be present if AbstractEditableListItem.prototype._createHelpComponent is not overridden
  this._saveButton = null;
  this._cancelButton = null;
    
  setupInheritance(AbstractEditableListItem);
  superConstructor(AbstractListItem, this, [div]);
  superConstructor(Editable, this);
};
AbstractEditableListItem.superClass = [AbstractListItem, Editable];
registerClass(AbstractEditableListItem, "AbstractEditableListItem");
AbstractEditableListItem.prototype.CLASS_NAME = "AbstractEditableListItem";
AbstractEditableListItem.prototype.init = function(){
  superMethod(AbstractListItem, this, "init");  
  this.setEditable(false);
};
AbstractEditableListItem.prototype.destroy = function(){
  if (this._editComponent) this._editComponent.destroy();
  superMethod(AbstractListItem, this, "destroy");  
};
AbstractEditableListItem.prototype._createEditComponent = function(div) {
  Debug.error("Subclasses must overwrite [AbstractEditableListItem.prototype._createEditComponent]");
  return null;
};
AbstractEditableListItem.prototype._createHelpComponent = function(div) {
  return null;
};
AbstractEditableListItem.prototype._createEditContent = function()
{
  this._editFormDiv = this.addChildTag("div", null, null, "editFormDiv");
  this._leftShadow = this.createTag("div", this._editFormDiv, null, null, "editFormTopLeftShadow");
  this._rightShadow = this.createTag("div", this._leftShadow, null, null, "editFormBottomRightShadow");
  this._editFormBackground = this.createTag("div", this._rightShadow, null, null, "editFormBackground");
  var fieldsDiv = this.createTag("div", this._editFormBackground, null, null, "editFieldsDiv");
  var submitDiv = this.createTag("div", this._editFormBackground, null, null, "editSubmitDiv");
  var helpDiv = this.createTag("div", submitDiv, null, null, "editHelpDiv");
  
  // Remove this comment and Uncomment the following line when enabling feature described in Bug #478
  this._helpComponent = this._createHelpComponent(helpDiv);
  var div = this.createTag("div", fieldsDiv, null, null);
  var editData = cloneObject(this._data);
  this._editComponent = this._createEditComponent(div);
  this._editComponent.addEventListeners([
    "onSubmitEnable", 
    "onSubmitFocus",
    "onSubmit",
    "onSubmitCancel"
    ], this);
  this._editComponent.setData(editData);
  this._saveButton = this.addChildComponent(Button, submitDiv, "button");
  this._saveButton.setDisabled(true);
  this._saveButton.setText("Save");
  this._saveButton.addEventListener("onClick", Delegate.create(this, this.onSave));
  
  this._cancelButton = this.addChildComponent(Button, submitDiv, "button");
  this._cancelButton.setText("Cancel");
  this._cancelButton.addEventListener("onClick", Delegate.create(this, this.onCancel));
  
  if ("getInputComponents" in this._editComponent) {
    // set tab indices
    var inputs = this._editComponent.getInputComponents();
    for(var i=0, len=inputs.length; i<len; i++) {
      var elt = inputs[i].getInputElement();
      if (elt instanceof Array) {
        for(var j=0, len2=elt.length; j<len2; j++)
        	elt[j].setAttribute("tabindex", String(i+1));
      }
      else
        elt.setAttribute("tabindex", String(i+1));
    }
    this._saveButton._body.setAttribute("tabindex", String(inputs.length+1));
    this._cancelButton._body.setAttribute("tabindex", String(inputs.length+2));
  }
  this._editComponent.setFocus();
};
AbstractEditableListItem.prototype._destroyEditContent = function()
{
  if (this._saveButton) {
    this._saveButton.destroy();
    delete this._saveButton;
  }
  
  if (this._cancelButton) {
    this._cancelButton.destroy();
    delete this._cancelButton;
  }
  
  if (this._helpComponent) {
    this._helpComponent.destroy();
    delete this._helpComponent;
  }
  
  if (this._editComponent) {
    this._editComponent.destroy();
    delete this._editComponent;
  }
  
  if (this._editFormDiv) {
    this._body.removeChild(this._editFormDiv);
  }
  
  this._editFormDiv = null;
  this._leftShadow = null;
  this._rightShadow = null;
  this._editFormBackground = null;
};
AbstractEditableListItem.prototype._enterState_editable = function(){
  this._destroyDisplayContent();
  
  if(this._emptyMessageLabel){
    // remove _emptyMessageLabel
    this._emptyMessageLabel.destroy();
    this._emptyMessageLabel = null;
  }
  this._createEditContent();
};
AbstractEditableListItem.prototype._exitState_editable = function(){
  this._destroyEditContent();
  this._createDisplayContent();
};
AbstractEditableListItem.prototype.onSubmitEnable = function(eventObject) {
  if (this._saveButton) {
    var enable = true == eventObject.enable;
    //
    // caution: enabling/disabling buttons grabs focus
    // so don't go from enabld to disabled because you might lose focus
    //
    //if (this._saveButton.getDisabled() && enable) {    
      this._saveButton.setDisabled(!enable);
  }
};
AbstractEditableListItem.prototype.onSubmitFocus = function(eventObject) {
  if (this._saveButton) {
    this._saveButton.setFocus();
  }
};
AbstractEditableListItem.prototype.onSubmit = function(event){
  this.onSave();
}
AbstractEditableListItem.prototype.onSubmitCancel = function(event){
  if (this._cancelButton) {
    this._cancelButton.setFocus();
  }
  this.onCancel();
}
AbstractEditableListItem.prototype.onSave = function() {
  if (this._saveButton.getDisabled()) return;
  this._disableButtons();
  var submitData = this._editComponent.getData();
  //this.setEditable(false);  // If this commented out, then who is flipping Editable to false?
  this.dispatchEvent({type:"onSave", target: this, submitData:submitData});
};
AbstractEditableListItem.prototype.onCancel = function() {
  this._disableButtons();
  //this.setEditable(false);  // If this commented out, then who is flipping Editable to false?
  this.dispatchEvent({type:"onCancel", target: this});
};
AbstractEditableListItem.prototype._disableButtons = function() {
  if (this._saveButton) {
    this._saveButton.setDisabled(true);
  }  
  if (this._cancelButton) {
    this._cancelButton.setDisabled(true);
  }
};
AbstractEditableListItem.prototype._enableButtons = function() {
  if (this._saveButton) {
    this._saveButton.setDisabled(false);
  }  
  if (this._cancelButton) {
    this._cancelButton.setDisabled(false);
  }
};
/* DATA EXAMPLE:
_data = {
}
HTML:
<li class="AbstractEditableListItem"></li>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * DateTimeLabel class
 * nick@metaweb.com
 *
 * Display a datetime in human readable format
 *
 * If data.fomat == 'friendly'  the output will be in "friendly" format such as "5 minutes ago"
 * If data.displayTZ == 'local' the output will be in the local timezone. Otherwise UTC is assumed.
 *
 */
function DateTimeLabel(div){
  // member vars:
  this._isoObject;
  this._formatedDateTimeString;
  this._isoString;
  this._displayLocal = 0;
  setupInheritance(DateTimeLabel);
  superConstructor(Label, this, [div]);
}
DateTimeLabel.superClass = [Label];
registerClass(DateTimeLabel, "DateTimeLabel");
DateTimeLabel.prototype.CLASS_NAME = "DateTimeLabel";
DateTimeLabel.prototype.init = function(){
  this.changeTag("span");
  this.addCSSClass(this.CLASS_NAME);
  var dateString = this._body.getAttribute("datevalue");
  if (dateString) {
    this.important = true;
    var format = this._body.getAttribute("dateformat");
    if (!format)
      format = "friendly";
    var displayTZ = this._body.getAttribute("displayTZ");
    if (!displayTZ)
      displayTZ = "local";
    this.setData({"text": dateString,
          "format": format,
          "displayTZ": displayTZ});
  }
}
DateTimeLabel.prototype.setData = function(data, onlyOnFormat){
  if (!onlyOnFormat) {onlyOnFormat = false;}
  if (data == null) return;
  this._data = data;
  if (this._data.displayTZ == 'local') {
    this._displayLocal=1;
  }
  this._isoObject = new Iso8601Date(this._data.text);
  if (this._data.text)
    this._formatedDateTimeString =
      DateTimeLabel.formatDateTimeString(this._isoObject,
                                         this._data.format,
                                         this._displayLocal);
  else
    this._formattedDateTimeString = "";
  if (onlyOnFormat && (this._formatedDateTimeString == this._data.text)) {
    this._formatedDateTimeString = "";
  }  
  var dateData = {text:this._formatedDateTimeString};
  superMethod(Label, this, "setData", [dateData]);
  
  if (this._formatedDateTimeString == "Invalid Date or Time") {
    this.addCSSClass("errormessage");
    this._isoString = "";
  } else {
    this.removeCSSClass("errormessage");
    this._isoString = this._isoObject.getIsoString();
  }
}
DateTimeLabel.prototype.getIsoString = function(){
  return this._isoString;
}
DateTimeLabel.formatDateTimeString = function(isoObject, format, displayLocal){
  var dateFormat = isoObject.getDateFormat();
  if (dateFormat == 'Invalid') {return ("Invalid Date or Time");}
  var dateObject = isoObject.getDateObject();
  var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
  if ((dateFormat == 'hh:mm:ss.s') || (dateFormat == 'hh:mm:ss') || (dateFormat == 'hh:mm')) {
    var timeString = DateTimeLabel.formatTime(dateFormat, dateObject, displayLocal);
    return (timeString);
  }
  var year = dateObject.getUTCFullYear() + "";
  if (displayLocal) {year = dateObject.getFullYear() + "";}
  if (year <= 0) {year = DateTimeLabel.formatBCE(year);}
  if (dateFormat == 'YYYY') {
    return (year);
  }
  if (dateFormat == 'YYYY-MM') {
    if (displayLocal) {
      return (months[dateObject.getMonth()] + ' ' + year);
    } else {
      return (months[dateObject.getUTCMonth()] + ' ' + year);
    }
  }
  if (dateFormat == 'YYYY-MM-DD') {
    if (displayLocal) {
      return (months[dateObject.getMonth()] + ' ' + dateObject.getDate() + ', ' + year);
    } else {
      return (months[dateObject.getUTCMonth()] + ' ' + dateObject.getUTCDate() + ', ' + year);
    }
  }
  if (format == "friendly") {
    dateFormat = 'YYYY-MM-DDThh:mm';
    var nowObj = new Date();
    var timePassed = nowObj - dateObject;
    timePassed = timePassed / (1000 * 60); // convert ms to minutes
    if ((timePassed > 0) && (timePassed < (60*24*7))) {
      if (timePassed < 60) {
        if (Math.floor(timePassed) == 1) {return ("1 minute ago")}
        return (Math.floor(timePassed) + " minutes ago");
      }
      timePassed = timePassed / 60;  //mins to hours;
      if (timePassed < 24) {
        if (Math.floor(timePassed) == 1) {return ("1 hour ago")}
        return (Math.floor(timePassed) + " hours ago");
      }
      var timeString = DateTimeLabel.formatTime(dateFormat, dateObject, displayLocal);
      if (timePassed < 48) {
        return ("Yesterday at " + timeString);
      }
      timePassed = timePassed / 24;  //hours to days;
      if (timePassed < 7) {
        return (Math.floor(timePassed) + " days ago at " + timeString);
      }     
    }
    var timeString = DateTimeLabel.formatTime(dateFormat, dateObject, displayLocal);
    if (displayLocal) {
      return (months[dateObject.getMonth()] + ' ' + dateObject.getDate() + ', ' + year + ' ' + timeString);    
    } else {
      return (months[dateObject.getUTCMonth()] + ' ' + dateObject.getUTCDate() + ', ' + year + ' ' + timeString);    
    }
  } else {  
    var timeString = DateTimeLabel.formatTime(dateFormat, dateObject, displayLocal);
    if (dateFormat == 'YYYY-MM-DDThh') {
      if (displayLocal) {
        return (timeString + ' ' + months[dateObject.getMonth()] + ' ' + dateObject.getDate() + ', ' + year);
      } else {
        return (timeString + ' ' + months[dateObject.getUTCMonth()] + ' ' + dateObject.getUTCDate() + ', ' + year);
      }
    }
    if (dateFormat == 'YYYY-MM-DDThh:mm') {
      if (displayLocal) {
        return (timeString + ' ' + months[dateObject.getMonth()] + ' ' + dateObject.getDate() + ', ' + year);
      } else {
        return (timeString + ' ' + months[dateObject.getUTCMonth()] + ' ' + dateObject.getUTCDate() + ', ' + year);
      }
    }
    if (dateFormat == 'YYYY-MM-DDThh:mm:ss') {
      if (displayLocal) {
        return (timeString + ' ' + months[dateObject.getMonth()] + ' ' + dateObject.getDate() + ', ' + year);
      } else {
        return (timeString + ' ' + months[dateObject.getUTCMonth()] + ' ' + dateObject.getUTCDate() + ', ' + year);
      }
    }
    if (dateFormat == 'YYYY-MM-DDThh:mm:ss.s') {
      if (displayLocal) {
        return (timeString + ' ' + months[dateObject.getMonth()] + ' ' + dateObject.getDate() + ', ' + year);
      } else {
        return (timeString + ' ' + months[dateObject.getUTCMonth()] + ' ' + dateObject.getUTCDate() + ', ' + year);
      }
    }
  }
  //// moved to top of function/// return ("Invalid Date or Time");
}
DateTimeLabel.formatBCE = function(year){
  if (year <= 0) {
    year= Math.abs(--year) + " BCE";
  }
  return year;
}
DateTimeLabel.formatTime = function(format, dateObject, displayLocal){
  if (displayLocal) {
    var hours = dateObject.getHours(); 
    var mins = dateObject.getMinutes() + "";
    var secs = dateObject.getSeconds() + "";	
  } else {
    var hours = dateObject.getUTCHours(); 
    var mins = dateObject.getUTCMinutes() + "";
    var secs = dateObject.getUTCSeconds() + "";	
  }
  if (mins.length ==1) {mins = "0" + mins;}
  format = format.substring(format.indexOf("T")+1);
  if (format == 'hh:mm:ss.s') {
    if (displayLocal) {
      var ms = (dateObject.getMilliseconds()) / 1000;
    } else {
      var ms = (dateObject.getUTCMilliseconds()) / 1000;
    }
    if (secs.length == 1) {
      secs = "0" + "" + parseFloat(parseInt(secs, 10) + parseFloat(ms, 10)); 
    } else {
      secs = parseFloat(parseInt(secs, 10) + parseFloat(ms, 10)); 
    }
    return (hours + ":" + mins + ":" + secs);
  }
  if (format == 'hh:mm:ss') {
    if (displayLocal) {
      var secs = dateObject.getSeconds() + "";
    } else {
      var secs = dateObject.getUTCSeconds() + "";
    }
    if (secs.length ==1) {secs = "0" + secs;}
    return (hours + ":" + mins + ":" + secs);
  }
  // Only showing am/pm with hh:mm not hh:mm:ss.sss
  var amPm = "am";
  if (hours == 12) {
    amPm = "pm";
  }
  if (hours > 12) {
    hours = hours-12;
    amPm = "pm";
  }
  if (hours == 0) {
    hours = "12";
  }
  if (format == 'hh:mm') {
    return (hours + ":" + mins + amPm);
  }
  if (format == 'hh') {
    return (hours + ":00" + amPm);
  }
  return "Invalid Time Format";
}
/*
data = {
  text: "some text",
  format:"friendly",
  displayTZ:"local"
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>FileBrowser</b>
 *
 * @author alee@metaweb.com
 */
  // TODO: The ACCEPT attribute gives a comma-separated list of media types accepted, allowing the browser to filter out inappropriate files. Current browsers generally ignore the ACCEPT attribute.
  // http://www.htmlhelp.com/reference/html40/forms/input.html
  // <INPUT TYPE=file NAME="html_file" ACCEPT="text/html">
function FileBrowser(div, name, size){
  this._name;
  if (typeof name != "undefined" && name != null)
    this._name = name;
  if (typeof size != "undefined" && size != null)
    this._size = size;
  
  
  setupInheritance(FileBrowser);
  superConstructor(AbstractInput, this, [div]);
}
FileBrowser.superClass = [AbstractInput];
registerClass(FileBrowser, "FileBrowser");
FileBrowser.prototype.CLASS_NAME = "FileBrowser";
FileBrowser.prototype._createInput = function(){
  // IE7/IE6 has problems with setAttribute("type", file") and this._body.type="file"
  this._input = createNamedTypedElement("input", this._name, null);
  this._input.setAttribute("type", "file");
  this._input.className = getClassName(this) + "-input";  
  if (this._size)
    this._input.setAttribute("size", this._size);
  this._body.appendChild(this._input);
  
  // TODO: key events are not visible on the textInput portion.
  
  // DEBUG: the clickHandler was commented out when it worked. Now put it back and just ignore the events in SimpleFileDialog
  // events: handlers must be added after the _input element is added to DOM otherwise, IE browser crashes when you submit the form
  DOMEvent.addEventHandler(this._input, "click", Delegate.create(this, this.onClick));
  return this._input;  
};
FileBrowser.prototype.getText = function(){
  if (isIE && !StringUtils.isEmpty(this._input.value) && this._data.text != this._input.value)
    // A problem in IE makes this._input.value not available onTextChange, so requests for this should check the input to get the latest value.
    this._data.text = this._input.value;
  return superMethod(AbstractInput, this, "getText");
}
FileBrowser.prototype.setComponentAttribute = function(attribute, value){
  this._input.setAttribute(attribute, value);
}
FileBrowser.prototype.getComponentAttribute = function(attribute){
  return this._input.getAttribute(attribute);
}
FileBrowser.prototype.checkTextChange = function(){
  return !StringUtils.isEmpty(this._input.value);
}
FileBrowser.prototype.onClick = function(e){
  if (!StringUtils.isEmpty(this._input.value)) {
    // In non-IE browsers, this onClick seems to be delayed so that we can check this._input.value; in IE, this test always fails
    this._data.text = this._input.value;
    this.dispatchEvent({type:"onTextChange", target: this, domEventObject:e});
  }
  this.dispatchEvent({type:"onFileBrowserClick", target: this});
  return true;
}
/*
data = {
  name:"imageFile",
  size:60
}
<input type="file" name="imageFile" size="60">
*/
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>RichTextEditor</b>
 *
 * @author alee@metaweb.com
 */
function RichTextEditor(div){  
  // member vars
  this._toolbar;
  this._editContentDiv;
  this._submitEnabled = false;
  
  setupInheritance(RichTextEditor);
  superConstructor(BaseComponent, this, [div]);
  superConstructor(DesignMode, this);
};
RichTextEditor.superClass = [BaseComponent, DesignMode];
registerClass(RichTextEditor, "RichTextEditor");
RichTextEditor.prototype.CLASS_NAME = "RichTextEditor";
RichTextEditor.EDIT_IFRAME = "EditIFrame";
RichTextEditor.EDIT_DIV = "EditDiv";
RichTextEditor.EDIT_CONTENT = "RichTextEditContent";
RichTextEditor.H4 = 0;
RichTextEditor.H3 = 1;
RichTextEditor.H2 = 2;
RichTextEditor.H1 = 3;
RichTextEditor.LEFT = 4;
RichTextEditor.CENTER = 5;
RichTextEditor.RIGHT = 6;
RichTextEditor.BOLD = 7;
RichTextEditor.ITALIC = 8;
RichTextEditor.UNDERLINE = 9;
RichTextEditor.STRIKEOUT = 10;
RichTextEditor.PRE = 11;
RichTextEditor.CREATELINK = 12;
RichTextEditor.OL = 13;
RichTextEditor.UL = 14;
RichTextEditor.OUTDENT = 15;
RichTextEditor.INDENT = 16;
RichTextEditor.STYLEWITHCSS = 17;
RichTextEditor.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  
  var frag = document.createDocumentFragment();
  
  // Toolbar
  this._toolbar = this.addChildComponent(Toolbar, frag, "div");
  this._toolbar.setData(this.toolbarElements());
  this._toolbarHandler = Delegate.create(this, this.onButtonSelect);
  this._toolbar.addEventListener("onButtonSelect", this._toolbarHandler);
  
  // Edit area contains iframe
  this._editContentDiv = this.createEditDiv(frag);
  // add to DOM
  this._body.appendChild(frag);
  
  // Associate iframeId, iframe, and iframe's document
  this.associate(RichTextEditor.EDIT_IFRAME);
}
RichTextEditor.prototype.toolbarElements = function () {
  // Use method call to get these elements to avoid compilation errors with access to static constants 
  return { 
    listItems: [
      { text : "H4", tip : "Make H4", cssClass: "MakeH4", value : RichTextEditor.H4, disabled : false },
      { text : "H3", tip : "Make H3", cssClass: "MakeH3", value : RichTextEditor.H3, disabled : false },
      { text : "H2", tip : "Make H2", cssClass: "MakeH2", value : RichTextEditor.H2, disabled : false },
      { text : "H1", tip : "Make H1", cssClass: "MakeH1", value : RichTextEditor.H1, disabled : false },
      { },
      { text : "Left", tip : "Align Left", cssClass: "AlignLeft", value : RichTextEditor.LEFT, disabled : false },
      { text : "Center", tip : "Center", cssClass: "AlignCenter", value : RichTextEditor.CENTER, disabled : false },
      { text : "Right", tip : "Align Right", cssClass: "AlignRight", value : RichTextEditor.RIGHT, disabled : false },
      { text : "Full", tip : "Align Both Ends", cssClass: "AlignFull", value : RichTextEditor.FULL, disabled : false },
      { },
      { text : "B", tip : "Make Bold", cssClass: "MakeBold", value : RichTextEditor.BOLD, disabled : false },
      { text : "I", tip : "Make Italic", cssClass: "MakeItalic", value : RichTextEditor.ITALIC, disabled : false },
      { text : "U", tip : "Make Underline", cssClass: "MakeUnderline", value : RichTextEditor.UNDERLINE, disabled : false },
      { text : "Strikeout", tip : "Make Strikeout", cssClass: "MakeStrikeout", value : RichTextEditor.STRIKEOUT, disabled : false },
      { text : "PRE", tip : "Make Preformatted", cssClass: "MakePreformatted", value : RichTextEditor.PRE, disabled : false },
      { text : "Link", tip : "Create Link", cssClass: "CreateLink", value : RichTextEditor.CREATELINK, disabled : false },
      { },
      { text : "OL", tip : "Insert Ordered List", cssClass: "OrderedList", value : RichTextEditor.OL, disabled : false },
      { text : "UL", tip : "Insert Bulleted List", cssClass: "BulletedList", value : RichTextEditor.UL, disabled : false },
      { text : "&#9668;-|", tip : "Outdent", cssClass: "Outdent", value : RichTextEditor.OUTDENT, disabled : false },
      { text : "|-&#9658;", tip : "Indent", cssClass: "Indent", value : RichTextEditor.INDENT, disabled : false }
    ]
  };
}
RichTextEditor.prototype.editCommand = function (commandValue) {
  switch(commandValue) {
    case RichTextEditor.H4:
      return { command: "formatblock", commandOption: "<h4>", value : RichTextEditor.H4 };
    case RichTextEditor.H3:
      return { command: "formatblock", commandOption: "<h3>", value : RichTextEditor.H3 };
    case RichTextEditor.H2:
      return { command: "formatblock", commandOption: "<h2>", value : RichTextEditor.H2 };
    case RichTextEditor.H1:
      return { command: "formatblock", commandOption: "<h1>", value : RichTextEditor.H1 };
    case RichTextEditor.LEFT:
      return { command: "justifyleft", commandOption: true, value : RichTextEditor.LEFT };
    case RichTextEditor.CENTER:
      return { command: "justifycenter", commandOption: true, value : RichTextEditor.CENTER };
    case RichTextEditor.RIGHT:
      return { command: "justifyright", commandOption: true, value : RichTextEditor.RIGHT };
    case RichTextEditor.FULL:
      return { command: "justifyfull", commandOption: true, value : RichTextEditor.FULL };
    case RichTextEditor.BOLD:
      return { command: "bold", commandOption: true, value : RichTextEditor.BOLD };
    case RichTextEditor.ITALIC:
      return { command: "italic", commandOption: true, value : RichTextEditor.ITALIC };
    case RichTextEditor.UNDERLINE:
      return { command: "underline", commandOption: true, value : RichTextEditor.UNDERLINE };
    case RichTextEditor.STRIKEOUT:
      return { command: "strikethrough", commandOption: true, value : RichTextEditor.STRIKEOUT };
    case RichTextEditor.PRE:
      return { command: "formatblock", commandOption: "<pre>", value : RichTextEditor.PRE };
    case RichTextEditor.CREATELINK:
      return { command: "createlink", commandOption: null, value : RichTextEditor.CREATELINK };
    case RichTextEditor.OL:
      return { command: "insertorderedlist", commandOption: true, value : RichTextEditor.OL };
    case RichTextEditor.UL:
      return { command: "insertunorderedlist", commandOption: true, value : RichTextEditor.UL };
    case RichTextEditor.OUTDENT:
      return { command: "outdent", commandOption: true, value : RichTextEditor.OUTDENT };
    case RichTextEditor.INDENT:
      return { command: "indent", commandOption: true, value : RichTextEditor.INDENT };
    case RichTextEditor.STYLEWITHCSS:
      return { command: "styleWithCSS", commandOption: false, value : RichTextEditor.STYLEWITHCSS };
    default:
      Debug.error("RichTextEditor.editCommand: Invalid command value : " + commandValue);
      return null;
  }
};
RichTextEditor.prototype.createEditDiv = function(frag, beforeIndex, beforeElement) {     
  var eDiv = this.createTag('div', frag, beforeIndex, beforeElement, RichTextEditor.EDIT_DIV);
  eDiv.id = RichTextEditor.EDIT_DIV; 
  var id = RichTextEditor.EDIT_IFRAME;
  
  // iframe's name and id are same http://www.quirksmode.org/js/iframe.html
  eDiv.innerHTML = "<iframe name='"+id+"' id='"+id+"' class='"+id+"' scrolling='auto'></iframe>";
  eDiv.iframe = $id(id); 
  return eDiv; 
} 
RichTextEditor.prototype.setData = function(data){
  this.clearData();
  if (!data)
    return;
  this._data = data;
  this._updateHTML();
}
RichTextEditor.prototype.getData = function(){
  this._data.text = this._data.html = this.getEditDocument();
  return this._data;
}
RichTextEditor.prototype._updateHTML = function(){
  var html = (StringUtils.isEmpty(this._data.html)) ? this._data.text:this._data.html;
  if (typeof html == "undefined" || html == null)
    html = "";    // Used to prime the input - used to contain "Enter Text Here"
  this.setEditDocument(html);
  this.edit(true);
  this.selectDocumentContent(false);
}
RichTextEditor.prototype.checkTextChange = function(){
  var html = (StringUtils.isEmpty(this._data.html)) ? this._data.text:this._data.html;
  var inputValue = this.getEditDocument();
  var initialInputValue = (inputValue == "");
  if((typeof html != "undefined" && html != null && html != inputValue && !initialInputValue) ||
    (StringUtils.isEmpty(html) && !initialInputValue))
    return true;
  else
    return false;
}
RichTextEditor.prototype._enableSubmit = function(){
  if (this._submitEnabled) return;
  
  this.dispatchEvent({type:"onSubmitEnable", target:this, enable:true});
  this._submitEnabled = true;
}
RichTextEditor.prototype.onButtonSelect = function(eventObject){
  eventObject.targetItem.setBlur();
  var editCommand = this.editCommand(eventObject.command);
  if (editCommand.value == RichTextEditor.CREATELINK) {
    var selectedText = this.getSelectionText();
    if (selectedText == null || selectedText.length < 1){
      new OverlayAlert("Please select the piece of text to create the link");
      return;
    }  
    var range = this.getSelectionRange();
    var overlay = new OverlayGetLink();
    overlay.addEventListener("onDoSubmit", 
      Delegate.create(this, this.onDoSubmit, [editCommand, range, overlay]));
    var range2 = this.getSelectionRange();
  }
  else {
    if (editCommand == null)
      Debug.error("RichTextEditor.onButtonSelect: Invalid button command id : " + eventObject.command);
    var editCommandValue = this.queryCommandValue(editCommand);
    switch(editCommand.value) {
      case RichTextEditor.H4:
      case RichTextEditor.H3:
      case RichTextEditor.H2:
      case RichTextEditor.H1:
      case RichTextEditor.PRE:
        if (StringUtils.isEmpty(editCommandValue)) break;
        if (this.sameFormatBlock(editCommand.commandOption, editCommandValue))
          // undo editCommand.commandOption to be <p>
          editCommand.commandOption = "<p>";
        break;
      default:
        break;
    }
    this.execCommand(editCommand);
    this._enableSubmit();
  }
};
RichTextEditor.prototype.onDoSubmit = function(editCommand, range, overlay, eventObject){
  var data = eventObject.submitData;
  var url;
  if (data.selected == OverlayGetLink.METAWEB) {
    // Metaweb input
    var type = data.metaweb.getType();
    var id = data.metaweb.getValue();
    url = Globals.combinedViewUrl(id);
  }
  else
    // Url input
    url = data.url.getText();
  this.execCommand(editCommand, [range, url]);
  overlay.hide();
  this._enableSubmit();
};
  
RichTextEditor.prototype.onEnterKey = function(eventObject){
  this.dispatchEvent(eventObject);
}
RichTextEditor.prototype.onEscapeKey = function(eventObject){
  this.dispatchEvent(eventObject);
}
RichTextEditor.prototype.onTextChange = function(eventObject){
  // The value actually has changed.  Enable the submit button if it has not been done so.
  this._enableSubmit();
  this.dispatchEvent(eventObject);
}
  
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * BooleanLabel class
 * nick@metaweb.com
 */
function BooleanLabel(div){
  setupInheritance(BooleanLabel);
  superConstructor(Label, this, [div]);
}
BooleanLabel.superClass = [Label];
registerClass(BooleanLabel, "BooleanLabel");
BooleanLabel.prototype.CLASS_NAME = "BooleanLabel";
BooleanLabel.prototype.init = function(){
  this.addCSSClass("BooleanLabel");
  this.clearData();
}
BooleanLabel.prototype._updateHTML = function(){
  if (this._data) {  
    var val = this._data.text;  
    if (val === true || val === "true") {
      this._body.innerHTML = "Yes";  
    }
    else if (val === false || val === "false") {
      this._body.innerHTML = "No";  
    }
    else  {
      this._body.innerHTML = "";
    }
  }
}
/*
_data = {
  text:true
}
HTML:
<div class="BooleanLabel">Yes</div>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * SimpleListItem class
 * mark@metaweb.com
 */
function SimpleListItem(div){
  this._label;
  setupInheritance(SimpleListItem);
  superConstructor(AbstractListItem, this, [div]);
};
SimpleListItem.superClass = [AbstractListItem];
registerClass(SimpleListItem, "SimpleListItem");
SimpleListItem.prototype.CLASS_NAME = "SimpleListItem";
SimpleListItem.prototype._destroyDisplayContent = function()
{
  if (this._label) {
    this._label.destroy();
    this._label = null;
  }
};
  
SimpleListItem.prototype._createDisplayContent = function()
{
  this._label = this.addChildComponent(Label, null, "span", null, null, null, [true]);
  this._label.setData({text:this._data.text, url: this._data.url, value:this._data.value});  
};
SimpleListItem.prototype.getText = function(){
  return this._data.text;
};
SimpleListItem.prototype.getValue = function(){
  return this._data.value;
};
SimpleListItem.prototype.onMouseOver = function(e){
  this.dispatchEvent({type: "onMouseOver", target: this});
};
SimpleListItem.prototype.onMouseOut = function(e){
  if (e) {
    this.dispatchEvent({type: "onMouseOut", target: this, clientX: e.clientX, clientY: e.clientY});
  }
};
/* DATA EXAMPLE:
_data = {
  text:"Item One",
  url:"item.html",
  value:"123456789",
  disabled: false
}
HTML:
<li class="SimpleListItem">
  <div class="Label">Item One</div>
</li>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * Label class
 * mark@apmindsf.com
 */
function Label(div, acceptHTML){
  this.acceptHTML = (typeof acceptHTML != "undefined" && acceptHTML != null)?acceptHTML:false;
  setupInheritance(Label);
  superConstructor(BaseComponent, this, [div]);
}
Label.superClass = [BaseComponent];
registerClass(Label, "Label");
Label.prototype.CLASS_NAME = "Label";
Label.prototype.init = function(){
  this.addCSSClass("Label");
  this.clearData();
}
Label.prototype.getText = function(){
  return (this._data ? this._data.text : null);
}
Label.prototype.setText = function(text){
  if (!this._data) this._data = {};
  this._data.text = text;
  this.setData(this._data);
  this._updateHTML();
}
Label.prototype.getURL = function(){
  return this._data.url;
}
Label.prototype.setURL = function(url){
  this.setData({text:this._data.text, url:url});
  this._updateHTML();
}
Label.prototype.setData = function(data){
  this.clearData();
  if (data == null) return;
  this._data = data;
  this._updateHTML();
}
Label.prototype.clearData = function(data){
  this._data = {"text":""};
}
Label.urlFixupPattern = new RegExp("^http://www.freebase.com", "i");
Label.bodyFixupPattern = new RegExp('"http://www.freebase.com', "gi");
Label.prototype._updateHTML = function(){
  if(this._data.text==null) this._data.text = "";
  else {
    // For now we'll go back to using innerHTML since the graph is escaping special chars
    // (e.g., & to &amp;) for us so that it is not a security hole.
    // Will leave the code in for acceptHTML should we go back to using it.
    if (true || this.acceptHTML) {
      if(this._data.url) {
        var url = this._data.url.replace(Label.urlFixupPattern, "");
        this._body.innerHTML = "<a href='"+url+"'>"+this._data.text+"</a>";
      } else {
        var text = this._data.text;
        if (typeof this._data.text == "string")
          // this._data.text can be boolean, number; This substitution to only apply to text of type "string"
          text = this._data.text.replace(Label.bodyFixupPattern, '"');
        this._body.innerHTML = text;
      }
    } else {
      if (this._body.childNodes) {
        for (var i=this._body.childNodes.length-1; i>=0; i--)
          this._body.removeChild(this._body.childNodes[i]);
      }
  
      if(this._data.url) {
        var anchor = this.createTag('a', this._body);
        anchor.setAttribute("href", this._data.url);
        var text = this.createText(this._data.text, anchor);
      } else {
        this.createText(this._data.text, this._body);
      }
    }
  }
}
/*
_data = {
  text:"",
  url:""
}
HTML:
<div class="Label">This is some text!</div>
<div class="Label"><a href="http://www.amazon.com">This will goto Amazon!</a></div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SimpleCollection</b>
 * 
 * SimpleCollection with a label for title rather than a EditableMenuLabel.
 * Its contents are not interactive, not editable, and not reorderable.
 *
 * @author alee@metaweb.com
 */
function SimpleCollection(div){
  // member vars:
  this._header;   // html elt
  this._content;  // html elt
  this._title; // html elt
  this._list;
  
  // empty message stuff
  this._emptyMessageDiv;
  this._emptyMessageLabel;
  this._emptyMessageAddLabel;
  this._emptyMessage;
  
  this._loadingMessage;
  this._shim;  
  this._footer;
  this._moreButton;
  this._limit;
  setupInheritance(SimpleCollection);
  superConstructor(AbstractCollection, this, [div]);
};
SimpleCollection.superClass = [AbstractCollection];
registerClass(SimpleCollection, "SimpleCollection");
SimpleCollection.prototype.CLASS_NAME = "SimpleCollection";
SimpleCollection.prototype.EMPTY_MESSAGE = "empty";
SimpleCollection.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  var frag = document.createDocumentFragment();
  
  // header and content
  this._header = this.addChildTag("div", frag, null, "header");
  this._content = this.addChildTag("div", frag, null, "content");
  // header containing title:
  this._createTitle();
  
  // SimpleDisplayList:
  var listElement = this.createTag("ul", this._content);  
  var listClassName = this._body.getAttribute("componentList");
  var listItemClassName = this._body.getAttribute("componentListItem");
  // TODO: both listClassName and listItemClassName are strings and need to be turned into classObject
  var listComponentClass = SimpleDisplayList;
  if (listClassName)
    listComponentClass = ObjectMap[listClassName];
  var listItemComponentClass = SimpleDisplayListItem;
  if (listItemClassName)
    listItemComponentClass = ObjectMap[listItemClassName];
  Debug.log("Creating list items from ", listItemComponentClass);
  this._list = this._createList(listElement, listComponentClass, [listClassName, listItemComponentClass]);
  
  var emptyMessage = this._body.getAttribute("emptyMessage");
  this._emptyMessage = (emptyMessage)?emptyMessage:this.EMPTY_MESSAGE;
  var loadingMessage = this._body.getAttribute("loadingMessage");
  this._loadingMessage = (loadingMessage)?loadingMessage:"loading...";
  // this div forces the _body border to wrap around
  // floated list items (if it's css style is set to 
  // clear:both):
  this._shim = this.addChildTag("div", this._content, null, "shim");
  
  this._body.appendChild(frag);
  
  this._updateEmptyMessage(true);
};
SimpleCollection.prototype._createTitle = function(){
  var title = this._body.getAttribute("componentTitle");
  if(!title)
    title = this.CLASS_NAME;
  this._title = this.addChildComponent(Label, this._header, "div", null, null, "title");
  this._title.setText(title);
}
SimpleCollection.prototype._createList = function(div, listComponentClass, listComponentArguments) {
  var listComponent;
  if(!listComponentClass) 
    listComponent = new SimpleDisplayList(div, listComponentArguments);
  else 
    listComponent = applyNew(listComponentClass, [div].concat(listComponentArguments));
  return listComponent;
};
SimpleCollection.prototype._createEditableList = function(div, listComponentClass, listComponentArguments) {
  return this._createList(div, listComponentClass, listComponentArguments)
}
SimpleCollection.prototype.setTitle = function(title){
  this._title.setText(title);
};
SimpleCollection.prototype.setCollectionMenu = function(menuData) {
}
SimpleCollection.prototype.setListItemMenu = function(menuData){
  this._liMenuData = menuData;
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * HTMLListItem class
 * mark@metaweb.com
 */
function HTMLListItem(div){
  this._onTextChangeHandler;
  
  setupInheritance(HTMLListItem);
  superConstructor(AbstractListItem, this, [div]);
}
HTMLListItem.superClass = [AbstractEditableListItem];
registerClass(HTMLListItem, "HTMLListItem");
HTMLListItem.prototype.CLASS_NAME = "HTMLListItem";
HTMLListItem.prototype._createDisplayContent = function()
{
  if (!this._data)
    return;
    
  // Fetch LOB if need be.
  if(this._data.src)
    new HTTPRequest(this._data.src, Delegate.create(this, this.onRequestLoaded));
  // Set content if there is something.
  if (this._data.html) {
    this._body.innerHTML = this._data.html;
    this.dispatchEvent({type: "onLobLoaded", target: this});
  }
};
HTMLListItem.prototype._destroyDisplayContent = function()
{
  this._body.innerHTML = "";
};
HTMLListItem.prototype._createEditComponent = function(div) {
  var editInput = (!isSafari)?new RichTextEditor(div) : new TextAreaInput(div);
  this._onTextChangeHandler = Delegate.create(this,  this.onTextChange);
  editInput.addEventListener("onTextChange", this._onTextChangeHandler);
  editInput.addEventListener("onEscapeKey", this);
  return (editInput);
};
HTMLListItem.prototype.onRequestLoaded = function(html, xml, status) {
  this._data.html = html;
  this._body.innerHTML = html;
  this.dispatchEvent({type: "onLobLoaded", target: this});
}
HTMLListItem.prototype.onTextChange = function(eventObject) {
  eventObject.target.dispatchEvent({type:"onSubmitEnable", enable:true, target: this});
};
HTMLListItem.prototype.onEscapeKey = function(e) {
  var input = e.target;
  input.dispatchEvent("onSubmitCancel");
};
/*
{
  uid:"123",
  url:"some.url",
  src:"some.url",
  name:"Dweller",
  type:"person",
  html:"<b>This is the <i>Dweller</i> article.</b>"
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>OverlayPrompt</b>
 * 
 * Abstract class:
 *
 * Subclasses must overwrite [OverlayPrompt.prototype.createPromptContent]
 * @author alee@metaweb.com
 */
/****************************************************** constructor
 */
function OverlayPrompt(overlayElement, msgData, buttons) {
  this._msgData = msgData;
  this._buttons = buttons;
  if (typeof this._buttons == "undefined" || this._buttons == null)
    this._buttons = [{
      text: "OK",
      event: "onOk",
      css: "okButton"
    }];
  setupInheritance(OverlayPrompt);
  superConstructor(Overlay, this, [overlayElement]);
};
OverlayPrompt.superClass = [Overlay];
registerClass(OverlayPrompt, "OverlayPrompt");
OverlayPrompt.prototype.CLASS_NAME = "OverlayPrompt";
OverlayPrompt.prototype.CONTENT_NAME = "OverlayPromptContent";
OverlayPrompt.prototype.createContent = function() {
  var frag = document.createDocumentFragment();
  var div = this.createTag("div", frag, null, null, "OverlayTopShadow");
  var div2 = this.createTag("div", div, null, null, "OverlayBottomShadow");
  var div3 = this.createTag("div", div2, null, null, this.CONTENT_NAME);
  this.createPromptContent(div3);
  this._promptButtons = this.createPromptButtons(div3); 
  this._body.appendChild(frag);
  return null;
};
OverlayPrompt.prototype.createPromptContent = function() {
  Debug.error(this.CLASS_NAME, "must overwrite [OverlayPrompt.prototype.createPromptContent]");
}
OverlayPrompt.prototype.createPromptButtons = function(frag){
  this._buttonDiv = this.createTag("div", frag, null, null, "buttonDiv");
  var promptButtons = [];
  var promptButton;
  for (var b=0, len=this._buttons.length; b<len; b++) {
    var promptButton = this.addChildComponent(Button, this._buttonDiv, "button", null, null, this._buttons[b].css);
    promptButton.setText(this._buttons[b].text);
    promptButton.addEventListener("onClick", this);
    promptButtons.push(promptButton);
  }
  return promptButtons;
}
OverlayPrompt.prototype.onClick = function(e) {
  for (var c=0, len=this._buttons.length; c<len; c++) {
    if (this._buttons[c].text == e.target.getText()) {
      this.hide();
      if (this._buttons[c].callback)
        this._buttons[c].callback(e);
      this.dispatchEvent({type:this._buttons[c].event, target: e.target});
      break;
    }
  }
};
OverlayPrompt.prototype.show = function (){
  superMethod(Overlay, this, "show");
  this.setDefaultButton();
}
OverlayPrompt.prototype.setDefaultButton = function(frag){
  if (this._promptButtons && this._promptButtons.length > 0) {
    // Set the first to be have the focus and hence default button
    this._promptButtons[0].setFocus();
  }
}
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * NumericLabel class
 * nick@metaweb.com
 */
function NumericLabel(div){
  setupInheritance(NumericLabel);
  superConstructor(Label, this, [div]);
}
NumericLabel.superClass = [Label];
registerClass(NumericLabel, "NumericLabel");
NumericLabel.prototype.setData = function(data){
  this._data = data;
  var str = this._data.text + "";
  if (str) {
    str = NumericLabel.format(str);
    this._data.text = str;
  }
  this._updateHTML();
}
NumericLabel.format = function(str) {
  //var NumObj = new Number(str);
  //str = NumObj.toLocaleString();
  if ((str == "Infinity") || (str == "-Infinity")) return str;
  if (str.match(/[0-9]+e\+[0-9]+/)) return str;
  str = str.replace(/[^0-9\.\-]/g,''); //strip all non numerics
  if (str.match(/\..*\./)) {  // more than one "."
    return "NaN";
  }
  if (str.match(/.-/)) {  // "-" anywhere other than very beginning
    return "NaN";
  }
  x = str.split('.');
  x1 = x[0];
  x2 = x.length > 1 ? '.' + x[1] : '';
  var rgx = /(\d+)(\d{3})/;
  while (rgx.test(x1)) {
    x1 = x1.replace(rgx, '$1' + ',' + '$2');
  }
  str= x1 + x2;
  return str;
}
/*
_data = {
  text:"123456789"
}
HTML:
<div class="Label">123,456,789</div>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * ImageComponent class
 * alee@metaweb.com
 */
function ImageComponent(div){
  setupInheritance(ImageComponent);
  superConstructor(BaseComponent, this, [div]);
  // member vars:
  this._label;
}
ImageComponent.superClass = [BaseComponent];
registerClass(ImageComponent, "ImageComponent");
ImageComponent.TITLE = "Image";
ImageComponent.prototype.CLASS_NAME = "ImageComponent";
ImageComponent.prototype.init = function(){
  this.addCSSClass("ImageComponent");
  // image
  this._imageDiv = this.addChildTag("div", this._body, null, "ImageDiv");
  this._label = new Label(this.addChildTag("div"));
}
ImageComponent.prototype.setEditable = function(state){}
ImageComponent.prototype.setData = function(data){
  this._data = data;
  if (!data) return;
  try{
    // cache image ?
    var image = ImageFactory.create(this._data.src, Delegate.create(this, this.onImageLoaded), this._data.alt);
    if (!this._data.src)
      this.addCSSClass("EmptyImage");
    if(this._data.url)
      this._imageDiv.innerHTML = "<a href='"+this._data.url+"'><img src='" + this._data.src + "'" + ((this._data.alt)?" alt='" + this._data.alt + "'" : "") + "/></a>";
    else 
      this._imageDiv.innerHTML = "<img src='" + this._data.src + "'" + ((this._data.alt)?" alt='" + this._data.alt + "'" : "") + "'/>";
  }
  catch(ex){
    Debug.error("ImageComponent.setData:", ex);
  }
  if (this._data.text)
    this._label.setData(this._data);
}
ImageComponent.prototype.onImageLoaded = function(e){
  this.dispatchEvent({type:"onImageLoaded"});
};
/*
JSON DATA:
{
  uid:"123",
  url:"some.url",
  alt:"Dweller",
  text:"A longer descriptive piece of text",
  type:"person",
  src:"some.url"
}
HTML:
<div class="ImageComponent" component="ImageComponent">
  <img alt="" src="http://www.google.com/intl/en/images/logo.gif">
  <div class="Label label">Google Logo</div>
</div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractDialog</b>
 *
 * Subclasses must overwrite:
 * [AbstractDialog.prototype.createInputComponents]
 * [AbstractDialog.prototype.destroyInputComponents]
 * [AbstractDialog.prototype.haveRequiredInputs]
 * [AbstractDialog.prototype.nextFocus]
 *
 * @author alee@metaweb.com
 */
function AbstractDialog(div){
  setupInheritance(AbstractDialog);
  superConstructor(BaseComponent, this, [div]);
  // member vars
  this._hide;
  this._inputComponents;
  this._toolbarDiv;
  this._submitButton;
  this._cancelButton;
}
AbstractDialog.superClass = [BaseComponent];
registerClass(AbstractDialog, "AbstractDialog");
AbstractDialog.prototype.CLASS_NAME = "AbstractDialog";
AbstractDialog.REMOTING_DIV = "RemotingDiv";
AbstractDialog.REMOTING_IFRAME = AbstractDialog.REMOTING_DIV+"IFrame";
AbstractDialog.REMOTING_FORM = AbstractDialog.REMOTING_DIV+"Form";
AbstractDialog.prototype.init = function(){
  this.addCSSClass("AbstractDialog");
  this.addCSSClass(this.CLASS_NAME);
  this.clearData();
  this._destroyDialogContent();
  this._createDialogContent();
};
AbstractDialog.prototype.setData = function(data){
  this.clearData();
  this._data = data;
};
AbstractDialog.prototype.setHide = function(hide){
  if (typeof this._hide != "undefined" && this._hide == hide)
    return;
  this._hide = hide;
  if (this._hide)
    this._body.style["visibility"] =  "hidden"; 
  else 
    this._body.style["visibility"] =  "visible"; 
};
AbstractDialog.prototype.createInputComponents = function(div) {
  Debug.error("Subclasses must overwrite [AbstractDialog.prototype.createInputComponents]");
  return null;
};
AbstractDialog.prototype.haveRequiredInputs = function(){
  Debug.error("Subclasses must overwrite [AbstractDialog.prototype.haveRequiredInputs]");
};
AbstractDialog.prototype.nextFocus = function(input) {  
  Debug.error("Subclasses must overwrite [AbstractDialog.prototype.nextFocus]");
};
AbstractDialog.prototype._createDialogContent = function()
{
  this._inputComponents = this.createInputComponents();
  this._inputComponents.addEventListeners([
    "onSubmitEnable", 
    "onSubmitFocus", 
    "onSubmit", 
    "onSubmitCancel"
    ], this);
  
  this._toolbarDiv = this.addChildTag("div", null, null, "DialogButtons");
  this._submitButton = this.addChildComponent(Button, this._toolbarDiv, "button", null, null, "submitButton");
  this._submitButton.addEventListener("onClick", Delegate.create(this, this.onDoSubmit));  
  this.setSubmitButtonText();
  
  this._cancelButton = this.addChildComponent(Button, this._toolbarDiv, "button", null, null, "cancelButton");
  this._cancelButton.addEventListener("onClick", Delegate.create(this, this.onCancel));
  this.setCancelButtonText();
  
  this._enableButtons();
};
AbstractDialog.prototype._destroyDialogContent = function()
{
  this._disableButtons();
  if (this._submitButton) {
    this._submitButton.destroy();
    delete this._submitButton;
  }
  if (this._cancelButton) {
    this._cancelButton.destroy();
    delete this._cancelButton;
  }
  
  if (this._toolbarDiv){
    this._body.removeChild(this._toolbarDiv);
    this._toolbarDiv = null;
  }
  
  if (this._inputComponents) {
    this._inputComponents.destroyInputComponents();
    delete this._inputComponents;
  }
};
AbstractDialog.prototype.destroy = function() {
  this._destroyDialogContent();
  superMethod(BaseComponent, this, "destroy");
}
AbstractDialog.prototype._clearDialogContent = function()
{
  this._inputComponents.clear();
  this.nextFocus(null);
}
AbstractDialog.prototype.createRemotingDiv = function(div, action, beforeIndex, formAttributes) {     
  if (!div)
    div=this._body;
  if (!formAttributes)
    formAttributes = "";
  var rDiv = this.createTag('div', div, beforeIndex, null, "Remoting");
  rDiv.id = AbstractDialog.REMOTING_DIV; 
  var id = AbstractDialog.REMOTING_IFRAME;
  // iframe - use innerHTML since we are not doing any event processing of iframe itself
  var style = 'border:0;width:0;height:0;'; 
  rDiv.innerHTML = "<iframe name='"+id+"' id='"+id+"' style='"+style+"'></iframe>";
  rDiv.iframe = $id(id); 
 
  // form - use innerHTML since we are not doing any event processing of iframe itself
  var form = $elt('div');
  form.innerHTML = '<form id="' + AbstractDialog.REMOTING_FORM + '" action="' + action + '" target="' + id + '" method="POST" ' + formAttributes + '></form>';
  rDiv.form = form.firstChild;
  rDiv.appendChild(rDiv.form); 
  return rDiv; 
} 
AbstractDialog.prototype.destroyRemotingDiv = function(rDiv) {     
  rDiv.removeChild(rDiv.form);
  rDiv.form = null;
  rDiv.id = null;
  rDiv.innerHTML = "";
  rDiv.iframe = null;
} 
AbstractDialog.prototype.setSubmitButtonText = function() {
  this._submitButton.setText("Submit");
}
AbstractDialog.prototype.setCancelButtonText = function() {
  this._cancelButton.setText("Cancel");
}
AbstractDialog.prototype._disableButtons = function() {
  if (this._submitButton) {
    this._submitButton.setDisabled(true);
  }  
  if (this._cancelButton) {
    this._cancelButton.setDisabled(true);
  }
};
AbstractDialog.prototype._enableButtons = function() {
  if (this._submitButton) {
    this._submitButton.setDisabled(true);
  }  
  if (this._cancelButton) {
    this._cancelButton.setDisabled(false);
  }
};
AbstractDialog.prototype.onSubmitEnable = function(eventObject) {
  if (this._submitButton) {
    var enable = true == eventObject.enable;
    //
    // caution: enabling/disabling buttons grabs focus
    // so only set the state if it is different from current because 
    // setting the state might cause you to lose focus
    //
    if (this._submitButton.getDisabled() != !enable)   
      this._submitButton.setDisabled(!enable);
  }
};
AbstractDialog.prototype.onSubmitFocus = function(eventObject) {
  if (this._submitButton && !this._submitButton.getDisabled())
    this._submitButton.setFocus();
};
AbstractDialog.prototype.onSubmit = function(event){
  this.onDoSubmit();
}
AbstractDialog.prototype.onSubmitCancel = function(event){
  if (this._cancelButton)
    this._cancelButton.setFocus();
  this.onCancel();
}
AbstractDialog.prototype.onDoSubmit = function() {
  this._disableButtons();
  var submitData = this._inputComponents.getData();
  this.dispatchEvent({type:"onDoSubmit", target: this, submitData:submitData});
};
AbstractDialog.prototype.onCancel = function() {
  this._disableButtons();
  this.dispatchEvent({type:"onCancel", target: this});
};
AbstractDialog.prototype.onEnterKey = function(eventObject) {
  this.nextFocus(eventObject.target);
};
AbstractDialog.prototype.onEscapeKey = function(eventObject) {
  this.dispatchEvent("onSubmitCancel");
};
AbstractDialog.prototype.onTextChange = function(eventObject){
  if (this.haveRequiredInputs())
    this.dispatchEvent({type:"onSubmitEnable", enable: true});
  else
    this.dispatchEvent({type:"onSubmitEnable", enable: false});
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SimpleEditableListItem</b>
 *
 * @author daepark@apmindsf.com
 */
function SimpleEditableListItem(div){
  // member vars:
  this._label;
  this._onTextChangeHandler;
  
  setupInheritance(SimpleEditableListItem);
  superConstructor(AbstractEditableListItem, this, [div]);
};
SimpleEditableListItem.superClass = [AbstractEditableListItem];
registerClass(SimpleEditableListItem, "SimpleEditableListItem");
SimpleEditableListItem.prototype.CLASS_NAME = "SimpleEditableListItem";
SimpleEditableListItem.prototype._createEditComponent = function(div)
{
  var c = new TextInput(div);
  c = Delegate.create(this, this.onTextChange);
  c.addEventListener("onTextChange", this._onTextChangeHandler);
  return (c);
};
SimpleEditableListItem.prototype._createDisplayContent = function()
{
  this._label =this.addChildComponent(Label, null, "span", null, null);
  this._label.setData({text:this._data.text, value:this._data.value}); 
};
SimpleEditableListItem.prototype._destroyDisplayContent = function()
{
  if (this._label) {
    this._label.destroy();
    this._label = null;
  }
};
SimpleEditableListItem.prototype.onTextChange = function(eventObject)
{
  eventObject.target.dispatchEvent({type:"onSubmitEnable", enable:true, target: this});
};
/* DATA EXAMPLE:
_data = {
  text:"Item One",
  value:"123456789"
}
HTML:
<li class="SimpleListItem">
  <div class="Label">Item One</div>
</li>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * SimpleDisplayListItem class
 * mark@metaweb.com
 */
function SimpleDisplayListItem(div){
  this._label;
  setupInheritance(SimpleDisplayListItem);
  superConstructor(SimpleListItem, this, [div]);
};
SimpleDisplayListItem.superClass = [SimpleListItem];
registerClass(SimpleDisplayListItem, "SimpleDisplayListItem");
SimpleDisplayListItem.prototype.CLASS_NAME = "SimpleDisplayListItem";
SimpleDisplayListItem.prototype.initInteractive = function(){
  this._selected = false;
};
/* DATA EXAMPLE:
_data = {
  text:"Item One",
  url:"item.html"
}
HTML:
<li class="SimpleDisplayListItem">
  <div class="Label">Item One</div>
</li>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * BaseComponent class
 * mark@apmindsf.com
 */
function BaseComponent(div){
  if(div==undefined) {
    Debug.warn("BaseComponent: must provide HTMLElement", getClassName(this));
    return;
  }
  // member vars:
  this._data;
  this._body = div; // of type HTMLElement
  setupInheritance(BaseComponent);
  superConstructor(EventDispatcher, this);
  superConstructor(CSSBehaviors, this, [div]);
  if (typeof this.CLASS_NAME == "undefined" || this.CLASS_NAME == null)
    this.CLASS_NAME = getClassName(this);
  this.init();
  // subclass init might have changed tag/element
  superMethod(CSSBehaviors, this, "_updateElement", [this._body]);
  // this must come after init:
  this._body._component = this;
  autoclean(this, BaseComponent.gc);
  this._body.setAttribute("initialized", "true");
}
BaseComponent.superClass = [EventDispatcher, CSSBehaviors];
registerClass(BaseComponent, "BaseComponent");
BaseComponent.prototype.CLASS_NAME = "BaseComponent";
BaseComponent.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
}
BaseComponent.prototype.addChildComponent = function(componentClass, parentElement, tagName, beforeIndex, beforeElement, cssClassName, componentAguments){
  if (typeof componentClass == "string") {
    // Temporary to catch calls that were not switched over from className to componentClass
    throw ("BaseComponent.addChildComponent: componentClass(" + componentClass + " is a string rather than component");
  }
  if (typeof parentElement == "undefined" || parentElement == null) 
    parentElement=this._body;
  if(!tagName) tagName = "div";
  var div = this._createDetachedTag(tagName);
  var child = applyNew(componentClass, [div].concat(componentAguments));
  // _addTag is lower-level and won't go through addCSSClass, so we'll
  // do it ourselves
  if (cssClassName)
    child.addCSSClass(cssClassName);
  
  this._addTag(child._body, parentElement, beforeIndex, beforeElement);
  return child;
}
BaseComponent.prototype.replaceChildComponent = function(className, tagName, index)
{
  if(!tagName) tagName = "div";
  var div = this._createDetachedTag(tagName);
  var childClass = ObjectMap[className];
  var newComponent = new childClass(div);
  this._body.replaceChild(newComponent._body, this._body.childNodes[index]);
  return (newComponent);
}
BaseComponent.prototype.addChildTag = function(tagName, beforeIndex, beforeElement, className){
  return this.createTag(tagName, this._body, beforeIndex, beforeElement, className);
};
BaseComponent.prototype._createDetachedTag = function(tagName) {
  return this._body.ownerDocument.createElement(tagName);
};
  
  
BaseComponent.prototype.createTag = function(tagName, parent, beforeIndex, beforeElement, className, innerText){
  var element = this._createDetachedTag(tagName);
  this._addTag(element, parent, beforeIndex, beforeElement, className);
  if (innerText)
    this.createText(innerText, element);
  return element;
}
BaseComponent.prototype.createText = function(text, parent, beforeIndex, beforeElement){
  var element = this._body.ownerDocument.createTextNode(text);
  return this._addTag(element, parent, beforeIndex, beforeElement);
};
BaseComponent.prototype.replaceText = function(text, parent) {
  if (parent.childNodes.length>0) {
    for (var i=parent.childNodes.length-1; i>=0; i--)
      parent.removeChild(parent.childNodes[i]);
  }
  return this.createText(text, parent);
}
  
BaseComponent.prototype._addTag = function(element, parent, beforeIndex, beforeElement, className){
  if (parent != undefined)
  {
    if(className!=undefined) element.className += " "+className;
    if(beforeIndex!=undefined){
      var targetNode = parent.childNodes[beforeIndex];
      if(targetNode) parent.insertBefore(element, targetNode);
      else parent.appendChild(element);
    }
    else if(beforeElement!=undefined){
      parent.insertBefore(element, beforeElement);
    }
    else parent.appendChild(element);
  }
  return element;
}
BaseComponent.prototype.changeTag = function(tagName){
  if(this._body.tagName.toLowerCase() == tagName.toLowerCase()) return;
  // for debugging
  //Debug.warn("Changing tag from ", this._body, " to ", tagName, ": ");
  //dumpStack();
  var parent = this._body.parentNode;
  var newTag = this._createDetachedTag(tagName);
  var oldTag = this._body;
  // this is a fix for IE, which doesn't copy 
  // class attribute correctly:
  var cssClassName = oldTag.className;
  // copy attributes over to the new tag
  for(var i=0,len=oldTag.attributes.length; i<len; i++){
    var attrName = oldTag.attributes[i].nodeName;
    var attrValue = oldTag.attributes[i].nodeValue;
    if(attrValue != undefined)
    {
      newTag.setAttribute(attrName, attrValue);
    }
  }
  newTag.className = cssClassName;
  if (parent)
    parent.replaceChild(newTag, oldTag);
  this._body = newTag;
  this._body._component = this;
  //this._body.className = cssClassName;
  return newTag;
}
BaseComponent.prototype.destroy = function(remove){
  superMethod(EventDispatcher, this, "destroy");
  superMethod(CSSBehaviors, this, "destroy");
  if (this._body && this._body.parentNode)
    this._body.parentNode.removeChild(this._body);
  
  BaseComponent.gc(this);
}
BaseComponent.prototype.getDataProvider = function(){
  return this._body.getAttribute("dataProvider2");
}
BaseComponent.prototype.requestData = function(){
  ////////////////////// query:
  // query is already running
  var running_query;
  
  if ('_query' in this._body) {
    // we'll also use this later to warn
    running_query = this._body._query; 
    
    running_query.attachComponent(this);
    running_query.configureComponent(this._body);
    running_query.flushReceivedData();
    // now unhook the query so it can be cleaned up
    // note that 'delete' doesn't work in IE6 here?
    this._body._query = null;
  }
  
  ////////////////////// data:
  // get data from a JSON component data model:
  var dataProperty = this._body.getAttribute("data");
  if (dataProperty) {
    try{
      var data = eval(dataProperty);
      this.receiveData(data);
    }
    catch(ex){
      Debug.error("BaseComponent.requestData(data):", ex, "\ndata:", dataProperty, "\nlineNo:", ex.lineNumber);
      dumpStack(ex);
    }
  }
  ////////////////////// dataProvider:
  // get data from dataProvider:
  var dataProviderProperty = this._body.getAttribute("dataProvider");
  // this next line should be removed, it's here for legacy support
  // while we transition from dataProvider2 to dataProvider:
  if(!dataProviderProperty) dataProviderProperty = this._body.getAttribute("dataProvider2");
  if(dataProviderProperty){
    try {
      var dataProvider = eval(dataProviderProperty);
      var select = this._body.getAttribute("select");
      if (select) {
        select = eval(select);
      }
      dataProvider.requestData(this, select);      
    } catch (ex) {
      Debug.error("BaseComponent.requestData(dataProvider):", ex, "\ndataProvider2:", dataProviderProperty);
    }
  }
  ////////////////////// query:
  // get data from a Query class:
  var queryProperty = this._body.getAttribute("query");
  if(queryProperty){
    if (running_query)
      Debug.warn("Component instance '", getClassName(this), 
           "' already has a query '", getClassName(running_query), 
           "' attached.");
    
    try{
      var queryClass = ObjectMap[queryProperty];
      var configAdapter = new MQLDivConfig(this._body);
      var query = new queryClass(configAdapter);
      
      // (from MQLComponentQuery...)
      // 5. configure component
      //
      query.attachComponent(this);
      query.configureComponent(this._body);
      query.flushReceivedData();
    }
    catch(ex){
      Debug.error("BaseComponent.requestData(query):", ex, "\nquery:", queryProperty);
      if (typeof(ex) == "object" && 'stack' in ex) {
        var stack = ex.stack.split('\n');
        var formattedStack = [];
        for (var i=0; i<stack.length; i++) {
          var frame = stack[i].split('@');
        formattedStack.push(frame[1] + ": " + frame[0].slice(0,50));
        }
        formattedStack = formattedStack.join("\n");
        Debug.log(formattedStack, "alecf");
      }
    }
  }
  return (dataProvider!=null || data!=null || queryProperty!=null);
}
BaseComponent.prototype.receiveData = function(data, metadata){
  Debug.time("component draw time: " + this.CLASS_NAME);
  this.setData(data, metadata);
  Debug.timeEnd("component draw time: " + this.CLASS_NAME);
  this.dispatchEvent({type:"onReceiveData", target:this});
}
BaseComponent.prototype.clearData = function(){
  this._data = {};
};
BaseComponent.prototype.setData = function(data){
  this._data = data;
}
BaseComponent.prototype.getData = function(){
  return this._data;
}
BaseComponent.prototype.getBody = function(){
  return this._body;
}
BaseComponent.prototype.setFocus = function(){
  if(this._body.focus) this._body.focus();
}
BaseComponent.prototype.setBlur = function(){
  if(this._body.blur) this._body.blur();
}
BaseComponent.prototype._getBorderWidth = function(side){
  var element = this._body;
  var width;
  if(window.getComputedStyle){
    if(side=="top") side = "borderTopWidth";
    else if(side=="right") side = "borderRightWidth";
    else if(side=="bottom") side = "borderBottomWidth";
    else side = "borderLeftWidth";
    width = window.getComputedStyle(element,null)[side];
  }
  else if(element.currentStyle){
    if(side=="top") side = 0;
    else if(side=="right") side = 1;
    else if(side=="bottom") side = 2;
    else side = 3;
    width = element.currentStyle.borderWidth.split(" ")[side];
  }
  width = parseInt(width, 10);
  width = (isNaN(width)?0:width);
  return width;
}
BaseComponent.prototype._getContentHeight = function(){
  var h = this._body.offsetTop+this._body.offsetHeight;
  var borderWidth;
  if(isIE){
    borderWidth = this._getBorderWidth("top")+this._getBorderWidth("bottom");
    var margin = this._body.currentStyle.margin.split(" ")[0];
    margin = parseInt(margin, 10);
    margin = (isNaN(margin)?0:margin);
    borderWidth += margin;
  }
  else borderWidth = this._getBorderWidth("bottom");
  h = h-borderWidth
  return h;
}
BaseComponent.prototype.showElement = function(element, state){
  if(state){//show
    if(element._originalDisplay!=null){
      DOMUtils.setElementStyle(element, "display: " + element._originalDisplay);
    }
    else DOMUtils.setElementStyle(element, "display: block");
  }
  else{//hide
    var display = DOMUtils.getElementStyle(element,"display");
    if(display!="none"){
      element._originalDisplay = display;
      DOMUtils.setElementStyle(element, "display: none");
    }
  }
}
BaseComponent.prototype.show = function(){
  this.showElement(this._body, true);
}
BaseComponent.prototype.hide = function(){
  this.showElement(this._body, false);
}
BaseComponent.CURRENT_ID=0;
BaseComponent.CSS_CHANGES = []; // mapping of ID -> class changes
BaseComponent.TAGS = []; // mapping of ID -> tag names
BaseComponent.COMPONENTS = [];  // mapping of ID -> component
BaseComponent.most_changes = function(max) {
  var lengths = [];
  for (var key=0; key<BaseComponent.CSS_CHANGES.length; key++) {
    var changeList = BaseComponent.CSS_CHANGES[key];
    lengths.push([changeList.length, key]);
  }
  function reverse(a,b) {
    if (a<b) return 1;
    if (a>b) return -1;
    return 0;
  }
  
  lengths.sort(reverse);
  var result = [];
  for (var i=0; i<max && i<lengths.length; i++) {
    var lengthItem = lengths[i];
    var len = lengthItem[0];
    var key = lengthItem[1];
    var component = BaseComponent.COMPONENTS[key];
    var changeLog = BaseComponent.CSS_CHANGES[key];
    result.push([changeLog, component, key]);
  }
  return result;
};
BaseComponent.prototype.onDataLoaded = function(){
  this.dispatchEvent("onDataLoaded");
}
BaseComponent.prototype.setToolTip = function(text){
    this._body.setAttribute("title",text);
}
BaseComponent.prototype.onError = function(synopsis, elaboration, details, siblingDiv) {
  var errorComponentDiv;
  if (siblingDiv == "undefined" || siblingDiv == null)
    errorComponentDiv = this.addChildTag("div", 1);
  else {
    errorComponentDiv = document.createElement("div");
    siblingDiv.parentNode.insertBefore(errorComponentDiv, siblingDiv.nextSibling);
  }
 
  var errorComponent = MakeErrorFeedback(errorComponentDiv,
                                          {synopsis:synopsis,
                                           elaboration:elaboration,
                                           details:details});
  errorComponent.addEventListener("onClose", Delegate.create(this, this.onErrorClose));
  return errorComponent;
};
BaseComponent.prototype.onErrorClose = function() {  
  // Do nothing but subclasses can override
};
BaseComponent.prototype.onNotify = function(message, siblingDiv) {
  var errorComponentDiv;
  if (siblingDiv == "undefined" || siblingDiv == null) {
    Debug.log("BaseComponent.onNotify: attaching to this._body");
    notifyComponentDiv = this.addChildTag("div", 1);
  }
  else {
    notifyComponentDiv = document.createElement("div");
    siblingDiv.parentNode.insertBefore(notifyComponentDiv, siblingDiv.nextSibling);
  }
 
  var notifyComponent = ConfirmationFeedback.MakeConfirmationFeedback(notifyComponentDiv, {synopsis:message});
  notifyComponent.addEventListener("onClose", Delegate.create(this, this.onNotifyClose));
  return notifyComponent;
};
BaseComponent.prototype.onNotifyClose = function() {  
  // Do nothing but subclasses can override
};
BaseComponent.gc = function(component) {
  delete component._data;
  if (component._body) {
    component._body._component = null;
    delete component._body;
  }
}
  
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>BreadCrumb</b>
 *
 * @author daepark@apmindsf.com
 */
function BreadCrumb(div){
  // member vars:
  this._crumbsDiv = null;
  this._crumbs = []; // list of Labels
  setupInheritance(BreadCrumb);
  superConstructor(BaseComponent, this, [div]);
};
BreadCrumb.superClass = [BaseComponent];
registerClass(BreadCrumb, "BreadCrumb");
BreadCrumb.SEPARATOR = "&nbsp;";
BreadCrumb.prototype.init = function() {
  this.addCSSClass("BreadCrumb");
  this._crumbsDiv = this.addChildTag("div", null, null, "crumbs");
};
BreadCrumb.prototype.setData = function(data) {
  superMethod(BaseComponent, this, "setData", [data]);
  this._clear();
  if (!data.crumbs) return;
  for(var i=0, len=data.crumbs.length; i<len; i++) {
  	var c = this._create(data.crumbs[i]);
  	this._crumbs.push(c);
  	if (i + 1 < len) {
  	  this._createSeparator();
  	}
  }
};
BreadCrumb.prototype._clear = function() {
  for(var i=0, len=this._crumbs.length; i<len; i++) {
  	this._crumbs[i].destroy();
  	delete this._crumbs[i];
  }
  this._crumbs = [];
  this._crumbsDiv.innerHTML = "";
};
BreadCrumb.prototype._create = function(crumbData) {
  var label = this.addChildComponent(Label, this._crumbsDiv, "span", null, null, "crumb");
  label.setData(crumbData);
  return label;
};
BreadCrumb.prototype._createSeparator = function(crumbData) {
  var span = this.createTag("span", this._crumbsDiv, null, null, "crumb-separator");
  span.innerHTML = BreadCrumb.SEPARATOR;
};
function Paginator(div) {
  this.resetCursor();
  
  setupInheritance(Paginator);
  superConstructor(BaseComponent, this, [div]);
}
Paginator.prototype.CLASS_NAME = "Paginator";
Paginator.superClass = [BaseComponent];
registerClass(Paginator, "Paginator");
Paginator.prototype.init = function() {
  superMethod(BaseComponent, this, 'init', []);
  this.addCSSClass(this.CLASS_NAME);
  
  this._previousButton = this.addChildComponent(Button, this._body, "button", null, null, "previousButton", [true]);
  this._previousButton.setText("&#171; Previous");
  this._previousButton.setDisabled(true);
  this._previousButton.addEventListener("onClick",
                                        Delegate.create(this, this.onPreviousClick));
  this._nextButton = this.addChildComponent(Button, this._body, "button", null, null, "nextButton", [true]);
  this._nextButton.setText("Next &#187;");
  this._nextButton.setDisabled(true);
  this._nextButton.addEventListener("onClick",
                                    Delegate.create(this, this.onNextClick));
  var pageLabelId = this._body.getAttribute("pageLabel");
  if (pageLabelId)
    this._pageLabel = $id(pageLabelId);
  var targetId = this._body.getAttribute("target");
  if (targetId)
    this._targetComponent = $id(targetId)._component;
};
Paginator.prototype.resetCursor = function() {
  
  this._previousCursors = [];   // stack of previous cursors
  this._currentCursor = true;   // value of cursor currently loaded
  this._nextCursor = null;      // value of cursor for 'next'
  this._previousIndexes = [];   // index of beginning of each page in
                                // previousCursors
  this._currentIndex = 0;       // index of current results
  this._currentLength = 0;      // length of current results
  this._maxIndex = 0;           // max index that we've seen
  this._maxIsEstimate = false;  // is max index just an estimate?
  this._finished_cursor = false; // have we reached the end of the results?
};
Paginator.prototype.setPosition = function(index) {
  // a way for clients to override the current position
  // should we be updating the label here too? And maybe
  // _currentLength? hrm...
  this._currentIndex = index;
}
Paginator.prototype.onNextClick = function(event) {
  this._previousCursors.push(this._currentCursor);
  this._previousIndexes.push(this._currentIndex);
  
  this._currentCursor = this._nextCursor;
  this._currentIndex += this._currentLength;
  
  this._nextCursor = null;      // for sanity-checking
  this._currentLength = 0;
  this._nextButton.setDisabled(true);
  if (this._targetComponent)
    this._targetComponent.onNextPage(this._currentCursor);
};
Paginator.prototype.onPreviousClick = function(event) {
  // throw away the current cursor, and go back to the last one
  
  this._nextCursor = null;      // will be set on load
  this._currentCursor = this._previousCursors.pop();
  this._currentIndex = this._previousIndexes.pop();
  this._nextButton.setDisabled(true);
  if (this._targetComponent)
    this._targetComponent.onPreviousPage(this._currentCursor);
};
Paginator.prototype.onPageLoaded = function(nextCursor, length,
                                            new_max, max_is_estimate) {
  this._nextCursor = nextCursor;
  
  if (this._nextCursor == false) {
    // nothing more
    this._nextButton.setDisabled(true);
    this._finished_cursor = true;
  } else {
    this._nextButton.setDisabled(false);
  }
  
  if (this._currentCursor == true)
    this._previousButton.setDisabled(true);
  else
    this._previousButton.setDisabled(false);
  
  this._currentLength = length;
  if (new_max === undefined) {
    if (this._currentIndex + length > this._maxIndex)
      this._maxIndex = this._currentIndex + length;
  } else {
    this._maxIndex = new_max;
    if (max_is_estimate !== undefined)
      this._maxIsEstimate = max_is_estimate;
    else
      this._maxIsEstimate = false;
  }
  
  var lastIndex = this._currentIndex + length;
  
  if (length == 0)
    this._pageLabel._component.setText("No items");
  else {
    var labelText;
    if (this._maxIsEstimate) {
      labelText = sprintf("Items %s - %s of about %s",
                          this._currentIndex + 1,
                          this._currentIndex + length,
                          this._maxIndex);
    } else  {
      var extra = "";
      if (!this._finished_cursor)
        extra = "+";
      labelText = sprintf("Items %s - %s of %s%s",
                          this._currentIndex + 1,
                          this._currentIndex + length,
                          this._maxIndex, extra);
    }
    this._pageLabel._component.setText(labelText);    
  }
};
Paginator.prototype.beginPageLoad = function() {
  this._nextButton.setDisabled(true);
};
  
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * OptionListItem class
 * alee@metaweb.com
 *
 * Simulates a single option of a similated select menu form input item.
 */
function OptionListItem(div){
  setupInheritance(OptionListItem);
  superConstructor(BaseComponent, this, [div]);
  // member vars:
  this._selected;
  this._label;
  this._arrow;
  this._mouseEventHandler;
}
OptionListItem.superClass = [BaseComponent];
registerClass(OptionListItem, "OptionListItem");
OptionListItem.prototype.CLASS_NAME = "OptionListItem";
OptionListItem.prototype.init = function(){
  this.changeTag("li");
  this.addCSSClass(this.CLASS_NAME);
  this.clearData();
  this._mouseEventHandler = new MouseEventHandler(this, this._body,
    MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT | MouseEventHandler.MOUSECLICK);
  this._selected = false;
  this._createDisplayComponent();
}
OptionListItem.prototype.clearData = function(data){
  this._data = {text:null, uid:null};
  // remove mouse events
  if (this._mouseEventHandler) 
    this._mouseEventHandler.setInteractive(false);
}
OptionListItem.prototype.setData = function(data){
  this.clearData();
  if(!data) return;
  this._data = data;
  this._mouseEventHandler.setInteractive(true);
  var labelData = {text:this._data.text, uid:this._data.uid};
  this._label.setData(labelData);
}
OptionListItem.prototype._createDisplayComponent = function(){
  if (this._label) this._label.destroy();
  this._label = this.addChildComponent(Label, null, "span", 1, null, "OptionListItem-label");
  this._label.setData({text:this._data.text,url:this._data.url, uid: this._data.uid});
  this._arrow =  this.addChildComponent(Label, null, "span", 0, null, "Arrow", [true]);
  this._arrow.setText("&#9660;");
  return (this._label);
}
OptionListItem.prototype.getId = function(){
  return this._data.uid;
}
OptionListItem.prototype.getText = function(){
  return this._data.text;
}
OptionListItem.prototype.setSelected = function(selected){
  if(selected == this._selected) return;
  this._selected = selected;
  if(this._selected) this.addCSSClass("selected");
  else this.removeCSSClass("selected");
}
OptionListItem.prototype.getSelected = function(selected){
  return this._selected;
}
OptionListItem.prototype.onMouseOver = function(e){
  if (isIE6) this.addCSSClass('listItemHover');
}
OptionListItem.prototype.onMouseOut = function(e){
  if (isIE6) this.removeCSSClass('listItemHover');
}
OptionListItem.prototype.onClick = function(e){
  this.dispatchEvent({type:"onOptionListItemClick", target:this, domEventObject:e});
}
/* DATA EXAMPLE:
_data = {
  text:""
}
HTML:
<li class="OptionListItem"><div class="Label">item 1</div></li>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * InfoListItem class
 * alee@metaweb.com
 */
function InfoListItem(div){
  this._label;
  setupInheritance(InfoListItem);
  superConstructor(AbstractListItem, this, [div]);
};
InfoListItem.superClass = [AbstractListItem];
registerClass(InfoListItem, "InfoListItem");
InfoListItem.prototype.CLASS_NAME = "InfoListItem";
InfoListItem.prototype.initInteractive = function(){
  this._selected = false;
};
InfoListItem.prototype._createDisplayContent = function(){
  frag = document.createDocumentFragment();
  if (this._data.text) {
    this._title = this.addChildComponent(Label, frag, "div", null, null, "InfoHeader");
    this._title.setText(this._data.text + ":");
  }
  if (this._data.tip) {
    this._content = this.addChildComponent(Label, frag, "div", null, null, "InfoContent");
    this._content.setText(this._data.tip);
  }
  if (this._data.typeName) {
    this._addendum = this.addChildComponent(Label, frag, "span", null, null, "InfoAddendum");
    var addendumText = (this._data.tip) ? ("(enter " + this._data.typeName + ")") : ("Enter " + this._data.typeName);
    this._addendum.setText(addendumText);
  }
  this._body.appendChild(frag);
};
InfoListItem.prototype._destroyDisplayContent = function(){
  if (this._addendum) {
    this._addendum.destroy();
    delete this._addendum;
  }
  if (this._content) {
    this._content.destroy();
    delete this._content;
  }
  if (this._title) {
    this._title.destroy();
    delete this._title;
  }
};
/* DATA EXAMPLE:
_data = {
  text:"Education",
  typeName: "Education",
  tip: "The education this person has received.",
  list: {
    listItems: [{
      text: "Institution",
      typeName: "Institution",
      tip: "The school the person attended"
    },{
      text: "Start Date",
      typeName: "Date/Time",
      tip: "The date the person began attendance"
    },{
      text: "End Date",
      typeName: "Date/Time",
      tip: "The date the person stopped attendance."
    },{
      text: "Major/Field of Study",
      typeName: "Field of Study"
    },{
      text: "Dissertation Title",
      typeName: "Dissertation"
    }]
  }
}
HTML:
<li class="InfoListItem">
  <div class="Label">The educattion this person has received</div>
</li>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>CheckBox</b>
 *
 * @author mark@metaweb.com
 * @author alee@metaweb.com
 *
 * Rewrote the state manager - HTML checkbox has a property checked.
 * Using it to get the uptodate state of the checkbox.  Update this._data.checked.
 * to coincide with this state.  When setState is called, checkbox.checked is set.
 */
function CheckBox(div){
  setupInheritance(CheckBox);
  superConstructor(AbstractOption, this, [div]);
}
CheckBox.superClass = [AbstractOption];
registerClass(CheckBox, "CheckBox");
CheckBox.prototype.CLASS_NAME = "CheckBox";
CheckBox.prototype._createOption = function(id){
  // can't use createTag because we need to set the
  // type attibute before appending to the DOM:
  var option = createNamedTypedElement("input", null, "checkbox");
  option.setAttribute("id", id);
  return option;
}
CheckBox.prototype.clearData = function(){
  this._data = { label:"", checked: false };
}
CheckBox.prototype.getData = function(){
  return {
    label: this._label.getText(),
    checked: this.getState(),
    text: this.getState() ? "true" : undefined
  };
}
/*
data = {
  checked:true,
  label:"check me"
}
<span class="CheckBox checked">
  <input type="checkbox">
  <span class="label">check me</span>
</span>
<span label="check me please" checked="true" component="CheckBox" class="">
  <input type="checkbox"/>
  <span class="label">check me please</span>
</span>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * UrlInput class
 * nick@metaweb.com
 */
function UrlInput(div){
  this._linkInput;
  this._titleInput;
  setupInheritance(UrlInput);
  superConstructor(BaseComponent, this, [div]);
}
UrlInput.superClass = [BaseComponent];
registerClass(UrlInput, "UrlInput");
UrlInput.prototype.CLASS_NAME = "UrlInput";
UrlInput.prototype.init = function(){
  //superMethod(BaseComponent, this, "init");
  this.addCSSClass("UrlInput");
  var frag = document.createDocumentFragment();
  this._linkInput = this.addChildComponent(TextInput, frag);
  this._linkInput.setPrompt("URL:");
  this._titleInput = this.addChildComponent(TextInput, frag);
  this._titleInput.setPrompt("Title:");
  this._titleInput.addEventListener("onTextChange", Delegate.create(this, this.onTitleChange));
  
  this._linkInput.addEventListener("onTextChange", Delegate.create(this, this.onLinkChange));
  this._titleInput.setDisabled(true);
  
  this._body.appendChild(frag);
}
UrlInput.prototype.onTitleChange = function(e){
  if (!(this._data)) {
    this._data = {};
  }
  if(this._data.text != this._titleInput.getText()) {
    // if the value actually has changed:
    this._data.text = this._titleInput.getText();
    this.dispatchEvent("onTextChange");
  }
}
UrlInput.prototype.onLinkChange = function(e){
  if (!(this._data)) {
    this._data = {};
  }
  if(this._data.url != this._linkInput.getText()) {
    // if the value actually has changed:
    this._data.url = this._linkInput.getText();
    this.dispatchEvent("onTextChange");
  }
  //If empty URL disable title.
  if (!(this._data.url)) { 
    this._titleInput.setDisabled(true);
  } else {
    this._titleInput.setDisabled(false);
  }
}
UrlInput.prototype.getLink = function(){
  return(this._linkInput.getText());
}
UrlInput.prototype.getTitle = function(){
  return(this._titleInput.getText());
}
UrlInput.prototype.getLinkInput = function(){
  return(this._linkInput);
}
UrlInput.prototype.getTitleInput = function(){
  return(this._titleInput);
}
UrlInput.prototype.setData = function(data){
  superMethod(BaseComponent, this, "setData", [data]);
  this._data = data;
  //If empty URL disable title.
  if (!(this._data.url)) { 
    this._titleInput.setDisabled(true);
  } else {
    this._titleInput.setDisabled(false);
  }
  this._linkInput.setText(this._data.url);
  this._titleInput.setText(this._data.text);
}
/*
DATA:
_data.text
_data.url
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractEditableList</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractEditableList.prototype._createEditableListItem]
 *
 * @author daepark@apmindsf.com
 */
function AbstractEditableList(div){
  setupInheritance(AbstractEditableList);
  superConstructor(AbstractList, this, [div]);
};
AbstractEditableList.superClass = [AbstractList];
registerClass(AbstractEditableList, "AbstractEditableList");
AbstractEditableList.prototype.CLASS_NAME = "AbstractEditableList";
AbstractEditableList.prototype._createListItem = function(div) {
  var listItem = this._createEditableListItem(div);
  listItem.addEventListeners([
    "onSave", 
    "onCancel"
    ], this);
  return listItem;
};
AbstractEditableList.prototype._destroyListItem = function(listItem) {
  if (listItem) {
    listItem.removeEventListeners([
      "onSave",
      "onCancel"
      ], this); 
  }
  superMethod(AbstractList, this, "_destroyListItem", [listItem]);
};
/****************************************************** _createEditableListItem
 */
AbstractEditableList.prototype._createEditableListItem = function(div) {
  Debug.error("Subclasses must overwrite [AbstractEditableList.prototype._createEditableListItem]");
  return null;
};
AbstractEditableList.prototype.onSave = function(e) {
  this.dispatchEvent(e);
};
AbstractEditableList.prototype.onCancel = function(e) {
  this.dispatchEvent(e);
};
/* 
DATA example:
data = {
  listItems:[
    {
    },
    {
    }
  ]
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SimpleList</b>
 *
 * @author daepark@apmindsf.com
 */
function SimpleDisplayList(div, listItemComponentClass){
  // member vars
  this.listItemComponentClass = (listItemComponentClass)?listItemComponentClass:SimpleDisplayListItem;
  
  setupInheritance(SimpleDisplayList);
  superConstructor(AbstractList, this, [div]);
};
SimpleDisplayList.superClass = [AbstractList];
registerClass(SimpleDisplayList, "SimpleDisplayList");
SimpleDisplayList.prototype.CLASS_NAME = "SimpleDisplayList";
SimpleDisplayList.prototype._createListItem = function(div) {
  return new this.listItemComponentClass(div);
};
SimpleDisplayList.prototype._addEventListeners = function(item){
}
SimpleDisplayList.prototype.removeItemEventListeners = function(item){
}
/*
  this._data = {
    id:"1234",
    list:{
      listItems:[
        {
          text:"Metaweb",
          url:"dweller.html",
          value: "dweller"
        },
        {
          text:"Wikipedia",
          url:"mechanism.html",
          value:"mechanism"
        },
        {
          text:"Homunculus",
          url:"homunculus.html",
          value:"homunculus"
        },
        {
          text:"Ooze",
          url:"ooze.html",
          value:"ooze"
        }
      ]
    }
  }
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * ImageFlipbook class
 * mark@metaweb.com
 */
function ImageFlipbook(div){
  // member vars:
  this._description;
  this._navigation;
  this._imageElement;
  this._imageDisplayElement;
  this._anchorElement;
  this._list;
  this._currentImage;
  this._nextButton;
  this._previousButton;
  this._navigationLabel;
  this._toolbar;
  this._input;
  this._addButton;
  this._deleteButton;
  this._emptyMessage;
  this._emptyMessageText;
  this._imageFrame;
  this._flipbookBottom;
  this._mouseEventHandler;
  
  // default menu
  this._menuData = {
    menu: [
      {text:"View this image", css:"ViewMenuItem",value:"View", disabled: false},
      {text:"Add an image", css:"AddMenuItem",value:"Add", disabled: false},
      {text:"View all", css:"ViewAllMenuItem",value:"View all", disabled: false},
      {text:"Remove this image", css:"RemoveMenuItem",value:"Remove", disabled: false}
    ]
  };
  setupInheritance(ImageFlipbook);
  superConstructor(BaseComponent, this, [div]);
}
ImageFlipbook.superClass = [BaseComponent];
registerClass(ImageFlipbook, "ImageFlipbook");
ImageFlipbook.prototype.CLASS_NAME = "ImageFlipbook";
ImageFlipbook.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  // make sure we're shown as "empty" even when loading
  this.updateCSS();
  
  var frag = document.createDocumentFragment();
  this._toolbar = this.createTag("div", frag, null, null, "toolbar");
  // add button
  this._menuButton = this.addChildComponent(PopupMenuLabel, this._toolbar, "div", null, null, "flipbookMenu");
  this._menuButton.setData(this._menuData);
  this._menuButton.addEventListeners([
    "onMenuSelect", 
    "onMenuLabelSelect"
    ], this);
  //imageFrame
  this._imageFrame = this.createTag("div", frag, null, null, "imageFrame");
  // imageDisplay
  this._imageDisplayElement = this.createTag("div", this._imageFrame, null, null, "imageDisplay");
  // emptyMessage
  this._emptyMessageText = this._body.getAttribute("emptyMessage");
  if(!this._emptyMessageText) this._emptyMessageText = "There are no images in this flipbook.";
  // anchor
  this._anchorElement = this.createTag("a",this._imageDisplayElement);
  // image
  this._imageElement = this.createTag("img",this._anchorElement);
  // description
  this._description = this.createTag("div", this._imageFrame, null, null, "description");
  //flipbookBottom
  this._flipbookBottom = this.createTag("div", frag, null, null, "flipbookBottom");
  // nav
  this._navigation = this.createTag("div", this._flipbookBottom, null, null, "navigation");
  this._previousButton = this.addChildComponent(Button, this._navigation, "button", null, null, "previous", [true]);
  this._previousButton.setText("&#9668;"); // 171;");//"&#9668;"
  this._previousButton.addEventListener("onClick", Delegate.create(this, this.previous));
  this._navigationLabel = this.addChildComponent(Label, this._navigation, "span");
  this._navigationLabel.setText(" ... ");
  this._nextButton = this.addChildComponent(Button, this._navigation, "button", null, null, "next", [true]);
  this._nextButton.setText("&#9658;"); //187;");//"&#9658;"
  this._nextButton.addEventListener("onClick", Delegate.create(this, this.next));
  this.showElement(this._imageElement,false);
  
  this.initInteractive();
  
  this._body.appendChild(frag);
}
ImageFlipbook.prototype.initInteractive = function(){
  this._mouseEventHandler = new MouseEventHandler(this, this._body, 
    MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT);
  this._mouseEventHandler.setInteractive(true);
};
ImageFlipbook.prototype.getSelection = function(){
  if (this._currentImage>-1 && this._currentImage<this._list.length)
    return this._list[this._currentImage];
}
ImageFlipbook.prototype.setData = function(data){
  this._data = data;
  this._list = new Array();
  this._list.getSelection = Delegate.create(this, this.getSelection);
  try{
    var listItemsData = this._data.list.listItems;
    for(var i=0, len=listItemsData.length; i<len; i++){
      var image = ImageFactory.create(listItemsData[i].src);
      image.getData = Delegate.create(this, this.getImageData, [image]);
      this._list.push(image);
    }
  }
  catch(ex){
    Debug.error("ImageFlipbook.setData:", ex);
  }
  this.updateCSS();
  this.setCurrentImage(0);
  this.showElement(this._imageElement,true);
  this._disableMenusForEmptyList();
}
ImageFlipbook.prototype.getImageData = function(image){
  for (var i=0, len=this._list.length; i<len; i++) {
    if (this._list[i] == image) 
      return this._data.list.listItems[i];
  }
  return null;
}
ImageFlipbook.prototype.setCollectionMenu = function(menuData){
  this._menuData = menuData;
  if (this._menuButton)
    this._menuButton.setData(this._menuData);
}
ImageFlipbook.prototype.getLength = function(){
  if (this._data && this._data.list && this._data.list.listItems)
    return this._data.list.listItems.length;
  else
    return 0;
};
ImageFlipbook.prototype.onMenuSelect = function(eventObject){
  var menuItem = eventObject.menuItem;
  var value = menuItem.getValue();  
  this._onMenuValue(value);
};
ImageFlipbook.prototype.onMenuLabelSelect = function(eventObject){
  if (this._menuData && this._menuData.menu && this._menuData.menu.length > 0) {
    this._menuButton.setLabelDisabled(true, true); // Re-enable after Save/Cancel - anything else will navigate of page
    this._onMenuValue(this._menuData.menu[0]["value"]);
  }
};
ImageFlipbook.prototype._onMenuValue = function(value){
  this.dispatchEvent({
    type: "onCollectionMenuSelect",
    target: this,
    value: value, 
    current: this._currentImage
  });
};
ImageFlipbook.prototype._disableMenusForEmptyList = function(){
  numImages = this._list.length;
  var menu = this._menuData.menu;
  for (var m=0, len=menu.length; m<len;) {
    if ((numImages<=0) && (menu[m].value == "Remove" ||
        menu[m].value.indexOf("View")>-1))
      menu[m].disabled = true;
    else if (numImages > 0)
      menu[m].disabled = false;
  }
}
ImageFlipbook.prototype.next = function(){
  this.setCurrentImage(this._currentImage+1);
}
ImageFlipbook.prototype.previous = function(){
  this.setCurrentImage(this._currentImage-1);
}
ImageFlipbook.prototype.updateNavigation = function(){
  var numberOfImages = this._data.list.listItems.length;
  if(this._currentImage<=0) this._previousButton.setDisabled(true);
  else this._previousButton.setDisabled(false);
  if(this._currentImage>=numberOfImages-1) this._nextButton.setDisabled(true);
  else this._nextButton.setDisabled(false);
  this._navigationLabel.setText(" image "+(this._currentImage+1)+" of "+numberOfImages+" ");
}
ImageFlipbook.prototype.updateCSS = function(){
  if(!this._list ||
     this._list.length<1) this.addCSSClass("EmptyCollection");
  else this.removeCSSClass("EmptyCollection");
}
ImageFlipbook.prototype.addImage = function(data, index){
  if (typeof index == "undefined" || index == null) 
    index = this._list.length;
  if(index<0 || index>this._list.length) return;
  var image = ImageFactory.create(data.src);
  image.getData = Delegate.create(this, this.getImageData, [image]);
  this._data.list.listItems.splice(index, 0, data);
  this._list.splice(index,0, image);
  this.updateCSS();
  this.setCurrentImage(index);
  this._disableMenusForEmptyList();
}
ImageFlipbook.prototype.removeImage = function(index){
  if(index<0 || index>this._list.length-1) return;
  this._data.list.listItems.splice(index,1);
  this._list.splice(index,1);
  this.updateCSS();
  if (this._currentImage > this._list.length-1)
    this.setCurrentImage(this._list.length-1);
  else
    this.setCurrentImage(this._currentImage);
  this._disableMenusForEmptyList();
}
ImageFlipbook.prototype.setCurrentImage = function(index){
  if(this._list.length<1){
    // need to clear this but:
    // 1) there's no way to remove an attribute in IE
    // 2) if we set it to "", then it causes the current page to load
    // as the image source!
    //this._imageElement.src = "";
    this._anchorElement.href = "";
    this._description.innerHTML = "";
    this._currentImage = -1;
    this._emptyMessage = this.createTag("div", this._imageDisplayElement, null, null, "emptyMessage");
    this._emptyMessage.innerHTML = this._emptyMessageText;
    this._anchorElement.style.display = "none";
  }
  else{
    if (this._currentImage == -1) {
      // Remove stuff added when there were no images
      // Accessing following line causes an IE error - accessing display property - could not get the display property
      this._anchorElement.style.display = "";
      this._emptyMessage.innerHTML = "";
      this._imageDisplayElement.removeChild(this._emptyMessage);
      this._emptyMessage = null;
    }
    index = Math.max(index,0);
    index = Math.min(index,this._list.length-1);
    this._currentImage = index;
    var imageData = this._data.list.listItems[index];
    this._anchorElement.href = imageData.url;
    this._imageElement.src = imageData.src;
    if (imageData.description) this._description.innerHTML = imageData.description;
  }
  this.updateNavigation();
}
ImageFlipbook.prototype.onMouseOver = function(e){
  if (isIE6) this.addCSSClass('ImageFlipbookHover');
}
ImageFlipbook.prototype.onMouseOut = function(e){
  if (isIE6) this.removeCSSClass('ImageFlipbookHover');
}
ImageFlipbook.prototype._onEnterKey = function(){
  this.add_checkPrivileges();
}
ImageFlipbook.prototype._onEscapeKey = function(){
  this.add_checkPrivileges();
}
ImageFlipbook.prototype.add_checkPrivileges = function(){
  this.add_begin(0, {});
}
ImageFlipbook.prototype.add_begin = function(index, data){
  this.add_submit(index, data);
}
ImageFlipbook.prototype.add_submit = function(index, data){
  this.add_finish(data, index);
}
ImageFlipbook.prototype.add_finish = function(data, index){
  if (typeof index == "undefined" || index == null)
    index = 0;
  this.addImage(data, index);
}
ImageFlipbook.prototype.delete_checkPrivileges = function(listItem){
  this.delete_begin(listItem);
}
ImageFlipbook.prototype.delete_begin = function(listItem){
  this.delete_submit(listItem);
}
ImageFlipbook.prototype.delete_submit = function(listItem){
  this.delete_finish(listItem);
  this.updateMenu(this._list.length - 1);
}
ImageFlipbook.prototype.delete_finish = function(listItem){
  this.removeImage(this._currentImage);
}
ImageFlipbook.prototype.onUploadError = function(synopsis, elaboration, details) {
  return new OverlayErrorFeedback({synopsis:synopsis, elaboration:elaboration, details:details});
};
/*
JSON Data Model:
data = {
  uid:"1234",
  list:{
    listItems:[
      {
        url:"http://ooze.apmindsf.com:8080/global/target.gif",
        src:"http://ooze.apmindsf.com:8080/global/target.gif",
        name:"target.gif",
        description:"Some guy who apparently likes to wear full body suits."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/target2.gif",
        src:"http://ooze.apmindsf.com:8080/global/target2.gif",
        name:"target2.gif",
        description:"Some other dude who apparently likes to wear full body suits."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/dude.jpg",
        src:"http://ooze.apmindsf.com:8080/global/dude.jpg",
        name:"target2.gif",
        description:"Dude."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/nixie.gif",
        src:"http://ooze.apmindsf.com:8080/global/nixie.gif",
        name:"nixie.gif",
        description:"This was hovering outside my window last night."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/chemio.gif",
        src:"http://ooze.apmindsf.com:8080/global/chemio.gif",
        name:"nixie.gif",
        description:"This also was hovering outside my window last night. This one scared the hell out of me!"
      }
    ]
  }
}
<div class="ImageFlipbook" component="ImageFlipbook" dataprovider2="mainDataProvider">
  <div class="toolbar">
    <button title="add an image" class="Button addButton"><span>add</span></button>
    <button title="delete this image" class="Button deleteButton"><span>delete</span></button>
  </div>
	<div class="imageFrame"
  <div class="imageDisplay">
    <a href="http://ooze.apmindsf.com:8080/global/target2.gif"><img src="http://ooze.apmindsf.com:8080/global/target2.gif" style="display: inline;"></a>
  </div>
  <div class="description">
    Some other dude who apparently likes to wear full body suits.
  </div>
</div>
<div class="flipbookBottom"
  <div class="navigation">
    <button disabled="disabled" class="Button previous disabled"><span>?</span></button>
    <span class="Label"> image 1 of 1 </span>
    <button disabled="disabled" class="Button next disabled"><span>?</span></button>
  </div>
</div>
</div>
{
  uid:"1234",
  list:{
    listItems:[
      {
        url:"http://ooze.apmindsf.com:8080/global/target.gif",
        src:"http://ooze.apmindsf.com:8080/global/target.gif",
        name:"target.gif",
        description:"Some guy who apparently likes to wear full body suits."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/target2.gif",
        src:"http://ooze.apmindsf.com:8080/global/target2.gif",
        name:"target2.gif",
        description:"Some other dude who apparently likes to wear full body suits."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/dude.jpg",
        src:"http://ooze.apmindsf.com:8080/global/dude.jpg",
        name:"target2.gif",
        description:"Dude."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/nixie.gif",
        src:"http://ooze.apmindsf.com:8080/global/nixie.gif",
        name:"nixie.gif",
        description:"This was hovering outside my window last night."
      },
      {
        url:"http://ooze.apmindsf.com:8080/global/chemio.gif",
        src:"http://ooze.apmindsf.com:8080/global/chemio.gif",
        name:"nixie.gif",
        description:"This also was hovering outside my window last night. This one scared the hell out of me!"
      }
    ]
  }
}
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * HTMLComponent class
 * mark@metaweb.com
 */
function HTMLComponent(div){
  setupInheritance(HTMLComponent);
  superConstructor(BaseComponent, this, [div]);
}
HTMLComponent.superClass = [BaseComponent];
registerClass(HTMLComponent, "HTMLComponent");
HTMLComponent.prototype.CLASS_NAME = "HTMLComponent";
HTMLComponent.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
}
HTMLComponent.prototype.setData = function(data){
  this._body.innerHTML = data;
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * DateTimeInput class
 * nick@metaweb.com
 */
function DateTimeInput(div){
  this._dateTimeLabel;
  this._hintLabel;
  setupInheritance(DateTimeInput);
  superConstructor(TextInput, this, [div]);
}
DateTimeInput.superClass = [TextInput];
registerClass(DateTimeInput, "DateTimeInput");
DateTimeInput.prototype.CLASS_NAME = "DateTimeInput";
if (typeof DATETIME_TEXT_INPUT_DELAY != "undefined")
{
  DateTimeInput.TEXT_INPUT_DELAY = DATETIME_TEXT_INPUT_DELAY;
}
else
{
  DateTimeInput.TEXT_INPUT_DELAY = 1000;
}
DateTimeInput.prototype.init = function(){
  superMethod(TextInput, this, "init");
  
  this.addCSSClass("DateTimeInput");
  var frag = document.createDocumentFragment();
  this._hintLabel = this.addChildComponent(Label, frag, "span", null, null, "DateTimeHint");
  this._dateTimeLabel = this.addChildComponent(DateTimeLabel, frag, "div", null, null, null);  
  this._body.appendChild(frag);
}
DateTimeInput.prototype.setData = function(data){
  // copy of data for this._dateTimeLabel
  var data1 = {
    format:data.format,
    text:data.text
  }
  if (data1.text) {
    // Set the label,  which formats the date.
    this._dateTimeLabel.setData(data1, false);
    if (this._dateTimeLabel.getIsoString()) { //if it is valid.
      data.text = this._dateTimeLabel.getText(); // set text to the formated date
      // Now must set label again, because matching dates shouldn't appear in label 
      data1.text = data.text;
      this._dateTimeLabel.setData(data1, true);
    }
    else {
      data.text = "";
    }
  }
  superMethod(TextInput, this, "setData", [data]);
  this._setHint();
}
DateTimeInput.prototype.isValid = function() {
  if (this._dateTimeLabel.getIsoString()) {
    return true;
  } else {
    if (this._dateTimeLabel.getText() == '') { // "" is a valid date...
      return true;
    } else { 
      return false;
    }
  }
}
DateTimeInput.prototype._setHint = function(){
  if ((this._data.hint) && (!(this._dateTimeLabel.getIsoString()))) {
    this._hintLabel.setText(this._data.hint);
  } else {
    this._hintLabel.setText('');
  }
}
DateTimeInput.prototype.onTextChange = function(eventObject){
  clearInterval(this._textChangeInterval);
  var timeDelay = DateTimeInput.TEXT_INPUT_DELAY - (200 * this._input.value.length);
  if (timeDelay < 20) timeDelay = 20;
  var timeout = Delegate.create(this, this.onTextChangeDelay, [false]);
  this._textChangeInterval = setInterval(timeout, timeDelay);
}
DateTimeInput.prototype.onTextChangeDelay = function(recursive)
{
  clearInterval(this._textChangeInterval);
  this._data.text = this._input.value;
  if ((!(recursive)) && (this._data.text)) {
    var isoObject = new Iso8601Date(this._data.text);
    var isoString = isoObject.getIsoString();
    if (isoString == "Invalid Date Format") { //invalid, do one more full delay to allow more input
      var timeout = Delegate.create(this, this.onTextChangeDelay, [1]);
      this._textChangeInterval = setInterval(timeout, DateTimeInput.TEXT_INPUT_DELAY);
      return;
    }
  }
  var data1 = {
    text:this.getText()
  }
  this._dateTimeLabel.setData(data1, true);
  this._setHint();
  this.dispatchEvent({type:"onTextChange", target:this});  
}
/*
data = {
  text: "some text",
  hint: "some hint"
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>OverlayAlert</b>
 *
 * Abstract class:
 *
 * @author daepark@apmindsf.com
 */
/****************************************************** constructor
 * @param overlayElement - the html element that we are overlaying over. 
 * If null, overlay over entire document body 
 */
function OverlayAlert(msg, callback, overlayElement) {
  var buttons = [{
    text: "OK",
    event: "onOk",
    css: "okButton", 
    callback: callback
  }];
  setupInheritance(OverlayAlert);
  superConstructor(OverlayPrompt, this, [overlayElement, (msg)?{text:msg}:null, buttons]);
};
OverlayAlert.superClass = [OverlayPrompt];
registerClass(OverlayAlert, "OverlayAlert");
OverlayAlert.prototype.CLASS_NAME = "OverlayAlert";
OverlayAlert.prototype.createPromptContent = function(frag) {
  this._iconDiv = this.createTag("span", frag, null, null, "icon");
  this._label = this.addChildComponent(Label, frag, "span");
  if (this._msgData)
    this._label.setData(this._msgData);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>BooleanInput</b>
 */
function BooleanInput(div){
  this.prompt;
  this.radioList;
  this.disabled = false;  
  setupInheritance(BooleanInput);
  superConstructor(BaseComponent, this, [div]);
};
BooleanInput.superClass = [BaseComponent];
registerClass(BooleanInput, "BooleanInput");
BooleanInput.prototype.CLASS_NAME = "BooleanInput";
BooleanInput.prototype.init = function() {
  superMethod(BaseComponent, this, "init");
  //componentClass, parentElement, tagName, beforeIndex, beforeElement, cssClassName, componentAguments){
  this.radioList = this.addChildComponent(RadioButtonList, null, "ul");
  this.radioList.setData({
    listItems: [{
      label: "Yes",
      value: true
    }, {
      label: "No",
      value: false
    }]
  });
  this.radioList.addEventListener("onSelectionChange", this);
};
BooleanInput.prototype.setFocus = function() {
  this.radioList._listItems[0]._radioButton._optionElement.focus();
};
BooleanInput.prototype.setData = function(data) {
  superMethod(BaseComponent, this, "setData", [data]);
  this.setText(data.text);
};
BooleanInput.prototype.onSelectionChange = function(e){
  this._data.text = e.targetIndex == 0; 
  this.dispatchEvent({type:"onChange"});
};
BooleanInput.prototype.setText = function(val){
  if (val === true || val === "true") {
    this.radioList.setSelection(null, 0);
    this._data.text = true;    
  }
  else if (val === false || val === "false") {
    this.radioList.setSelection(null, 1); 
    this._data.text = false;     
  }
  else  {
    this.radioList.setSelection(null);
    this._data.text = null;     
  }
};
BooleanInput.prototype.getText = function(val){
  return this._data.text;
};
BooleanInput.prototype.setDisabled = function(state){
  this.disabled = true == state;
  if(this.disabled) {
    this.addCSSClass("disabled");
  }
  else {
    this.removeCSSClass("disabled");    
  }
  this.radioList._listItems[0]._radioButton.setDisabled(this.disabled);
  this.radioList._listItems[1]._radioButton.setDisabled(this.disabled);  
}
BooleanInput.prototype.getDisabled = function(){
  return this.disabled;
}
BooleanInput.prototype.setPrompt = function(prompt){
  if(!this.prompt) this.prompt = this.addChildComponent(Label, null, "div", 0);
  this.prompt.setText(prompt);
}
BooleanInput.prototype.getInputElement = function() {
  var elts = [];
  for(var i=0, len=this.radioList._listItems.length; i<len; i++) {
  	elts.push(this.radioList._listItems[i]._radioButton._optionElement);
  }  
  return elts;
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ConfirmationFeedback</b>
 *
 * @author nick@metaweb.com
 */
function ConfirmationFeedback(div, givenCSSclass) {  
  // member vars
  this._header;
  this._icon;
  this._synopsis;
  this._givenCSSclass = null;
  if (givenCSSclass) {
    this._givenCSSclass = givenCSSclass; }
  
  setupInheritance(ConfirmationFeedback);
  superConstructor(BaseComponent, this, [div]);
};
ConfirmationFeedback.superClass = [BaseComponent]; 
registerClass(ConfirmationFeedback, "ConfirmationFeedback");
ConfirmationFeedback.prototype.CLASS_NAME = "ConfirmationFeedback";
ConfirmationFeedback.prototype.init = function() {
  if (this._givenCSSclass) {
    this.addCSSClass(this._givenCSSclass);
  } else {
    this.addCSSClass(this.CLASS_NAME);
  }
  var frag = document.createDocumentFragment();
  this._header = this.createTag("div", frag, null, null, "confirmationFeedbackHeader");
  this._icon = this.createTag("div", this._header, null, null, "icon");
  this._synopsis = this.addChildComponent(Label, this._header, "div", null, null, "synopsis"); 
  this._elaboration = this.addChildComponent(Label, this._header, "div", null, null, "elaboration");
  this._body.appendChild(frag);
}
ConfirmationFeedback.prototype.setData = function(data) {
  this.clearData();
  this._data = data;
  if (!this._data) return;
  this._synopsis.setText(this._data.synopsis);
  if (typeof this._data.elaboration != "undefined" && this._data.elaboration != null) {
    this._elaboration.setText(this._data.elaboration);
  }
};
ConfirmationFeedback.MakeConfirmationFeedback = function (div, msgData) {
  return new InlineFeedback(div, msgData, ConfirmationFeedback, "successFeedback");
}
ConfirmationFeedback.MakeWarningFeedback = function(div, msgData) {
  return new InlineFeedback(div, msgData, ConfirmationFeedback, "warningFeedback");
}
ConfirmationFeedback.MakeInfoFeedback = function (div, msgData) {
  return new InlineFeedback(div, msgData, ConfirmationFeedback, "infoFeedback");
}
/*
Data:
{
  synopsis: "You just pressed a button."
};
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractOption</b>
 *
 * Abstract class:
 * Supports concrete implementations of HTML checkboxes and radio buttons
 *
 * Subclasses must overwrite [AbstractOption.prototype._createOption]
 * Subclasses must overwrite [AbstractOption.prototype.getData]
 *
 * @author alee@metaweb.com
 */
function AbstractOption(div){
  // member vars:
  this._id;   // randomly generated ID to allow <label for="_id"/> associate with <input id="_id">
  this._checked;
  this._optionElement;  // html element
  this._label;
  
  setupInheritance(AbstractOption);
  superConstructor(BaseComponent, this, [div]);
}
AbstractOption.superClass = [BaseComponent];
registerClass(AbstractOption, "AbstractOption");
AbstractOption.prototype.CLASS_NAME = "AbstractOption";
AbstractOption.prototype.init = function(){
  this.changeTag("span");
  this.addCSSClass(this.CLASS_NAME);
  
  // this is to handle component like RadioButton put in html as <div component="RadioButton" name="gender" value="M" label="Male"></div>
  this.setData({});
  // Create the option as part of setData.  
  // Because of the IE problem with setting name attribute on HTML element (radiobuttons
  // need name attribute, it's better to build the HTML as part of setData - hence _createOption in setData
}
AbstractOption.prototype._createOption = function(id){
  // add id Attribute with passed in value for the html option element
  Debug.error("Subclasses must overwrite [AbstractOption.prototype._createOption]");
  return null;
}
AbstractOption.prototype._destroyOption = function(){
  if (this._optionElement) {
    if (this._body)
      this._body.removeChild(this._optionElement);
    this._optionElement = null;
  }
  if (this._label) {
    this._label.destroy();
    delete this._label;
  }
}
AbstractOption.prototype.clearData = function(){
  this._data = { label:"", checked: false };
}
AbstractOption.prototype.setData = function(data){
  // Create the option as part of setData.  
  // Because of the IE problem with setting name attribute on HTML element (radiobuttons
  // need name attribute), it's better to create the HTML input as part of setData - hence _createOption in setData
  // Also, part of data is in input element so this._data is partial.
  // Clients should be wary in accessing this._data directly as it 
  // miminally contains value of clearData();
  this._data = data;
  this._destroyOption();
  
  // Option element
  this._id = this._idGenerator();
  this._optionElement = this._createOption(this._id);
  this._body.appendChild(this._optionElement);  // deferring the append until all children of this is created.
  DOMEvent.addEventHandler(this._optionElement, "click", Delegate.create(this, this._onClick));
  if(typeof this._data.checked!="undefined" && this._data.checked != null) 
    this.setState(this._data.checked);
  else {
    var checked = this._body.getAttribute("Checked");
    this.setState(checked=="true"?true:false);
  }
  // Label for the option
  this._label = this.addChildComponent(Label, null, "label", null, null, getClassName(this) + "-Label");
  if (!isIE) 
    this._label._body.setAttribute("for", this._id);   // make the label targetable.
  else 
    // let's hope that this gets fixed in IE8?
    this._label._body.setAttribute("htmlFor", this._id);
  if(typeof this._data.label!= "undefined")
    this._label.setText(this._data.label);
  else {
    var label = this._body.getAttribute("label");
    if (label)
      this.setText(label);
  }
}
AbstractOption.prototype.getData = function(){
  // Since data state may be partly stored in the element, subclasses should overrride and provide implementation
  Debug.error("Subclasses must overwrite [AbstractOption.prototype.getData]");
  return null;
}
AbstractOption.prototype.setText = function(text){
  this._label.setText(text);
}
AbstractOption.prototype.getText = function(){
  return this._label.getText(text);
}
AbstractOption.prototype.setLabel = AbstractOption.prototype.setText;
AbstractOption.prototype.getLabel = AbstractOption.prototype.getText;
AbstractOption.prototype.setState = function(state){
  // definitive value stored in HTML input r;r,rmy
  var curState = this.getState();
  if (curState != state)
    this._optionElement.checked = state;
  this.updateCSS(curState, "checked");
}
AbstractOption.prototype.getState = function(){
  // definitive value stored in HTML input element
  var checked = this._optionElement.checked;
  if (typeof checked == "undefined")
    checked = false;
  return checked;
}
AbstractOption.prototype.setDisabled = function(newState){
  // definitive value stored in HTML input element
  var curState = this.getDisabled();
  if(curState != newState)
    // Is this necessary - is it related to problems if focus is in the element when disabled?
    this.setBlur();
  this.updateCSS(newState, "disabled");
  this._optionElement.disabled = newState;
}
AbstractOption.prototype.getDisabled = function(){
  // definitive value stored in HTML input element
  var disabled = this._optionElement.disabled;
  if (typeof disabled == "undefined")
    disabled = false;
  return disabled;
}
AbstractOption.prototype.updateCSS = function(newState, cssClass){
  if(typeof newState == "undefined" || newState == false)
    // remove old setting of checked
    this.removeCSSClass(cssClass);
  else
    // add in checked as new setting is checked
    this.addCSSClass(cssClass);
    
  this._data[cssClass] = newState;
}
AbstractOption.prototype._idGenerator = function(){
  var rand1 = Math.round(Math.random() * 1000);
  var rand2 = Math.round(Math.random() * 100);
  return "ID" + (rand1*rand2);
}
AbstractOption.prototype._onClick = function(){
  var newState = this.getState();
  this.updateCSS(newState, "checked");
  this.dispatchEvent({type:"onClick", target:this, state:newState});
}
/*
data = {
  checked:true,
  label:"check me"
}
<span class="AbstractOption checked">
  <input type="checkbox">
  <label class="label">check me</label>
</span>
<span label="check me please" checked="true" component="AbstractOption" class="AbstractOption">
  <input type="checkbox"/>
  <label class="label">check me please</label>
</span>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ImageSlideShow</b>
 *
 * @author alee@metaweb.com
 * 
 * The image dissolve effects code uses work presented by article entitled
 * "Cross-browser BlendTrans Filter JavaScript" on 
 *   http://www.brainerror.net/scripts_js_blendtrans.php
 * 
 */
function ImageSlideShow(div){
  // member vars
  this._currentImage;
  this._images;
  this._imageFrame;
  this._imageElement;
  this._FFUnsupported;
  this._opacityIncrement = 5;
  
  setupInheritance(ImageSlideShow);
  superConstructor(BaseComponent, this, [div]);
}
ImageSlideShow.superClass = [BaseComponent];
registerClass(ImageSlideShow, "ImageSlideShow");
ImageSlideShow.prototype.CLASS_NAME = "ImageSlideShow";
ImageSlideShow.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  var fragment = document.createDocumentFragment();
  //imageFrame
  this._imageFrame = this.addChildTag("div", fragment, null, "imageFrame");
  
  // image
  this._imageElement = this.createTag("img",this._imageFrame);
  this._body.appendChild(fragment);
  this._FFUnsupported = false;
  if (isFirefox) {
    var userAgent = navigator.userAgent.toLowerCase();
    if (userAgent.indexOf("ppc mac os x")>0 || userAgent.indexOf("linux")>0)
      // Can't do slideshow in FF PPC Mac (not an issue with FF Intel Mac) as it flickers;
      // Can't do slideshow in FF Linux as it grinds to a halt;
      this._FFUnsupported = true;
  }
}
ImageSlideShow.prototype.setData = function(data){
  this._data = data;
  var style = "background-repeat: no-repeat;" + "width:" + this._data.width + "px;" + "height: " + this._data.height + "px;";
  DOMUtils.setElementStyle(this._imageFrame, style);
  this._images = new Array();
  var width = this._data.width;
  var height = this._data.height;
  var listItems = this._data.listItems;
  try{
    for(var i=0, len=listItems.length; i<len; i++){
      var image = ImageFactory.create(listItems[i].src, null, null, width, height);
      this._images.push(image);
    }
  }
  catch(ex){
    Debug.error("ImageSlideShow.setData:", ex);
    throw(ex);
  }
  this.runEffect();
}
ImageSlideShow.prototype.setCurrentImage = function(index){
  if(this._images.length<1){
    this._imageElement.src = "";
    this._currentImage = -1;
  }
  else{
    this._currentImage = index;
    var imageData = this._data.listItems[index];
    this._imageElement.src = imageData.src;
  }
}
ImageSlideShow.prototype.runEffect = function() {
  if (this._FFUnsupported){
    // Can't do slideshow in FF PPC Mac (not an issue with FF Intel Mac) as it flickers;
    // so, display randomly selected image
    var randomImageIndex = Math.floor (this._data.listItems.length * Math.random ());
    this.setCurrentImage(randomImageIndex);
  }
  else {
    // begin the automatic slide show.
    this.setCurrentImage(0);
    var handler = Delegate.create(this, this.slideShow0, [this._data.blendSeconds, this._data.holdSeconds]);
    setTimeout(handler, 1000);
  }
}
ImageSlideShow.prototype.changeOpacity = function(opacity) {
  // jman on the list noticed in Firefox that fades flicker (when fading in)
  // at the end. The fix does not solve the flicker on FF (<=FF1.5 on Mac but FF1.5 on Windows works).
  // However, the short timeout in finishBlendImage1 does fix things. So, the .99 and 
  // -.01 is not relevant.
  
  var object = this._imageElement.style; 
  object.opacity = (opacity / 100);
  object.MozOpacity = (opacity / 100);
  object.KhtmlOpacity = (opacity / 100);
  object.filter = "alpha(opacity=" + opacity + ")";
}
ImageSlideShow.prototype.blendImage = function(nextImageNumber, milliseconds) {
  //set the current image in this._imageElement as a background for the _imageFrame
  var newImageData = this._data.listItems[this._currentImage];
  this._imageFrame.style.backgroundImage = "url(" + newImageData.src + ")";
  
  //make the current _imageElement transparent: imageElement is the next image in sequence
  this.finishBlendImage1(nextImageNumber, milliseconds);
}
ImageSlideShow.prototype.finishBlendImage1 = function(nextImageNumber, milliseconds) {
  this.changeOpacity(0);
  // this timeout to prevent flicker effect between changeOpacity and setting new foreground image (_imageElement)
  var finish_handler = Delegate.create(this, this.finishBlendImage, [nextImageNumber, milliseconds]);
  setTimeout(finish_handler, 50);
}
ImageSlideShow.prototype.finishBlendImage = function(nextImageNumber, milliseconds) {
  this.setCurrentImage(nextImageNumber);  // switch this._imageElement to nextImageNumber
  this.revealImage(this._opacityIncrement, nextImageNumber, milliseconds);  // reveal image through fadein
}
ImageSlideShow.prototype.revealImage = function(opacity, imageNumber, milliseconds) {
  this.changeOpacity(opacity);
  if (opacity >= 100)
    return;
  var handler = Delegate.create(this, this.revealImage, [opacity + this._opacityIncrement, imageNumber, milliseconds]);
  setTimeout(handler, Math.round(milliseconds/20));
}
ImageSlideShow.prototype.slideShow0 = function(blendSeconds, holdSeconds) { 
  //set the current image in this._imageElement as a background for the _imageFrame
  var newImageData = this._data.listItems[this._currentImage];
  this._imageFrame.style.backgroundImage = "url(" + newImageData.src + ")";
  
  // schedule the subsequent slideShow
  var handler = Delegate.create(this, this.slideShow, [blendSeconds, holdSeconds]);
  setTimeout(handler,blendSeconds*1000);
}
ImageSlideShow.prototype.slideShow = function(blendSeconds, holdSeconds) { 
  this.changeOpacity(0); 
  var nextImage = (this._currentImage+1 == this._images.length)?0:this._currentImage+1;
  this.blendImage(nextImage,blendSeconds*1000); 
  var handler = Delegate.create(this, this.slideShow, [blendSeconds, holdSeconds]);
  var displaySeconds = holdSeconds+blendSeconds;
  setTimeout(handler,displaySeconds*1000);
} 
 
/*
{
  holdSeconds: 2,  // Seconds in which the image is held stable after blend
  blendSeconds: 1, // Number of seconds in which the foreground image is blended with background
  width: 702,      // Number of pixels for image width
  height: 360,     // NUmber of pixels for image height
  listItems:[
    {
      src:"http://ooze.apmindsf.com:8080/global/target.gif"
    },
    {
      src:"http://ooze.apmindsf.com:8080/global/target2.gif"
    },
    {
      src:"http://ooze.apmindsf.com:8080/global/dude.jpg"
    },
    {
      src:"http://ooze.apmindsf.com:8080/global/nixie.gif"
    },
    {
      src:"http://ooze.apmindsf.com:8080/global/chemio.gif"
    }
  ]
}
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>PasswordInput</b>
 */
function PasswordInput(div, name){
  // member vars:
  this._label;
  if (typeof name != "undefined")
    this._name = name;
  else {
    name = div.getAttribute("name");
    if (name)
      this._name = name;
    else
      this._name = null;
  }
  setupInheritance(PasswordInput);
  superConstructor(AbstractInput, this, [div]);
}
PasswordInput.superClass = [AbstractInput];
registerClass(PasswordInput, "PasswordInput");
PasswordInput.prototype.CLASS_NAME = "PasswordInput";
PasswordInput.prototype._createInput = function(div) {
  this._input = createNamedTypedElement("input", this._name, "password");
  this._body.appendChild(this._input);
  
  // default - disable browser autocomplete feature
  this.setBrowserAutoComplete(false);
  var prompt = this._body.getAttribute("prompt");
  if(prompt) this.setPrompt(prompt);
  this._input.className = getClassName(this) + "-input";
  return this._input;  
};
PasswordInput.prototype.setBrowserAutoComplete = function(state){
  if(state) this._input.setAttribute("autocomplete", "on");
  else this._input.setAttribute("autocomplete", "off");
}
PasswordInput.prototype.getPrompt = function(){
  if(this._label) this._label.getText();
  else null;
}
PasswordInput.prototype.setPrompt = function(prompt){
  if(!this._label) this._label = this.addChildComponent(Label, null, "div", 0);
  this._label.setText(prompt);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>EditableLabel</b>
 * 
 * Plain old Label with edit button after it.
 * Does not have a title and should be done as a separate Label component.
 *
 * @author alee@metaweb.com
 */
function EditableLabel(div, includeEditButton){
  // member vars
  this._prompt;
  this._promptText;
  this._label;
  this._clickHandler;
  this._editButton;
  this._includeEditButton = (typeof includeEditButton == "undefined" || includeEditButton==null)?true:includeEditButton;
  setupInheritance(EditableLabel);
  superConstructor(AbstractEditableItem, this, [div]);
}
EditableLabel.superClass = [AbstractEditableItem];
registerClass(EditableLabel, "EditableLabel");
EditableLabel.prototype.CLASS_NAME = "EditableLabel";
EditableLabel.prototype.PROMPT_TEXT = "double-click to edit text";
EditableLabel.prototype.init = function(){
  // override because there is no _header and _content div elements.  Also does not have title
  this.addCSSClass(this.CLASS_NAME);
  this._promptText = this._body.getAttribute("prompt");
  
  var emptyMessage = this._body.getAttribute("emptyMessage");
  this._emptyMessage = (emptyMessage)?emptyMessage:"empty";
  var loadingMessage = this._body.getAttribute("loadingMessage");
  this._loadingMessage = (loadingMessage)?loadingMessage:"loading...";
  
  var emptyMessageAddText = this._body.getAttribute("emptyMessageAddText");
  this._emptyMessageAddText = (emptyMessageAddText)?emptyMessageAddText:"You can add it.";
  var deleteConfirmationMessage = this._body.getAttribute("deleteConfirmationMessage");
  this._deleteConfirmationMessage = 
    (deleteConfirmationMessage)?deleteConfirmationMessage : "Are you sure you want to delete this item?"
  
  this.setEditable(false);
}
EditableLabel.prototype._isEmpty = function(){
  // no data
  return (!this._data || !this._data.text);
};
EditableLabel.prototype._isEditable = function(){
  return true;
};
EditableLabel.prototype.setText = function(text){
  if (this._label)
    this._label.setText(text);
};
EditableLabel.prototype._createTitle = function(title){
  // override as there is no Title.
}
EditableLabel.prototype._createPrompt = function()
{
  if (!this._promptText && this.PROMPT_TEXT == "")
    return;
  
  this._prompt = this.addChildComponent(Label, null, "i", 0, null, "prompt");
  if(this._promptText) {
    this.setToolTip(this._promptText);
    this._prompt.setText(this._promptText);
  }
  else if (this.PROMPT_TEXT != "") {
    this.setToolTip(EditableLabel.PROMPT_TEXT);
    this._prompt.setText(this.PROMPT_TEXT);
    DOMEvent.addEventHandler(this._prompt._body, "dblclick", this._clickHandler);
  }
}
EditableLabel.prototype._destroyPrompt = function()
{
  if(this._prompt) {
    this._prompt.destroy();
    this._prompt = null;
    DOMEvent.removeEventHandler(this._label._body, "dblclick", this._clickHandler);
  }
}
EditableLabel.prototype._createDisplayContent = function(){
  if (!this._data)
    return;
    
  // Click handler used in various places including dblclick
  this._clickHandler = Delegate.create(this, this.onEditButton);
  
  // Title and prompt  
  if(!this._data.text || this._data.text=="")
    this._createPrompt();
  // text content
  this._label = this.addChildComponent(Label, this._body, "span", null, null, "label", [true]);
  
  if (this._includeEditButton) {
    this._editButton = this.addChildComponent(Button, null, "button", null, null, "edit");
    this._editButton.setLabel("edit");
    this._editButton.setToolTip("edit");
    this._editButton.addEventListener("onClick", this._clickHandler);
  }
  if (this._data.text) {
    this._label.setData(this._data);
    if (this._data.text && this._data.text != "") {
      if (this._isEditable())
        // add dblclick handler
        DOMEvent.addEventHandler(this._label._body, "dblclick", this._clickHandler);
    }
    this._updateEmptyMessage(false);
  }
}
EditableLabel.prototype._destroyDisplayContent = function()
{
  if (this._isEditable() && this._label)
    // remove dblclick handler
    DOMEvent.removeEventHandler(this._label._body, "dblclick", this._clickHandler);
  
  // text content
  this._destroyPrompt();
  if (this._label){
    this._label.destroy();
    this._label = null;
  }
  if (this._editButton){
    this._editButton.destroy();
    this._editButton = null;
  }
  if (this._clickHandler) {
    Delegate.destroy(this._clickHandler);
    this._clickHandler = null;
  }
};
EditableLabel.prototype._createEditComponent = function(div) {
  this.addCSSClass("Editing");
  this._label = new TextInput(div);
  this._label.addEventListener("onTextChange", Delegate.create(this, this._onContentChange));
  this._label.addEventListeners([
    "onEscapeKey", 
    "onEnterKey"
    ], this);
  return this._label;
};
EditableLabel.prototype._destroyEditComponent = function()
{
  this.removeCSSClass("Editing");
  if (this._label) {
    this._label.destroy();
    delete this._label;
  }
};
EditableLabel.prototype.onEditButton = function(eventObject){
  this.edit_checkPrivileges(this);
};
EditableLabel.prototype.onEnterKey = function(event){
  var input = event.target;
  if (!StringUtils.isEmpty(input.getText())) {
    input.dispatchEvent("onSubmitFocus");
  }  
}
EditableLabel.prototype.onEscapeKey = function(eventObject) {
  var input = eventObject.target;
  input.dispatchEvent("onSubmitCancel");
};
EditableLabel.prototype._onContentChange = function(eventObject)
{
  eventObject.target.dispatchEvent({type:"onSubmitEnable", enable:true, target: this});
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>InlineFeedback</b>
 *
 * @author nick@metaweb.com
 */
function InlineFeedback(div, msgData, feedbackClass, givenCSSclass) {
  // member vars
  this._feedbackContent;
  this._closeButton;
  this._msgData = null;  
  if (typeof msgData != "undefined" && msgData != null)
    this._msgData = msgData;
  
  this._feedbackClass = feedbackClass;
  if (givenCSSclass) {this._givenCSSclass = givenCSSclass;}
  setupInheritance(InlineFeedback);
  superConstructor(BaseComponent, this, [div]);
  
};
InlineFeedback.superClass = [BaseComponent];
registerClass(InlineFeedback, "InlineFeedback");
InlineFeedback.prototype.CLASS_NAME = "InlineFeedback";
InlineFeedback.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  if (this._givenCSSclass) {
    this.addCSSClass(this._givenCSSclass);}
  var frag = document.createDocumentFragment();
  var div = this.createTag("div", frag);
  this._closeButton = this._createCloseButton(div);
  div = this.createTag("div", frag);
  this._feedbackContent = this._createFeedbackContent(div);
  this._feedbackContent.setData(this._msgData);
  this._feedbackContent.addEventListener("onEmailSuccess", Delegate.create(this, this._onEmailSuccess));
  this._body.appendChild(frag);
}
InlineFeedback.prototype._createFeedbackContent = function(div){
  return applyNew(this._feedbackClass, [div]);
}
InlineFeedback.prototype._destroyFeedbackContent = function(div){
  if (this._givenCSSclass) {
    this.removeCSSClass(this._givenCSSclass);
    delete this._givenCSSclass;
  }
  this._feedbackContent.destroy();
  this._feedbackContent = null;
}
InlineFeedback.prototype.setData = function(data){
  this.clearData();
  this._data = data;
  if (!this._data) return;
  if (!this._feedbackContent) return;
  this._feedbackContent.setData(data);
}
InlineFeedback.prototype._createCloseButton = function(div){
  closeButton = this.addChildComponent(Button, div, "button",null,null,"closebutton");
  closeButton.setLabel("Close");
  closeButton.setToolTip("Close");
  closeButton.addEventListener("onClick", Delegate.create(this, this._onCloseButton));
  return closeButton;
}
InlineFeedback.prototype._onCloseButton = function() { 
  this.dispatchEvent("onClose");
  this.destroy();
}
InlineFeedback.prototype._onEmailSuccess = function(eventObject) {
  this._destroyFeedbackContent();
  this._feedbackClass = ConfirmationFeedback;
  var div = this.createTag("div", this._body);
  this._feedbackContent = this._createFeedbackContent(div);
  this.addCSSClass("successFeedback");
  this._msgData = {synopsis:eventObject.message};
  this._feedbackContent.setData(this._msgData);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractCollection</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractCollection.prototype._createEditableList]
 *
 * @author daepark@apmindsf.com
 */
function AbstractCollection(div){
  // member vars:
  this._header;   // html elt
  this._content;  // html elt
  this._titlebar; // html elt
  this._titleMenu;
  this._list;
  
  // empty message stuff
  this._emptyMessageDiv;
  this._emptyMessageLabel;
  this._emptyMessageAddLabel;
  this._emptyMessage;
  
  this._deleteConfirmationMessage;
  this._loadingMessage;
  this._shim;  
  this._footer;
  this._moreButton;
  this._reorderButtons = [];
  this._toTopButton = null;
  this._upOneButton = null;
  this._downOneButton = null;
  this._toEndButton = null;
  
  this._mouseEventHandler;
  this._limit;
  this._isOrdered = false;
  
  // default menu
  this._menuData = {
    menu: [
      {text:"Add", css:"AddMenuItem",value:"Add"},
      {text:"Edit", css:"EditMenuItem",value:"Edit"},
      {text:"Remove", css:"RemoveMenuItem",value:"Remove"}
    ]
  };
  
  setupInheritance(AbstractCollection);
  superConstructor(BaseComponent, this, [div]);
};
AbstractCollection.superClass = [BaseComponent];
registerClass(AbstractCollection, "AbstractCollection");
AbstractCollection.prototype.CLASS_NAME = "AbstractCollection";
AbstractCollection.prototype.EMPTY_MESSAGE = "empty";
AbstractCollection.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  
  // check if ordered collection
  var isOrdered = this._body.getAttribute("ordered");
  if (isOrdered && isOrdered.toLowerCase() == "true") {
    this._isOrdered = true;
  }
    
  var frag = document.createDocumentFragment();
  
  // header and content
  this._header = this.addChildTag("div", frag, null, "header");
  this._content = this.addChildTag("div", frag, null, "content");
  // header containing titlebar:
  this._titlebar = this.createTag("span", this._header, null, null, "titlebar");
  var title = this._body.getAttribute("componentTitle");
  if(!title) {
    title = this.CLASS_NAME;
  }  
  this._menuData.label = title;
  this._createTitle();
  
  // AbstractEditableList:
  var listElement = this.createTag("ul", this._content);  
  this._list = this._createEditableList(listElement);
  this._list.addEventListeners([
    "onSave", 
    "onCancel", 
    "onSelectionChange", 
    "onListItemMouseOver",
    "onListItemMouseOut"
    ], this);
  
  var emptyMessage = this._body.getAttribute("emptyMessage");
  this._emptyMessage = (emptyMessage)?emptyMessage:this.EMPTY_MESSAGE;
  var deleteConfirmationMessage = this._body.getAttribute("deleteConfirmationMessage");
  this._deleteConfirmationMessage = 
    (deleteConfirmationMessage)?deleteConfirmationMessage : "Are you sure you want to delete this item?"
  var loadingMessage = this._body.getAttribute("loadingMessage");
  this._loadingMessage = (loadingMessage)?loadingMessage:"loading...";
  // this div forces the _body border to wrap around
  // floated list items (if it's css style is set to 
  // clear:both):
  this._shim = this.addChildTag("div", this._content, null, "shim");
  this._footer = this.addChildTag("div", frag, null, "collection-footer"); 
  this._mouseEventHandler = new MouseEventHandler(this, this._body, MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT);
  this._mouseEventHandler.setInteractive(true);
  
  this._body.appendChild(frag);
  
  this._updateEmptyMessage(true);
};
AbstractCollection.prototype.setData = function(data){
  if (!data) {
    this.clearData();
    return;
  }
  this._data = data;
  this._list.setData(this._data.list);
  this._updateEmptyMessage(false);
  this._updateMoreMessage();
};
AbstractCollection.prototype._createTitle = function(){
  this._titleMenu = this.addChildComponent(PopupMenuLabel, this._titlebar, "div", null, null, "title");
  this._titleMenu.setData(this._menuData);
  this._titleMenu.addEventListeners([
    "onMenuSelect", 
    "onMenuLabelSelect"
    ], this);
}
AbstractCollection.prototype.setOrdered = function(b){
  if (this._isOrdered && true == b) {
    return;
  }
  if (!this._isOrdered && false == b) {
    return;
  }
  
  if (b) {
    this._isOrdered = true;
    this._createReorderToolbar();
  }
  else {
    this._destroyReorderToolbar();
    this._isOrdered = false;
  }
};
 
AbstractCollection.prototype._destroyReorderToolbar = function(){  
  for(var i=0, len=this._reorderButtons.length; i<len; i++) {
    this._reorderButtons[i].destroy();
    this._reorderButtons[i] = null;
  }
  this._toTopButton = null;
  this._upOneButton = null;
  this._downOneButton = null;
  this._toEndButton = null;
  this._reorderButtons = [];
  
  if (this._reorderToolbarSpan && this._reorderToolbarSpan.id && this._reorderToolbarSpan.id!="reorderToolbar") {
    // remove _reorderToolbarSpan as it was added to _titlebar rather than some other location
    this._reorderToolbarSpan.parentNode.removeChild(this._reorderToolbarSpan);
    delete this._reorderToolbarSpan;
  }
};
AbstractCollection.prototype._createReorderToolbar = function(){
  if (!this._isOrdered)
    // No need for reorder buttons toolbar.
    return;
    
  this._reorderToolbarSpan = $id("reorderToolbar");  // different location for reorder toolbar
  if (!this._reorderToolbarSpan)
    this._reorderToolbarSpan = this.createTag("span", this._titlebar, 0, null, "Reorder");
  
  // toTopButton
  var button = this.addChildComponent(Button, this._reorderToolbarSpan, "button", null, null, "toTop", [true]);
  this._toTopButton = button;
  button.setToolTip("move selected item to top");
  button.setLabel("&#9650; Top");
  button.addEventListener("onClick", Delegate.create(this, this._onToTopButton));
  this._reorderButtons.push(button);
  
  // upOneButton
  this._upOneButton = button = this.addChildComponent(Button, this._reorderToolbarSpan, "button", null, null, "upOne", [true]);
  button.setToolTip("move selected item up one position");
  button.addEventListener("onClick", Delegate.create(this, this._onUpOneButton));
  this._reorderButtons.push(button);
  
  // downOneButton
  this._downOneButton = button = this.addChildComponent(Button, this._reorderToolbarSpan, "button", null, null, "downOne", [true]);
  button.setToolTip("move selected item down one position");
  button.setLabel("&#9661; One");
  button.addEventListener("onClick", Delegate.create(this, this._onDownOneButton));
  this._reorderButtons.push(button);
  
  // toEndButton
  this._toEndButton = button = this.addChildComponent(Button, this._reorderToolbarSpan, "button", null, null, "toEnd",[true]);
  button.setToolTip("move selected item to bottom");
  button.setLabel("&#9660; End");
  button.addEventListener("onClick", Delegate.create(this, this._onToEndButton));
  this._reorderButtons.push(button);
  
  // Disable all the buttons until a selection is made
  this._disableReorderButtons();
}
  
AbstractCollection.prototype._disableReorderButtons = function(){
  if (!this._isOrdered)
    // No reorder buttons
    return;
    
  // disable the buttons that are enabled.
  var toTopButtonIsDisabled = this._toTopButton.getDisabled();
  var upOneButtonIsDisabled = this._upOneButton.getDisabled();
  var toEndButtonIsDisabled = this._toEndButton.getDisabled();
  var downOneButtonIsDisabled = this._downOneButton.getDisabled();
  if (!toTopButtonIsDisabled) {
    this._toTopButton.setDisabled(true);
    this._toTopButton.addCSSClass("toTopDisabled");
  }
  if (!upOneButtonIsDisabled){
    this._upOneButton.setDisabled(true);
    this._upOneButton.addCSSClass("upOneDisabled");
  }
  if (!downOneButtonIsDisabled){
    this._downOneButton.setDisabled(true);
    this._downOneButton.addCSSClass("downOneDisabled");
  }
  if (!toEndButtonIsDisabled) {
    this._toEndButton.setDisabled(true);
    this._toEndButton.addCSSClass("toEndDisabled");
  }
}
  
AbstractCollection.prototype._checkReorderButtons = function(){
  if (!this._isOrdered)
    // No reorder buttons
    return;
    
  // update reorder buttons
  var selectedIndex = this._list.getSelectionIndex();
  var lastIndex = this._list.getLength() - 1;
  var toTopButtonIsDisabled = this._toTopButton.getDisabled();
  var upOneButtonIsDisabled = this._upOneButton.getDisabled();
  var toEndButtonIsDisabled = this._toEndButton.getDisabled();
  var downOneButtonIsDisabled = this._downOneButton.getDisabled();
  if (selectedIndex == 0) {
    if (!toTopButtonIsDisabled) {
      this._toTopButton.setDisabled(true);
      this._toTopButton.addCSSClass("toTopDisabled");
    }
    if (!upOneButtonIsDisabled){
      this._upOneButton.setDisabled(true);
      this._upOneButton.addCSSClass("upOneDisabled");
    }
    if (toEndButtonIsDisabled){
      this._toEndButton.setDisabled(false);
      this._toEndButton.removeCSSClass("toEndDisabled");
    }
    if (downOneButtonIsDisabled){
      this._downOneButton.setDisabled(false);
      this._downOneButton.removeCSSClass("downOneDisabled");
    }
  }
  else if (selectedIndex == lastIndex) {
    if (!toEndButtonIsDisabled) {
      this._toEndButton.setDisabled(true);
      this._toEndButton.addCSSClass("toEndDisabled");
    }
    if (!downOneButtonIsDisabled){
      this._downOneButton.setDisabled(true);
      this._downOneButton.addCSSClass("downOneDisabled");
    }
    if (toTopButtonIsDisabled) {
      this._toTopButton.setDisabled(false);
      this._toTopButton.removeCSSClass("toTopDisabled");
    }
    if (upOneButtonIsDisabled) {
      this._upOneButton.setDisabled(false);
      this._upOneButton.removeCSSClass("upOneDisabled");
    }
  }
  else {
    if (toTopButtonIsDisabled) {
      this._toTopButton.setDisabled(false);
      this._toTopButton.removeCSSClass("toTopDisabled");
    }
    if (upOneButtonIsDisabled) {
      this._upOneButton.setDisabled(false);
      this._upOneButton.removeCSSClass("upOneDisabled");
    }
    if (toEndButtonIsDisabled) {
      this._toEndButton.setDisabled(false);
      this._toEndButton.removeCSSClass("toEndDisabled");
    }
    if (downOneButtonIsDisabled) {
      this._downOneButton.setDisabled(false);
      this._downOneButton.removeCSSClass("downOneDisabled");
    }
  }
}
AbstractCollection.prototype._createEditableList = function(div) {
  Debug.error("Subclasses must overwrite [AbstractCollection.prototype._createEditableList]");
  return null;  
};
AbstractCollection.prototype.setCollectionMenu = function(menuData){
  this._menuData = menuData;
  if (this._titleMenu)
    this._titleMenu.setData(this._menuData);
}
AbstractCollection.prototype.requestData = function(){
  if(this._body.getAttribute("dataProvider") || this._body.getAttribute("query")) {
    this._updateEmptyMessage(true);
  }
  return superMethod(BaseComponent, this, "requestData");
};
AbstractCollection.prototype.setLimit = function(limit){
  this._limit = limit;
  this._list.setLimit(limit);
};
AbstractCollection.prototype.getLimit = function(){
  return this._limit;
};
AbstractCollection.prototype.getLength = function(){
  return this._list.getLength();
};
AbstractCollection.prototype.getList = function(){
  return this._list;
};
AbstractCollection.prototype.setEmptyMessage = function(msg, update){
  this._emptyMessage = msg;
  if (update) {
    this._updateEmptyMessage(false);
  }
};
AbstractCollection.prototype.setTitle = function(title){
  this._titleMenu.setLabel(title);
};
AbstractCollection.prototype._isEmpty = function(){
  // no this._data.
  return (this._list.getLength()<1);
};
AbstractCollection.prototype._updateEmptyMessage = function(loading){
  this._destroyEmptyMessage();
  var empty = this._isEmpty();
  
  if (empty){
    if(!this._emptyMessageDiv){
      // empty message div
      this._emptyMessageDiv = this.createTag("span", this._content, 0, null, "EmptyMessageContainer");
      // Empty message
      this._emptyMessageLabel = this.addChildComponent(Label, this._emptyMessageDiv, "span", 0, null, "emptyMessage");
      DOMEvent.addEventHandler(this._emptyMessageLabel._body, "dblclick", Delegate.create(this, this.onAdd));
      if (loading) 
        // Loading message
        this._emptyMessageLabel.setText(this._loadingMessage);
      else {
        // Empty message add LinkButton
        this._emptyMessageAddLabel = this.createTag("a", this._emptyMessageDiv, null, null, "emptyMessageAdd");
        this.createText("You can add it.", this._emptyMessageAddLabel);
        this._emptyMessageAddLabel.setAttribute("href", "javascript:void(0);");
        DOMEvent.addEventHandler(this._emptyMessageAddLabel, "click", Delegate.create(this, this.onAdd));
        
        // Empty message text
        this._emptyMessageLabel.setText(this._emptyMessage);
      }
    }
    this.addCSSClass("EmptyCollection");
  }
  else{
    this.removeCSSClass("EmptyCollection");
  }
  
  this.dispatchEvent("onCollectionLoad", {
    loading: loading, empty: empty});
};
AbstractCollection.prototype._destroyEmptyMessage = function(){
  if (this._emptyMessageDiv) {
    // onDblClick - empty message add LinkButton - might not have this if emptyMessage was 'loading' message.
    if (this._emptyMessageAddLabel){
      this._emptyMessageAddLabel.parentNode.removeChild(this._emptyMessageAddLabel);
      this._emptyMessageAddLabel = null;
    }
    // onDblClick - empty message
    this._emptyMessageLabel.destroy();
    delete this._emptyMessageLabel;
    
    // empty message div wrapping message and LinkButton
    this._emptyMessageDiv.parentNode.removeChild(this._emptyMessageDiv);
    this._emptyMessageDiv = null;
  }
}
AbstractCollection.prototype._updateMoreMessage = function(){
  if(this._moreButton){
    this._moreButton.parentNode.removeChild(this._moreButton);
    this._moreButton = null;
  }
  if (!(typeof this._limit == "undefined" || this._limit == null)) {
    var len = 0;
    var listData = this._list.getData();
    if (listData && listData["listItems"]) {
      len = listData["listItems"].length;
    }
    if (this._limit < len) {
      this._moreButton = this.createTag("a", this._footer, null, null, "more");
      //this._moreButton.setAttribute("href", "javascript:void(0);");
      this._moreButton.setAttribute("href", "javascript:void(0);");
      this.createText("[ more ]", this._moreButton);
      DOMEvent.addEventHandler(this._moreButton, "click", Delegate.create(this, this.onMore));
    } 
  }
};
AbstractCollection.prototype._overrideComponentMenus = function() {
  // called onReceiveData and after add/edit/remove
  var componentData = this.getData();
  var numItems = this.getLength();
  
  if (numItems < 1)
    // override the defaults for collection's menu data based on list size
    this._disableMenusForEmptyList(true, numItems);
  else
    this._disableMenusForEmptyList(false, numItems);
}
AbstractCollection.prototype._disableMenusForEmptyList = function(isEmpty, numItems) {
  // ICH!!! Modifying this live and setting it - should really have a _defaultMenus method
  // change only if different.
  this._menuData.menu[1].disabled= isEmpty;  // Edit
  this._menuData.menu[2].disabled = isEmpty;  // Remove
  this.setCollectionMenu(this._menuData);
}
AbstractCollection.prototype.add_checkPrivileges = function(){
  this.add_begin({});
};
AbstractCollection.prototype.add_begin = function(addData, index){
  if (typeof index == "undefined") {
    index = 0;
  }
  
  var listItem = this._list.addListItem(addData, index);
  listItem.ADDING = true;
  this._updateEmptyMessage(false);
  listItem.setEditable(true);
  return listItem;
};
AbstractCollection.prototype.add_submit = function(listItem, data){
  this.add_finish(data);
};
AbstractCollection.prototype.add_finish = function(data, index){
  if (typeof index == "undefined")
    index = 0;
  var temp = this._list.getItemByIndex(index);
  this._list.removeListItem(temp);
  var newItem = this._list.addListItem(data, index);
  this._list.setSelection(newItem);
  try {
    // title menu may be hidden and IE complains when trying
    // to set focus on a hidden element
    if (this._titleMenu) this._titleMenu.setFocus();
  }
  catch (ex) {
    Debug.warn("AbstractCollection: Can't focus on titleMenu");
  }
  // update menus if need be
  this._overrideComponentMenus();
};
AbstractCollection.prototype.delete_checkPrivileges = function(listItem){
  this.delete_begin(listItem);
};
AbstractCollection.prototype.delete_begin = function(listItem){
  this.delete_submit(listItem);
};
AbstractCollection.prototype.delete_submit = function(listItem){
  this.delete_finish(listItem);
};
AbstractCollection.prototype.delete_finish = function(listItem){
  this._list.removeListItem(listItem);
  this._updateEmptyMessage(false);
  
  // update menus if need be
  this._overrideComponentMenus();
};
AbstractCollection.prototype.edit_checkPrivileges = function(listItem){
  this.edit_begin(listItem);
}
AbstractCollection.prototype.edit_begin = function(listItem){
  listItem.setEditable(true);
};
AbstractCollection.prototype.edit_submit = function(listItem, submitData){
  this.edit_finish(listItem, submitData);
};
AbstractCollection.prototype.edit_finish = function(listItem, submitData){
  var itemIndex = this._list.getItemIndex(listItem);
  this._list.removeListItem(listItem);
  var newItem = this._list.addListItem(submitData, itemIndex);
  this._list.setSelection(newItem);
  if (this._titleMenu) this._titleMenu.setFocus();
  
  // update menus if need be
  this._overrideComponentMenus();
};
AbstractCollection.prototype.reorder_checkPrivileges = function(currentIndex, newIndex, listItem){
  this.reorder_begin(currentIndex, newIndex, listItem);
}
AbstractCollection.prototype.reorder_begin = function(currentIndex, newIndex, listItem){
  this._disableReorderButtons();
  this.reorder_submit(currentIndex, newIndex, listItem);
};
AbstractCollection.prototype.reorder_submit = function(currentIndex, newIndex, listItem){
  this.reorder_finish(currentIndex, newIndex, listItem);
};
AbstractCollection.prototype.reorder_finish = function(currentIndex, newIndex, listItem){
  this._list.moveItem(currentIndex, newIndex);
  this._checkReorderButtons();
  this.dispatchEvent({type:"onReorderFinish", listItem: listItem});
};
AbstractCollection.prototype.onMouseOver = function(e){
  if (isIE6) this.addCSSClass("collectionHover");
};
AbstractCollection.prototype.onMouseOut = function(e){
  if (isIE6) this.removeCSSClass("collectionHover");
};
AbstractCollection.prototype.onListItemMouseOver = function(e){
};
AbstractCollection.prototype.onListItemMouseOut = function(e){
};
AbstractCollection.prototype.onSave = function(eventObject){
  var listItem = eventObject.target;
  var submitData = eventObject.submitData;
  if (true == listItem.ADDING) {
    this.add_submit(listItem, submitData);
  }
  else {
    this.edit_submit(listItem, submitData);
  }
  this._titleMenu.setLabelDisabled(false, true);
  this.dispatchEvent("onSave");
};
AbstractCollection.prototype.onCancel = function(eventObject){
  Debug.log("AbstractCollection.prototype.onCancel", eventObject);
  var listItem = eventObject.target;
  if (listItem && listItem.getEditable()) {
    if (true == listItem.ADDING) 
    {  
      this._list.removeListItem(listItem);
    } else {
    //  listItem.setEditable(false);
    // Need to add and remove for proper formating
      var listItemData = listItem._data;     
      var itemIndex = this._list.getItemIndex(listItem);
      this._list.removeListItem(listItem);
      var newItem = this._list.addListItem(listItemData, itemIndex);
      this._list.setSelection(newItem);
     }
    this._updateEmptyMessage(false);
    this._titleMenu.setLabelDisabled(false, true);
    this.dispatchEvent("onCancel");
  }
};
AbstractCollection.prototype.onSelectionChange = function(eventObject){
  this.dispatchEvent(eventObject);
  this._checkReorderButtons();
}
AbstractCollection.prototype.onMenuSelect = function(eventObject){
  var menuItem = eventObject.menuItem;
  var value = menuItem.getValue();  
  this._onMenuValue(value);
};
AbstractCollection.prototype.onMenuLabelSelect = function(eventObject){
  if (this._menuData && this._menuData.menu && this._menuData.menu.length > 0) {
    this._titleMenu.setLabelDisabled(true, true); // Re-enable after Save/Cancel - anything else will navigate of page
    this._onMenuValue(this._menuData.menu[0]["value"]);
  }
};
AbstractCollection.prototype._onMenuValue = function(value){
  switch (value) {
    case "Add":
      this.add_checkPrivileges();
      break;
    case "Edit":
      var listItem = this._list.getSelection();
      if (listItem) {
        this.edit_checkPrivileges(listItem);
      }
      break;
    case "Remove":
      var listItem = this._list.getSelection();
      if (listItem && confirm(this._deleteConfirmationMessage)) {
        this.delete_checkPrivileges(listItem);
      }
      break;
    default:
      this.dispatchEvent({
        type: "onCollectionMenuSelect",
        target: this,
        value: value
      });
      break;
  };
};
AbstractCollection.prototype.onMore = function(eventObject){
  this.dispatchEvent({type:"onMore", target:this});
  return false;
};
AbstractCollection.prototype.onAdd = function(eventObject){
  this.dispatchEvent({type:"onAdd", target:this});
};
AbstractCollection.prototype._onToTopButton = function(){
  var selectedItem = this._list.getSelection();
  if (!selectedItem) {
    return;
  }
  var selectedItemIndex = this._list.getItemIndex(selectedItem);
  if (selectedItemIndex == 0) {
    // already the first item
    return;
  }
  this.reorder_checkPrivileges(selectedItemIndex, 0, selectedItem);
};
AbstractCollection.prototype._onUpOneButton = function(){
  var selectedItem = this._list.getSelection();
  if (!selectedItem) {
    return;
  }
  var selectedItemIndex = this._list.getItemIndex(selectedItem);
  if (selectedItemIndex == 0) {
    // already first item
    return;
  }
  this.reorder_checkPrivileges(selectedItemIndex, selectedItemIndex-1, selectedItem);
};
AbstractCollection.prototype._onDownOneButton = function(){
  var selectedItem = this._list.getSelection();
  if (!selectedItem) {
    return;
  }
  var selectedItemIndex = this._list.getItemIndex(selectedItem);
  var len = this.getLength();
  if (selectedItemIndex == len - 1) {
    // already last item
    return;
  }
  this.reorder_checkPrivileges(selectedItemIndex, selectedItemIndex+1, selectedItem);
};
AbstractCollection.prototype._onToEndButton = function(){
  var selectedItem = this._list.getSelection();
  if (!selectedItem) {
    return;
  }
  var selectedItemIndex = this._list.getItemIndex(selectedItem);
  var len = this.getLength();
  if (selectedItemIndex == len - 1) {
    // already last item
    return;
  }
  this.reorder_checkPrivileges(selectedItemIndex, len-1, selectedItem);
};
AbstractCollection.prototype.onListItemEditError = function(listItem, synopsis, elaboration, details) {
  var index = this._list.getItemIndex(listItem);
  if (index < 0) {
    Debug.warn("[AbstractCollection.onListItemEditError] listItem not found:", listItem);
    return;
  }
  //alert("oldStyle: " + oldStyle);
  listItem._body.style.display = "none";
  var feedback = this.addChildComponent(InlineFeedback,
                                        this._list._body,
                                        "li", null, listItem._body,
                                        undefined, // default cssClassName
                                        [{synopsis:synopsis,
                                          elaboration:elaboration,
                                          details:details}, ErrorFeedback, "InlineErrorFeedback detailsContracted"]);
  feedback.addEventListener("onClose", Delegate.create(this, this.onListItemEditErrorClose, [listItem]));
  return feedback;
};
AbstractCollection.prototype.onListItemEditErrorClose = function(listItem) {  
  this.onCancel({target:listItem});
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * PopupInfo class
 * alee@metaweb.com
 */
function PopupInfo(targetElement, infoData, noShadow){
  // member vars:
  this._title;
  this._list;
  this._infoData = infoData;
  setupInheritance(PopupInfo);
  superConstructor(Popup, this, [targetElement,noShadow]);
}
PopupInfo.superClass = [Popup];
registerClass(PopupInfo, "PopupInfo");
PopupInfo.prototype.CLASS_NAME = "PopupInfo";
PopupInfo.prototype.init = function(){
  superMethod(Popup, this, "init");
  var frag = document.createDocumentFragment();
  var infoDiv = this.createTag("div", frag, null, null, "PopupInfoTitle");
  this._title = this.addChildComponent(Label, infoDiv, "div", null, null, "PopupInfoSubTitle");
  this._list = this.addChildComponent(SimpleDisplayList, infoDiv, "ul", null, null, "PopupInfoDetail", [InfoListItem]);
  if (this._infoData) 
    this.setData(this._infoData);
  this._body.appendChild(frag);
}
PopupInfo.prototype.setData = function(data){
  this.clearData();
  if (!data) return;
  this._data = data;
  if (this._data.tip)
    this._title.setText(this._data.tip);
  else
    this._title.setText("Enter " + this._data.typeName);
  this._list.setData(this._data.list);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>Overlay</b>
 *
 * Abstract class:
 *
 * @author daepark@apmindsf.com
 */
/****************************************************************** constructor
 * @param overlayElement - the html element that we are overlaying over. 
 * If null, overlay over entire document body 
 */
function OverlayProgress(overlayElement, msg) {
  this._msg = msg;
  setupInheritance(OverlayProgress);
  superConstructor(Overlay, this, [overlayElement]);
};
OverlayProgress.superClass = [Overlay];
registerClass(OverlayProgress, "OverlayProgress");
OverlayProgress.prototype.CLASS_NAME = "OverlayProgress";
OverlayProgress.prototype.createContent = function() {
  var div = this.addChildTag("div", null, null, "OverlayTopShadow");
  var div2 = this.createTag("div", div, null, null, "OverlayBottomShadow");
  div = this.createTag("div", div2);
  var c = new OverlayProgressContent(div, this._msg);
  return c;
};
OverlayProgress.prototype.onClick = function() {
};
function OverlayProgressContent(div, msg) {
  this._msg = msg;
  this._label = null;
  setupInheritance(OverlayProgressContent);
  superConstructor(BaseComponent, this, [div]);
};
OverlayProgressContent.superClass = [BaseComponent]; 
registerClass(OverlayProgressContent, "OverlayProgressContent");
OverlayProgressContent.prototype.CLASS_NAME = "OverlayProgressContent";
OverlayProgressContent.prototype.init = function() {
  this.addCSSClass(this.CLASS_NAME);
  if (this._msg) {
    this._label = this.addChildComponent(Label);
    this._label.setText(this._msg);
  }
};
function mql_with_progress(q, parentNode, successCallback, errorCallback, msg) {
  if (!msg)
    msg = "Saving...";
  var progress = new OverlayProgress(parentNode, msg);
  if (errorCallback) {
    q.mql_error = function(error) {
      progress.hide();
      errorCallback(error);
    };
  } else {
    // call the default mql_error - this should really be cleaned up
    var old_mql_error = q.mql_error;
    q.mql_error = function(error) {
      progress.hide();
      old_mql_error(error);
    }
  }
  q.requestData(function(queryResult, metadata) {
    progress.hide();
    successCallback(queryResult, metadata);
  });
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 */
function EnumInput(div){
  this._enumList;
  this._enumContainer;
  setupInheritance(EnumInput);
  superConstructor(TextInput, this, [div]);
};
EnumInput.superClass = [TextInput];
registerClass(EnumInput, "EnumInput");
EnumInput.prototype.CLASS_NAME = "EnumInput";
EnumInput.UID_EMPTY = "UID_EMPTY";
EnumInput.EMPTY_LIST_DATA = {
  listItems: [{
    text: "no matches",
    value: EnumInput.UID_EMPTY
  }]
};
EnumInput.prototype.init = function(){
  superMethod(TextInput, this, "init");
  this.addEventListener("onFocus", Delegate.create(this, this.onEnumInputFocus));  
  this.addEventListener("onBlur", Delegate.create(this, this.onEnumInputBlur));
  this.addEventListener("onTextChange", Delegate.create(this, this.onEnumInputTextChange));
  this.addEventListener("onEnterKey", Delegate.create(this, this.onEnumInputEnterKey));
};
EnumInput.prototype.initInteractive = function(div) {
  this._inputEventHandler = new InputEventHandler(this, this._input, 
    InputEventHandler.ENTERKEY | InputEventHandler.ESCAPEKEY | 
    InputEventHandler.TEXTCHANGE | InputEventHandler.MOUSEPASTE | 
    InputEventHandler.KEYPRESS | InputEventHandler.KEYDOWN);
  this._mouseEventHandler = new MouseEventHandler(this, this._body,
    MouseEventHandler.MOUSECLICK);
  this._mouseEventHandler.setInteractive(true);
};
EnumInput.prototype.onClick = function(e) {
  this.showEnumList();
};
EnumInput.prototype.onEnumInputFocus = function(e) {
  //this.showEnumList();
};
EnumInput.prototype.onEnumInputBlur = function(e){
  clearInterval(this._inputBlurInterval);
  this._inputBlurInterval = 
    setInterval(Delegate.create(this, this.onEnumInputBlurDelay), 200);
};
EnumInput.prototype.onEnumInputBlurDelay = function() {
  clearInterval(this._inputBlurInterval);
  if (this._listHasFocus || this.hasFocus) return;
  this.hideEnumList();
};
EnumInput.prototype.onKeyDown = function(e) {
  var keyCode = e.keyCode;
  var domEvt = e.domEventObject;
  switch (keyCode) {
    case 40: // DOWN_ARROW
      DOMEvent.preventDefault(domEvt);
      this._onDownArrow();
      break;
    case 38: // UP_ARROW
      DOMEvent.preventDefault(domEvt);
      this._onUpArrow();
      break;
    default:
      break;
  }
};
EnumInput.prototype.onKeyPress = function(e) {
  var keyCode = e.keyCode;
  switch (keyCode) {
    case 40: // DOWN_ARROW
    case 38: // UP_ARROW
    //case 9: // TAB
      DOMEvent.preventDefault(e.domEventObject);
      break;
    default:
      break;
  }
};
EnumInput.prototype.showEnumList = function() {  //Debug.log("showEnumList");
  var input = this._input;
  var pos = DOMUtils.getElementLocation(input);
  var dim = DOMUtils.getElementDimension(input);
  var left = pos.x;
  var top = pos.y + dim.h;
  var width = dim.w;
  if (!this._enumContainer) {
    this._enumContainer = $elt("div", null, "EnumListTopShadow");
    var shadow2 = $elt("div", this._enumContainer, "EnumListBottomShadow");
    var div = $elt("ul", shadow2);    
    this._enumList = new EnumList(div);
    this._enumList.addEventListener("onListItemClick", this); 
    this._enumList.addEventListener("onSelectionChange", this);  
    DOMEvent.addEventHandler(this._enumList._body, "mousedown", Delegate.create(this, this.onEnumListMouseDown));
    DOMEvent.addEventHandler(this._enumList._body, "mouseup", Delegate.create(this, this.onEnumListMouseUp));
    var style = sprintf("display: none; width: %spx; left: %spx; top: %spx", width, left, top);
    DOMUtils.setElementStyle(this._enumContainer, style);
    document.body.appendChild(this._enumContainer);
  }
  var style = "display: block;";
  if (left != DOMUtils.getElementStyle(this._enumContainer, "left")  &&
      top != DOMUtils.getElementStyle(this._enumContainer, "top")) {
    style += sprintf("width: %spx; left: %spx; top: %spx", width, left, top);
  }
  DOMUtils.setElementStyle(this._enumContainer, style);
  
  var enumData = this.filterEnumData(this.getText());
  this._enumList.setData(enumData);
  this._enumList.highlight(this.getText());
};
EnumInput.prototype.filterEnumData = function(text) { //  Debug.log("filterEnumData", this._data);
  var filtered = [];
  if (text == null) text = "";
  text = StringUtils.trim(text);
  if (this._data && this._data.enumList) {
    if (StringUtils.isEmpty(text)) {
      filtered = this._data.enumList;
    }
    else {
      text = text.toLowerCase();
      for(var i=0, len=this._data.enumList.length; i<len; i++) {
        var data = this._data.enumList[i];
        if (data && data.text != null && data.text.toLowerCase().indexOf(text) == 0) {
          filtered.push(data);
        }
      }
    }
  }
  if (filtered.length > 0) {
    return {listItems:filtered};
  } 
  else {
    return (EnumInput.EMPTY_LIST_DATA);
  }
};
EnumInput.prototype.onListItemClick = function(eventObject) {  //Debug.log("EnumInput.prototype.onListItemClick");
  // when list gets a selection change event (with a mouse click), the
  // autocompleteInput will lose focus. The following workaround give
  // focus back to the autocomplete input.
  
  // clear blurInterval to prevent call to _release
  clearInterval(this._inputBlurInterval);
  this.onEnumInputEnterKey();
};
EnumInput.prototype.onEnumInputEnterKey = function(e) {  //Debug.log("EnumInput.prototype.onEnterKey");
  if (!this._enumList) return;
  var item = this._enumList.getSelection();
  if (item && item._data && item._data.value != EnumInput.UID_EMPTY) {
    this.setText(item._data.text);
    this.hideEnumList();
    this.dispatchEvent({type:"onEnumSelect", listItem:item});
  }
};
EnumInput.prototype.onEnumInputTextChange = function(e) {
  if (this._isEnumListVisible()) {
    var enumData = this.filterEnumData(this.getText());
    this._enumList.setData(enumData);
    this._enumList.highlight(this.getText());
  //if (!this._isEnumListVisible()) this.showEnumList();
  }
  else {
    this.showEnumList();  
  }
  this._enumList.setSelection(null, 0);  
  this.dispatchEvent({type:"onEnumInputTextChange"});  
};
EnumInput.prototype._isEnumListVisible = function() {
  return (this._enumContainer && this._enumContainer.style && 
          "none" != this._enumContainer.style.display);
}
EnumInput.prototype._onDownArrow = function(e) {  //Debug.log("EnumInput.prototype._onDownArrow");
  if (!this._isEnumListVisible()) this.showEnumList();
  var index = this._enumList.getSelectionIndex() + 1;
  var listItem = null;
  if (index >=0 && index < this._enumList.getLength()) {
    listItem = this._enumList.setSelection(null, index);
  }
  if (listItem) DOMUtils.scrollIntoView(listItem._body, this._enumList._body);
};
EnumInput.prototype._onUpArrow = function(e) {
  var index = this._enumList.getSelectionIndex() - 1;
  var listItem = null;
  if (index >=0 && index < this._enumList.getLength())
  {
    listItem = this._enumList.setSelection(null, index);
  }
  if (listItem) DOMUtils.scrollIntoView(listItem._body, this._enumList._body);
};
EnumInput.prototype.onEnumListMouseDown = function(eventObj) {
  this._listHasFocus = true;
};
EnumInput.prototype.onEnumListMouseUp = function(eventObj) { 
  this._listHasFocus = false;
  this.removeEventListener("onFocus", this);
  this._input.focus();
  this.addEventListener("onFocus", this);
};
EnumInput.prototype.hideEnumList = function(e) {
  if (this._enumContainer != null) {
    var style = "display: none;";
    DOMUtils.setElementStyle(this._enumContainer, style);
    this._enumList.setSelection(null);
  }
};
EnumInput.prototype.onSelectionChange = function(eventObject){
  this.dispatchEvent({type:"onEnumListSelectionChange", listItem:eventObject.targetItem});
}
function EnumList(div){  
  setupInheritance(EnumList);
  superConstructor(AutocompleteList, this, [div]);
};
EnumList.superClass = [AutocompleteList];
registerClass(EnumList, "EnumList");
EnumList.prototype.CLASS_NAME = "EnumList";
EnumList.prototype._createListItem = function(div) {
	return new EnumListItem(div);
};
function EnumListItem(div){
  this._label;
  setupInheritance(EnumListItem);
  superConstructor(AbstractListItem, this, [div]);
};
EnumListItem.superClass = [AbstractListItem];
registerClass(EnumListItem, "EnumListItem");
EnumListItem.prototype.CLASS_NAME = "EnumListItem";
EnumListItem.prototype._createDisplayContent = function() {
  this._label = this.addChildComponent(Label, null, "div", null, null, null, [true]);
  this._label.setText(this._data.text);
};
EnumListItem.prototype._destroyDisplayContent = function() {
  if (this._label) {
    this._label.destroy();
    delete this._label;
  }
  if (this._body)
    this._body.innerHTML = "";
};
EnumListItem.prototype.highlight = function(text) {
  if (this._data && this._data.value == EnumInput.UID_EMPTY) return;
  if (!this._label) return;
  if (!text) return;
  var origText = this._label.getText();
  var newText = origText;
  var index = origText.toLowerCase().indexOf(text.toLowerCase());
  if (index >= 0) {    
    newText = origText.substring(0, index) + 
      '<span class="highlight">' +
      origText.substring(index, index+text.length) +
      '</span>' +
      origText.substring(index + text.length);
    this._label._body.innerHTML = newText;     
  }
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SimpleEditableList</b>
 *
 * @author daepark@apmindsf.com
 */
function SimpleEditableList(div){
  setupInheritance(SimpleEditableList);
  superConstructor(AbstractEditableList, this, [div]);
};
SimpleEditableList.superClass = [AbstractEditableList];
registerClass(SimpleEditableList, "SimpleEditableList");
SimpleEditableList.prototype.CLASS_NAME = "SimpleEditableList";
/****************************************************** _createEditableListItem
 */
SimpleEditableList.prototype._createEditableListItem = function(div) {
  return new SimpleEditableListItem(div);
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>EditableHTMLList</b>
 *
 * @author alee@metaweb.com
 */
function EditableHTMLList(div){
  setupInheritance(EditableHTMLList);
  superConstructor(AbstractEditableList, this, [div]);
};
EditableHTMLList.superClass = [AbstractEditableList];
registerClass(EditableHTMLList, "EditableHTMLList");
EditableHTMLList.prototype.CLASS_NAME = "EditableHTMLList";
EditableHTMLList.prototype._createEditableListItem = function(div) {
  var listItem = new HTMLListItem(div);
  listItem.addEventListeners([
    "onLobLoaded", 
    "onReloading"
    ], this);
  return listItem;
};
EditableHTMLList.prototype._destroyListItem = function(listItem) {
  if (listItem) 
    listItem.removeEventListener("onLobLoaded", this);  
  superMethod(AbstractEditableList, this, "_destroyListItem", [listItem]);
};
EditableHTMLList.prototype.onLobLoaded = function(eventObject) {
  var targetIndex = this.getItemIndex(eventObject.target);
  this.dispatchEvent({type:"onLobLoaded", target: eventObject.target, targetIndex: targetIndex});
}
EditableHTMLList.prototype.onReloading = function(eventObject) {
  var targetIndex = this.getItemIndex(eventObject.target);
  this.dispatchEvent({type:"onReloading", target: eventObject.target, targetIndex: targetIndex});
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>RadioButtonList</b>
 *
 * @author alee@metaweb.com
 */
function RadioButtonList(div){
  // member vars
  this._disabled;
  
  setupInheritance(RadioButtonList);
  superConstructor(OptionsList, this, [div]);
};
RadioButtonList.superClass = [OptionsList];
registerClass(RadioButtonList, "RadioButtonList");
RadioButtonList.prototype.CLASS_NAME = "RadioButtonList";
RadioButtonList.prototype.setData = function(data) {
  superMethod(OptionsList, this, "setData", [data]);
  
  if (typeof this._data.selected != "undefined" && this._data.selected != null) {
    if (typeof this._data.selected == "number") {
      this.setSelection(null, this._data.selected);
    }
    else {
      for (var i=0, len=this._listItems.length; i<len; i++){
        var listItem = this._listItems[i];
        var listItemData = listItem.getData();
        if (listItemData.valueText == this._data.selected) {
          this.setSelection(null, i);
          break;
        }
      }
    }      
  }
};
RadioButtonList.prototype._createListItem = function(div) {
  return new RadioButtonListItem(div);
};
RadioButtonList.prototype.setDisabled = function(state) {
  if (this._disabled == state)
    return;
  this._disabled = state;
  if(this._disabled) {
    if(this._listItems){
      for(var i=0, len=this._listItems.length; i<len; i++){
        this._listItems[i].setDisabled(this._disabled);
        this._listItems[i].removeEventListeners([
          "onListItemClick", 
          "onListItemDblClick", 
          "onMouseOver", 
          "onMouseOut"
          ], this); 
      }
    }
    this.addCSSClass("disabled");
  }
  else {
    if(this._listItems){
      for(var i=0, len=this._listItems.length; i<len; i++){
        this._listItems[i].setDisabled(this._disabled);
        this._listItems[i].addEventListeners([
          "onListItemClick", 
          "onListItemDblClick", 
          "onMouseOver", 
          "onMouseOut"
          ], this); 
      }
    }
    this.removeCSSClass("disabled");
  }
};
/*
DATA: example
  {
    selected: "vertical",
    listItems: [{
      label: "Vertical List",
      value: 0,
      valueText: "vertical",
      name: "orientation"
    }, {
      label: "Horizontal List",
      value: 1,
      valueText: "horizontal",
      name: "orientation"
    }]
  }
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>Overlay</b>
 *
 * Abstract class:
 *
 * @author daepark@apmindsf.com
 */
/****************************************************** constructor
 * @param overlayElement - the html element that we are overlaying over. 
 * If null, overlay over entire document body 
 */
function Overlay(overlayElement) {
  
  this._overlayElement = overlayElement;
  
  this._mouseEventHandler;
  this._windowResizeHandler;
  this._windowScrollHandler;
  this._content;
  this._contentClickHandler;
  var div = $elt("div", document.body);
  setupInheritance(Overlay);
  superConstructor(BaseComponent, this, [div]);
};
Overlay.superClass = [BaseComponent];
registerClass(Overlay, "Overlay");
Overlay.prototype.CLASS_NAME = "Overlay";
Overlay.prototype.init = function() {
  this.addCSSClass(this.CLASS_NAME); 
  var style = "visibility: hidden; position: absolute; left: 0px; top: 0px; width: 0px; height: 0px;";
  DOMUtils.setElementStyle(this._body, style);
  
  this._mouseEventHandler = new MouseEventHandler(this, this._body, MouseEventHandler.MOUSECLICK);
  this.show();
};
Overlay.prototype.onClick = function() {
  this.hide();
};
Overlay.prototype.createContent = function() {
  var label = this.addChildComponent(Label);
  label.setText("Hello. Click outside the content to hide the overlay");
  return label;
};
Overlay.prototype.onClickContent = function(domEvt) {
  DOMEvent.stopPropagation(domEvt);
};
Overlay.prototype.show = function() {
  this._mouseEventHandler.setInteractive(true);
  this._windowResizeHandler = Delegate.create(this, this._onWindowResize);
  DOMEvent.addEventHandler(window, "resize", this._windowResizeHandler);  
  this._windowScrollHandler = Delegate.create(this, this._onWindowScroll);
  DOMEvent.addEventHandler(window, "scroll", this._windowScrollHandler);  
  this._content = this.createContent();
  if (this._content && this._content._body) {
    this._contentClickHandler = Delegate.create(this, this.onClickContent);
    DOMEvent.addEventHandler(this._content._body, "click", this._contentClickHandler); 
  }
  this._setPositionAndSize();
  this._body.style["visibility"] =  "visible";  
};
Overlay.prototype.hide = function() {
  this._body.style["visibility"] =  "hidden";    
  if (this._content) {
    DOMEvent.removeEventHandler(this._content._body, "click", this._contentClickHandler); 
    Delegate.destroy(this._contentClickHandler);
    this._contentClickHandler = null;
    this._content.destroy();
    this._content = null;    
  }
  DOMEvent.removeEventHandler(window, "scroll", this._windowScrollHandler);
  Delegate.destroy(this._windowScrollHandler);  
  this._windowScrollHandler = null;
  DOMEvent.removeEventHandler(window, "resize", this._windowResizeHandler);
  Delegate.destroy(this._windowResizeHandler);  
  this._windowResizeHandler = null;
  this._mouseEventHandler.setInteractive(false); 
};
Overlay.prototype._onWindowResize = function(){
  this._setPositionAndSize();
};
Overlay.prototype._onWindowScroll = function(){
  this._setPositionAndSize();
};
Overlay.prototype._setPositionAndSize = function(){
  var pos = null;
  var sz = null;
  if (this._overlayElement) {
    pos = DOMUtils.getElementLocation(this._overlayElement);
    sz = DOMUtils.getElementDimension(this._overlayElement);
  }
  else {  
    pos = DOMUtils.getWindowScrollPos();
    sz = DOMUtils.getWindowSize();
  }
  this._body.style["left"] =  pos.x + "px";
  this._body.style["top"] =  pos.y + "px";
  
  if (sz && typeof sz.w != "undefined" && typeof sz.h != "undefined") {
    this._body.style["width"] =  sz.w + "px";
    this._body.style["height"] =  sz.h + "px";
  }
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractListItem</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractListItem.prototype._createDisplayContent]
 * Subclasses must overwrite [AbstractListItem.prototype._destroyDisplayContent]
 *
 * @author daepark@apmindsf.com
 */
function AbstractListItem(div){
  // member vars:
  this._selected;
  this._mouseEventHandler;
  
  setupInheritance(AbstractListItem);
  superConstructor(BaseComponent, this, [div]);
};
AbstractListItem.superClass = [BaseComponent];
registerClass(AbstractListItem, "AbstractListItem");
AbstractListItem.prototype.CLASS_NAME = "AbstractListItem";
AbstractListItem.prototype.MENU_DATA = {};
AbstractListItem.prototype.DELIM = ",";
AbstractListItem.prototype.init = function(){
  this.changeTag("li");
  this.addCSSClass(this.CLASS_NAME);
  this.initInteractive();
  this.clearData();
};
AbstractListItem.prototype.initInteractive = function(){
  this._mouseEventHandler = new MouseEventHandler(this, this._body);
  this._mouseEventHandler.setInteractive(true);
  this._selected = false;
};
AbstractListItem.prototype.setData = function(data){
  this.clearData();
  this._data = data;
  this._destroyDisplayContent();
  this._createDisplayContent();
};
AbstractListItem.prototype._createDisplayContent = function()
{
  Debug.error(this.CLASS_NAME, "must overwrite [AbstractListItem.prototype._createDisplayContent]");
};
AbstractListItem.prototype._destroyDisplayContent = function()
{
  Debug.error(this.CLASS_NAME, "must overwrite [AbstractListItem.prototype._destroyDisplayContent]");
};
AbstractListItem.prototype.setInteractive = function(b){
  this._mouseEventHandler.setInteractive(true == b);
};
AbstractListItem.prototype.setSelected = function(selected){
  if(selected == this._selected) return;
  this._selected = selected;
  if(this._selected) this.addCSSClass("listItemSelected");
  else this.removeCSSClass("listItemSelected");
};
AbstractListItem.prototype.getSelected = function(selected){
  return this._selected;
};
AbstractListItem.prototype.onMouseOver = function(e){
  if (e) {
    if (isIE6) this.addCSSClass('listItemHover');
    this.dispatchEvent({type: "onMouseOver", target: this, clientX: e.clientX, clientY: e.clientY});
  }
};
AbstractListItem.prototype.onMouseOut = function(e){
  if (e) {
    if (isIE6) this.removeCSSClass('listItemHover');
    this.dispatchEvent({type: "onMouseOut", target: this, clientX: e.clientX, clientY: e.clientY});
  }
};
AbstractListItem.prototype.onClick = function(e){
  if (e) {
    this.dispatchEvent({type:"onListItemClick", target:this, domEventObject:e});
  }
};
AbstractListItem.prototype.onDblClick = function(e){
  if (e) {
    this.dispatchEvent({type:"onListItemDblClick", target:this, domEventObject:e});
  }
};
/* DATA EXAMPLE:
_data = {}
HTML:
<li class="AbstractListItem"></li>
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * PopupMenuLabel class
 * alee@metaweb.com
 */
function PopupMenuLabel(div){
  setupInheritance(PopupMenuLabel);
  superConstructor(BaseComponent, this, [div]);
  
  // member vars
  this._defaultLabel;
  this._labelButtonDisabled;
  
  this._labelButton;
  this._labelClickHandler;
  this._popup;
  this._popupClickHandler;
  this._menu;
  this._menuSelectHandler;
  
  this._mouseEventHandler;
}
PopupMenuLabel.superClass = [BaseComponent];
registerClass(PopupMenuLabel, "PopupMenuLabel");
PopupMenuLabel.prototype.CLASS_NAME = "PopupMenuLabel";
PopupMenuLabel.prototype.init = function(){
  this.addCSSClass("PopupMenuLabel");
  this.clearData();
  // labelElement and button
  this._labelButtonDisabled = false;
  this._popup = new Button(this.addChildTag("button", 0, null, "Menu"), true);
  this._popup.setLabel("&#9660;");
  this._defaultLabel = this._body.getAttribute("componentTitle");
  if (this._defaultLabel)
    this.setLabel(this._defaultLabel);    
  this._popupClickHandler = Delegate.create(this, this._onMenuClick);
  DOMEvent.addEventHandler(this._popup._body, "click", this._popupClickHandler);
  this.initInteractive();
}
PopupMenuLabel.prototype.initInteractive = function(){
  this._mouseEventHandler = new MouseEventHandler(this, this._body,
    MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT);
};
PopupMenuLabel.prototype.destroy = function(){
  
  if (this._menu) {
    if (this._menuSelectHandler) {
      this._menu.removeEventListener("onMenuSelect", this._menuSelectHandler);
      Delegate.destroy(this._menuSelectHandler);
      this._menuSelectHandler = null;
    }
    this._menu.destroy();
    this._menu = null;
  }
  
  if (this._labelButton) {
    if (this._labelClickHandler) {
      DOMEvent.removeEventHandler(this._labelButton._body, "click", this._labelClickHandler);      
      Delegate.destroy(this._labelClickHandler);
      this._labelClickHandler = null;
    }
    this._labelButton.destroy();
    this._labelButton = null;
  }
  
  if (this._popup) {
    if (this._popupClickHandler) {
      DOMEvent.removeEventHandler(this._popup._body, "click", this._popupClickHandler);
      Delegate.destroy(this._popupClickHandler);
      this._popupClickHandler = null;
    }
    this._popup.destroy();
    this._popup = null;
  }
  
  if (this._mouseEventHandler) {
    this._mouseEventHandler.setInteractive(false);
    this._mouseEventHandler = null;
  }
  superMethod(BaseComponent, this, "destroy");
};
PopupMenuLabel.prototype.setMenu = function(menu){
  this._menu = menu;
  if(this._menuSelectHandler){
    // replace listener
    this._menu.removeEventListener("onMenuSelect", this._menuSelectHandler);
    Delegate.destroy(this._menuSelectHandler);
    this._menuSelectHandler = Delegate.create(this, this.onMenuSelect);
    this._menu.addEventListener("onMenuSelect", this._menuSelectHandler);
  }
}
PopupMenuLabel.prototype.setLabel = function(label){
  if (!label) {
    return; 
  }
  if (this._labelButton) {
    if (this._labelButton.getText() == label) {
      return;
    }
    if (this._mouseEventHandler)
      this._mouseEventHandler.setInteractive(false); 
    if (this._labelClickHandler) {
      DOMEvent.removeEventHandler(this._labelButton._body, "click", this._labelClickHandler);      
      Delegate.destroy(this._labelClickHandler);
      this._labelClickHandler = null;
    }
    this._labelButton.destroy();
  }
  
  this._labelButton = new Button(this.addChildTag("button", 1, null, "MenuLabel"));
  this._labelButton.setLabel(label);
  if (this._labelButtonDisabled) {
    this._labelButton.setDisabled(this._labelButtonDisabled);
    this._labelClickHandler = null;
  }
  else {
    this._labelClickHandler = Delegate.create(this, this._onLabelClick);
    DOMEvent.addEventHandler(this._labelButton._body, "click", this._labelClickHandler);
  }
  if (this._mouseEventHandler)
    this._mouseEventHandler.setInteractive(true);
}
PopupMenuLabel.prototype.setLabelDisabled = function(disabled, dontReplaceCSS){
  // disable/enable both _labelButton and _popup button - function is misnamed for both these operations
  if (this._labelButtonDisabled == disabled)
    return;
    
  if (this._labelButton) {
    // label button
    this._labelButton.setDisabled(disabled, dontReplaceCSS);  // true is for dontReplaceCSS
    if (disabled) {
      if (this._labelClickHandler) {
        DOMEvent.removeEventHandler(this._labelButton._body, "click", this._labelClickHandler);
        Delegate.destroy(this._labelClickHandler);
        this._labelClickHandler = null;
      }
    }
    else{
      this._labelClickHandler = Delegate.create(this, this._onLabelClick);
      DOMEvent.addEventHandler(this._labelButton._body, "click", this._labelClickHandler);
    }
  }
  
    
  if (this._popup) {
    // dropdown menu
    this._popup.setDisabled(disabled, dontReplaceCSS);  // true is for dontReplaceCSS
    if (disabled) {
      if (this._popupClickHandler) {
        DOMEvent.removeEventHandler(this._popup._body, "click", this._popupClickHandler);
        Delegate.destroy(this._popupClickHandler);
        this._popupClickHandler = null;
      }
    }
    else{
      this._popupClickHandler = Delegate.create(this, this._onMenuClick);
      DOMEvent.addEventHandler(this._popup._body, "click", this._popupClickHandler);
    }
  }
  
  this._labelButtonDisabled = disabled;
}
PopupMenuLabel.prototype.setData = function(data){
  this.clearData();
  
  this._data = data;
  var label = data["label"];
  if (!label)
    label = this._defaultLabel;
  this.setLabel(label);
}
PopupMenuLabel.prototype.clearData = function(data){
  this._data = {"label": null, "menu": [] };
}
PopupMenuLabel.prototype.onMouseOver = function(e){
  if (isIE6) this.addCSSClass('popupMenuLabelHover');
}
PopupMenuLabel.prototype.onMouseOut = function(e){
  if (isIE6) this.removeCSSClass('popupMenuLabelHover');
}
PopupMenuLabel.prototype._onLabelClick = function(e){
  this.dispatchEvent({type:"onMenuLabelSelect", target: this, menu: this._menu});
}
PopupMenuLabel.prototype._onMenuClick = function(e){
  if(!this._menu){
    this._menu = new PopupMenu();
    this._menuSelectHandler = Delegate.create(this, this.onMenuSelect);
    this._menu.addEventListener("onMenuSelect", this._menuSelectHandler);
  }
  var anchor = this._popup._body;
  this._menu.show(anchor);
  this._menu.setData(this._data.menu);
  this.dispatchEvent({type:"onMenuClick", target: this});
}
PopupMenuLabel.prototype.onMenuSelect = function(eventObject){
  eventObject.menu = this._menu;
  this.dispatchEvent(eventObject);
}
PopupMenuLabel.prototype.setFocus = function(){
  if (this._labelButton) {
    this._labelButton._body.focus();
    this._labelButton.addCSSClass("menuFocus");
  }
  else if (this._popup) {
    this._popup._body.focus();
  }
}
/*
this._data = {
  label: "optionalLabel",
  menu: [
	  {text:"item zero",value:0},
	  {text:"item one",value:1, disabled: true},
	  {text:"item two",value:2, disabled: true},
	  {text:"item three",value:3},
	  {},
	  {text:"item four",value:4},
	  {text:"item five",value:5},
	  {text:"item six",value:6},
	  {},
	  {text:"item seven",value:7, disabled: true},
	  {text:"item eight",value:8},
	  {},
	  {text:"item nine",value:9}
	]
}
HTML:
<div class="PopupMenuLabel">This is some text!</div>
<div class="PopupMenuLabel"><a href="http://www.amazon.com">This will goto Amazon!</a></div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractInput</b>
 *
 * Abstract class:
 * This represents SINGLE HTML input elements (e.g., textarea, input, button?).
 *
 * Subclasses must overwrite [AbstractInput.prototype._createInput]
 * Subclasses may overwrite [AbstractInput.prototype.initInteractive] for InputEvents of interest
 *
 * Based on original TextInput written by mark@metaweb.com.
 * Refactored to allow creation of TextAreaInput, TextInput, etc.
 *
 * @author alee@metaweb.com
 */
function AbstractInput(div){
  // member vars:
  this._input;
  this._inputEventHandler;
  this._disabled = false;
  this.hasFocus = false;
  
  setupInheritance(AbstractInput);
  superConstructor(BaseComponent, this, [div]);
}
AbstractInput.superClass = [BaseComponent];
registerClass(AbstractInput, "AbstractInput");
AbstractInput.prototype.CLASS_NAME = "AbstractInput";
AbstractInput.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  this.clearData();
  this._input = this._createInput();
  // keyboard events of interest
  this.initInteractive();
  // events:
  DOMEvent.addEventHandler(this._input, "focus", Delegate.create(this, this._onFocus));
  DOMEvent.addEventHandler(this._input, "blur", Delegate.create(this, this._onBlur));
}
AbstractInput.prototype.initInteractive = function(div) {
  this._inputEventHandler = new InputEventHandler(this, this._input, 
    InputEventHandler.ENTERKEY | InputEventHandler.ESCAPEKEY | InputEventHandler.TEXTCHANGE | InputEventHandler.MOUSEPASTE);
}
AbstractInput.prototype._createInput = function(div) {
  Debug.error("Subclasses must overwrite [AbstractInput.prototype._createInput]");
  return null;  
};
AbstractInput.prototype.getData = function(){
  // always defer to this._input.value as definitive value
  if (this._data)
    this._data.text = this._input.value;
  return superMethod(BaseComponent, this, "getData");
}
AbstractInput.prototype.setData = function(data){
  this.clearData();
  if (data)
    this._data = data;
  this.setText(this._data.text);
}
AbstractInput.prototype.clearData = function(){
  this._data = { text: "" };
}
AbstractInput.prototype.getText = function(){
  try {
    return this._data.text = this._input.value;
  } catch(ex) {
    return undefined;
  }
}
AbstractInput.prototype.setText = function(text){
  if (!text)
    text = "";
  try {
    this._data.text = this._input.value = text;
  } catch(ex) {
    return undefined;
  }
}
AbstractInput.prototype.setDisabled = function(state){
  this._disabled = state;
  if(this._disabled) {
    this._input.setAttribute("disabled", "true");
    this.addCSSClass("disabled");
  }
  else {
    this._input.removeAttribute("disabled");
    this.removeCSSClass("disabled");    
  }
}
AbstractInput.prototype.getDisabled = function(){
  return this._disabled;
}
AbstractInput.prototype.setFocus = function(selectAll){
  // doing this asynchronously seems to fix some focus issues:
  doLater(this,this._setFocus, undefined, [selectAll]);
}
AbstractInput.prototype._setFocus = function(selectAll){
  // this sometimes throws an "uncatchable" error in FF.
  // "This is apparently a bug in FireFox's AutoComplete code." - internet
  // can be fixed by setting the inputElement attribute: autocomplete="off"
  try{
    this._input.focus();
  }
  catch(e){
    Debug.warn(this.CLASS_NAME, "._setFocus:",e);
  }
  if (typeof selectAll != "undefined" && selectAll == true)
    this._input.select();
}
AbstractInput.prototype.checkTextChange = function(){
  var curText = this._data.text;
  var inputText;
  try { inputText = this._input.value; }
  catch (ex) { }
  if((typeof curText != "undefined" && curText != null && curText != inputText) ||
    (StringUtils.isEmpty(curText) && !StringUtils.isEmpty(inputText)))
    return true;
  else
    return false;
}
AbstractInput.prototype._onFocus = function(e){
  this.hasFocus = true;
  this._inputEventHandler.setInteractive(true);
  this.dispatchEvent({type:"onFocus", target: this, domEventObject:e});
  return true;
}
AbstractInput.prototype._onBlur = function(e){
  this.hasFocus = false;
  this._inputEventHandler.setInteractive(false);
  this.dispatchEvent({type:"onBlur", target: this, domEventObject:e});
  return true;
}
AbstractInput.prototype.onEnterKey = function(eventObject){
  this.dispatchEvent(eventObject);
}
AbstractInput.prototype.onEscapeKey = function(eventObject){
  this.dispatchEvent(eventObject);
}
AbstractInput.prototype.onTextChange = function(eventObject){
  // The value actually has changed:
  this._data.text = this._input.value;
  this.dispatchEvent({type:"onTextChange", target: this});
}
AbstractInput.prototype.getInputComponents = function(){
  return [this];
}
AbstractInput.prototype.getInputElement = function(){
  return this._input;
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AbstractItem</b>
 *
 * Abstract class:
 *
 * Subclasses must overwrite [AbstractItem.prototype._createTitle] if componentTitle exists
 * Subclasses must overwrite [AbstractItem.prototype._createDisplayContent]
 * Subclasses must overwrite [AbstractItem.prototype._destroyDisplayContent]
 *
 * @author alee@metaweb.com
 */
function AbstractItem(div){
  // member vars
  this._header;
  this._content;
  this._emptyMessageLabel;
  this._emptyMessage;
  this._loadingMessage;
  setupInheritance(AbstractItem);
  superConstructor(BaseComponent, this, [div]);
}
AbstractItem.superClass = [BaseComponent];
registerClass(AbstractItem, "AbstractItem");
AbstractItem.prototype.CLASS_NAME = "AbstractItem";
AbstractItem.prototype.CONTENT_CSSCLASS = "content";
AbstractItem.prototype.TOOLTIP_TEXT = "";
AbstractItem.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  
  // header and content
  this._header = this.addChildTag("div", null, null, "header");
  this._content = this.addChildTag("div", null, null, this.CONTENT_CSSCLASS);
  this._createTitle();
  
  var emptyMessage = this._body.getAttribute("emptyMessage");
  this._emptyMessage = (emptyMessage)?emptyMessage:"empty";
  var loadingMessage = this._body.getAttribute("loadingMessage");
  this._loadingMessage = (loadingMessage)?loadingMessage:"loading...";
}
AbstractItem.prototype.setData = function(data){
  this.clearData();
  if(!data) {
    return;
  }
    
  this._data = data;
  
  this._destroyDisplayContent();
  this._createDisplayContent(); // implementation should call this._updateEmptyMessage(loading);
}
AbstractItem.prototype._isEmpty = function(){
  // no this._data.
  return (!this._data);
};
AbstractItem.prototype.requestData = function(){
  if(this._body.getAttribute("dataProvider") || this._body.getAttribute("query")) {
    this._updateEmptyMessage(true);
  }
  return superMethod(BaseComponent, this, "requestData");
};
AbstractItem.prototype.setEmptyMessage = function(msg, update){
  this._emptyMessage = msg;
  if (typeof update != "undefined")
    this._updateEmptyMessage(update);
};
AbstractItem.prototype._createTitle = function(title){
  Debug.error("Subclasses must overwrite [AbstractItem.prototype._createTitle]");
}
AbstractItem.prototype._createDisplayContent = function()
{
  // Should typically call this._updateEmptyMessage(loading) - use loading=true if fetching LOB or loading=false if data set. 
  Debug.error("Subclasses must overwrite [AbstractItem.prototype._createDisplayContent]");
};
AbstractItem.prototype._destroyDisplayContent = function()
{
  Debug.error("Subclasses must overwrite [AbstractItem.prototype._destroyDisplayContent]");
};
AbstractItem.prototype._updateEmptyMessage = function(loading){
  this._destroyEmptyMessage();
  
  if(this._isEmpty()){
    if(!this._emptyMessageLabel){
      this._emptyMessageLabel = this.addChildComponent(Label, this._content, "span", 0, null, "emptyMessage");
      if (loading) 
        this._emptyMessageLabel.setText(this._loadingMessage);
      else
        this._emptyMessageLabel.setText(this._emptyMessage);
    }
    this.addCSSClass("empty");
  }
  else{
    this.removeCSSClass("empty");
  }
};
AbstractItem.prototype._destroyEmptyMessage = function(){
  if(this._emptyMessageLabel){
    this._emptyMessageLabel.destroy();
    this._emptyMessageLabel = null;
  }
}
/*
_data = {}
HTML:
<div class="AbstractItem">This is some text!</div>
<div class="AbstractItem"><a href="http://www.amazon.com">This will goto Amazon!</a></div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>TextAreaInput</b>
 *
 * @author alee@metaweb.com
 */
function TextAreaInput(div){
  setupInheritance(TextAreaInput);
  superConstructor(AbstractInput, this, [div]);
  
  // member vars
  this._rows;
  this._cols;
}
TextAreaInput.superClass = [AbstractInput];
registerClass(TextAreaInput, "TextAreaInput");
TextAreaInput.prototype.CLASS_NAME = "TextAreaInput";
TextAreaInput.ROWS = 15;
TextAreaInput.COLS = 60;
TextAreaInput.prototype._createInput = function(div) {
  var input = this.addChildTag("textarea");
  var title = this._body.getAttribute("componentTitle");
  if (title)
    input.setAttribute("name", title);
  
  var rows = this._body.getAttribute("rows");
  if (rows==null && (typeof this._rows == "undefined" || this._rows == null))
    this._rows = TextAreaInput.ROWS;
  else
    this._rows = rows;
  input.setAttribute("rows", this._rows);
  
  var cols = this._body.getAttribute("cols");
  if (cols==null && (typeof this._cols == "undefined" || this._cols == null))
    this._cols = TextAreaInput.COLS;
  else
    this._cols = cols;
  input.setAttribute("cols", this._cols);
  input.className = getClassName(this) + "-input";
  return input;
}
TextAreaInput.prototype.setDimensions = function(rows, cols) {
  if (typeof rows != "undefined" && rows!=null) {
    this._rows = rows;
    if (this._input) 
      this._input.setAttribute("rows", this._rows);
  }
  if (typeof cols != "undefined" && cols!=null) {
    this._cols = cols;
    if (this._input) 
      this._input.setAttribute("cols", this._cols);
  }
}
/*
DATA:
_data.text
HTML:
<div class="TextAreaInput">
  <textarea name="description" class="TextInputArea" cols=NUM_COLS rows=NUM_ROWS>default text here</textarea>
</div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ErrorFeedback</b>
 *
 * @author alee@metaweb.com
 */
/****************************************************** constructor
 */
function ErrorFeedback(div) {
  // member vars
  this._header;
  this._icon;
  this._synopsis;
  this._elaboration;
  this._details;
  this._expand;
  this._detailsExpandButton;
  this._emailChrome;
  this._emailInput;
  this._emailButton;
  this._shimDiv;
  
  setupInheritance(ErrorFeedback);
  superConstructor(BaseComponent, this, [div]);
};
ErrorFeedback.superClass = [BaseComponent]; 
registerClass(ErrorFeedback, "ErrorFeedback");
ErrorFeedback.prototype.CLASS_NAME = "ErrorFeedback";
ErrorFeedback.prototype.createDisplayContent = function() {
  var frag = document.createDocumentFragment();
  
  this._header = this.createTag("div", frag, null, null, "errorFeedbackHeader");
  
  // Header elements
  this._icon = this.createTag("div", this._header, null, null, "icon");
  this._synopsis = this.addChildComponent(Label, this._header, "div", null, null, "synopsis");
  this._synopsis.setText(this._data.synopsis);
  this._elaboration = this.addChildComponent(Label, this._header, "div", null, null, "elaboration");
  if (typeof this._data.elaboration != "undefined" && this._data.elaboration != null) 
    this._elaboration.setText(this._data.elaboration);
   
  // No content elements since there is no data
  if (typeof this._data.details == "undefined" || this._data.details == null) {
    this._body.appendChild(frag);
    return;
  }
  
  // Content elements: title (expand button and detailsTitle) and details
  this._content = this.createTag("div", frag, null, null, "errorFeedbackContent");
  this._title = this.createTag("div", this._content, null, null, "errorFeedbackContentTitle");
  // details section: detailsExpandButton, details, email comment
  this._detailsExpandButton = this.addChildComponent(Button, this._title, "button", null, null, "detailsExpandButton", [true]);
  this._detailsExpandButton.addEventListener("onClick", Delegate.create(this, this.onExpandButton));
  this._detailsTitle = this.addChildComponent(Label, this._title, "span", null, null, "detailsTitle");
  this._detailsTitle.setText("Error details");
  this._details = this.addChildComponent(Label, this._content, "div", null, null, "details", [true]);
  this._details.setText(this._data.details);
  // Email comment via TextAreaInput
  this._emailChrome = this.createTag("div", this._content, null, null, "emailChrome");
  this._emailChrome.innerHTML = "What were you doing when you received this error?";
  this._emailInput = this.addChildComponent(TextAreaInput, this._content, "div", null, null, "emailComment");
  this._emailInput.setDimensions(15, 10);
  this._emailButton = this.addChildComponent(Button, this._content, "button", null, null, "emailButton");
  this._emailButton.setText("Report this Error");
  this._emailButton.addEventListener("onClick", Delegate.create(this, this.onEmailReport));
  this._shimDiv = this.createTag("div", this._content, null, null, "errorShim");
 
  this._body.appendChild(frag);
  this.expand(false);
}
ErrorFeedback.prototype.destroyDisplayContent = function() {
  if (this._content) {
    this._removeEmailComponents();
    
    if (this._details) {
      this._details.destroy();
      delete this._details;
    }
    if (this._title) {
      if (this._detailsExpandButton) {
        this._detailsExpandButton.destroy();
        delete this._detailsExpandButton;
      }
      if (this._detailsTitle) {
        this._detailsTitle.destroy();
        delete this._detailsTitle;
      }
      this._title.parentNode.removeChild(this._title);
      delete this._title;
    }
    this._content.parentNode.removeChild(this._content);
    delete this._content;
  }
  if (this._header) {
    if (this._icon) {
      this._icon.parentNode.removeChild(this._icon);
      delete this._icon;
    }
    if (this._synopsis) {
      this._synopsis.destroy();
      delete this._synopsis;
    }
    if (this._elaboration){
      this._elaboration.destroy();
      delete this._elaboration;
    }
    this._header.parentNode.removeChild(this._header);
  }
}
ErrorFeedback.prototype._removeEmailComponents = function() {
  if (this._emailButton) {
    this._emailButton.destroy();
    delete this._emailButton;
  }
  if (this._emailInput) {
    this._emailInput.destroy();
    delete this._emailInput;
  }
  if (this._emailChrome) {
    this._emailChrome.parentNode.removeChild(this._emailChrome);
    delete this._emailChrome;
  }
  if (this._shimDiv) {
    this._shimDiv.parentNode.removeChild(this._shimDiv);
    delete this._shimDiv;
  }
  if (this._message) {
    this._message.destroy();
    delete this._message;
  }
}
ErrorFeedback.prototype.setData = function(data) {
  this.clearData();
  this._data = data;
  if (!this._data) return;
  this.destroyDisplayContent();
  this.createDisplayContent();
}
ErrorFeedback.prototype.expand = function(state){
  this._expand = state;
  if (!this._details || !this._detailsExpandButton) return;
  if(this._expand){
    this._detailsExpandButton.setLabel("-"); // show expanded:
    this.removeCSSClass("detailsContracted");
    this.addCSSClass("detailsExpanded");
  }
  else{
    this._detailsExpandButton.setLabel("+"); // show collapsed:
    this.removeCSSClass("detailsExpanded");
    this.addCSSClass("detailsContracted");
  }
}
ErrorFeedback.prototype.onExpandButton = function(e){
  this.expand(!this._expand);
  this._detailsExpandButton._body.blur();
};
ErrorFeedback.prototype.onEmailReport = function(e){
  this._emailButton.setDisabled(true);
  var emailData = this._emailInput.getData();
  var progress = new OverlayProgress(this._emailChrome, "Emailing..."); 
  var params = new Params();
  params.add('message', emailData.text);       
  params.extend('tid', g_transactionIdList);  
  params.add('error_response', this._data.details);  
  params = params.toQueryString();
  var sDP = service(Globals.ERRORBACK, params);
  sDP.service_error = Delegate.create(this, this.onEmailResponse, [progress, false]);  
  sDP.requestData(Delegate.create(this, this.onEmailResponse, [progress, true])); 
};
ErrorFeedback.prototype.onEmailResponse = function (progress, isSuccess, error) {
  // remove the progress and emailDialog and put up success/failure message.
  progress.hide();
  progress.destroy();
  delete progress;
  var message = (isSuccess)?"Thank you.  Your feedback has been recorded":"We're sorry.  There was a problem submitting your feedback";
  this.dispatchEvent({type:"onEmailSuccess", target: this, message: message});
}
function MakeErrorFeedback(div, msgData) {
  // helper function
  return new InlineFeedback(div, msgData, ErrorFeedback, "InlineErrorFeedback detailsContracted");
}
  
/*
Data:
{
  synopsis: "We're sorry, there was an error accessing the requested page",
  elaboration: "Either the specified page does not exist or there was an error connecting to the page",
  details: ""
};
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * ButtonLink class
 * alee@metaweb.com
 */
function ButtonLink(div){
  setupInheritance(ButtonLink);
  superConstructor(BaseComponent, this, [div]);
  
  // member vars
  this._disabled;
  this._icon;   // Button whose text is ignored and a background icon is used
  this._linkLabel;    // A Label with link
  this._clickHandler;
  
  this._mouseEventHandler;
}
ButtonLink.superClass = [BaseComponent];
registerClass(ButtonLink, "ButtonLink");
ButtonLink.prototype.CLASS_NAME = "ButtonLink";
ButtonLink.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  this.clearData();
  
  // labelElement and button
  this._iconButton = this.addChildComponent(Button, this._body, "button", null, null, this.CLASS_NAME+"Icon");
  this._linkLabel = this.addChildComponent(Label, this._body, "span", null, null, this.CLASS_NAME+"Label");
  // Listen for and pass on the following button events
  this._mouseEventHandler = new MouseEventHandler(this, this._body,
    MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT | MouseEventHandler.MOUSECLICK);
  this._clickHandler = Delegate.create(this, this.onButtonLinkClick);
  this._iconButton.addEventListener("onClick", this._clickHandler);
  
  var title = this._body.getAttribute("componentTitle");
  if (title) {
    this._data.text = title;
    this.setData(this._data);
  } 
}
ButtonLink.prototype.setDisabled = function(disabled){
  // disable/enable both _linkLabel and _popup button - function is misnamed for both these operations
  if (typeof disabled == "undefined")
    disabled = false;
  if (typeof this._disabled != "undefined" && this._disabled == disabled)
    return;
  
  if (this._linkLabel && this._data && this._data.url) {
    // link label
    if (disabled) 
      this._linkLabel.setData( {text: this._data.text });
    else
      // Put back the url
      this._linkLabel.setData(this._data);
  }
  if (disabled) {
    this.addCSSClass("disabled");
    DOMEvent.removeEventHandler(this._body, "click", this._clickHandler);
  }
  else {
    this.removeCSSClass("disabled");
    DOMEvent.addEventHandler(this._body, "click", this._clickHandler);
  }
  this._iconButton.setDisabled(disabled);
  this._disabled = disabled;
}
ButtonLink.prototype.getDisabled = function(disabled){
  return this._disabled;
}
ButtonLink.prototype.setData = function(data){
  this.clearData();
  this._data = data;
  this._linkLabel.setData(this._data);
  this.setDisabled(this._data.disabled);
}
ButtonLink.prototype.onButtonLinkClick = function(e){
  if (this._data.url)
    openLocation(this._data.url);
  this.dispatchEvent({type:"onButtonLinkClick", target: this});
}
ButtonLink.prototype.onMouseOver = function(e){
  if (isIE6) this.addCSSClass('ButtonLinkHover');
}
ButtonLink.prototype.onMouseOut = function(e){
  if (isIE6) this.removeCSSClass('ButtonLinkHover');
}
/*
this._data = {
  text:"item zero",
  url: "http://www.google.com   // Don't provide this, and list for onClick to do an action
  disabled: true
}
HTML:
<div class="ButtonLink">This is some text!</div>
<div class="ButtonLink"><a href="http://www.amazon.com">This will goto Amazon!</a></div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ButtonList</b>
 *
 * @author alee@metaweb.com
 */
function ButtonList(div){  
  setupInheritance(ButtonList);
  superConstructor(AbstractList, this, [div]);
};
ButtonList.superClass = [AbstractList];
registerClass(ButtonList, "ButtonList");
ButtonList.prototype.CLASS_NAME = "ButtonList";
ButtonList.prototype._createListItem = function(div) {
	return new ButtonListItem(div);
};
ButtonList.prototype._addListItem = function(itemData, beforeIndex, parent){
  if (typeof parent == "undefined" || parent == null) {
    parent = this._body;
  }
  var listItem;
  var div = this.createTag('li', parent, beforeIndex);
  var listItem = this._createListItem(div);
  if(typeof beforeIndex == "undefined" || beforeIndex == null)
    this._listItems.push(listItem);
  else
    this._listItems.splice(beforeIndex, 0, listItem);
  listItem.setData(itemData);  	
  if (itemData.disabled == false) {
    listItem.addEventListeners([
      "onListItemClick",
      "onListItemDblClick",
      "onMouseOver",
      "onMouseOut"
      ], this);
  }
  else {
    listItem.setDisabled(true);
  } 
  return listItem;
};
ButtonList.prototype.setDisabled = function(state) {
  if (this._disabled == state)
    return;
  this._disabled = state;
  if(this._disabled) {
    if(this._listItems){
      for(var i=0, len=this._listItems.length; i<len; i++){
        this._listItems[i].setDisabled(this._disabled);
        this._listItems[i].removeEventListeners([
          "onListItemClick", 
          "onListItemDblClick", 
          "onMouseOver", 
          "onMouseOut"
        ], this); 
      }
    }
    this.addCSSClass("disabled");
  }
  else {
    if(this._listItems){
      for(var i=0, len=this._listItems.length; i<len; i++){
        this._listItems[i].setDisabled(this._disabled);
        this._listItems[i].addEventListeners([
          "onListItemClick", 
          "onListItemDblClick",
          "onMouseOver", 
          "onMouseOut"
          ], this); 
      }
    }
    this.removeCSSClass("disabled");
  }
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>OverlayErrorFeedback</b>
 *
 * @author alee@metaweb.com
 */
/****************************************************** constructor
 */
function OverlayErrorFeedback(msgData, overlayElement, buttons) {
  setupInheritance(OverlayErrorFeedback);
  superConstructor(OverlayPrompt, this, [overlayElement, msgData, buttons]);
};
OverlayErrorFeedback.superClass = [OverlayPrompt];
registerClass(OverlayErrorFeedback, "OverlayErrorFeedback");
OverlayErrorFeedback.prototype.CLASS_NAME = "OverlayErrorFeedback";
OverlayErrorFeedback.prototype.createPromptContent = function(frag, feedbackClass){
  if (!feedbackClass)
    feedbackClass = ErrorFeedback;
  this._feedbackComponent = this.addChildComponent(feedbackClass, frag, "div", 0);
  this._feedbackComponent.addEventListener("onEmailSuccess", Delegate.create(this, this._onEmailSuccess));
  this._feedbackComponent.setData(this._msgData);
}
OverlayErrorFeedback.prototype._onEmailSuccess = function(eventObject) { 
  var body = this._feedbackComponent._body.parentNode;
  this._feedbackComponent.destroy();
  this._msgData = {synopsis:eventObject.message};
  this.createPromptContent(body, ConfirmationFeedback);
  this._feedbackComponent.addCSSClass("OverlaySuccessFeedback");
}
/*
 Data:
{
  synopsis: "We're sorry, there was a problem uploading your image",
  elaboration: "Please provide a GIF, PNG, or JPEG image",
  details: "JSON string of the JSON data structure with error details"
};
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>RadioButtonListItem</b>
 *
 * @author alee@metaweb.com
 */
function RadioButtonListItem(div){
  // member vars:
  this._radioButton;
  this._label;
  this._disabled;
  setupInheritance(RadioButtonListItem);
  superConstructor(AbstractListItem, this, [div]);
}
RadioButtonListItem.superClass = [AbstractListItem];
registerClass(RadioButtonListItem, "RadioButtonListItem");
RadioButtonListItem.prototype.CLASS_NAME = "RadioButtonListItem";
RadioButtonListItem.prototype.initInteractive = function(){
  this._mouseEventHandler = new MouseEventHandler(this, this._body,
    MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT);    // RadioButton will take care of onClick
  this._mouseEventHandler.setInteractive(true);
  this._selected = false;
};
RadioButtonListItem.prototype._createDisplayContent = function(){
  this._radioButton = this.addChildComponent(RadioButton, null, "span");
  this._radioButton.addEventListener("onClick", Delegate.create(this, this.onClick));
  this._radioButton.setData(this._data);
}
RadioButtonListItem.prototype._destroyDisplayContent = function()
{
  if (this._radioButton) {
    this._radioButton.destroy();
    delete this._radioButton;
  }
};
RadioButtonListItem.prototype.setText = function(text){
  if(!text) text = "";
  this._radioButton.setText(text);
  this._data.text = text;
}
RadioButtonListItem.prototype.setState = function(state){
  if (this._data && typeof this._data.checked != "undefined" && this._data.checked != null)
    this._data.checked = state;
  this._radioButton.setState(state);
}
RadioButtonListItem.prototype.getState = function(){
  return this._radioButton.getState();
}
RadioButtonListItem.prototype.setDisabled = function(state){
  if (this._disabled == state)
    return;
  this._disabled = state;
  if(this._disabled)
    this.addCSSClass("disabled");
  else
    this.removeCSSClass("disabled");
  // set the actual RadioButton disabled.
  this._radioButton.setDisabled(this._disabled);
}
RadioButtonListItem.prototype.setSelected = function(selected){
  // override to setState on the radiobutton respectively.
  if(selected == this._selected) return;
  this._selected = selected;
  if(this._selected) {
    this.addCSSClass("listItemSelected");
    this.setState(true);
  }
  else {
    this.removeCSSClass("listItemSelected");
    this.setState(false);
  }
};
/*
data = {
  label:"yes",
  name:"answer",
  value: 1
}
<span class="RadioButtonListItem">
  <input id="RB1356" type="radio" name="answer" value="1">
  <label id="RB1356" class="label">yes</label>
</span>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>OptionsList</b>
 *
 * This houses listitems whose contents include RadioButtons, Checkboxes, etc.
 * in which one wants to toggle/set/unset the input item's checked property.  A GOTCHA
 * issue with IE as documented:
 *    http://cf-bill.blogspot.com/2006/02/dom-gotchas.html
 *    IE's rendering doesn't care what I do to the checkbox before I insert it into the document. 
 *    IE only reflects those changes made after the insertion - so the order of events be appendChild
 *    and then checkbox.checked = true.
 * 
 * So, this class creates its list and list items directly onto the  document's DOM.  A subclass
 * component that uses this is RadioButtonList.  If this gets used inside another component (e.g., 
 * SchemaEditor's PropertyListItem), that component should either:
 *   a) create its element directly onto the DOM
 *   b) do a setData afterwards to set its items data.
 *
 * @author alee@metaweb.com
 */
function OptionsList(div, elementTagName){
  setupInheritance(OptionsList);
  superConstructor(AbstractList, this, [div, elementTagName]);
};
OptionsList.superClass = [AbstractList];
registerClass(OptionsList, "OptionsList");
OptionsList.prototype.CLASS_NAME = "OptionsList";
OptionsList.prototype.setData = function(data){
  this.clearData();
  
  this._data = data;
  
  // create all listitem directly onto the document's DOM - don't use a document fragment
  this._addListItems(this._body);  
  
  // mark the first and last item
  this._cssUpdate();
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>Toolbar</b>
 *
 * Abstract class:
 *
 * @author alee@metaweb.com
 */
function Toolbar(div){  
  // member vars
  this._list;
  this._onButtonSelectHandler;
  
  setupInheritance(Toolbar);
  superConstructor(BaseComponent, this, [div]);
};
Toolbar.superClass = [BaseComponent];
registerClass(Toolbar, "Toolbar");
Toolbar.prototype.CLASS_NAME = "Toolbar";
Toolbar.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
  
  var frag = document.createDocumentFragment();
  
  this._list = this.addChildComponent(ButtonList, frag, "ul");
  
  this._body.appendChild(frag);
}
Toolbar.prototype.setData = function(data){
  if (!data || data.listItems.length<=0) {
    this._data = {listItems:[]};
    return;
  }
  this._data = data;
  var others = [];
  var listData = {listItems:[]};
  this._list.setData(listData);
  for(var i=0, len=this._data.listItems.length; i<len; i++){
    var listItemData = this._data.listItems[i];
    if(typeof listItemData.text=="undefined") 
      others.push({position: i, data: null});
    else {
      if (typeof listItemData.disabled == "undefined")
        // make sure disabled is set to false as this is default of Toolbar
        listItemData.disabled = false;
      
      var listItem = this._list.addListItem(listItemData);
      if (typeof listItemData.cssClass != "undefined" && listItemData.cssClass != null)
        listItem.addCSSClass(listItemData.cssClass)
      if (listItemData.disabled == true) {
        listItem.setInteractive(false);
        listItem.setDisabled(true);
        listItem.addCSSClass("disabled");
      }
    }
  }
  for(var i=0, len=others.length; i<len; i++){
    // TODO fix here
    this._list.addListItemComponent(Separator, others[i].data, others[i].position, [true]);
  }
  this._list.addEventListener("onListItemClick", Delegate.create(this, this.onButtonSelect));
}
Toolbar.prototype.onButtonSelect = function(eventObject){
  this.dispatchEvent({type: "onButtonSelect", target: this, targetItem: eventObject.targetItem, targetItemIndex: eventObject.targetItemIndex, command: eventObject.targetItem.getValue()});
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * Separator class
 * mark@metaweb.com
 *
 * This component is used as the menu item separator
 * in the PopupMenu class, its main function is to create
 * a separator element that consumes mouse clicks so the
 * popup menu doesn't disappear if the separator is clicked
 */
function Separator(div, isVerticalRule){
  // member vars:
  this._mouseEventHandler;
  this._separator;
  this._isVerticalRule = false;
  if (typeof isVerticalRule != "undefined" && isVerticalRule == true)
    this._isVerticalRule = true;
  setupInheritance(Separator);
  superConstructor(BaseComponent, this, [div]);
}
Separator.superClass = [BaseComponent];
registerClass(Separator, "Separator");
Separator.prototype.CLASS_NAME = "Separator";
Separator.prototype.init = function(){
  this.changeTag("li");
  this.addCSSClass(this.CLASS_NAME);
  if (this._isVerticalRule) {
    this._separator = this.createTag("span", this._body);
    this._separator.innerHTML = "|";
  }
  else
    this._separator = this.createTag("hr", this._body);
  DOMEvent.addEventHandler(this._body, "click", Separator.consumeClickEvent);
}
Separator.consumeClickEvent = function(eventObject){
  DOMEvent.stopPropagation(eventObject);
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>InputEventHandler</b>
 *
 * @author alee@metaweb.com
 *
 * Owner (e.g., SomeComponent) must define event handlers for the enableEvents.
 * e.g., for InputEventHandler.ENTERKEY
 *
 * SomeComponent.prototype.onEnterKey = function(e){
 * // handle event here
 * }
 *
 * Owner that enable InputEventHandler.TEXTCHANGE event must, in addition, define
 * SomeComponent.prototype.checkTextChange() = function {
 * // Return boolean for whether value changed or not
 * }
 */
function InputEventHandler(owner, htmlElement, enableEvents){
  // member vars:
  this._interactive = false;
  this._owner = owner;
  this._body = htmlElement;
  this._inputEventHandlers;
  this._checkMousePasteTimer;
  if (enableEvents == undefined)
    this._events = InputEventHandler.ENTERKEY | InputEventHandler.ESCAPEKEY | InputEventHandler.TEXTCHANGE | InputEventHandler.MOUSEPASTE;
  else 
    this._events = enableEvents;
    
  InputEventHandler._heap.push(this);
}
InputEventHandler.ENTERKEY = 1;
InputEventHandler.ESCAPEKEY = 2;
InputEventHandler.KEYUP = 4;
InputEventHandler.KEYDOWN = 8;
InputEventHandler.KEYPRESS = 16;
InputEventHandler.TEXTCHANGE = 32;
InputEventHandler.MOUSEPASTE = 64;
InputEventHandler.prototype.setInteractive = function(state){
  if(state == this._interactive) return;
  this._interactive = state;
  if(this._interactive) this._enterState_interactive();
  else this._exitState_interactive();
}
InputEventHandler.prototype._enterState_interactive = function(){
  this._inputEventHandlers = new Object();
  var myself = this;
  
  // key events:
  // * onKeyPress seems to act differently between IE and FF
  //   (tab/arrow keys fire off this event in FF but not IE)
  //   recommendation: don't use it
  // * onKeyDown happens before the input's text is updated
  // * onKeyUp happens after the input's text is updated
  // * the onChange event is fired only after focus blur
  if ((this._events & InputEventHandler.KEYDOWN) == InputEventHandler.KEYDOWN) {
    // onKeyDown
    this._inputEventHandlers.onKeyDown = Delegate.create(this, this._onKeyDown);
    DOMEvent.addEventHandler(this._body, "keydown", this._inputEventHandlers.onKeyDown);
  }
  if ((this._events & InputEventHandler.ENTERKEY) == InputEventHandler.ENTERKEY ||
      (this._events & InputEventHandler.ESCAPEKEY) == InputEventHandler.ESCAPEKEY ||
      (this._events & InputEventHandler.KEYUP) == InputEventHandler.KEYUP ||
      (this._events & InputEventHandler.TEXTCHANGE) == InputEventHandler.TEXTCHANGE) {
    // onKeyUp
    this._inputEventHandlers.onKeyUp = Delegate.create(this, this._onKeyUp);
    DOMEvent.addEventHandler(this._body, "keyup", this._inputEventHandlers.onKeyUp);
  }
  
  if ((this._events & InputEventHandler.KEYPRESS) == InputEventHandler.KEYPRESS) {
    // onKeyPress
    this._inputEventHandlers.onKeyPress = Delegate.create(this, this._onKeyPress);
    DOMEvent.addEventHandler(this._body, "keypress", this._inputEventHandlers.onKeyPress);
  }
  
  // This is a kludge to do mouse paste (which is not the same key press ctrl+v)
  // As noted above: could listen for onChange but it does not happen until focus blur
  // Only IE has clipboard events - other browsers don't.  So, this kludge is cross-browser.
  // Small price to check every fifth of second until a paste happens or key press happened 
  // and a fifth of second is not too long to wait if paste happens.
  if ((this._events & InputEventHandler.MOUSEPASTE) == InputEventHandler.MOUSEPASTE) {
    // onMouseUp
    this._inputEventHandlers.onMouseUp = Delegate.create(this, this._onMouseUp);
    DOMEvent.addEventHandler(this._body, "mouseup", this._inputEventHandlers.onMouseUp);
  }
}
InputEventHandler.prototype._exitState_interactive = function(){
  this._clearTimer();
    
  if(this._inputEventHandlers){
    if ((this._events & InputEventHandler.KEYDOWN) == InputEventHandler.KEYDOWN) {
      // onKeyDown
      DOMEvent.removeEventHandler(this._body, "keydown", this._inputEventHandlers.onKeyDown);
      Delegate.destroy(this._inputEventHandlers.onKeyDown);
      this._inputEventHandlers.onKeyDown = null;
    }
    
    if ((this._events & InputEventHandler.ENTERKEY) == InputEventHandler.ENTERKEY ||
        (this._events & InputEventHandler.ESCAPEKEY) == InputEventHandler.ESCAPEKEY ||
        (this._events & InputEventHandler.KEYUP) == InputEventHandler.KEYUP ||
        (this._events & InputEventHandler.TEXTCHANGE) == InputEventHandler.TEXTCHANGE) {
      // onKeyUp
      DOMEvent.removeEventHandler(this._body, "keyup", this._inputEventHandlers.onKeyUp);
      Delegate.destroy(this._inputEventHandlers.onKeyUp);
      this._inputEventHandlers.onKeyUp = null;
    }
    
    if ((this._events & InputEventHandler.KEYPRESS) == InputEventHandler.KEYPRESS) {
      // onKeyPress
      DOMEvent.removeEventHandler(this._body, "keypress", this._inputEventHandlers.onKeyPress);
      Delegate.destroy(this._inputEventHandlers.onKeyPress);
      this._inputEventHandlers.onKeyPress = null;
    }
  
    if ((this._events & InputEventHandler.MOUSEPASTE) == InputEventHandler.MOUSEPASTE) {
      // onMouseUp
      DOMEvent.removeEventHandler(this._body, "mouseup", this._inputEventHandlers.onMouseUp);
      Delegate.destroy(this._inputEventHandlers.onMouseUp);
      this._inputEventHandlers.onMouseUp = null;
    }
  }
  this._inputEventHandlers = null;
  
  this._clearTimer();   // remove _checkMousePasteTimer if it is running.
}
InputEventHandler.prototype._clearTimer = function(e){
  if (this._checkMousePasteTimer){
    clearTimeout(this._checkMousePasteTimer);
    this._checkMousePasteTimer = null;
  }
}
InputEventHandler.prototype._onKeyUp = function(e){
  try {
    this._clearTimer();
    
    // enterKey
    if (e.keyCode == 13) {
      if (((this._events & InputEventHandler.ENTERKEY) == InputEventHandler.ENTERKEY) &&
           typeof this._owner.onEnterKey!="undefined")
        this._owner.onEnterKey({type:"onEnterKey", target: this._owner, domEventObject:e});
    }
    
    // escapeKey
    if (e.keyCode == 27) {
      if (((this._events & InputEventHandler.ESCAPEKEY) == InputEventHandler.ESCAPEKEY) &&
           typeof this._owner.onEscapeKey!="undefined") {
        this._owner.onEscapeKey({type:"onEscapeKey", target: this._owner, domEventObject:e});
      }
    }
    
    // check for TextChange
    if ((this._events & InputEventHandler.TEXTCHANGE) == InputEventHandler.TEXTCHANGE)
      this._onTextChange(e);
    
    // keyUp
    if (((this._events & InputEventHandler.KEYUP) == InputEventHandler.KEYUP) &&
          typeof this._owner.onKeyUp!="undefined")
      this._owner.onKeyUp({type:"onKeyUp", target: this._owner, keyCode:e.keyCode, domEventObject:e});
  }
  catch(ex){
        // Don't do anything. This catches a FireFox error stating that
        // DOMEvent is not defined. This happens when navigating to another
        // page, FireFox kills the JS session, but these event handlers are still
        // somehow active.
  }
}
InputEventHandler.prototype._onKeyDown = function(e){
  try {
    this._clearTimer();
    
    if (((this._events & InputEventHandler.KEYDOWN) == InputEventHandler.KEYDOWN) &&
          typeof this._owner.onKeyDown!="undefined")
      this._owner.onKeyDown({type:"onKeyDown", target: this._owner, keyCode:e.keyCode, domEventObject:e});
  }
  catch(ex){
        // Don't do anything. This catches a FireFox error stating that
        // DOMEvent is not defined. This happens when navigating to another
        // page, FireFox kills the JS session, but these event handlers are still
        // somehow active.
  }
}
InputEventHandler.prototype._onKeyPress = function(e){
  try {
    this._clearTimer();
    
    if (((this._events & InputEventHandler.KEYPRESS) == InputEventHandler.KEYPRESS) &&
          typeof this._owner.onKeyPress!="undefined")
      this._owner.onKeyPress({type:"onKeyPress", target: this._owner, keyCode:e.keyCode, domEventObject:e});
  }
  catch(ex){
        // Don't do anything. This catches a FireFox error stating that
        // DOMEvent is not defined. This happens when navigating to another
        // page, FireFox kills the JS session, but these event handlers are still
        // somehow active.
  }
}
InputEventHandler.prototype._onMouseUp = function(e){
  try {
    this._clearTimer();
    if (e.ctrlKey == true || e.button==1)
      this._checkMousePasteTimer = doLater(this, this._checkMousePaste, 200);
  }
  catch(ex){
        // Don't do anything. This catches a FireFox error stating that
        // DOMEvent is not defined. This happens when navigating to another
        // page, FireFox kills the JS session, but these event handlers are still
        // somehow active.
  }
}
InputEventHandler.prototype._onTextChange = function(e){
  if (this._checkTextChange()){
    if (this._owner.onTextChange!="undefined") {
      this._owner.onTextChange({type:"onTextChange", target: this._owner, domEventObject:e});
      return true;
    }
  }
  return false;
}
InputEventHandler.prototype._checkTextChange = function(){
  if (this._owner.checkTextChange != "undefined")
    return this._owner.checkTextChange();
  else {
    Debug.error("InputEventHandler._checkTextChange:", this._owner.CLASS_NAME, "requests notification of TEXTCHANGE event but did not define checkTextChange method.");
    return false;
  }
}
InputEventHandler.prototype._checkMousePaste = function(){
  this._clearTimer();
  if (!this._onTextChange()) 
    // so, far no mouse paste changed the text: keep checking
    this._checkMousePasteTimer = doLater(this, this._checkMousePaste, 200);
}
InputEventHandler._heap = [];
InputEventHandler._gc = function() {
  for(var i=0, len=InputEventHandler._heap.length; i<len; i++) {
    if (InputEventHandler._heap[i]){
      InputEventHandler._heap[i]._owner = null;
      InputEventHandler._heap[i]._body = null;
    }
   	delete InputEventHandler._heap[i];
  }
  delete InputEventHandler._heap;
};
addWindowOnunloadHandler(InputEventHandler._gc);
/*
use example:
  //----------------------------------------------------- init
  SomeComponent.prototype.init = function(){
    this._inputEventHandler = new InputEventHandler(this, this._body);
    this._inputEventHandler.setInteractive(true);
  }
  //----------------------------------------------------- onEnterKey
  SomeComponent.prototype.onEnterKey = function(e){
    // handle event here
  }
  //----------------------------------------------------- onEscapeKey
  SomeComponent.prototype.onEscapeKey = function(e){
    // handle event here
  }
  //----------------------------------------------------- destroy
  // note: this may not be necessary...
  SomeComponent.prototype.destroy = function(){
    this._inputEventHandler.setInteractive(false);
    superMethod(BaseComponent, this, "destroy");
  }
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * EventDispatcher class
 * mark@metaweb.com
 */
function EventDispatcher() { };
EventDispatcher.prototype.destroy = function() {
  delete this._eventListeners;
};
EventDispatcher.prototype.addEventListener = function(type, listener)
{
  if (this._eventListeners == undefined)
    this._eventListeners = new Object();
  if (this._eventListeners[type] == undefined)
    this._eventListeners[type] = new Array();
  this._eventListeners[type][this._eventListeners[type].length] = listener;
  
  EventDispatcher._heap.push(listener);
};
EventDispatcher.prototype.addEventListeners = function(types, listener)
{
  for (var i=0; i<types.length; i++)
    this.addEventListener(types[i], listener);
}
EventDispatcher.prototype.removeEventListeners = function(types, listener)
{
  for (var i=0; i<types.length; i++)
    this.removeEventListener(types[i], listener);
}
EventDispatcher.prototype.chainEvent = function(type, receiver, retarget)
{
    function redispatch(eventObject) {
        if (retarget)
            eventObject.target = receiver;
        receiver.dispatchEvent(eventObject);
    }
    // remember the dispatcher so we can unhook it later
    redispatch.target_dispatcher = this;
    this.addEventListener(type, redispatch);
};
EventDispatcher.prototype.unchainEvent = function(type, receiver)
{
  if (!(this._eventListeners == undefined ||
        this._eventListeners[type] == undefined))
  {
    var tmp = new Array();
    for (var i=0; i<this._eventListeners[type].length; i++)
    {
      if (this._eventListeners[type][i].target_dispatcher != receiver)
        tmp[tmp.length] = this._eventListeners[type][i];
      else 
          // clear the dispatcher to avoid leaks
          this._eventListeners[type][i].target_dispatcher = null;
    }
    this._eventListeners[type] = tmp;
  }
};
EventDispatcher.prototype.removeEventListener = function(type, listener)
{
  if (!(this._eventListeners == undefined ||
        this._eventListeners[type] == undefined))
  {
    var tmp = new Array();
    for (var i=0; i<this._eventListeners[type].length; i++)
    {
      if (this._eventListeners[type][i] != listener)
        tmp[tmp.length] = this._eventListeners[type][i];
    }
    this._eventListeners[type] = tmp;
  }
}
EventDispatcher.prototype.dispatchEvent = function(eventObject, extraObject)
{
  if (typeof eventObject == "string") {
    if (extraObject) {
      // extra parameters passed, use them
      // note: this mutates the eventObject. This should be fine
      // because eventObjects should be transitory!
      var eventType = eventObject;
      eventObject = extraObject;
      eventObject.type = eventType;
    } else {
      // just make a small raw object
      eventObject = {type: eventObject}
    }
  }
  
  if(!eventObject.target) 
    eventObject.target = this;
  /*
   * Use local variable (listeners) to point to list of listeners so that all listeners receive this event.
   * Changing the current approach of destructive replacement of this._eventListeners[eventObject.type]
   * to in place modification will require keeping a copy of the listeners to start so that all current
   * listeners receive this event.
   * NOTE: If the listeners add additional listeners for this event, they will not be notified this time.
   * Similarly, if the listener removes one of the other listeners, that other listener still gets
   * notified this time but not subsequently.
   */ 
  if (!(this._eventListeners == undefined ||
        this._eventListeners[eventObject.type] == undefined))
  {
    var listeners = this._eventListeners[eventObject.type];
    for (var i=0, len=listeners.length; i<len; i++)
    {
      var listener = listeners[i];
      if (typeof listener == "object" && listener[eventObject.type])
        listener[eventObject.type].call(listener, eventObject);
      else if (typeof listener == "function")
        listener.call(null, eventObject);
    }
  }
}
EventDispatcher._heap = [];
EventDispatcher._gc = function() {
  for(var i=0, len=EventDispatcher._heap.length; i<len; i++) {
	  delete EventDispatcher._heap[i];
  }
  delete EventDispatcher._heap;
};
addWindowOnunloadHandler(EventDispatcher._gc);
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * MouseEventHandler class
 * mark@apmindsf.com
 */
function MouseEventHandler(owner, htmlElement, enableEvents){
  // member vars:
  this._interactive = false;
  this._owner = owner;
  this._body = htmlElement;
  this._mouseEventHandlers;
  if (enableEvents == undefined)
    this._events = MouseEventHandler.MOUSEOVER | MouseEventHandler.MOUSEOUT | MouseEventHandler.MOUSECLICK | MouseEventHandler.MOUSEDBLCLICK;
  else 
    this._events = enableEvents;
  MouseEventHandler._heap.push(this);
}
MouseEventHandler.MOUSEOVER = 1;
MouseEventHandler.MOUSEOUT = 2;
MouseEventHandler.MOUSEMOVE = 4;
MouseEventHandler.MOUSEDOWN = 8;
MouseEventHandler.MOUSEUP = 16;
MouseEventHandler.MOUSECLICK = 32;
MouseEventHandler.MOUSEDBLCLICK = 64;
MouseEventHandler.prototype.setInteractive = function(state){
  if(state == this._interactive) return;
  this._interactive = state;
  if(this._interactive) this._enterState_interactive();
  else this._exitState_interactive();
}
MouseEventHandler.prototype._enterState_interactive = function(){
  this._mouseEventHandlers = new Object();
  var myself = this;
  if ((this._events & MouseEventHandler.MOUSEMOVE) == MouseEventHandler.MOUSEMOVE) {
    // onMouseMove
    this._mouseEventHandlers.onMouseMove = Delegate.create(this, this.onMouseMove);
    DOMEvent.addEventHandler(this._body, "mousemove", this._mouseEventHandlers.onMouseMove);
  }
  if ((this._events & MouseEventHandler.MOUSEOVER) == MouseEventHandler.MOUSEOVER) {
    // onMouseOver
    this._mouseEventHandlers.onMouseOver = Delegate.create(this, this.onMouseOver);
    DOMEvent.addEventHandler(this._body, "mouseover", this._mouseEventHandlers.onMouseOver);
  }
  if ((this._events & MouseEventHandler.MOUSEOUT) == MouseEventHandler.MOUSEOUT) {
    // onMouseOut
    this._mouseEventHandlers.onMouseOut = Delegate.create(this, this.onMouseOut);
    DOMEvent.addEventHandler(this._body, "mouseout", this._mouseEventHandlers.onMouseOut);
  }
  if ((this._events & MouseEventHandler.MOUSEDOWN) == MouseEventHandler.MOUSEDOWN) {
    // onMouseDown
    this._mouseEventHandlers.onMouseDown = Delegate.create(this, this.onMouseDown);
    DOMEvent.addEventHandler(this._body, "mousedown", this._mouseEventHandlers.onMouseDown);
  }
  if ((this._events & MouseEventHandler.MOUSEUP) == MouseEventHandler.MOUSEUP) {
    // onMouseUp
    this._mouseEventHandlers.onMouseUp = Delegate.create(this, this.onMouseUp);
    DOMEvent.addEventHandler(this._body, "mouseup", this._mouseEventHandlers.onMouseUp);
  }
  if ((this._events & MouseEventHandler.MOUSECLICK) == MouseEventHandler.MOUSECLICK) {
    // onClick
    this._mouseEventHandlers.onClick = Delegate.create(this, this.onClick);
    DOMEvent.addEventHandler(this._body, "click", this._mouseEventHandlers.onClick);
  }
  if ((this._events & MouseEventHandler.MOUSEDBLCLICK) == MouseEventHandler.MOUSEDBLCLICK) {
    // onDblClick
    this._mouseEventHandlers.onDblClick = Delegate.create(this, this.onDblClick);
    DOMEvent.addEventHandler(this._body, "dblclick", this._mouseEventHandlers.onDblClick);
  }
}
MouseEventHandler.prototype._exitState_interactive = function(){
  if(this._mouseEventHandlers){
    if ((this._events & MouseEventHandler.MOUSEMOVE) == MouseEventHandler.MOUSEMOVE) {
      // onMouseMove
      DOMEvent.removeEventHandler(this._body, "mousemove", this._mouseEventHandlers.onMouseMove);
      Delegate.destroy(this._mouseEventHandlers.onMouseMove);
      this._mouseEventHandlers.onMouseMove = null;
    }
    if ((this._events & MouseEventHandler.MOUSEOVER) == MouseEventHandler.MOUSEOVER) {
      // onMouseOver
      DOMEvent.removeEventHandler(this._body, "mouseover", this._mouseEventHandlers.onMouseOver);
      Delegate.destroy(this._mouseEventHandlers.onMouseOver);
      this._mouseEventHandlers.onMouseOver = null;
    }
    if ((this._events & MouseEventHandler.MOUSEOUT) == MouseEventHandler.MOUSEOUT) {
      // onMouseOut
      DOMEvent.removeEventHandler(this._body, "mouseout", this._mouseEventHandlers.onMouseOut);
      Delegate.destroy(this._mouseEventHandlers.onMouseOut);
      this._mouseEventHandlers.onMouseOut = null;
    }
    if ((this._events & MouseEventHandler.MOUSEDOWN) == MouseEventHandler.MOUSEDOWN) {
      // onMouseDown
      DOMEvent.removeEventHandler(this._body, "mousedown", this._mouseEventHandlers.onMouseDown);
      Delegate.destroy(this._mouseEventHandlers.onMouseDown);
      this._mouseEventHandlers.onMouseDown = null;
    }
    if ((this._events & MouseEventHandler.MOUSEUP) == MouseEventHandler.MOUSEUP) {
      // onMouseUp
      DOMEvent.removeEventHandler(this._body, "mouseup", this._mouseEventHandlers.onMouseUp);
      Delegate.destroy(this._mouseEventHandlers.onMouseUp);      
      this._mouseEventHandlers.onMouseUp = null;
    }
    if ((this._events & MouseEventHandler.MOUSECLICK) == MouseEventHandler.MOUSECLICK) {
      // onClick
      DOMEvent.removeEventHandler(this._body, "click", this._mouseEventHandlers.onClick);
      Delegate.destroy(this._mouseEventHandlers.onClick);          
      this._mouseEventHandlers.onClick = null;
    }
    if ((this._events & MouseEventHandler.MOUSEDBLCLICK) == MouseEventHandler.MOUSEDBLCLICK) {
      // onDblClick
      DOMEvent.removeEventHandler(this._body, "dblclick", this._mouseEventHandlers.onDblClick);
      Delegate.destroy(this._mouseEventHandlers.onDblClick);    
      this._mouseEventHandlers.onDblClick = null;
    }
  }
  this._mouseEventHandlers = null;
}
MouseEventHandler.prototype.onMouseMove = function(e){
  try {
    if (this._owner.onMouseMove!=undefined) {
      this._owner.onMouseMove(e);
    }
  } 
  catch(e) {
    // Don't do anything. This catches a FireFox error stating that
    // DOMEvent is not defined. This happens when navigating to another
    // page, FireFox kills the JS session, but these event handlers are still
    // somehow active.
  }
};
MouseEventHandler.prototype.onMouseOver = function(e){
  try {
    if (this._owner.onMouseOver!=undefined) {
      this._owner.onMouseOver(e);
    }
  }
  catch(e) {
    // see above
  }
};
MouseEventHandler.prototype.onMouseOut = function(e){
  try {
    if (this._owner.onMouseOut!=undefined) {
      this._owner.onMouseOut(e);
    }
  }
  catch(e) {
    // see above
  }
};
MouseEventHandler.prototype.onMouseDown = function(e){
  try {
    if (this._owner.onMouseDown!=undefined) {
      this._owner.onMouseDown(e);
    }
  }
  catch(e) {
    // see above
  }
};
MouseEventHandler.prototype.onMouseUp = function(e){
  try {
    if (this._owner.onMouseUp!=undefined) {
      this._owner.onMouseUp(e);
    }
  }
  catch(e) {
    // see above
  }
};
MouseEventHandler.prototype.onClick = function(e){
  try {
    if (this._owner.onClick!=undefined) {
      this._owner.onClick(e);
    }
  }
  catch(e) {
    // see above
  }
};
MouseEventHandler.prototype.onDblClick = function(e){
  try {
    if (this._owner.onDblClick!=undefined) {
      this._owner.onDblClick(e);
    }
  }
  catch(e) {
    // see above
  }
};
MouseEventHandler._heap = [];
MouseEventHandler._gc = function() {
  for(var i=0, len=MouseEventHandler._heap.length; i<len; i++) {
    if (MouseEventHandler._heap[i]) {
      MouseEventHandler._heap[i]._owner = null;
      MouseEventHandler._heap[i]._body = null;
    }
   	delete MouseEventHandler._heap[i];
  }
  delete MouseEventHandler._heap;
};
addWindowOnunloadHandler(MouseEventHandler._gc);
/*
use example:
  //----------------------------------------------------- init
  SomeComponent.prototype.init = function(){
    this._mouseEventHandler = new MouseEventHandler(this, this._body);
    this._mouseEventHandler.setInteractive(true);
  }
  //----------------------------------------------------- onMouseOver
  SomeComponent.prototype.onMouseOver = function(e){
    // handle event here
  }
  //----------------------------------------------------- onMouseOut
  SomeComponent.prototype.onMouseOut = function(e){
    // handle event here
  }
  //----------------------------------------------------- destroy
  SomeComponent.prototype.destroy = function(){
    this._mouseEventHandler.setInteractive(false);
    this._mouseEventHandler = null;
    superMethod(BaseComponent, this, "destroy");
  }
*/
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * DOMEvent class
 */
function DOMEvent(){}
DOMEvent.addEventHandler = NO_OP;
DOMEvent.removeEventHandler = NO_OP;
DOMEvent.preventDefault = NO_OP;
DOMEvent.stopPropagation = NO_OP;
if (document.addEventListener) {
  DOMEvent.addEventHandler = function(target, eventType, handler) {
    if (target&&eventType&&handler) {
      target.addEventListener(eventType, handler, false);
      DOMEvent._heap.push([target, eventType, handler]);
    }
  };
}
else if (document.attachEvent) {
  DOMEvent.addEventHandler = function(target, eventType, handler) {
    if (target&&eventType&&handler) {
      target.attachEvent("on" + eventType, handler);
      DOMEvent._heap.push([target, eventType, handler]);
    }
  };
}
if (document.removeEventListener) {
  DOMEvent.removeEventHandler = function(target, eventType, handler) {
    if (target&&eventType&&handler) {
      target.removeEventListener(eventType, handler, false);
    }
  };
}
else if (document.attachEvent) {
  DOMEvent.removeEventHandler = function(target, eventType, handler) {
    if (target&&eventType&&handler) {
      target.detachEvent("on" + eventType, handler);
    }
  };
}
if (isIE) {
  DOMEvent.preventDefault = function(domEvent) {
    if (domEvent) domEvent.returnValue = false;
  };
}
else {
  DOMEvent.preventDefault = function(domEvent) {
    if (domEvent) domEvent.preventDefault();
  };  
}
if (isIE) {
  DOMEvent.stopPropagation = function(domEvent) {
    if (domEvent) domEvent.cancelBubble = true;
  };
}
else {
  DOMEvent.stopPropagation = function(domEvent) {
    if (domEvent) domEvent.stopPropagation();
  };
}
DOMEvent._heap = [];
DOMEvent._gc = function() {
  for(var i=0, len=DOMEvent._heap.length; i<len; i++) {
    DOMEvent.removeEventHandler(DOMEvent._heap[i][0], DOMEvent._heap[i][1], DOMEvent._heap[i][2]);
    DOMEvent._heap[i][0] = null;
    delete DOMEvent._heap[i][1];
    delete DOMEvent._heap[i][2];
  	delete DOMEvent._heap[i];
  }
  delete DOMEvent._heap;
};
addWindowOnunloadHandler(DOMEvent._gc);
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AutocompleteListItem</b>
 *
 * @author daepark@apmindsf.com
 */
function AutocompleteListItem(div){
  this._label;
  this._typesDiv;
  this._typeLabels = [];
  this._propertiesDiv;
  this._propertyLabels = [];
  setupInheritance(AutocompleteListItem);
  superConstructor(AbstractListItem, this, [div]);
};
AutocompleteListItem.superClass = [AbstractListItem];
registerClass(AutocompleteListItem, "AutocompleteListItem");
AutocompleteListItem.prototype.CLASS_NAME = "AutocompleteListItem";
AutocompleteListItem.prototype._getName = function(obj) {
  // backwards compatibility with data.text and data.name  
  if ("text" in obj) return obj.text;
  else if ("name" in obj) return obj.name;
  return null;
};
AutocompleteListItem.prototype._createDisplayContent = function() {
  var displayName = this._getName(this._data);
  var id = this._data.id;
  switch(id) {
    case AutocompleteManager.UID_CREATE_NEW:
    case AutocompleteManager.UID_NO_MATCH:
      break;
    default:
      if (displayName.length > 20) {
     //   displayName = displayName.substring(0,20) + "...";
      }
      break;
  };
  var types = [];
  var typesAndDomains = [this._data.type, this._data.domain];
  for(var i=0, len=typesAndDomains.length; i<len; i++) {
    if (typesAndDomains[i]) {
      for(var j=0, len2=typesAndDomains[i].length; j<len2; j++) {
        var type = null;
        if (typeof typesAndDomains[i][j] == "string")
          type = typesAndDomains[i][j];
  	    else if (typeof typesAndDomains[i][j] == "object")
  	      type = this._getName(typesAndDomains[i][j])
  	    if (type && type.toLowerCase() != "topic") {
  	      types.push(type);
        }  	  	  
  	  }
  	}
  }
  types = types.join(", ");
  
  var aliases = [];
  if (this._data.alias) {
    for(var i=0, len=this._data.alias.length; i<len; i++) {
      aliases.push(this._data.alias[i])
    }
  }
  aliases = aliases.join(", ");
  
  var properties = [];
  if (this._data.properties) {
    for(var i=0, len=this._data.properties.length; i<len; i++) {
      properties.push(this._data.properties[i])
    }
  }
  properties = properties.join(", ");
  var frag = document.createDocumentFragment();
  var div = this.createTag("div", frag);
  
  this._aliasesLabel = this.addChildComponent(Label, null, "div", null, null, "ac_aliases", [true]);
  this._nameLabel = this.addChildComponent(Label, null, "div", null, null, "ac_name", [true]);
  this._typesLabel = this.addChildComponent(Label, null, "div", null, null, "ac_types", [true]);  
  this._propertiesLabel = this.addChildComponent(Label, null, "div", null, null, "ac_properties", [true]);
  
  if(types.length==0) {this._typesLabel.addCSSClass("ac_emptyLabel")}
  if(properties.length==0) {this._propertiesLabel.addCSSClass("ac_emptyLabel")}
  if(aliases.length==0) {
    this._aliasesLabel.addCSSClass("ac_emptyLabel")
  } else {
    aliases = "("+aliases+")"
  }
  this._nameLabel.setText(displayName);
  this._typesLabel.setText(types);
  this._aliasesLabel.setText(aliases);
  this._propertiesLabel.setText(properties);
};
AutocompleteListItem.prototype._destroyDisplayContent = function() {
  if (this._nameLabel) {
    this._nameLabel.destroy();
    this._nameLabel = null;
  }
  if (this._typesLabel) {
    this._typesLabel.destroy();
    this._typesLabel = null;
  }
  if (this._aliasesLabel) {
    this._aliasesLabel.destroy();
    this._aliasesLabel = null;
  }  
  if (this._propertiesLabel) {
    this._propertiesLabel.destroy();
    this._propertiesLabel = null;
  }
  if (this._body)
    this._body.innerHTML = "";
};
AutocompleteListItem.prototype.getText = function() {
  // backwards compatibility with data.text and data.name  
  return this._getName(this._data);
};
AutocompleteListItem.prototype.getId = function() {
  return this._data["id"];
};
AutocompleteListItem.prototype.highlight = function(text) {
  this._highlightLabel(this._nameLabel, text);
  this._highlightLabel(this._aliasesLabel, text);
};
AutocompleteListItem.prototype._highlightLabel = function(l, text) {
  if (!l) return;
  if (!text) return;
  var origText = l.getText();
  var newText = origText;
  var index = origText.toLowerCase().indexOf(text.toLowerCase());
  if (index >= 0) {    
    newText = origText.substring(0, index) + 
      '<span class="highlight">' +
      origText.substring(index, index+text.length) +
      '</span>' +
      origText.substring(index + text.length);
    l._body.innerHTML = newText;     
  }
};
/* DATA EXAMPLE:
_data = {
  "text" : "Type",
  "id" : "#8f02a8c0400073cf800000000000000c",
  "type" : [{
  	"id": "/type/type",
  	"text": "Type"
  },{
  	"id": "/common/topic",
  	"text": "Topic"
  }]
}
HTML:
<li class="AutocompleteListItem">
  <span class="Label">Type</span>
  <span class="Label type">Type</span>
  <span class="Label type">Topic</span>
</li>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AutocompleteInfo</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** AutocompleteInfo
 * @constructor
 */
function AutocompleteInfo()
{
  setupInheritance(AutocompleteInfo);
  this._text = "";
  this._type = "";
  this._value = "";
  this._lastPrefix = "";
  this._lastMatches = new Array();
}
AutocompleteInfo.superClass = [];
registerClass(AutocompleteInfo, "AutocompleteInfo");
AutocompleteInfo.prototype.CLASS_NAME = "AutocompleteInfo";
/******************************************************** update
 * @param newText:String
 */
AutocompleteInfo.prototype.update = function(newText)
{
  if (newText != this._text)
  {
    this._text = newText;
    this._type = "";
    this._value = "";
    if (StringUtils.isEmpty(this._lastPrefix) ||
        newText.toLowerCase().indexOf(this._lastPrefix.toLowerCase()) != 0)
    {
      this._lastPrefix = "";
      this._lastMatches = new Array();
    }
  }
}
/******************************************************** getText
 */
AutocompleteInfo.prototype.getText = function()
{
  return (this._text);
}
/******************************************************** setText
 */
AutocompleteInfo.prototype.setText = function(a)
{
  this._text = a;
}
/******************************************************** getType
 */
AutocompleteInfo.prototype.getType = function()
{
  return (this._type);
}
/******************************************************** setType
 */
AutocompleteInfo.prototype.setType = function(a)
{
  this._type = a;
}
/******************************************************** getValue
 */
AutocompleteInfo.prototype.getValue = function()
{
  return (this._value);
}
/******************************************************** setValue
 */
AutocompleteInfo.prototype.setValue = function(a)
{
  this._value = a;
}
/******************************************************** getLastPrefix
 */
AutocompleteInfo.prototype.getLastPrefix = function()
{ 
  return (this._lastPrefix);
}
/******************************************************** setLastPrefix
 */
AutocompleteInfo.prototype.setLastPrefix = function(a)
{ 
  this._lastPrefix = a;
}
/******************************************************** getLastMatches
 */
AutocompleteInfo.prototype.getLastMatches = function()
{ 
  return (this._lastMatches);
}
/******************************************************** setLastMatches
 */
AutocompleteInfo.prototype.setLastMatches = function(a)
{ 
  this._lastMatches = a;
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AutocompleteManager</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** AutocompleteManager
 * @constructor
 */
function AutocompleteManager()
{
  setupInheritance(AutocompleteManager);
  superConstructor(EventDispatcher, this);
  this._stateMachine = new StateMachine
    ([
      ["START", new _Start(this)],
      ["GETTING", new _Getting(this)], 
      ["AUTOCOMPLETING", new _Autocompleting(this)]
      ]);
  // attach listeners
  this._stateMachine.addEventListener("onLoadAutocomplete", this);
  this._stateMachine.addEventListener("onAutocompleteSelect", this);
  this._stateMachine.addEventListener("onAutocompleteSubmit", this);
  this._autocompleteInput = null;
  this._autocompleteURL = null;
  this._autocompleteContainer = null;  
  this._autocompleteList = null;
  this._autocompleteInfo = null;
  this.managing = false;
}
AutocompleteManager.superClass = [EventDispatcher];
registerClass(AutocompleteManager, "AutocompleteManager");
AutocompleteManager.prototype.CLASS_NAME = "AutocompleteManager";
if (typeof AUTOCOMPLETE_MIN_LEN != "undefined") 
{
  AutocompleteManager.MIN_LEN = AUTOCOMPLETE_MIN_LEN;
}
else
{
  AutocompleteManager.MIN_LEN = 1;
}
if (typeof AUTOCOMPLETE_PAGE_SIZE != "undefined") 
{
  AutocompleteManager.PAGE_SIZE = AUTOCOMPLETE_PAGE_SIZE;
}
else
{
  AutocompleteManager.PAGE_SIZE = 10;
}
AutocompleteManager.MANAGE_INTERVAL = 200;
AutocompleteManager.INPUT_BLUR_INTERVAL = 200;
AutocompleteManager.SIGID_EMPTY = 0;
AutocompleteManager.SIGID_TEXT_CHANGE = 1;
AutocompleteManager.SIGID_ENTER_KEY = 2;
AutocompleteManager.SIGID_DOWN_ARROW = 3;
AutocompleteManager.SIGID_UP_ARROW = 4;
AutocompleteManager.SIGID_LOAD_AUTOCOMPLETE = 5;
AutocompleteManager.SIGID_LIST_ITEM_CLICK = 6;
AutocompleteManager.UID_CREATE_NEW = "UID_CREATE_NEW";
AutocompleteManager.UID_NO_MATCH = "UID_NO_MATCH";
AutocompleteManager.getTimeout = function(x) {
  var t = .3;
  if (x > 0) {
    t = 1/(6 * (x-0.7)) + .3;
  }
  return t * 1000;
};
AutocompleteManager.prototype.onAutocompleteListMouseDown = function(eventObj) {
  //Debug.log("onAutocompleteListMouseDown");
  // hack in IE to keep autocomplete list from disappearing when click/scrolling
  // through autocomplete list
  this._listHasFocus = true;
}
AutocompleteManager.prototype.onAutocompleteListMouseUp = function(eventObj) {
  //Debug.log("onAutocompleteListMouseUp");  
  // hack in IE to keep autocomplete list from disappearing when click/scrolling
  // through autocomplete list  
  this._listHasFocus = false;
  if (this._autocompleteInput) {
    // temporarily disable onFocus so that we don't reinitialize ac manager
    this._autocompleteInput.removeEventListener("onFocus", this._autocompleteInput);
    //Debug.log("AutocompleteManager.prototype.onAutocompleteListMouseUp BEFORE focus");
    this._autocompleteInput._input.focus();
    //Debug.log("AutocompleteManager.prototype.onAutocompleteListMouseUp AFTER focus");    
    this._autocompleteInput.addEventListener("onFocus", this._autocompleteInput);
  }  
}
AutocompleteManager.prototype.onCreateNewListMouseOver = function(eventObj) {
  //Debug.log("onAutocompleteListMouseOut");
  // hack in IE to keep autocomplete list from disappearing when click/scrolling
  // through autocomplete list
  if (this._autocompleteList) this._autocompleteList.setSelection(null);  
}
/******************************************************** showAutocompleteList
 * @param data List data model
 */
AutocompleteManager.prototype.showAutocompleteList = function(data)
{
  var input = this._autocompleteInput.getInput();
  var pos = DOMUtils.getElementLocation(input);
  var dim = DOMUtils.getElementDimension(input);
  var left = pos.x;
  var top = pos.y + dim.h;
  var width = dim.w;
  if (!this._autocompleteContainer) {
    this._autocompleteContainer = $elt("div", null, "AutocompleteListTopShadow");
    var shadow2 = $elt("div", this._autocompleteContainer, "AutocompleteListBottomShadow");
    var div = $elt("ul", shadow2);    
    this._autocompleteList = new AutocompleteList(div);
    this._autocompleteList.addEventListener("onListItemClick", this);
    this._autocompleteList.addEventListener("onListItemMouseOver", this);
    this._autocompleteList.addEventListener("onListItemMouseOut", this);    
    this._autocompleteList.addEventListener("onSelectionChange", this);
    if (this._createNewText) {
      var sl = $elt("ul", shadow2, "CreateNewList");
      var l = new SimpleList(sl);
      l.setData({listItems:[{text:this._createNewText}]});
      l.addEventListener("onListItemClick", Delegate.create(this, this._onClickCreateNew));
      DOMEvent.addEventHandler(sl, "mouseover", Delegate.create(this, this.onCreateNewListMouseOver));      
    }    
    DOMEvent.addEventHandler(this._autocompleteList._body, "mousedown", Delegate.create(this, this.onAutocompleteListMouseDown));
    DOMEvent.addEventHandler(this._autocompleteList._body, "mouseup", Delegate.create(this, this.onAutocompleteListMouseUp));
    var style = sprintf("display: none; width: %spx; left: %spx; top: %spx", width, left, top);
    DOMUtils.setElementStyle(this._autocompleteContainer, style);
    document.body.appendChild(this._autocompleteContainer);
  }
  var style = "display: block;";
  if (left != DOMUtils.getElementStyle(this._autocompleteContainer, "left")  &&
      top != DOMUtils.getElementStyle(this._autocompleteContainer, "top")) {
    style += sprintf("width: %spx; left: %spx; top: %spx", width, left, top);
  }
  DOMUtils.setElementStyle(this._autocompleteContainer, style);
  
  if (data) {
    var listData = {listItems:data};
    this._autocompleteList.setData(listData);
    this._autocompleteList.highlight(this._autocompleteInfo.getText());
  }
}
/******************************************************** hideAutocompleteList
 */
AutocompleteManager.prototype.hideAutocompleteList = function()
{
  if (this._autocompleteContainer != null)
  {
    var style = "display: none;";
    DOMUtils.setElementStyle(this._autocompleteContainer, style);
  }
}
/****************************************************** updateAutocompleteInput
 */
AutocompleteManager.prototype.updateAutocompleteInput = function()
{
  var info = this._autocompleteInfo;
  if (info == null)
  {
    return;
  }
  var text = info.getText();
  this._autocompleteInput.setText(text);
}
/****************************************************** getAutocompleteInfo
 * @return AutocompleteInfo
 */
AutocompleteManager.prototype.getAutocompleteInfo = function()
{
  return (this._autocompleteInfo);
}
/****************************************************** getAutocompleteList
 * @return List
 */
AutocompleteManager.prototype.getAutocompleteList = function()
{
  return this._autocompleteList;
}
/****************************************************** onBlur
 */
AutocompleteManager.prototype.onBlur = function(e)
{
  clearInterval(this._inputBlurInterval);
  var timeout = Delegate.create(this, this._onBlurDelay);
  this._inputBlurInterval = setInterval(timeout, 
    AutocompleteManager.INPUT_BLUR_INTERVAL );
}
/****************************************************** _onInputBlurDelay
 */
AutocompleteManager.prototype._onBlurDelay = function()
{
  clearInterval(this._inputBlurInterval);
  if (this._listHasFocus || this._autocompleteInput.hasFocus) return;
  this.dispatchEvent({type:"onAutocompleteBlur", 
                         autocompleteInfo:this._autocompleteInfo});
  //doLater(this, this.release);
  this.release();
}
/******************************************************** _manage
 * @param ac:AutocompleteInput
 * @param acUrl:String
 */
AutocompleteManager.prototype.manage = function(ac, acUrl, acCreateNewText)
{
  clearInterval(this._manageInterval);
  var timeout = Delegate.create(this, this._manageDelay, [ac, acUrl, acCreateNewText]);
  this._manageInterval = setInterval(timeout, AutocompleteManager.MANAGE_INTERVAL );
}
/******************************************************** _manageDelay
 * @param ac:AutocompleteInput
 * @param acUrl:String
 */
AutocompleteManager.prototype._manageDelay = function(ac, acUrl, acCreateNewText)
{
  clearInterval(this._manageInterval);
  this.release();
  this._autocompleteInput = ac;
  this._autocompleteURL = acUrl;
  this._createNewText = acCreateNewText;
  this._autocompleteInput.addEventListener("onTextChange", this);
  this._autocompleteInput.addEventListener("onEnterKey", this);
  this._autocompleteInput.addEventListener("onKeyDown", this);
  this._autocompleteInput.addEventListener("onKeyPress", this);
  this._autocompleteInput.addEventListener("onBlur", this);
  this._autocompleteInfo = new AutocompleteInfo();
  var t = this._autocompleteInput.getText();
  if (!StringUtils.isEmpty(t)) this._autocompleteInfo.update(t);
  // hack to initialize autocompleteInfo with input data
  var acdata = this._autocompleteInput._data;
  if (acdata) {
    if (!StringUtils.isEmpty(acdata.text)) this._autocompleteInfo.setText(acdata.text);
    if (!StringUtils.isEmpty(acdata.id)) this._autocompleteInfo.setValue(acdata.id);
  }
  this._stateMachine.transition("START");
  this.managing = true;
}
/******************************************************** _release
 */
AutocompleteManager.prototype.release = function()
{  Debug.log("AutocompleteManager.prototype.release");
  this._stateMachine.transition("START");  
  if (this._autocompleteInput)
  {
    this._autocompleteInput.removeEventListener("onTextChange", this);
    this._autocompleteInput.removeEventListener("onEnterKey", this);
    this._autocompleteInput.removeEventListener("onKeyDown", this);
    this._autocompleteInput.removeEventListener("onKeyPress", this);
    this._autocompleteInput.removeEventListener("onBlur", this);
  }
  if (this._autocompleteList) {
    this._autocompleteList.removeEventListener("onListItemClick", this);
    this._autocompleteList.removeEventListener("onListItemMouseOver", this);
    this._autocompleteList.removeEventListener("onListItemMouseOut", this);   
    this._autocompleteList.removeEventListener("onSelectionChange", this);
    this._autocompleteList.destroy();
    delete this._autocompleteList;
  }
  discardElement(this._autocompleteContainer);
  delete this._autocompleteContainer;
    
  this._autocompleteInfo = null;
  this.managing = false;
}
/******************************************************** onEnterKey
 */
AutocompleteManager.prototype.onEnterKey = function(eventObject)
{
  Debug.log("AutocompleteManager.prototype.onEnterKey");  
  // don't autocomplete if no autocompleteURL:
  /*if(!this._autocompleteInput._data.autocompleteURL){
    this.onAutocompleteSelect();
    return;
  }*/
  var signal = {sigId:AutocompleteManager.SIGID_ENTER_KEY};
  this._stateMachine.handle(signal);
  this.dispatchEvent(eventObject);
}
/******************************************************** onKeyDown
 */
AutocompleteManager.prototype.onKeyDown = function(eventObject)
{
  var keyCode = eventObject.keyCode;
  var domEvt = eventObject.domEventObject;
  switch (keyCode)
  {
  case 40: // DOWN_ARROW
    DOMEvent.preventDefault(domEvt);
    this._onDownArrow();
    break;
  case 38: // UP_ARROW
    DOMEvent.preventDefault(domEvt);
    this._onUpArrow();
    break;
  default:
    break;
  }
}
/******************************************************** onKeyPress
 */
AutocompleteManager.prototype.onKeyPress = function(eventObject)
{
  var keyCode = eventObject.keyCode;
  switch (keyCode)
  {
  case 40: // DOWN_ARROW
  case 38: // UP_ARROW
  //case 9: // TAB
    DOMEvent.preventDefault(eventObject.domEventObject);
    break;
  default:
    break;
  }
}
/************************************************** _isAutocompleteListVisible
 */
AutocompleteManager.prototype._isAutocompleteListVisible = function()
{
  var currentState = this._stateMachine.getCurrentState();
  return ((currentState == "AUTOCOMPLETING" ||
           currentState == "SELECTED") &&
          "none" != this._autocompleteContainer.style.display);
}
/******************************************************** _onDownArrow
 */
AutocompleteManager.prototype._onDownArrow = function()
{
  var signal = {sigId:AutocompleteManager.SIGID_DOWN_ARROW};
  this._stateMachine.handle(signal);
}
/******************************************************** _onUpArrow
 */
AutocompleteManager.prototype._onUpArrow = function()
{
  var signal = {sigId:AutocompleteManager.SIGID_UP_ARROW};
  this._stateMachine.handle(signal);
}
/******************************************************** onTextChange
 */
AutocompleteManager.prototype.onTextChange = function(eventObject)
{
  clearInterval(this._textChangeInterval);
  var newValue = this._autocompleteInput.getText();  
  this._autocompleteInfo.update(newValue);
  this._lastTypedValue = newValue;
  var timeout = Delegate.create(this, this.onTextChangeDelay);  
  var l = newValue.length
  var delay = AutocompleteManager.getTimeout(l);
  //Debug.log("AutocompleteManager timeout:", delay, "length:", l);
  this._textChangeInterval = setInterval(timeout, delay);
}
/******************************************************** onTextChangeDelay
 */
AutocompleteManager.prototype.onTextChangeDelay = function()
{ 
  clearInterval(this._textChangeInterval);
  var signal = {sigId:AutocompleteManager.SIGID_TEXT_CHANGE};
  this._stateMachine.handle(signal);
}
/******************************************************** onReceiveAutompleteData
 */
AutocompleteManager.prototype.onReceiveAutompleteData = function(listData, prefix) {
  if (!listData) listData = [];
  if (listData.length == 0) {
    listData.push({id:AutocompleteManager.UID_NO_MATCH, text:"no matches"})
  }
  var signal = {sigId:AutocompleteManager.SIGID_LOAD_AUTOCOMPLETE, prefix:prefix, listData:listData};
  this._stateMachine.handle(signal);
}
/****************************************************** _onClickCreateNew
 */
AutocompleteManager.prototype._onClickCreateNew = function(eventObject) {
  this._autocompleteList.setSelection(null);
  this.hideAutocompleteList();
  this._autocompleteInfo.setValue(AutocompleteManager.UID_CREATE_NEW);
  this.dispatchEvent({type:"onAutocompleteCreateNew", 
                         autocompleteInfo:this._autocompleteInfo});
  this._stateMachine.transition("START", {sigId:AutocompleteManager.UID_CREATE_NEW});    
};
/******************************************************** onAutocompleteSelect
 */
AutocompleteManager.prototype.onAutocompleteSelect = function(eventObject)
{
  this.dispatchEvent({type:"onAutocompleteSelect", 
                         autocompleteInfo:this._autocompleteInfo});
}
/******************************************************** onAutocompleteSubmit
 */
AutocompleteManager.prototype.onAutocompleteSubmit = function(eventObject)
{

console.log("AM cons",e);
alert ("uuuu");

  this.dispatchEvent({type:"onAutocompleteSubmit", 
                         autocompleteInfo:this._autocompleteInfo});
}
/******************************************************** onLoadAutocomplete
 */
AutocompleteManager.prototype.onLoadAutocomplete = function(eventObject)
{
  if (this._autocompleteURL) 
  {
    var params = new Params();
    params.add("prefix", eventObject.prefix);
    params.add("limit", "" + AutocompleteManager.PAGE_SIZE);
    var url = this._autocompleteURL;
    if (url.indexOf("?") > 0) {
      url += "&" + params.toQueryString();
    }
    else {
      url += "?" + params.toQueryString();
    }
    // cancel pending requests
    if (this._autocompleteDP && this._autocompleteDP._httpRequest) {
      try { 
        this._autocompleteDP._httpRequest.abort();
      }
      catch(ex) {
        Debug.warn("Couldn't abort autocomplete request:", this._autocompleteDP, ex);
      }
    }
    var owner = this;
    this._autocompleteDP = new DataProvider(url);
    this._autocompleteDP.onError = function(httpRequest) {
      Debug.log("autocompleteDP.onError", httpRequest.responseText);
    };
    this._autocompleteDP.requestData(function(data){     
      // FIX, autocomplete service is returning "#guid"
      var result;
      if (data.list && data.list.listItems)
        result = data.list.listItems;
      else if (data.result)
        result = data.result;
      if (result)
        owner.onReceiveAutompleteData(result, data.prefix);
    });
  }
  else {
    doLater(this, this.onReceiveAutompleteData, 1, [[], eventObject.prefix]);
  }
}
/******************************************************** onListItemClick
 */
AutocompleteManager.prototype.onListItemClick = function(eventObject)
{
  // when list gets a selection change event (with a mouse click), the
  // autocompleteInput will lose focus. The following workaround give
  // focus back to the autocomplete input.
  // clear blurInterval to prevent call to _release
  clearInterval(this._inputBlurInterval);
  // give focus back to autocompleteInput
  // the above call will fire off ACMgr.manage again so we clear the
  // manage delay interval right away
  clearInterval(this._manageInterval);
  // finally we're ready to send the signal to the state machine
  var signal = {sigId:AutocompleteManager.SIGID_LIST_ITEM_CLICK, listItem:eventObject.targetItem};
  this._stateMachine.handle(signal);
};
AutocompleteManager.prototype.onSelectionChange = function(eventObject){
  this.dispatchEvent({type:"onAutocompleteListSelectionChange", listItem:eventObject.targetItem});
}
/******************************************************************************
 * 
 * Autocomplete States 
 *
 *****************************************************************************/
/*********************************************************** _AutocompleteState
 *
 * All autocomplete states are subclasses of _AutocompleteState.
 *
 * @param manager:AutocompleteManager
 */
function _AutocompleteState(manager)
{
  setupInheritance(_AutocompleteState);
  superConstructor(State, this, []);
  this._manager = manager;
};
_AutocompleteState.superClass = [State];
registerClass(_AutocompleteState, "_AutocompleteState");
_AutocompleteState.prototype.getManager = function()
{
  return (this._manager);
}
/*********************************************************************** _Start
 * @param manager:AutocompleteManager
 */
function _Start(manager)
{
  setupInheritance(_Start);
  superConstructor(_AutocompleteState, this, [manager]);
};
_Start.superClass = [_AutocompleteState];
registerClass(_Start, "_Start");
/**************************************************************** _Start.handle
 */
_Start.prototype.handle = function(signal)
{   
  if (typeof signal == "undefined")
    return;
  var sm = this.getStateMachine();
  var mgr = this.getManager();
  var info = mgr.getAutocompleteInfo();
  var sigId = signal["sigId"];
  switch (sigId)
  {
  case AutocompleteManager.SIGID_TEXT_CHANGE:
  case AutocompleteManager.SIGID_DOWN_ARROW:  
 
    var text = info.getText();
    if (text.length >= AutocompleteManager.MIN_LEN)
    {
      sm.transition("GETTING");
    }
    else
    {
      mgr.hideAutocompleteList();
    }    
    return;
  case AutocompleteManager.SIGID_ENTER_KEY: 
    sm.dispatchEvent("onAutocompleteSubmit"); 
    return;
  default:
    break;
  };
};
/********************************************************************* _Getting
 * @param manager:AutocompleteManager
 */
function _Getting(manager)
{
  this._loadingPopup = null;
  setupInheritance(_Getting);
  superConstructor(_AutocompleteState, this, [manager]);
};
_Getting.superClass = [_AutocompleteState];
registerClass(_Getting, "_Getting");
/*************************************************************** _Getting.enter
 */
_Getting.prototype.enter = function()
{
  superMethod(_AutocompleteState, this, "enter", []); 
  var sm = this.getStateMachine();
  var mgr = this.getManager();
  var info = mgr.getAutocompleteInfo();
  
  if (!this._loadingPopup) {
    this._loadingPopup = new AutocompleteLoadingPopup(mgr._autocompleteInput.getInput(), [{text:"Loading..."}]);
  }
  this._loadingPopup.show();
  
  var text = info.getText();
  info.setLastPrefix(text);
  sm.dispatchEvent({type:"onLoadAutocomplete", prefix:text});
};
_Getting.prototype.exit = function()
{
  this._loadingPopup.hide();
}
/************************************************************** _Getting.handle
 */
_Getting.prototype.handle = function(signal)
{  
  if (typeof signal == "undefined")
    return;
  var sm = this.getStateMachine();
  var mgr = this.getManager();
  var info = mgr.getAutocompleteInfo();
  var sigId = signal["sigId"];
  switch (sigId)
  {
  case AutocompleteManager.SIGID_TEXT_CHANGE:
    var text = info.getText();
    sm.transition("START", signal);
    return;
  case AutocompleteManager.SIGID_LOAD_AUTOCOMPLETE:
    var currentLastPrefix = info.getLastPrefix();
    if (currentLastPrefix == signal["prefix"])
    {
      info.setLastMatches(signal["listData"]);
      sm.transition("AUTOCOMPLETING");
    };
    return; 
  case AutocompleteManager.SIGID_ENTER_KEY:
    sm.dispatchEvent("onAutocompleteSubmit");
    return;
  default:
    break;
  };
};
/************************************************************** _Autocompleting
 * @param manager:AutocompleteManager
 */
function _Autocompleting(manager)
{
  setupInheritance(_Autocompleting);
  superConstructor(_AutocompleteState, this, [manager]);
};
_Autocompleting.superClass = [_AutocompleteState];
registerClass(_Autocompleting, "_Autocompleting");
/*********************************************************_Autocompleting.enter
 */
_Autocompleting.prototype.enter = function()
{
  superMethod(_AutocompleteState, this, "enter", []); 
  var mgr = this.getManager();
  var info = mgr.getAutocompleteInfo();
  var matches = info.getLastMatches();
  mgr.showAutocompleteList(matches);
  var list = mgr.getAutocompleteList();
  var len = list.getLength();
  // always select the first item
  if (len > 0) {
	  list.setSelection(null, 0);
  }
};
_Autocompleting.prototype.exit = function()
{
  var mgr = this.getManager();
  mgr.hideAutocompleteList();
}
/******************************************************* _Autocompleting.handle
 */
_Autocompleting.prototype.handle = function(signal)
{
  if (typeof signal == "undefined")
    return;
  var sm = this.getStateMachine();
  var mgr = this.getManager();
  var info = mgr.getAutocompleteInfo();
  var sigId = signal["sigId"];
  switch (sigId) 
  {
  case AutocompleteManager.SIGID_TEXT_CHANGE:
    var list = mgr.getAutocompleteList();
    list.setSelection(null);
    sm.transition("START", signal);
    return;
  case AutocompleteManager.SIGID_DOWN_ARROW:
    var list = mgr.getAutocompleteList();
    var index = list.getSelectionIndex() + 1;
    var listItem = null;
    if (index >=0 && index < list.getLength())
    {
      listItem = list.setSelection(null, index);
    }
    if (listItem) DOMUtils.scrollIntoView(listItem._body, list._body);
    return;
  case AutocompleteManager.SIGID_UP_ARROW:
    // wrap around selection
    
    var list = mgr.getAutocompleteList();
    var index = list.getSelectionIndex() - 1;
    var listItem = null;
    if (index >=0 && index < list.getLength())
    {
      listItem = list.setSelection(null, index);
    }
    if (listItem) DOMUtils.scrollIntoView(listItem._body, list._body);
    return;
  case AutocompleteManager.SIGID_ENTER_KEY:
    var list = mgr.getAutocompleteList();
    sigId = AutocompleteManager.SIGID_LIST_ITEM_CLICK;
    signal = {sigId:sigId, listItem:list.getSelection()};
    // specifically commented this out so that it falls directly into
    // "case SIGID_LIST_ITEM_CLICK:"
  case AutocompleteManager.SIGID_LIST_ITEM_CLICK:
    var listItem = signal["listItem"];
    var id = listItem.getId();
    switch(id) {
      case AutocompleteManager.UID_NO_MATCH:
        break;
      default:
        info.setText(listItem.getText());
        //info.setType(listItem.getType());
        info.setValue(listItem.getId());
        mgr.updateAutocompleteInput();
        mgr.hideAutocompleteList();
        mgr.getAutocompleteList().setSelection(null);
        sm.dispatchEvent("onAutocompleteSelect");
        sm.transition("START", signal);
        break;
    }
    return;
  default:
    break;
  };
};
function AutocompleteLoadingPopup(targetElement, menuData, noShadow){
  setupInheritance(AutocompleteLoadingPopup);
  superConstructor(PopupMenu, this, [targetElement, menuData, noShadow]);
}
AutocompleteLoadingPopup.superClass = [PopupMenu];
registerClass(AutocompleteLoadingPopup, "AutocompleteLoadingPopup");
AutocompleteLoadingPopup.prototype.CLASS_NAME = "AutocompleteLoadingPopup";
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AutocompleteList</b>
 *
 * @author daepark@apmindsf.com
 */
function AutocompleteList(div){  
  setupInheritance(AutocompleteList);
  superConstructor(AbstractList, this, [div]);
};
AutocompleteList.superClass = [AbstractList];
registerClass(AutocompleteList, "AutocompleteList");
AutocompleteList.prototype.CLASS_NAME = "AutocompleteList";
AutocompleteList.prototype._createListItem = function(div) {
	return new AutocompleteListItem(div);
};
AutocompleteList.prototype.onMouseOver = function(eventObject){
  // For IE that does not support multiple CSS classname
  var listItem = eventObject.target;
  if (listItem) {
    this.setSelection(listItem);
  }
};
AutocompleteList.prototype._dispatchSelectionChangeEvent = function(index, listItem){
  clearInterval(this._selectionChangeInterval);
  var timeout = Delegate.create(this, this._dispatchSelectionChangeEventDelay, [index, listItem]);
  this._selectionChangeInterval = setInterval(timeout, 200);
  
};
AutocompleteList.prototype._dispatchSelectionChangeEventDelay = function(index, listItem){
  clearInterval(this._selectionChangeInterval);
  this.dispatchEvent({type:"onSelectionChange", 
                      targetIndex:index, 
                      targetItem:listItem});
};
AutocompleteList.prototype._addListItem = function(data, beforeIndex, parent){
  var listItem = superMethod(AbstractList, this, "_addListItem", [data, beforeIndex, parent]);
  
  var id = data["id"];
  switch(id) {
    case AutocompleteManager.UID_NO_MATCH:
      listItem.addCSSClass("noMatch");
      break;
    default:
      break;
  }
  return listItem;
};
AutocompleteList.prototype.highlight = function(text) {
  for(var i=0, len=this._listItems.length; i<len; i++) {
    this._listItems[i].highlight(text);
  }
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>AutocompleteInput</b>
 *
 * @author daepark@apmindsf.com
 */
/******************************************************** AutocompleteInput
 * @constructor
 */
function AutocompleteInput(div, name)
{
  this._mgr;
  this._createNewText = "New";
  
  setupInheritance(AutocompleteInput);
  superConstructor(TextInput, this, [div, name]);
}
AutocompleteInput.superClass = [TextInput];
registerClass(AutocompleteInput, "AutocompleteInput");
AutocompleteInput.prototype.CLASS_NAME = "AutocompleteInput";
/******************************************************** init
 */
AutocompleteInput.prototype.init = function()
{
  superMethod(TextInput, this, "init",[]);
  this.addCSSClass("Autocomplete");
  this._input.setAttribute("autocomplete", "off");
  this.addEventListener("onFocus", this);
  this._mgr = new AutocompleteManager();
  //var mgr = AutocompleteManager.getInstance();
  this._mgr.chainEvent("onAutocompleteCreateNew", this, true);
  this._mgr.chainEvent("onAutocompleteSelect", this, true);
  this._mgr.chainEvent("onAutocompleteBlur", this, true);   
  this._mgr.chainEvent("onAutocompleteListSelectionChange", this, true);
  this._mgr.chainEvent("onAutocompleteSubmit", this, true);
}
/******************************************************** destroy
 */
AutocompleteInput.prototype.destroy = function(e)
{
  this._mgr.release();
  superMethod(TextInput, this, "destroy");
};
AutocompleteInput.prototype.initInteractive = function(div) {
  this._inputEventHandler = new InputEventHandler(this, this._input, 
    InputEventHandler.ENTERKEY | InputEventHandler.ESCAPEKEY | InputEventHandler.TEXTCHANGE | InputEventHandler.MOUSEPASTE | InputEventHandler.KEYPRESS | InputEventHandler.KEYDOWN);
}
/****************************************************** setData
 */
AutocompleteInput.prototype.setData = function(data)
{
  superMethod(TextInput, this, "setData", [data]);
  if (this._data.createNewText) {
    this._createNewText = this._data.createNewText;
  }
}
/****************************************************** setAutocompleteURL
 */
AutocompleteInput.prototype.setAutocompleteURL = function(url)
{
  this._data.autocompleteURL = url;
}
AutocompleteInput.prototype.setCreateNewText = function(text){
  this._createNewText = text;
}
/****************************************************** getInput
 * @return HTMLInputElement
 */
AutocompleteInput.prototype.getInput = function()
{
  return (this._input);
}
/****************************************************** getCaretIndex
 */
AutocompleteInput.prototype.getCaretIndex = function()
{ 
  var input = this.getInput();
  var index = 0;
  if (input.caretpos)
  {
    // IE
    index = input.caretPos;
  }
  else if (input.selectionStart)
  {
    // mozilla
    index = input.selectionStart;
  }
  return (index);
}
/****************************************************** setCaretIndex
 */
AutocompleteInput.prototype.setCaretIndex = function(caretPos)
{
  var input = this.getInput();
  if (input.createTextRange)
  {
    // IE
    var range = input.createTextRange();;
    range.collapse(true);
    range.moveEnd("character", caretPos);
    range.moveStart("character", caretPos);
    range.select();
  }
  else if (input.setSelectionRange)
  {
    // mozilla
    input.setSelectionRange(caretPos, caretPos);
  }
}
/******************************************************** onFocus
 */
AutocompleteInput.prototype.onFocus = function(e)
{
    if (!(this.disableAutocomplete || this._mgr.managing))
        this._mgr.manage(this, this._data.autocompleteURL, this._createNewText);
}
AutocompleteInput.prototype.onKeyDown = function(eventObject){
  this.dispatchEvent(eventObject);
}
  
AutocompleteInput.prototype.onKeyPress = function(eventObject){
  this.dispatchEvent(eventObject);
}
/*
DATA:
_data.text
_data.name
_data.autocompleteURL
*/
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * HTTPRequest class
 * mark@metaweb.com
 */
function HTTPRequest(url, callback, postData, headers){
  this._postData = postData;
  if(postData != undefined)
      this._method="POST";
  else
      this._method="GET";
  this._url = url;
  this._request = new XMLHttpRequest();
  this._id = HTTPRequest._makeId();
  this._headers = headers || {};
  this._json = undefined;
  if (typeof(callback) == "function") {
    // Old-style callback functions need an adapter.
    this._callback = { "object":this, "callback":this._functionCallback };
    this._handler = callback;
  } else {
    // New-style callback descriptors don't need an adapter.
    this._callback = callback;
  }
  // default headers
  if (postData != undefined && typeof this._headers["Content-Type"] == "undefined")
    this._headers["Content-Type"] =  "application/x-www-form-urlencoded";
  if (typeof this._headers["Referer"] == "undefined")
    this._headers["Referer"] =  window.location;
  
  // Unique browser page id (BrowserPID cookie) is forwarded.
  if (typeof this._headers["X-Metaweb-PGID"] == "undefined")
    this._headers["X-Metaweb-PGID"] =  readCookie("BrowserPID");
  // required by all writing APIs
  this._headers["X-Metaweb-Request"] = "XMLHttpRequest";
  
  this._request.onreadystatechange = Delegate.create(this, this.onreadystatechange);
  doLater(this, this._send, 0, [url, 0]);
  
  HTTPRequest._heap.push(this);
}
HTTPRequest.prototype._send = function(url, iteration){
  this._request.open(this._method, url, true);
  try{
    for(var key in this._headers)
      this._request.setRequestHeader(key, this._headers[key]);
    this._request.send(this._postData);
  } catch(e) {
    // Wait a second and try again.
    if(iteration <= 20) {
      Debug.warn("HTTPRequest setRequestHeader (", iteration , "of 20):", e);
      doLater(this, this._send, 1, [url, iteration + 1]);
    } else {
      Debug.error("HTTPRequest setRequestHeader:", e);
    }
  }
}
HTTPRequest.prototype.accounting = function() {
    // Why is this overridden?
}
HTTPRequest.prototype.abort = function() {
  Debug.log("HTTPRequest.abort", this._url);
  if (this._request) this._request.abort();
};
HTTPRequest.prototype._functionCallback = function() {
  this._handler(this.responseText(), this.responseXML(), this.status());
}
HTTPRequest.prototype.onreadystatechange = function(e){
  // Ready states other than 4 are handled inconsistently, so don't bother trying.
  if(this._request.readyState != 4)
    return;
  // Avoid memory leak in MSIE and prevent infinte recursion in Firefox      
  this._request.onreadystatechange = NO_OP;
  
  try {
    // Add the Transaction ID from the server to the list of relevant
    // transaction IDs, for debugging.
    var tid = this._request.getResponseHeader("X-Metaweb-TID");
    if (tid) {
      g_transactionIdList.push(tid);
    }
    // Call the appropriate callback depending on the status.
    // Like MochiKit, we treat 200, 201, 204, and 304 as success.
    try {
      var status = "aborted";
      // this next statement may thrown an exception in Firefox if the
      // request was aborted
      status = this._request.status;
      var status_success = (status == 200 ||
                            status == 201 ||
                            status == 204 ||
                            status == 304 ||
                            status == 0); // local file gets
    } catch (ex) {
      status_success = false;
    }
    if (status_success) {
      try {
          this._callback.callback.apply(this._callback.object, [ this ]);
      } 
      catch (ex) {
        // Callback failed; report it.
        Debug._group("HTTPRequest: Success callback failed\n"); 
        Debug.error("Error:", ex, "\n",
                    "X-Metaweb-TID:", tid, "\n",
                    "URL:", this._url, "\n",
                    "postData:", this._postData, "\n",
                    "Response:", this._request.responseText);
        Debug._groupEnd();
        throw(ex);
      }
    } 
    else {
      Debug._group("HTTPRequest: Server Error");
      Debug.error("Status:", status, "\n",
                  "X-Metaweb-TID:", tid, "\n", 
                  "URL:", this._url, "\n",
                  "postData:", this._postData, "\n",
                  "Response:", this._request.responseText);
      try {
        if (this._callback.errback) {
          this._callback.errback.apply(this._callback.object, [ this ]);
        }
      }
      catch (ex) {
        // Error callback failed?
        Debug.error("Error callback failed: ", ex);
        throw(ex);            
      }
      finally {
        Debug._groupEnd(); 
      }
    }
  } 
  catch (ex) {
    // Catch any other errors and report them.
    Debug.error("HTTPRequest: ", ex);
    Debug._trace(ex);
    dumpStack(ex);
    this.abort();
    throw(ex);
  }
};
HTTPRequest._currentId = 0;
HTTPRequest._makeId = function(){
  HTTPRequest._currentId = HTTPRequest._currentId || 0;
  return ++HTTPRequest._currentId;
}
HTTPRequest.prototype.status = function() {
  return this._request.status;
}
HTTPRequest.prototype.responseText = function() {
  return this._request.responseText;
}
HTTPRequest.prototype.responseXML = function() {
  return this._request.responseXML;
}
HTTPRequest.prototype.responseJSON = function() {
  if (!this._json) {
    try{
      Debug.time("JSON eval time");
      this._json = eval("("+ this._request.responseText +")");
      Debug.timeEnd("JSON eval time");
    } catch(ex) {
      Debug.warn("HTTPRequest.responseJSON: Ill-formed JSON: ", ex, "\n",
           "JSON: ", this._request.responseText);
      this._json = null;
      throw ex;
    }
  }
  return this._json;
}
HTTPRequest._heap = [];
HTTPRequest._gc = function() {
  for(var i=0, len=HTTPRequest._heap.length; i<len; i++) {
    if (HTTPRequest._heap[i]) {
      if (HTTPRequest._heap[i]._request) {
        HTTPRequest._heap[i]._request.onreadystatechange = NO_OP;
        try{HTTPRequest._heap[i]._request.abort();}catch(ignore){}
        HTTPRequest._heap[i]._request = null;
      }
      delete HTTPRequest._heap[i]._json;
    }
    delete HTTPRequest._heap[i];
  }
  delete HTTPRequest._heap;
};
addWindowOnunloadHandler(HTTPRequest._gc);
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * BatchHTTPRequestManager
 * alecf@metaweb.com
 *
 * Technologies, Inc.
 */
function batch_request(url, callback, errorback) {
    // errorback ignored for now
    var request = BatchHTTPRequestManager.requestData(url, callback, errorback);
    return request;
}
BatchHTTPRequestManager = {};                 // Global singleton
BatchHTTPRequestManager.pendingRequests = {}; // url -> request mapping
BatchHTTPRequestManager.resultQueue = [];     // urls that have
                                              // loaded, awaiting
                                              // callback
BatchHTTPRequestManager.TIMER_INTERVAL = 500;
BatchHTTPRequestManager.requestData = function(url, callback, errorback) {
  // fire the request and remember the callback for later
  var request;
  
  if (!(url in BatchHTTPRequestManager.pendingRequests)) {
    var handlers = {
      callback: BatchHTTPRequestManager.handleSuccess,
      errback: BatchHTTPRequestManager.handleError
    };
    request = new HTTPRequest(url, handlers);
    handlers.object = request;
    // create a new request
    BatchHTTPRequestManager.pendingRequests[url] = request;
    // this is where we'll store callbacks
    request.blob_handlers = [];
  } else {
    // there's already a request in the queue
    request = BatchHTTPRequestManager.pendingRequests[url];
  }
  request.blob_handlers.push({
    callback:callback,
    errorback:errorback
  });
  return request;
};
BatchHTTPRequestManager.handleSuccess = function(httpRequest) {
  BatchHTTPRequestManager.handleResponse(httpRequest, false);
}
BatchHTTPRequestManager.handleError = function(httpRequest) {
  BatchHTTPRequestManager.handleResponse(httpRequest, true);
}
BatchHTTPRequestManager.handleResponse = function(httpRequest, problem) {
  BatchHTTPRequestManager.startTimer();
  // HTTPRequest has loaded - queue this up to render later
  var url = httpRequest._url;
  // httpRequest and BatchHTTPRequestManager.pendingRequests[url] should be the same
  // move from pending results to finished results
  delete BatchHTTPRequestManager.pendingRequests[url];
  var result = { text: httpRequest.responseText(),
                 xml: httpRequest.responseXML(),
                 status: httpRequest.status(),
                 request: httpRequest,
                 problem: problem };
  BatchHTTPRequestManager.resultQueue.push(result);
};
BatchHTTPRequestManager.fireTimer = function() {
  // run all pending callbacks in one sweep
  
  BatchHTTPRequestManager.timerStarted = false;
  
  var queue = BatchHTTPRequestManager.resultQueue;
  // the queue could get modified by the callbacks
  var queue_length = queue.length;
  for (var i=0; i<queue_length; i++) {
    var result = queue[i]; // destructive removal
    
    var request = result.request;
    if (!request) continue;  // request may have been aborted
    for (var j=0; j<request.blob_handlers.length; j++) {
      var handlers = request.blob_handlers[j];
      if(result.problem) {
          if (handlers && handlers.errorback)
            handlers.errorback(result.text, result.xml, result.status);
          else
            BatchHTTPRequestManager.errorHandler(result);
      }
      else{
          if (handlers && handlers.callback)
            handlers.callback(result.text, result.xml, result.status);
          break;
      }
    }
  }
  // remove all the entries we just processed
  queue.splice(0, queue_length);
  // if there are still requests, refire the timer this is nasty
  // because if there is an error loading a url and thus we never
  // get called, this timer will keep firing
  var have_more = false;
  for (var url in BatchHTTPRequestManager.pendingResults) {
      have_more = true;
      break;
  }
  if (have_more)
      BatchHTTPRequestManager.startTimer();
};
BatchHTTPRequestManager.startTimer = function() {
    if (BatchHTTPRequestManager.timerStarted)
        return;
    setTimeout(BatchHTTPRequestManager.fireTimer,
               BatchHTTPRequestManager.TIMER_INTERVAL);
    BatchHTTPRequestManager.timerStarted = true;
}
BatchHTTPRequestManager.errorHandler = function(badRequest) {
  if (!badRequest) {
    Debug.error("BatchHTTPRequestManager.errorHandler: non-existent badRequest", badRequest);
    return;
  }
  var status = badRequest.status;
  if (!status) {
    Debug.error("BatchHTTPRequestManager.errorHandler: Missing status in badRequest", badRequest);
    return;
  }
  var message = null;
  if (badRequest.text)
    message = badRequest.text;
  if (!message) {
    Debug.error("BatchHTTPRequestManager.errorHandler: Missing responseText in badRequest", badRequest.text);
    return;
  }  
  if (status == 400 && messagetext == "authentication required") {
    // auth required else redirect to signin
    openLocation(Globals.signinUrl());
    return;    
  }
  else {
    BatchHTTPRequestManager.mql_error(message);
  }
}
BatchHTTPRequestManager.mql_error = function(errorMessage) {
  Debug.error("BatchHTTPRequestManager.mql_error", errorMessage);
  if (g_application && typeof g_application.readError == "function")
    g_application.readError(errorMessage);
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * Application class
 * mark@metaweb.com
 */
var g_application;
function Application(){
  // member vars:
  this._components = [];
  
  setupInheritance(Application);
  superConstructor(EventDispatcher, this);
  g_application = this;
  this._init();
  
  Application._heap.push(this);
};
Application.superClass = [EventDispatcher];
registerClass(Application, "Application");
Application.prototype.CLASS_NAME = "Application";
Application.prototype._init = function () {
  // listen for window.onload:
  $(document).ready(Delegate.create(this, this._onDOMLoad));
}
Application.prototype._onDOMLoad = function(){
  this._DOMLoaded = true;
  this._buildPage();
};
Application.prototype._buildPage = function() {
  // build page actually works by chaining together:
  // _preBuild
  // _build
  // _postBuild
  // rather than calling them all in a row, each stage calls the next
  // stage. This allows us to make multiple asynchronous requests
  // during the build process
  this._preBuild();
}
Application.prototype._buildStaticComponents = function() {
  // create and build components that already exist in the DOM
  var app = this;
  $("[@component]").each(function (i) {
      if (this.getAttribute("initialized") != "true")
        app.buildComponent(this, this.getAttribute("component"));
    })
};
Application.prototype._build = function(){
  Debug.time("Application build time");
  Debug.log("Application._buildStaticComponents");
  this._buildStaticComponents();
  Debug.timeEnd("Application build time");
  this._restoreState();
  
  Debug.log("Application._requestData");  
  this._requestData(this._components);
  
  // chain to _postBuild
  Debug.log("Application._postBuild");  
  this._postBuild();
};
Application.prototype.buildComponent = function(div, componentClassName) {
  var componentClass = ObjectMap[componentClassName];
  if (!componentClass) {
    Debug.warn("Couldn't find class for component named", componentClassName, " in ", div);
    return;
  }
  var component = new componentClass(div);
  this._components.push(component);
  return component;
};
Application.prototype._restoreState = function() {
  // this evaluates the hash (if any) and restores state to the app
  // (if any)
  // implement restoreState if you want restoreState() to be called
  // when there is data in #
  if (!this.restoreState ||
      !window.location.hash || window.location.hash == "#")
    return;
  // everything after the '#'
  try {
    var hash = window.location.hash.slice(1);
    var state = JSONUtils.decode(hash);
    this.restoreState(state);
  }
  catch (ex) {
    Debug.warn("Invalid restore state:", hash);
    Debug.warn(ex);
    dumpStack(ex);
  }
};
Application.prototype.saveState = function(state) {
  if (state)
    window.location.href = "#" + JSONUtils.encode(state);
  else
    window.location.href = "#";
};
Application.prototype._requestData = function(components){
  this._dataRequesters = new Array();
  for(var i=0, len=components.length; i<len; i++){
    var component = components[i];
    var listener = Delegate.create(this, this._receiveData)
    component.addEventListener("onReceiveData", listener);
    var requestObj = {component:component,listener:listener};
    this._dataRequesters.push(requestObj);
    var requesting = component.requestData();
    if(!requesting){
      component.removeEventListener("onReceiveData", listener);
      var last = this._dataRequesters.length-1;
      this._dataRequesters.splice(last,1);
    }
  }
  this._doneRequesting = true;
  this._checkAllDataReceived();
}
Application.prototype._receiveData = function(eventObject){
  var component = eventObject.target;
  for(var i=0, len=this._dataRequesters.length; i<len; i++){
    var requestObj = this._dataRequesters[i];
    if(component == requestObj.component){
      component.removeEventListener("onReceiveData", requestObj.listener);
      requestObj.listener.onReceiveData = null;
      this._dataRequesters.splice(i,1);
      break;
    }
  }
  this._checkAllDataReceived();
}
Application.prototype._checkAllDataReceived = function(){
  if(this._doneRequesting && this._dataRequesters.length==0)
    this._onDataLoaded();
}
Application.prototype._onDataLoaded = function(){
  this.dispatchEvent({type:"onDataLoaded", target:this});
}
Application.prototype._preBuild = function(){
  // chain to _build
  this._build();
}
Application.prototype._postBuild = function(){
}
Application.prototype.getUser = function() {
    return null;
}
Application.prototype.hasPermission = function() {
  return false;
}
Application.prototype.alert = function(msg, callback) {
  var a = new OverlayAlert(msg, callback);
  return a;
};
Application._heap = [];
Application._gc = function() {
  for(var i=0, len=Application._heap.length; i<len; i++) {
    if (Application._heap[i]) {
      delete Application._heap[i]._components;
    }
   	delete Application._heap[i];
  }
  delete Application._heap;
  delete g_application;
};
addWindowOnunloadHandler(Application._gc);
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * CSS Effects
 * alecf@metaweb.com
 */
function CSSEffects() {
}
CSSEffects._init = function() {
  // allow CSSEffects to be called multiple times
  if (!CSSEffects.sheet) {
    if (document.createStyleSheet) {
      CSSEffects.sheet = document.createStyleSheet();
    } 
    else if (document.styleSheets && document.styleSheets.length > 0) {
      CSSEffects.sheet = document.styleSheets[0];
    }
    else {
      return false;
    }
  }
  
  if (!CSSEffects.rule) {
    // at the end of this block, CSSEffects.rules will be set to the
    // rules used in hide/show
    
    if (CSSEffects.sheet.insertRule) {
      if (CSSEffects.sheet.rules) {
      // Safari
      CSSEffects.addRule = function(selector, style) {
        return CSSEffects.sheet.insertRule(selector + " {" + style + "}",
                                         (CSSEffects.sheet.rules.length));
      };
      CSSEffects.removeRule = function(index) {
        CSSEffects.sheet.deleteRule(index);
      }
      CSSEffects.rules = CSSEffects.sheet.rules;
     } 
     else {
      // Firefox
      CSSEffects.addRule = function(selector, style) {
        return CSSEffects.sheet.insertRule(selector + " {" + style + "}",
                                           CSSEffects.rules.length);
      };
      CSSEffects.removeRule = function(index) {
        CSSEffects.sheet.deleteRule(index);
      }
      CSSEffects.rules = CSSEffects.sheet.cssRules;
     }
    } 
    else if (CSSEffects.sheet.addRule) {
      // IE
      CSSEffects.addRule = function(selector, style) {
        CSSEffects.sheet.addRule(selector, style);
        return CSSEffects.rules.length - 1;
      };
      CSSEffects.removeRule = function(index) {
        return CSSEffects.sheet.removeRule(index);
      };
      CSSEffects.rules = CSSEffects.sheet.rules;
    }
    else {
      return false;
    }
  }
  // now that we have CSSEffects.rules, these function are
  // browser-independent (but we're defining them in _init to make
  // sure they're lazily loaded)
  CSSEffects.hide = function(ruleIndex) {
    // simple for now, but if we do fade-out here's where to do it
    var rule = CSSEffects.rules[ruleIndex];
    rule.style.display = "none";
  };
  
  CSSEffects.show = function(ruleIndex, display) {
    if (!display)
      display = "block";          // allow for inline
    var rule = CSSEffects.rules[ruleIndex];
    
    rule.style.display = display;
  }
  // initialization successful
  return true;
}
CSSEffects.lazyLoaded = function(methodName) {
  function lazycall() {
    if (CSSEffects._init())
      return CSSEffects[methodName].apply(CSSEffects, arguments);
    Debug.warn("CSSEffects could not initialize running '", methodName, "' - is the DOM loaded yet?");
  }
  return lazycall;
};
  
CSSEffects.addRule = CSSEffects.lazyLoaded('addRule');
CSSEffects.removeRule = CSSEffects.lazyLoaded('removeRule');
CSSEffects.show = CSSEffects.lazyLoaded('show');
CSSEffects.hide = CSSEffects.lazyLoaded('hide');
/**
 * Copyright 2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>DesignMode</b>
 *
 * @author alee@metaweb.com
 *
 * This is a wrapper and a set of convenience methods to support RichText capabilities on
 * a document housed in an iframe.  There are two flavors (Midas supported by FF/Moz and
 * IE/Safari).  While IE supports contentEditable on a DOM node, we choose to designmode that
 * is supported by the two flavors.  Also, to manage the variations, rich text editing is done
 * on a document housed in an iframe.
 *
 * Also, contains methods that shield RichTextEditor component from DesignMode manipulations (e.g.,
 * getEditDocument, getSelectionRange). So, methods in RichTextEditor are those that our components
 * typically have and those in DesignMode are the implementation methods.
 *
 * Information about DesignMode and two flavors based on:
 * http://kb.mozillazine.org/Midas
 * http://developer.mozilla.org/en/docs/Rich-Text_Editing_in_Mozilla
 * http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/designmode.asp
 * http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/execcommand.asp
 */
function DesignMode(){  
  // member vars:
  this.iframeId;
  this.iframe;
  this.editDocument;
  this._checkDocumentLoadedTimer;
  this._waitingToNotify;
}
registerClass(DesignMode, "DesignMode");
DesignMode.prototype.associate = function(iframe){
  var typeofParameter = typeof iframe;
  
  // keep iframe object
  if (typeofParameter == "string") {
    this.iframeId = iframe;
    this.iframe = $id(iframe);
  }
  else if (typeofParameter == "object") {
    this.iframeId = iframe.tagName;
    this.iframe = iframe;
  }
  else
    Debug.warn("DesignMode: unknown parameter for iframe " + iframe);
    
  // keep editDocument object - note: FF & IE respond to this.iframe.contentWindow and technically
  // this.editDocument = this.iframe.contentWindow.document works for both.  Because of diffferences with selection
  // on document, we choose the following route so that things are right across the board for IE.
  if (this.iframe.contentDocument) {
    // FF-like
    this.editDocument = this.iframe.contentDocument;
  }
  else {
    // IE-like
    this.editDocument = this.iframe.contentWindow.document;
  }
};
DesignMode.prototype.edit = function(enable){
  if (typeof this.iframe == "undefined" || typeof this.editDocument == "undefined")
    Debug.warn("DesignMode: no iframe/editDocument to switch edit state");
  if (!enable && this.editDocument.designMode != "On")
    return;
    
  this.editDocument.designMode = (enable)?"On":"Off";
  if (this.iframe.contentDocument){
    // FF-like
    var editCommand = this.editCommand(RichTextEditor.STYLEWITHCSS);
    this.execCommand(editCommand);
  }
  else
    // IE-like - need to obtain editDocument after designMode is set
    this.editDocument = this.iframe.contentWindow.document;
  if (enable) {
    this._keyUpHandler = Delegate.create(this, this.onKeyUp);
    this.addEventHandler("keyup", this._keyUpHandler);
  }
  else if (this._keyUpHandler) {
    Delegate.destroy(this._keyUpHandler);
    delete this._keyUpHandler;
  }
}
DesignMode.prototype.setFocus = function(){
  this.iframe.contentWindow.focus();    // both IE & FF respond to this.iframe.contentWindow.
}
DesignMode.prototype.execCommand = function(editCommand, otherOptions) {
  if (!this.editDocument) {
    Debug.warn("DesignMode: no edit document set");
    return;
  }
  
  // Set focus to document
  this.setFocus();    
  
  if (editCommand.value == RichTextEditor.CREATELINK){
    var selectRange = otherOptions[0];
    var url = otherOptions[1];   
    if (document.all) {
      // IE-like
      // Must reselect the user selected Text because we lose focus when doing OverlayGetLink
      this._selectRange(selectRange);
    }
    this.editDocument.execCommand(editCommand.command, false, url);
  }
  else
    this.editDocument.execCommand(editCommand.command, false, editCommand.commandOption);
}
DesignMode.prototype.queryCommandValue = function(editCommand) {
  if (!this.editDocument) {
    Debug.warn("DesignMode: no edit document set");
    return;
  }
  
  var value = this.editDocument.queryCommandValue(editCommand.command);
  return value;
}
DesignMode.prototype.sameFormatBlock = function(command, value){
  if (this.iframe.contentDocument){
    // FF-like
    return (command == "<"+value+">");
  }
  else{
    // IE-like - need to obtain editDocument after designMode is set
    return ((command == "<h1>" && value == "Heading 1") ||
            (command == "<h2>" && value == "Heading 2") ||
            (command == "<h3>" && value == "Heading 3") ||
            (command == "<h4>" && value == "Heading 4") ||
            (command == "<pre>" && value == "Formatted"));
  }
}
DesignMode.prototype.setEditDocument = function(html) {
  if (StringUtils.isEmpty(this.iframeId))
    Debug.error("DesignMode.setEditDocument: no iframeId set - please use associated first");
  this.setFocus();    
  var iframeDocument = "<html id=\"" + this.iframeId + "\">\n";
  iframeDocument += "<head>\n";
  iframeDocument += "  <link type=\"text/css\" rel=\"stylesheet\" href=\"/resources/css/chrome.css\"/>";
  iframeDocument += "</head>\n<body>\n";
  iframeDocument += html + "\n";
  iframeDocument += "</body>\n";
  iframeDocument += "</html>";
  try {
    this.editDocument.open();
    this.editDocument.write(iframeDocument);
    this.editDocument.close();
  }
  catch (ex) {
    this.onError("Problem with loading content into the editor.", "Please retry your edit again", ex);
    this.dispatchEvent("onCancel");
  }
}
DesignMode.prototype.getEditDocument = function() {
  return this.editDocument.body.innerHTML;
}
DesignMode.prototype._getSelectionRange = function() {
  //debugger;
  if (document.all) {
    // IE-like
    var selection = this.editDocument.selection;
    return selection.createRange();
  }
  else {
    var selection = this.iframe.contentWindow.getSelection();
    return selection.getRangeAt(selection.rangeCount-1).cloneRange();
  }
}
DesignMode.prototype.getSelectionRange = function() {
  return this._getSelectionRange();
}
DesignMode.prototype.getSelectionText = function() {
  //debugger;
  var range = this._getSelectionRange();
  if (typeof range == "undefined" || range == null)
    return null;
  if (document.all)
    // IE-like
    return range.text;
  else
    return range.toString();
}
DesignMode.prototype.selectDocumentContent = function(doSelectAll) {
  // Must ensure that document is loaded.  Then, either select whole document or place cursor at end of document.
  var selectDocument = (typeof doSelectAll == "undefined" || doSelectAll == null || doSelectAll == false) ? false:true;   // default should be false
  if ((document.all && this.editDocument.readyState != "complete") || !this.editDocument.body)
    // document body is not loaded
    this.addWaitingDocumentLoad(this._selectDocumentContent, [selectDocument]);
  else
    this._selectDocumentContent(selectDocument);
}
DesignMode.prototype._selectDocumentContent = function(selectDocument) {
  // Guaranteed that this.editDocument.body is loaded 
  // Select whole document or set cursor to end of document
  this.setFocus();
  if (document.all) {
    // IE-like
    var docRange = this.editDocument.body.createTextRange();
    if (selectDocument)
      // Select whole document
      docRange.select();
    else {
      // set selection to end of document
      docRange.select();
      var selectionRange = this.getSelectionRange();
      selectionRange.collapse(false);
      selectionRange.select();
    }
  }
  else {
    // FF-like
    var selection = this.iframe.contentWindow.getSelection();
    if (selectDocument) 
      // select the whole document
      selection.selectAllChildren(this.editDocument.body);
    else {
      // set selection to end of document
      var children = this.editDocument.body.childNodes;
      selection.collapse(this.editDocument.body, children.length);
      this.setFocus();  
    };
  }
}
DesignMode.prototype._selectRange = function(range) {
  // Guaranteed that this.editDocument.body is loaded 
  this.iframe.contentWindow.focus();  // both IE & FF respond to this.iframe.contentWindow.
  if (document.all) {
    // IE-like
    range.select();
  }
  else {
    // FF-like
    var s = this.editDocument.selection;
    if (s.rangeCount > 0) s.removeAllRanges();
    s.addRange(range);
  }
}
DesignMode.prototype.addWaitingDocumentLoad = function(handler, args) {
  if (!this._waitingToNotify)
    this._waitingToNotify = [];
  this._waitingToNotify.push({handler: handler, args: args});
  if (!this._checkDocumentLoadedTimer)
    this._checkDocumentLoadedTimer = doLater(this, this.checkDocumentLoaded, 100);
}
DesignMode.prototype.checkDocumentLoaded = function() {
  this._clearTimer();
  if ((document.all && this.editDocument.readyState != "complete") || !this.editDocument.body){
    // IE - first case ; second case is otherwise
    this._checkDocumentLoadedTimer = doLater(this, this.checkDocumentLoaded, 100);
    return;
  }
  
  // Document loaded - run all the handlers waiting on this
  for (var i=0,len=this._waitingToNotify.length; i<len; i++) {
    var item = this._waitingToNotify[i];
    item.handler.apply(this, item.args);
  }
  this._waitingToNotify = null;
}
DesignMode.prototype._clearTimer = function(){
  if (this._checkDocumentLoadedTimer){
    clearTimeout(this._checkDocumentLoadedTimer);
    this._checkDocumentLoadedTimer = null;
  }
}
DesignMode.prototype._onTextChange = function(e){
  if (this._checkTextChange()){
    if (typeof this.onTextChange!="undefined") {
      this.onTextChange({type:"onTextChange", target: this, domEventObject:e});
      return true;
    }
  }
  return false;
}
DesignMode.prototype._checkTextChange = function(){
  if (typeof this.checkTextChange != "undefined")
    return this.checkTextChange();
}
DesignMode.prototype.addEventHandler = function(eventType, handler) {
  // Must ensure that document is loaded 
  if ((document.all && this.editDocument.readyState != "complete") || !this.editDocument.body)
    // document body is not loaded
    this.addWaitingDocumentLoad(this._addEventHandler, [eventType, handler]);
  else
    this._addEventHandler(eventType, handler);
}
DesignMode.prototype._addEventHandler = function(eventType, handler) {
  // Guaranteed that this.editDocument.body is loaded 
  if (this.iframe.contentDocument) {
    // FF-like
    DOMEvent.addEventHandler(this.editDocument, eventType, handler);
  }
  else {
    // IE-like - this.iframe.contentWindow is frame-equivalent object
    DOMEvent.addEventHandler(
      this.editDocument, 
      eventType, 
      this.bindEventHandler(this.iframe.contentWindow, this._keyUpHandler));
  }
}
DesignMode.prototype.bindEventHandler = function(frame, keyHandler) {
  return function () { keyHandler(frame.event); };
}
DesignMode.prototype.onKeyUp = function(e) {
  this.dispatchEvent({type:"onKeyUp", target: this, keyCode:e.keyCode, domEventObject:e});
  this._onTextChange(e);
    
  try {
    // enterKey
    if (e.keyCode == 13) {
      this.onEnterKey({type:"onEnterKey", target: this, domEventObject:e});
    }
    
    // escapeKey
    if (e.keyCode == 27) {
      this.onEscapeKey({type:"onEscapeKey", target: this, domEventObject:e});
    }
  }
  catch(ex){
    // Don't do anything. This catches a FireFox error stating that
    // DOMEvent is not defined. This happens when navigating to another
    // page, FireFox kills the JS session, but these event handlers are still
    // somehow active.
  }
} 
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * Editable class
 * mark@metaweb.com
 */
function Editable(){
  // member vars:
  this._editable;
}
Editable.prototype.setEditable = function(state){
  if(state == this._editable) return;
  this._editable = state;
  if(this._editable) this._enterState_editable();
  else this._exitState_editable();
}
Editable.prototype.getEditable = function(state){
  return this._editable;
}
Editable.prototype._enterState_editable = function(){
}
Editable.prototype._exitState_editable = function(){
}
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * CSSBehaviors class
 */
function CSSBehaviors(htmlElement){
  // member vars:
  this._cssClass = new Object();
  if (htmlElement.className) {
    var cssClasses = htmlElement.className.split(" ");
    for(var i=0, len=cssClasses.length; i<len; i++) {
      if (cssClasses[i]) {
        this._cssClass[new String(cssClasses[i])] = true;
      }
    }
  }
  this._htmlElement = htmlElement;
};
CSSBehaviors.prototype.addCSSClass = function(classString){
  if (!this._cssClass) return;
  if (classString in this._cssClass && this._cssClass[classString] == true) return;
  this._cssClass[classString] = true;
  this._updateCSSClass();
};
CSSBehaviors.prototype.removeCSSClass = function(classString){
  if (!this._cssClass) return;
  if (!(classString in this._cssClass)) return;
  delete this._cssClass[classString];
  this._updateCSSClass();
};
CSSBehaviors.prototype.hasCSSClass = function(classString){
  if (this._cssClass)
    return true == this._cssClass[classString];
  else 
    return false;
};
CSSBehaviors.prototype.addCSSClasses = function(classes){
  if (!this._cssClass) return;  
  // convert space-separated class list to a class
  if (typeof classes == "string")
    classes = classes.split(" ");
  for (var i=0; i<classes.length; i++) {
    if (classes[i])
      this._cssClass[classes[i]] = true;
  }
  this._updateCSSClass();
};
CSSBehaviors.prototype._updateCSSClass = function(){
  if (!this._htmlElement) return;
  var classes = [];
  for(var item in this._cssClass){
    var itemValue = this._cssClass[item];
    if(itemValue == false || typeof itemValue == "undefined") continue;
    classes.push(item);
  }
  classes = classes.sort().join(' ');
  this._htmlElement.className = classes;
  return classes;
};
CSSBehaviors.prototype._updateElement = function(htmlElement){
  this._htmlElement = htmlElement;
  this._updateCSSClass();
};
CSSBehaviors.prototype.destroy = function(){
  this._htmlElement = null;
  delete this._cssClass;
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>ServiceDataProvider: Performs a post to the serviceUrl</b>
 *
 * @author alee@metaweb.com
 */
/****************************************************** constructor
 */
function ServiceDataProvider(serviceUrl, data, headers) {
  setupInheritance(ServiceDataProvider);
  superConstructor(DataProvider, this, [serviceUrl, data, null, headers]);
};
ServiceDataProvider.superClass = [DataProvider];
registerClass(ServiceDataProvider, "ServiceDataProvider");
ServiceDataProvider.prototype.checkData = function(data, dontThrowError) {
  if (!(data && data.code)) {
    Debug.error("Invalid Service result: missing one or more required field: data, data.code. data =", data);
    return; 
  }
  var code = data.code;
  if (code != "/api/status/ok" && code != "/api/status/info" && code != "/api/status/warning")
    this.onError(null, data);
  return data;
};
ServiceDataProvider.prototype.onError = function(httpRequest, json){
  if (httpRequest)
    json = httpRequest.responseJSON();
  Debug.log("ServiceDataProvider.onError: ", JSONUtils.format(json));
  var code = json.code;
  var messages = json.messages;
  if (!(code && json.messages)) {
    Debug.error("Missing code and/or messages in result", json);
    return;
  }
  var message = null;
  if (messages && messages.length > 0)
    message = json.messages[0];
  if (!message) {
    Debug.error("Missing error message in result", json);
    return;
  } 
  this.service_error(json);
};
ServiceDataProvider.prototype.service_error = function(result) {
  Debug.error("ServiceDataProvider.prototype.service_error", result);
  if (g_application && typeof g_application.onError == "function")
    g_application.onError(JSONUtils.format(result));
};
/******************************************************* service
 * 
 * Utility to create a ServiceDataProvider object submitting data.
 * 
 * @param serviceUrl:String
 * @param data:String
 * @return return - DataProvider
 */ 
function service(serviceUrl, data, headers) {
  return new ServiceDataProvider(serviceUrl, data, headers);
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>UploadDataProvider</b>
 *
 * @author alee@metaweb.com
 */
 
/************************************************************* UploadDataProvider
 * 
 * An extension of DataProvider which overwrites the default checkData method.
 * 
 * @param url:String
 * @param postData:String
 * @see DataProvider
 */
function UploadDataProvider(url, postData) {
  setupInheritance(UploadDataProvider);
  superConstructor(DataProvider, this, [url, postData]);
};
UploadDataProvider.superClass = [DataProvider];
registerClass(UploadDataProvider, "UploadDataProvider");
UploadDataProvider.SERVICE =  "/api/service/upload";
UploadDataProvider.prototype.checkData = function(data) {
  // TODO: check status == "200 OK" and check messages  
  if ("200 OK" != data["status"]) {  
    var msg  = "none";
    if (data["messages"].length > 0) {
      msg = data["messages"][0]["text"];
    }
    Debug.error("upload status:", data["status"], "\n  messages: ", msg);
  }
  return (data["result"]);
};
/************************************************************************* upload
 * 
 * Utility method to create a new UploadDataProvider object given the queryString
 * and whether or not to post.
 * 
 * @param textBlob:String
 * @param documentId:guid
 * @param createDocument: Boolean
 * @return return - DataProvider
 *
 */ 
function upload(textBlob, documentId, createDocument, otherParams) {
  var q="?lang=" + encodeURIComponent("/lang/en");
  // upload service seems to be incomplete from documentation.
  if (typeof documentId != "undefined" && documentId) 
    q +="&document=" + encodeURIComponent(documentId);
  else if (typeof createDocument != "undefined" && createDocument == true)
    q += "&document=";
  if (typeof otherParams != "undefined" && otherParams) 
    q += "&" + otherParams;
  var clientDP = new UploadDataProvider(UploadDataProvider.SERVICE + q, textBlob);
  return (clientDP);
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * MetawebUser class
 *
 * This implements the protocol expected by Application.js, namely
 * that it has a "getUser" method that returns an object with a
 * property "name."
 *
 * In particular, note that this is the only place that has any knowledge
 * of the Metaweb cookie format.  It also encapsulates some of the
 * login/logout protocol that is specific to Metaweb.
 *
 * This is both a consumer and producer of "login" events.  Whenever
 * a component thinks there may be a change in login status (for
 * example, because it saw a "Set-Cookie" or "Set-Cookie2" header),
 * it should broadcast a "login" event.  This object will catch that
 * and update itself.  Similarly, when this object participates in
 * a "login" or "logout" transaction, it will update itself and
 * generate a "login" event.  Other components can watch for that
 * event, call g_application.getUser() to retrieve the user object,
 * and update themselves based on that.
 */
function MetawebUser(app) {
  this.guid = undefined;
  this.name = undefined;
  this.path = undefined;
  this._update();
  
  // Update our contents whenever we see a "login" event.
  var owner = this;
  app.addEventListener("login", function(ev){owner.onLoginEvent();});
};
MetawebUser.prototype._update = function(){
  // get user info from cookie:
  var cookieInfo = readCookie("metaweb-user-info");
  var oldGuid = this.guid;
  var oldName = this.name;
  if (cookieInfo.indexOf('A|') == 0) {
    // Using new cookie format (which is extensible and Unicode-safe)
    // 'g' = User GUID, 'u' = user account name, 'p' = path name of user obj
    this.guid = this._cookieItem(cookieInfo, 'g');
    this.name = this._cookieItem(cookieInfo, 'u');
    this.path = this._cookieItem(cookieInfo, 'p');
    if (!this.path)
	this.path = '/user/' + this.name;
  } else {
    // Using old cookie format
    cookieInfo = unescape(cookieInfo).split("|");
    if (cookieInfo && cookieInfo.length >= 2) {
      this.guid = cookieInfo[0];
      this.name = cookieInfo[1];
      this.path = '/user/' + this.name;
    } else {
      this.guid = undefined;
      this.name = undefined;
      this.path = undefined;
    }
  }  
  this.displayName = this.name;
  // Return "true" if anything really changed.
  return (oldGuid != this.guid || oldName != this.name);
};
MetawebUser.prototype.onLoginEvent = function(){
  // Since we send a login event recursively, we will get
  // entered recursively; prevent that from going too far.
  if (this._handlingLoginEvent > 0)
    return;
  
  // If nothing changed, we're done.
  if (!this._update())
    return;
  
  // Dispatch a login event, but ignore the event we receive from this.
  this._handlingLoginEvent = 1;
  if (g_application && g_application.dispatchEvent)
    g_application.dispatchEvent("login");
  this._handlingLoginEvent = 0;
};
MetawebUser.prototype._cookieItem = function(c, i){
  var s = c.indexOf('|'+i+'_');
  if (s != -1){
    s = s + 2 + i.length;
    var e = c.indexOf('|',s);
    if (e != -1) return decodeURIComponent(c.substr(s,e-s));
  }
  return null;
};
MetawebUser.prototype.getUser = function() {
  if (this.guid)
    return(this);
  return undefined;
};
MetawebUser.prototype.signOutUrl = function() {
  return sprintf("/api/account/logout?onsucceed=%s", encodeURIComponent(window.location));
};
MetawebUser.prototype.url = function() {
  try {
    return Globals.viewUrl(this.guid,"/type/user");
  } catch(e) {
    Debug.error("login.url() failed:", e, "tim");
  }
};
MetawebUser.prototype.signOut = function() {
  // When the request below returns, just throw out a "login" event
  // so everyone (including ourselves!) updates.
  var handler = function(text, xml, status) {
    g_application.dispatchEvent("login");
  }
  try {
    this._httpRequester = new HTTPRequest("/api/account/logout", handler, null);
  } catch(e) {
    Debug.warn("HTTPRequest failed");
  }
  return false;
};
function PermissionCache() {}
PermissionCache.lookup = function(user, permission, callback)
{
  if (!user) {
    callback(false);
    return;
  }
  result = PermissionCache.get(user, permission);
  
  if (result !== undefined) {
    callback(result);
    return;
  }
  PermissionCache.query(user, permission, callback);
};
  
PermissionCache.query = function(user, permission, callback) {
  
  var q = {"q":
           {"query": 
            {"id": permission,
             "/type/permission/permits": {
               "limit": 1,
               "optional": true,
               "member": user
             }
            }
           }
  };
  
  function permissionLoaded(result) {
    if (!result) {
      Debug.error("Error loading permission for ", user);
      callback(false);
    }
    result = result.q.result;
    if (result["/type/permission/permits"])
      has_permission = true;
    else
      has_permission = false;
    PermissionCache.set(user, permission, has_permission);
    callback(has_permission);
  }
  
  q = mql(q);
  q.requestData(permissionLoaded);
};
PermissionCache.get = function(user, permission) {
  if (!PermissionCache.cache) {
    var cacheStr = readCookie("permissionCache")
    if (JSONUtils.is_valid(cacheStr))
      PermissionCache.cache = eval("("+cacheStr+")");
    else
      PermissionCache.cache = {}
  }
  
  if (!PermissionCache.cache)
    return undefined;
  var cache = PermissionCache.cache;
  if (user == cache.user &&
      permission in cache.permissions)
    return cache.permissions[permission];
    
  return undefined;
};
PermissionCache.set = function(user, permission, value) {
  // flush the cache if we switch users  
  if (PermissionCache.cache.user != user)
    PermissionCache.cache = {"user": user,
                             "count": 0,
                             "permissions": {}};
  var cache = PermissionCache.cache;
      
  // skip the rest if we know the value
  if (permission in cache.permissions)
    return;
  // flush the cache if it's full - we're not trying to be real smart here
  if (cache.count > 5) {
    // but all_permission is sticky
    var all_permission = PermissionCache.cache.permissions["/boot/all_permission"];
    
    cache.permissions = {};
    cache.count = 0;
    if (all_permission !== undefined) {
      cache.permissions["/boot/all_permission"] = all_permission;
      cache.count = 1;
    }
  }
  cache.count += 1;
  
  // now store the result
  cache.permissions[permission] = value;
  writeCookie("permissionCache", JSONUtils.format(cache, true));
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MQLDataProvider</b>
 *
 * @author daepark@apmindsf.com
 */
/************************************************************** MQLDataProvider
 */
function MQLDataProvider(url, postData, headers) {
  setupInheritance(MQLDataProvider);
  superConstructor(DataProvider, this, [url, postData, null, headers]);
};
MQLDataProvider.superClass = [DataProvider];
registerClass(MQLDataProvider, "MQLDataProvider");
MQLDataProvider.prototype.SERVICE =  "/api/service/mqlread";
MQLDataProvider.prototype.checkData = function(data, dontThrowError) {
  // TODO: check status == "200 OK" and check messages    
  if (!(data && data.status)) {
    Debug.error("Invalid MQL result", data);
    return; 
  }
  var code = data.code;
  if (code.indexOf("/api/status/error") == 0) {
    this.onError(null, data);
    return data;
  }
  for(var q in data) {
    if ("status" == q ||
        "code" == q)
      continue;
    code = data[q].code;
    
    switch(code) {
      case "/api/status/error":
        Debug.error("MQLDataProvider.checkData: ", q, data[q]);
        this.onError(null, data[q]);
        if (!dontThrowError){
          // writes always throw error as parameter is missing and when explicitly false
          throw("/api/status/error");
          return null;
        }
      default:
        break;
    }
  }
  return data;
};
MQLDataProvider.prototype.onError = function(httpRequest, json){
  if (httpRequest) {
    json = httpRequest.responseJSON();
  }
  var code;
  if (json.messages && json.messages.length > 0) {
    var first_message = json.messages[0];
    code = first_message.code;
    message = first_message.message;
  }
  if (!message) {
    Debug.error("Missing error message in result", json);
    return;
  }  
  if (code.indexOf("/api/status/error/input/invalid") == 0 &&
      message == "authentication required") {
    // auth required else redirect to signin
    openLocation(Globals.signinUrl());
    return;    
  }
  else if (code.indexOf("/api/status/error") == 0) {
    if (/\/api\/status\/(\w+)/.test(code)) {
      var mql_error_type = RegExp.$1;
      if (mql_error_type in this) {
        // those who wish to handle specific mql errors, 
        // they should provide corresponding mql error methods:
        // 
        // parse_error
        // result_error
        // timeout_error
        // type_error
        // graph_error
        // access_error
        // internal_error
        // internal_parse_error
        // connection_error
        // envelope_error
        this[mql_error_type].apply(this, [json]);
        return;
      }
    }
  }
  this.mql_error(json);
};
MQLDataProvider.prototype.mql_error = function(result) {
  Debug.error("MQLDataProvider.prototype.mql_error", result);
  if (this.SERVICE == "/api/service/mqlwrite") {
    this.mql_write_error(result);
  }
  else {
    this.mql_read_error(result);
  }
};
MQLDataProvider.prototype.mql_read_error = function(result) {
  if (g_application && typeof g_application.readError == "function")
    g_application.readError(JSONUtils.format(result));
};
MQLDataProvider.prototype.mql_write_error = function(result) {
  if (g_application && typeof g_application.writeError == "function")
    g_application.writeError(JSONUtils.format(result));
};
MQLDataProvider.prototype.getMetaData = function(data) {
    // copy specific attributes over from the envelope
    var result = {}
    
    var envelopeAttrs = ["cursor", "status"];
    
    //Debug.log("MQLDataProvider.prototype.getMetaData", data);
    // hack to support cursor/permission options for the new queries= syntax
    // this needs to be redone to support multiple cursors/permission for each query
    for (var q in data) {    
      for (var i=0; i<envelopeAttrs.length; i++) {
        if (typeof data[q][envelopeAttrs[i]] != "undefined") {
          result[envelopeAttrs[i]] = data[q][envelopeAttrs[i]];
        }
      }
    }
    //Debug.log("MQLDataProvider.prototype.getMetaData result", result);
    return result;
}
/********************************************************* MQLWriteDataProvider
 */
function MQLWriteDataProvider(queryObject, options, headers) {
  setupInheritance(MQLWriteDataProvider);
  if (options) {
    // we can't collapse queries with cursor and permission options
    var cursor;
    var permission;
    if ('cursor' in options) {
      cursor = options.cursor;
    }
    if ('permission' in options) {
      permission = options.permission;
      if (typeof permission == "object" && 'id' in permission) {
        permission = permission.id;
      }
    }   
    var count = 0;
    for(var q in queryObject) {
      if (count > 1) {
        // hack to support cursor/permission options for the new queries= syntax
        // this needs to be redone to support multiple cursors/permission for each query
        Debug.error("You cannot have multiple query objects when specifying cursors and permissions");
        return;
      }
      if (typeof cursor != "undefined") queryObject[q].cursor = cursor;
      if (typeof permission != "undefined") queryObject[q].use_permission_of = permission;
      count++;    
    }
  }
  var queryStr = JSONUtils.format(queryObject, true);
  queryStr = "queries=" + encodeURIComponent(queryStr);  
  superConstructor(MQLDataProvider, this, [this.SERVICE, queryStr, headers]);
};
MQLWriteDataProvider.superClass = [MQLDataProvider];  
registerClass(MQLWriteDataProvider, "MQLWriteDataProvider");
MQLWriteDataProvider.prototype.SERVICE =  "/api/service/mqlwrite";
/********************************************************** MQLReadDataProvider
 */
function MQLReadDataProvider(queryObject, options, headers, nocollapse) {
  setupInheritance(MQLReadDataProvider);
  
  this.cid = 0;
  
  // collapse id and map
  this._cidMap = {};
  this._collapse = (typeof MQL_COLLAPSE == "boolean" && MQL_COLLAPSE == true);
  if (nocollapse) {
    this._collapse = false;
  }
  
  if (options) {
    // we can't collapse queries with cursor and permission options
    var cursor;
    var permission;
    if ('cursor' in options) {
      cursor = options.cursor;
    }
    if ('permission' in options) {
      permission = options.permission;
      if (typeof permission == "object" && 'id' in permission) {
        permission = permission.id;
      }
    }   
    var count = 0;
    for(var q in queryObject) {
      if (count > 1) {
        // hack to support cursor/permission options for the new queries= syntax
        // this needs to be redone to support multiple cursors/permission for each query
        Debug.error("You cannot have multiple query objects when specifying cursors and permissions");
        return;
      }
      if (typeof cursor != "undefined") queryObject[q].cursor = cursor;
      if (typeof permission != "undefined") queryObject[q].use_permission_of = permission;
      count++;    
    }
  }
  else if (this._collapse) {
    queryObject = this._collapseQueryObject(queryObject);
  }
  
  var queryStr = JSONUtils.format(queryObject, true);
  queryStr = "queries=" + encodeURIComponent(queryStr);
  
  var url = sprintf("%s?%s", this.SERVICE, queryStr);
  superConstructor(MQLDataProvider, this, [url, null, headers]);
};
MQLReadDataProvider.superClass = [MQLDataProvider];  
registerClass(MQLReadDataProvider, "MQLReadDataProvider");
MQLReadDataProvider.prototype.newCid = function() {
  return "c" + this.cid++;
};
MQLReadDataProvider.prototype.checkData = function(data) {
  data = superMethod(MQLDataProvider, this, "checkData", [data, true]);
  
  var uncollapsed = {};
  if (this._collapse) {
    for (var key in data) {
      var qids = this._cidMap[key];
      if (typeof qids == "undefined" || qids.length == 0) {
        Debug.log("[MQLReadDataProvider] problem collapsing/uncollapsing query");
        continue;
      }
      for(var i=0, len=qids.length; i<len; i++) {
        	uncollapsed[qids[i]] = data[key];
      }
    }
  }
  else {
    uncollapsed = data;
  }
  return uncollapsed;
};
MQLReadDataProvider.prototype._collapseQueryObject = function(queryObject) {
  var collapsedQueryObject = {};
  var collapsable = [];
  for (var qid in queryObject) {
    if (this._isCollapsable(queryObject[qid])) {
      //Debug.log("YES:", queryObject[qid]);
      collapsable.push(qid);
    }
    else {
      //Debug.log("NO:", queryObject[qid]);
      var cid = this.newCid();      
      collapsedQueryObject[cid] = queryObject[qid];
      this._cidMap[cid] = [qid];
    }    
  }
  
  var collapsed = {};
  for(var i=0, len=collapsable.length; i<len; i++) {
    var qid1 = collapsable[i];
    if (collapsed[qid1]) {
      continue;
    }
    
    var cid = this.newCid();
    var q1 = queryObject[qid1];
    var collapseArr = [q1];
    collapsed[qid1] = true;
    this._cidMap[cid] = [qid1];
    
    for(var j=i+1, len2=collapsable.length; j<len2; j++) {
      var qid2 = collapsable[j];
      if (collapsed[qid2]) {
        continue;
      }
      
      var q2 = queryObject[qid2];
      if (this._collapsableEquals(q1, q2)) {
        collapseArr.push(q2);
        collapsed[qid2] = true;
        this._cidMap[cid].push(qid2);
      }
    }
    	
    	var collapsedQuery = this._collapseQueries(collapseArr);
    	collapsedQueryObject[cid] = collapsedQuery;
  }
  
  return collapsedQueryObject;
};
MQLReadDataProvider.prototype._collapseQueries = function(arr) {
  var collapsed = {query:{}};
  
  for(var i=0, len=arr.length; i<len; i++) {
    	var q = arr[i];
  	
    	for (var key in q.query) {
    	  collapsed.query[key] = q.query[key];
    	}
  }
  return collapsed;
};
MQLReadDataProvider.prototype._collapsableEquals = function(obj1, obj2) {
  return this._collapsableCompare(obj1, obj2) &&
         this._collapsableCompare(obj2, obj1);
};
MQLReadDataProvider.prototype._collapsableCompare = function(left, right) {
  for (var key in left.query) {
    switch (typeof left.query[key]){
      case "object":
        if (typeof right.query[key] != "undefined") {//Debug.warn(1);
          return false;
        }
        if (left.query[key] instanceof Array) {
          if (left.query[key].length == 1) {
            var item = left.query[key][0];
            if (typeof item["optional"] == "undefined") {//Debug.warn(2);
              return false;
            }
            else if (typeof item["optional"] == "boolean" && false == item["optional"]) {//Debug.warn(3);
              return false;
            }
          }
          else {//Debug.warn(4);
            return false;
          }
        }
        else {
          if (left.query[key] == null) {
            if (right.query[key] != null) {//Debug.warn(5);
              return false;
            }
          }
          else if (typeof left.query[key]["optional"] == "undefined") {//Debug.warn(6);
            return false;
          }
          else if (typeof left.query[key]["optional"] == "boolean" && false == left.query[key]["optional"]) {//Debug.warn(7);
            return false;
          }
        }
        break;
      default:
        if (typeof left.query[key] != typeof right.query[key]) {//Debug.warn(8);
          return false;
        }
        else if (left.query[key] != right.query[key]) {//Debug.warn(9);
          return false;
        }
        break;
    }
  }  
  return true;
};
MQLReadDataProvider.prototype._isCollapsable = function(queryObj) {
  if (!"query" in queryObj) return false;
  if (queryObj.query instanceof Array) return false;
  for (var key in queryObj.query) {
    var obj = queryObj.query[key];
    switch (typeof obj){
      case "object":
        if(obj instanceof Array){
          if (obj.length > 0) {
            var optional = obj[0]["optional"];
            if (typeof optional == "undefined" || (typeof optional == "boolean" && !optional)) {
              //Debug.log("_isCollapsable", queryObj, false);
              return false;
            }
          }
        }
        else {
          if (obj != null) {
            var optional = obj["optional"];
            if (typeof optional == "undefined" || (typeof optional == "boolean" && !optional)) {
              //Debug.log("_isCollapsable", queryObj, false);
              return false;
            }
          }
        }
        break;
      default:
        break;
    }
  }
  //Debug.log("_isCollapsable", queryObj, true);
  return true;
};
/***************************************************** MQLBatchReadDataProvider
 * Not a DataProvider. Works with MQLQueryMannager to make batch queries.
 * Works like DataProvider in that you can call requestData(callback)
 * which is asynchronous where callback will be called with the result data.
 */
function MQLBatchReadDataProvider(queryObject, headers) {
  this._queryObject = queryObject;
  this._dataRequesters = [];
  this._data = null;
  MQLQueryManager.queue(this);
};
MQLBatchReadDataProvider.prototype.requestData = function(dataHandler, transform){
  if(!dataHandler) {
    slkdfjlksjdfxyz[ldksja] = lkjlkjlkj;
    //throw "MQLDataProvider.requestData: dataHandler must not be null";
  }
  var requester = {dataHandler:dataHandler,transform:transform};
  // already received data:
  if(this._data) this.setSelectedData(requester);
  // data still loading:
  else this._dataRequesters.push(requester);
}
MQLBatchReadDataProvider.prototype.getQueryObject = function() {
  return this._queryObject;
};
MQLBatchReadDataProvider.prototype.handleData = function(data) {
  this._data = data; 
  if (this._data) {
    for(var i=0, len=this._dataRequesters.length; i<len; i++){
      if(this._dataRequesters[i])
        this.setSelectedData(this._dataRequesters[i]);
    }
    this._dataRequesters = null;
  }
};
MQLBatchReadDataProvider.prototype.setSelectedData = function(requester){
  var data;
  //Debug.log("data " + this._data + "; transform " + requester.transform);
  var metadata = requester._metadata;
  if(requester.transform){
    if(typeof requester.transform == "function"){
      data = requester.transform(this._data);
    }
  }
  else data = this._data;
  if (typeof requester.dataHandler == "object" && 
      requester.dataHandler.receiveData ){
    requester.dataHandler.receiveData(data, metadata);//setData
  }
  else if (typeof requester.dataHandler == "function") {
    requester.dataHandler.call(null, data, metadata);
  }
};
/************************************************************** MQLQueryManager
 */
MQLQueryManager = {
 _queue: [],
 _timeout: [],
 // batch id and map
 // bidMap is a mapping of batch ID (i.e. "b3") to the actual query name
 // (i.e. "q")
 _bid: 0,
 _bidMap: {}
};
MQLQueryManager.newBid = function() {
  return "b" + MQLQueryManager._bid++;
};
MQLQueryManager.queue = function(/* MQLBatchReadDataProvider */ dp) {
  clearTimeout(MQLQueryManager._timeout);
  MQLQueryManager._timeout = null;  
  MQLQueryManager._queue.push(dp);
  
  this._timeout = setTimeout(MQLQueryManager.checkQueue, 
                             MQLQueryManager.QUERY_DELAY);  
};
MQLQueryManager.checkQueue = function() {
  // batch up all pending queries into one big query envelope
  var SPLIT_ACROSS_GETS = false;
  
  clearTimeout(MQLQueryManager._timeout);
  MQLQueryManager._timeout = null   
  var batchQuery = {};
  var batchQueries = [batchQuery];  
  var currentSize = 0;
  for(var i=0, len=MQLQueryManager._queue.length; i<len; i++) {
    var dp = MQLQueryManager._queue[i];
    var queryObj = dp.getQueryObject();
    
    for (var queryName in queryObj) {
      // this ends up being useless because we can't collapse across
      // queries that have been broken up this way. I'm leaving the
      // code in for now because I have some ideas to make this
      // work -alecf
      
      if (SPLIT_ACROSS_GETS) {
        // make a throw-away string to try and guess if it overflows the
        // POST threshold. It would be nicer to avoid double-formatting
        // each query, but we'll do it for now and see how it affects
        // performance
        var queryString = JSONUtils.format(queryObj[queryName]);
        queryString = encodeURIComponent(queryString);
        
        // try to avoid POSTs - the 6 is for the eventual '"c0":' and
        // possible comma
        if (currentSize > 0 &&
            currentSize + queryString.length + DataProvider.FUDGE_FACTOR + 6 >
            DataProvider.POST_SIZE_THRESHOLD) {
          
          Debug.log("Query ", queryName, " overflows with ", queryString.length);
          // save off the current query and start a new one
          currentSize = 0;
          batchQuery = {};
          batchQueries.push(batchQuery);
        } else {
          
          // keep working on this one
          currentSize += queryString.length;
          Debug.log("Query ", queryName, " is fine with ", queryString.length);
        }
        Debug.log("Now have ", currentSize, " bytes");
      }
      
      var bid = MQLQueryManager.newBid();
      batchQuery[bid] = queryObj[queryName];
      MQLQueryManager._bidMap[bid] = {queryName:queryName, dp:dp};
    }
  }
  // when we get here, batchQueries contains all pending queries
  MQLQueryManager._queue = [];
  
  //var collapsedQuery = MQLQueryManager._collapseQuery(batchQuery);
  for (var i=0; i<batchQueries.length; i++) {
    var dp = new MQLReadDataProvider(batchQueries[i]);
    dp.requestData(MQLQueryManager.batchResponse);
  }
};
MQLQueryManager.batchResponse = function(data) {
  //Debug.log("batchResponse ", data);
  for (var bid in data) {
    // first pull out original query name & dataprovider from the results
    var queryInfo = MQLQueryManager._bidMap[bid];
    if (!queryInfo) {
      Debug.warn("Unknown batch query result (bid=", bid, "):", data[bid]);
      continue;
    }
    // now synthesize a data result with the original queryName
    var resultData = queryInfo.dp._result;
    // haven't seen this one before, create a new result object
    if (!resultData)
      queryInfo.dp._result = resultData = {};
    resultData[queryInfo.queryName] = data[bid];       
  }
  // now go through all pending queries and let them know the result
  // has arrived, handing off the matching synthetic result objects
  for (var bid in MQLQueryManager._bidMap) {
    var queryInfo = MQLQueryManager._bidMap[bid];
    if (!(queryInfo && queryInfo.dp._result)) continue;
    if (!queryInfo.dp._data)
      queryInfo.dp.handleData(queryInfo.dp._result);
    delete MQLQueryManager._bidMap[bid];
  }
};
MQLQueryManager.QUERY_DELAY = 0; // ms
/************************************************************************** mql
 * 
 * Utility method to create a new MQLDataProvider object given the queryString
 * and whether or not to post.
 * 
 * @param queryObject:String
 * @param write:Boolean
 * @return return - DataProvider
 */ 
function mql(queryObject, write, nobatch, options, headers) {
  if (write) {
    return new MQLWriteDataProvider(queryObject, options, headers);
  }
  else if (nobatch) {
    return new MQLReadDataProvider(queryObject, options, headers, true);
  }
  else {
    if (typeof MQL_BATCH == "boolean" && MQL_BATCH == true) {
      return new MQLBatchReadDataProvider(queryObject, headers);
    }
    else {
      return new MQLReadDataProvider(queryObject, options, headers);
    }
  }
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebDevToolbar</b>
 *
 * @author daepark@apmindsf.com
 */
function MetawebDevToolbar() {
};
MetawebDevToolbar.set_option = function(div, selector, cookieName, callback) {
  function onChangeSelect(e) {
    var target = e.target;
    var value = target.value;
    writeCookie(cookieName, value, 336, "/");
    if (callback)
      callback(value);
  }
  
  var select = $(div).find(selector);
  var value = readCookie(cookieName);
  select.change(onChangeSelect);
  select.each(function() {
      var opt = this;
      children = $(this).children("option").each(function(i) {
          if (this.value == value)
            opt.selectedIndex = i;
        });
    });
}
MetawebDevToolbar.init = function(div) {
  if (!div)
    return;
  MetawebDevToolbar.div = div;
  $(document).keydown(MetawebDevToolbar._onKeyDown);
  $(div).find(".refreshCache").click(MetawebDevToolbar._refreshCache);
  $(div).find(".viewTransactionLog").click(MetawebDevToolbar._viewTransactionIdLog);
  $(div).find(".hide").click(MetawebDevToolbar._toggleVisible);
  MetawebDevToolbar.set_option(div, ".lookup-source", "lookupSource");
  MetawebDevToolbar.set_option(div, ".debug-level", "debugLevel", Debug.enable);
  // check devbar cookie
  var devbarVisible = false;
  cookie = readCookie("devbar");
  if(cookie.toLowerCase()=="true") {
    devbarVisible = true;  
  }
  $(div).show(devbarVisible);
}
  
MetawebDevToolbar.superClass = [BaseComponent];
registerClass(MetawebDevToolbar, "MetawebDevToolbar");
MetawebDevToolbar._onKeyDown = function(e){
  // check for f8 key:119, f12:123 + ctrl or shift modifiers
  // or just in case those don't work,
  // check for ctrl+shift+d
  if(e.keyCode == 119 || e.keyCode == 123
    || (e.keyCode == 68 && e.shiftKey && e.ctrlKey)){
    $(MetawebDevToolbar.div).toggle();
  }
  return true;
};
MetawebDevToolbar._viewTransactionIdLog = function(){
  if(viewTransactionIdLogURL != ""){
    var form = $(document.body).append("<form>").children();
    form = form[form.length-1]; // last element
    
    createHiddenInput(form, "tid", g_transactionIdList);
    form.setAttribute("action",viewTransactionIdLogURL);
    form.setAttribute("method","get");
    form.target = "_blank";
    form.submit();
  }
  return false;
};
MetawebDevToolbar._refreshCache = function(e){
  var div = MetawebDevToolbar.div;
  var httpRequestHandler = {
    object: div,
    callback: MetawebDevToolbar._freshenResponse,
    errback: MetawebDevToolbar._freshnenError
  };
  new HTTPRequest("/api/service/mqlwrite", httpRequestHandler, "");
  return false;
}
MetawebDevToolbar._freshenResponse = function(httpRequest){
  // do nothing if it was okay.
}
MetawebDevToolbar._freshnenError = function(httpRequest){
  if (httpRequest) {
    json = httpRequest.responseJSON();
  }
  var status = json.status;
  if (!status) {
    Debug.error("Missing status in result", json);
    return;
  }
  var message = null;
  if (json.messages && json.messages.length > 0) {
    message = json.messages[0];
  }
  if (!message) {
    Debug.error("Missing error message in result", json);
    return;
  }  
  if (status.indexOf("400") == 0) {
    if (message.text && message.text == "one of q=, query=, or queries= must be provided"){
      Debug.error("No cache present - Unable to fulfill request to freshen to newest");
      return;
    }
  }
  else {
    // Should not arise
    Debug.error("Unknown error (text:", message.text,  "type: ", message.type, ") - Unable to fulfill request to freshen to newest");
    return;
  }
}
MetawebDevToolbar._toggleVisible = function(){
  var div = MetawebDevToolbar.div;
  if ($(div).css("display") == "none") { //show
    $(div).show();
    writeCookie("devbar", "true", 336, "/");
  }
  else { //hide
    $(div).hide();
    writeCookie("devbar", "false", 336, "/");    
  }
  return false;
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MediatorList</b>
 *
 * @author daepark@apmindsf.com
 */
function MediatorList(div) {
  setupInheritance(MediatorList);
  superConstructor(SimpleMetawebList, this, [div]);
};
MediatorList.superClass = [SimpleMetawebList];
registerClass(MediatorList, "MediatorList");
MediatorList.prototype.CLASS_NAME = "MediatorList";
MediatorList.prototype._createMetawebListItem = function(div) {
  return new MediatorListItem(div);
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>PreviewList</b>
 * List of PreviewListItems
 *
 * @author alecf@metaweb.com
 */
function PreviewList(div) {
  this._batchArticle = true;
  setupInheritance(PreviewList);
  superConstructor(AbstractList, this, [div]);
}
PreviewList.superClass = [AbstractList];
registerClass(PreviewList, "PreviewList");
PreviewList.prototype.CLASS_NAME = "PreviewList";
PreviewList.prototype._createListItem = function(div) {
  var item = new PreviewListItem(div);
  item.batchArticle(this._batchArticle);
  item.addEventListener("onResourcesLoaded", this);
  return item;
};
PreviewList.prototype.batchArticle = function(b) {
  this._batchArticle = (true == b);
};
PreviewList.prototype.onResourcesLoaded = function(e) {
  this.dispatchEvent({type:"onItemResourcesLoaded", listItem:e.target});
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MediatorCollection</b>
 *
 * @author daepark@apmindsf.com
 */
function MediatorCollection(div){
  setupInheritance(MediatorCollection);
  superConstructor(SimpleMetawebCollection, this, [div]);
}
MediatorCollection.superClass = [SimpleMetawebCollection];
registerClass(MediatorCollection, "MediatorCollection");
MediatorCollection.prototype.CLASS_NAME = "MediatorCollection";
MediatorCollection.prototype._createMetawebList = function(div) {
  return new MediatorList(div);
}
/* DATA EXAMPLE:
_data = {
  list: {
	  listItems: [{
		  id: "#1",
		  properties: [
		    {
		      property: "Actor",
		      propertyId: "/movie/acting_gig/actor",
		      type: "Actor",
		      typeId: "/movie/actor",
		      id: "#1actor",
		      text:"Sean Connery",
		      url: "http://www.yahoo.com",
		      autocompleteURL: "../../autocomplete.json"
		    },
		    {
		      property: "Role",
		      propertyId: "/movie/acting_gig/role",
		      type: "Text",
		      typeId: "/type/text",
		      text:"007"
		    }
		  ]
		},{
		  id: "#2",
		  properties: [
		    {
		      property: "Actor",
		      propertyId: "/movie/acting_gig/actor",
		      type: "Actor",
		      typeId: "/movie/actor",
		      id: "2actor",
		      text:"Roger Moore",
		      url: "http://www.yahoo.com",
		      autocompleteURL: "../../autocomplete.json"
		    },
		    {
		      property: "Role",
		      propertyId: "/movie/acting_gig/role",
		      type: "Text",
		      typeId: "/type/text",
		      text:"008"
		    }
		  ]
		}]
	}
};
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>EditableMetawebSummary</b>
 *
 * @author alee@metaweb.com
 */
function EditableMetawebSummary(div){
  setupInheritance(EditableMetawebSummary);
  superConstructor(EditableMetawebText, this, [div]);
}
EditableMetawebSummary.superClass = [EditableMetawebText];
registerClass(EditableMetawebSummary, "EditableMetawebSummary");
EditableMetawebSummary.prototype.CLASS_NAME = "EditableMetawebSummary";
EditableMetawebSummary.prototype._createDisplayContent = function(){
  superMethod(EditableMetawebText, this, "_createDisplayContent", []);
  if (!this._data) return;
  
  if (this._data.seedWithWikipedia != null && (typeof this._data.seedWithWikipedia.requested == "undefined" || this._data.seedWithWikipedia.requested == null)) {
    // If seedWithWikipedia data structure exists, fetch wikipedia
    // blurb and put it in content area
    this._data.seedWithWikipedia.requested = true;
    var callback = {
      callback: Delegate.create(this, this.onRequestLoaded, ["emptyMessage"]),
      errback: Delegate.create(this, this.onRequestError)
    };
    new HTTPRequest(this._data.seedWithWikipedia.blurbSrc, callback);
  }
}
EditableMetawebSummary.prototype._updateEmptyVariables = function(loading) {
  // Override superClass._updateEmptyVariables for EditableMetawebSummary's rules
  if (typeof loading == "undefined" || loading == false){
    var data = this.getData();
    if (this._isEmpty()) {
      // change emptyMessage if need be
      if (data.seedWithWikipedia && !StringUtils.isEmpty(data.seedWithWikipedia.html)){
        // using Wikipedia article as seed
        this.EMPTY_COMPONENT_CSSCLASS = "WikipediaSeedEmpty";
        this.EMPTY_MESSAGE_CSSCLASS = "seedWikipedia"
        this._emptyMessage = data.seedWithWikipedia.html;
        this._licenseMessage = data.seedWithWikipedia.attribution;
        this._emptyMessageAddText = "You can replace it.";
        this._articleOperations[EditableMetawebText.EDIT_COMMAND].text = "replace";
        this._buttons[EditableMetawebText.EDIT_COMMAND].setLabel("replace");
      }
      else if (!data.seedWithWikipedia && !StringUtils.isEmpty(data.id)){
        // had a user summary but it was wiped out
        this.EMPTY_COMPONENT_CSSCLASS = "UserDeletedEmpty";
        this._licenseMessage = null;
        this._emptyMessage = sprintf('The description for "%s" was removed.', g_application.conceptName);
        this._emptyMessageAddText = "Write a new one.";
        if (this._buttons[EditableMetawebText.EDIT_COMMAND].getLabel() != "edit") {
          this._articleOperations[EditableMetawebText.EDIT_COMMAND].text = "edit";
          this._buttons[EditableMetawebText.EDIT_COMMAND].setLabel("edit");
        }
      }
    }
  }
}
EditableMetawebSummary.prototype._buildEmptyMessageDiv = function(loading){
  superMethod(EditableMetawebText, this, "_buildEmptyMessageDiv", [loading]);
  
  // insert after the  _emptyMessageLabel and before a possibly existing _emptyMessageAddLabel
  // licenseMessage (text and dblclick interaction) if it exists
  if (!this._licenseMessage) return;
  
  this._licenseMessageLabel = this.addChildComponent(Label, this._emptyMessageDiv, "div", 1, null, "licenseText", [true]);
  if (!loading) {
    // Empty message add LinkButton
    // Don't hang dblclick as per bug # 3613 and # 3646
    // wikipedia license text associated with _emptyMessage which is the wikipedia blurb
    this._licenseMessageLabel.setText(this._licenseMessage);
  }
}
EditableMetawebSummary.prototype._destroyEmptyMessageDiv = function(){
  if (this._licenseMessageLabel) {
    // Don't hang dblclick as per bug # 3613 and # 3646
    this._licenseMessageLabel.destroy();
    this._licenseMessageLabel = null;
  }
  superMethod(EditableMetawebText, this, "_destroyEmptyMessageDiv");
}
EditableMetawebSummary.prototype.onRequestLoaded = function(dataFieldName, httpRequest){
  var html = httpRequest.responseText();
  if (dataFieldName == "emptyMessage") {
    // use this blurb as full content of MetawebSummary but change the emptyMessage to caveat about this being automatically extracted summary from Wikipedia
    this._data.seedWithWikipedia.html = html;
    this._data.seedWithWikipedia.text = html;
    this._updateEmptyMessage(false);
    Debug.log("EditableMetawebSummary.onRequestLoaded", this._emptyMessageLabel.acceptHTML)
  }
  else{
    if (this._buttons &&
        this._buttons[EditableMetawebText.EDIT_COMMAND].getLabel() != "edit") {
      // Set it back to "edit"-labeled button once there is content - might previously been "replace' because it was seeded with automatically extracted wikipedia content.
      this._articleOperations[EditableMetawebText.EDIT_COMMAND].text = "edit";
      this._buttons[EditableMetawebText.EDIT_COMMAND].setLabel("edit");
    }
    superMethod(EditableMetawebText, this, "onRequestLoaded", [dataFieldName, httpRequest]);
  }
}
EditableMetawebSummary.prototype.onRequestError = function(httpRequest) {
  this._data.seedWithWikipedia.html = " ";
  this._data.seedWithWikipedia.text = " ";
  this._updateEmptyMessage(false);
  var errorMessage = sprintf("We're sorry, there was a problem loading the article blurb for \"%s\".", g_application.conceptName);
  this.onError(errorMessage, null, httpRequest.responseText());
}
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebUserCollection</b>
 *
 * @author stephanie@metaweb.com
 */
function MetawebUserCollection(div){
  setupInheritance(MetawebUserCollection);
  superConstructor(AbstractCollection, this, [div]);
  // default menu
  this.setCollectionMenu({ menu: []});
};
MetawebUserCollection.superClass = [AbstractCollection];
registerClass(MetawebUserCollection, "MetawebUserCollection");
MetawebUserCollection.prototype.CLASS_NAME = "MetawebUserCollection";
MetawebUserCollection.prototype._createEditableList = function(div) {
  return new MetawebUserList(div);
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebImageCollection</b>
 * 
 * @author alee@metaweb.com
 */
function MetawebImageCollection(div){
  setupInheritance(MetawebImageCollection);
  superConstructor(DeepPropertyCollection, this, [div]);
};
MetawebImageCollection.superClass = [DeepPropertyCollection];
registerClass(MetawebImageCollection, "MetawebImageCollection");
MetawebImageCollection.prototype.CLASS_NAME = "MetawebImageCollection";
MetawebImageCollection.prototype._createMetawebList = function(div) {
  var list = new MetawebImageList(div);
  list.addEventListener("onLoadDeepProperty", this);
  return list;
};
MetawebImageCollection.prototype.add_finish = function(data, index){
  if (typeof index == "undefined")
    index = 0;
  var newItem = this._list.addListItem(data, index);
  this._list.setSelection(newItem);
  // update menus if need be
  this._overrideComponentMenus();
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>EditableMetawebLabel</b>
 *
 * @author alee@metaweb.com
 */
function EditableMetawebLabel(div){
  // member vars
  this._menuData;
  this._menu;
  this._popup;
  setupInheritance(EditableMetawebLabel);
  superConstructor(AbstractEditableItem, this, [div]);
}
EditableMetawebLabel.superClass = [AbstractEditableItem];
registerClass(EditableMetawebLabel, "EditableMetawebLabel");
EditableMetawebLabel.prototype.CLASS_NAME = "EditableMetawebLabel";
EditableMetawebLabel.prototype.setData = function(data){
  superMethod(AbstractEditableItem, this, "setData", [data]);
  this._data.label = this._data.text;
  if (this._item)
    this._item.setData(this._data);
}
EditableMetawebLabel.prototype._isEmpty = function(){
  // Either no this._data or this._data.text is null/""
  return (!this._data || !this._data.text || this._data=="");
};
EditableMetawebLabel.prototype.setItemMenu = function(menuData){
  this._menuData = menuData;
}
EditableMetawebLabel.prototype._defaultMenus = function() {
  return {
    menu: [
      {text:"Add",value:"Add",disabled: false},
      {text:"Edit",value:"Edit",disabled: false},
      {text:"Remove",value:"Remove",disabled: false},
      {text:"View page history", value:"History", disabled:false}
    ]
  };
}
EditableMetawebLabel.prototype._overrideComponentMenus = function() {
  // called onReceiveData and after add/edit/remove - CHANGE else action if need be if called by others
  if (this._isEmpty())
    // no document object instance
    this._disableMenusForEmptyList(true);
  else
    // change point.
    this.setItemMenu(this._defaultMenus());
}
EditableMetawebLabel.prototype._disableMenusForEmptyList = function(isEmpty) {
  var defaultMenus = this._defaultMenus();
  defaultMenus.menu[1].disabled = true; // Edit
  defaultMenus.menu[2].disabled = true; // Remove
  defaultMenus.menu[3].disabled = true; // View page history
  this.setItemMenu(defaultMenus);
}
EditableMetawebLabel.prototype._createTitle = function(title){
}
EditableMetawebLabel.prototype._createDisplayContent = function()
{
  // Item label plus item's popupMenu.
  this._item = this.addChildComponent(Label, this._content, "span");
  this._popup = this.addChildComponent(Button, this._content, "button", null, null, "Menu", [true]);
  this._popup.setLabel("&#9660;");
  this._popupClickHandler = Delegate.create(this, this._onMenuClick);
  DOMEvent.addEventHandler(this._popup._body, "click", this._popupClickHandler);
  this._updateEmptyMessage(false);
};
EditableMetawebLabel.prototype._destroyDisplayContent = function()
{
  if (this._item) 
    this._item.destroy();
    
  if (this._popup)
    this._popup.destroy();
  this._content.innerHTML = "";
};
EditableMetawebLabel.prototype._createEditComponent = function(div) {
  this._item = new TextInput(div, "Topic");
  this._itemTextChange = Delegate.create(this, this._onContentChange)
  this._item.addEventListener("onTextChange", this._itemTextChange);
  this._item.addEventListeners([
    "onEscapeKey", 
    "onEnterKey"
    ], this);
  return this._item;
};
EditableMetawebLabel.prototype.onAdd = function(eventObject){
  this.edit_checkPrivileges();
};
EditableMetawebLabel.prototype.onSave = function(eventObject){
  // must override to copy entered text to html
  this._disableButtons();
  var submitData = this._editComponent.getData();
  submitData.html = submitData.text;
  this.setEditable(false);
  this.edit_submit(submitData);
  this.dispatchEvent({type:"onSave", target: this, submitData:submitData});
};
EditableMetawebLabel.prototype.onEnterKey = function(e) {
  var input = e.target;
  if (!StringUtils.isEmpty(input.getText())) {
    input.dispatchEvent("onSubmitFocus");
  }  
};
EditableMetawebLabel.prototype.onEscapeKey = function(eventObject) {
  var input = eventObject.target;
  input.dispatchEvent("onSubmitCancel");
};
EditableMetawebLabel.prototype._onContentChange = function(eventObject)
{
  eventObject.target.dispatchEvent({type:"onSubmitEnable", enable:true, target: this});
};
EditableMetawebLabel.prototype._onMenuClick = function(e){
  if(!this._menu){
    this._menu = new PopupMenu();
    this._itemMenuSelect = Delegate.create(this, this._onItemMenuSelect);
    this._menu.addEventListener("onMenuSelect", Delegate.create(this, this._itemMenuSelect));
  }
  var anchor = this._popup._body;
  this._menu.show(anchor);
  this._menu.setData(this._menuData);
  this.dispatchEvent({type:"onMenuClick", target: this});
}
EditableMetawebLabel.prototype._onItemMenuSelect = function(eventObject){
  var menuItem = eventObject.menuItem;
  this._onMenuItem(menuItem.getValue());
};
EditableMetawebLabel.prototype._onMenuItem = function(value){
  // override AbstractCollection because all Unique* has "Edit" serve as "Add" and "Edit"
  this.dispatchEvent({
    type: "onItemMenuSelect",
    target: this,
    value: value
  });
}
EditableMetawebLabel.prototype.add_finish = function(data){
  superMethod(AbstractEditableItem, this, "add_finish", [data]);
  // update menus if need be
  this._overrideComponentMenus();
};
EditableMetawebLabel.prototype.delete_finish = function(){
  superMethod(AbstractEditableItem, this, "delete_finish", []);
  
  // update menus if need be
  this._overrideComponentMenus();
};
EditableMetawebLabel.prototype.edit_finish = function(data){
  superMethod(AbstractEditableItem, this, "edit_finish", [data]);
  // update menus if need be
  this._overrideComponentMenus();
}
EditableMetawebLabel.prototype.onEditError = function(synopsis, elaboration, details) {
  var h1Div = this._body.parentNode;
  superMethod(AbstractEditableItem, this, "onEditError", [synopsis, elaboration, details, h1Div]);
}
EditableMetawebLabel.prototype.onEditSuccess = function(message) {
  var h1Div = this._body.parentNode;
  superMethod(AbstractEditableItem, this, "onEditSuccess", [message, h1Div]);
}
/* 
DATA:
  _data.text
HTML:
  <div class="EditableMetawebLabel">
    <span class="Label">Arnult Swortsenanger</span>
    <span class="editButton">edit</span>
  </div>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebUserList</b>
 * List of MetawebUserListItems
 *
 * @author stephanie@metaweb.com
 */
function MetawebUserList(div) {
  setupInheritance(MetawebUserList);
  superConstructor(AbstractList, this, [div]);
}
MetawebUserList.superClass = [AbstractList];
registerClass(MetawebUserList, "MetawebUserList");
MetawebUserList.prototype.CLASS_NAME = "MetawebUserList";
MetawebUserList.prototype._createListItem = function(div) {
  return new MetawebUserListItem(div);
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>DeepPropertyListItem</b>
 *
 * @author daepark@apmindsf.com
 */
function DeepPropertyListItem(div){
  setupInheritance(DeepPropertyListItem);
  superConstructor(AbstractMetawebListItem, this, [div]);
};
DeepPropertyListItem.superClass = [AbstractMetawebListItem];
registerClass(DeepPropertyListItem, "DeepPropertyListItem");
DeepPropertyListItem.prototype.CLASS_NAME = "DeepPropertyListItem";
DeepPropertyListItem.prototype._createEditContent = function() {
  superMethod(AbstractMetawebListItem, this, "_createEditContent");
  this._editComponent._stateMachine.transition("ADDING", null);
};
DeepPropertyListItem.prototype._createEditComponent = function(div) {
  var c = new DeepPropertyEditComponent(div);
  c.addEventListener("onLoadDeepProperty", this);
  return c;
};
DeepPropertyListItem.prototype.onLoadDeepProperty = function(e) {
  this.dispatchEvent({type:"onLoadDeepProperty", target:this, id:e.id});
};
DeepPropertyListItem.prototype.setData = function(data) {
  if (!(this._checkLabelData(data) && data.autocompleteURL)) {
    Debug.error("DeepPropertListItem: missing primary field data", data);
    return;    
  }
  var properties = data["properties"];
  superMethod(AbstractMetawebListItem, this, "setData", [data]);
};
DeepPropertyListItem.prototype.loadDeepProperty = function(data) {
  if (this._editComponent) {
    this._editComponent.loadDeepProperty(data);
  }
};
function DeepPropertyEditComponent(div) {
  setupInheritance(DeepPropertyEditComponent);
  superConstructor(BaseComponent, this, [div]);
  superConstructor(AutocompleteFlyOut, this);
  this._primaryElt = null;
  this._primaryInput = null;
  this._secondaryElt = null;
  this._secondaryInputs = [];  
  this._stateMachine = new StateMachine([
    ["ADDING", new _DeepPropertyAdding(this)],
    ["CREATING", new _DeepPropertyCreating(this)],
    ["EDITNG", new _DeepPropertyEditing(this)]
  ]);
};
DeepPropertyEditComponent.superClass = [BaseComponent, AutocompleteFlyOut];
registerClass(DeepPropertyEditComponent, "DeepPropertyEditComponent");
DeepPropertyEditComponent.prototype.CLASS_NAME = "DeepPropertyEditComponent";
DeepPropertyEditComponent.prototype.init = function(){
  this.addCSSClass(this.CLASS_NAME);
};
DeepPropertyEditComponent.prototype.disablePrimaryInput = function(b) {
  if (this._primaryInput) {
    this._primaryInput.setDisabled(b);
  }
};
DeepPropertyEditComponent.prototype.disableSecondaryInputs = function(b) {
  if (this._secondaryInputs) {
    for(var i=0, len=this._secondaryInputs.length; i<len; i++) {
    	this._secondaryInputs[i].setDisabled(b);
    }
  }
};
DeepPropertyEditComponent.prototype.setFocus = function() {
  if (this._primaryInput) {
    this._primaryInput.setFocus();
  }
};
DeepPropertyEditComponent.prototype.resetSecondaryInputs = function() {
  if (this._secondaryInputs) {
    for(var i=0, len=this._secondaryInputs.length; i<len; i++) {
    	var data = this._secondaryInputs[i].getData();
    	data["id"] = null;
    	this._secondaryInputs[i].setText("");
    }
  }
};
DeepPropertyEditComponent.prototype.setData = function(data) {
  superMethod(BaseComponent, this, "setData", [data]);
  this._cleanInnerHTML();
  //var form = this.addChildTag("div", null, null, "form");
  this._primaryElt = this.addChildTag("span", null, null, "primary");
  this._primaryInput = this._createPrimaryInput(this._primaryElt);
  this._secondaryElt = this.addChildTag("span", null, null, "secondary");
  this._secondaryInputs = this._createSecondaryInputs(this._secondaryElt);
  this._primaryInput.setFocus();
};
DeepPropertyEditComponent.prototype._createPrimaryInput = function(div) {
  var input = this._createInput(div, this._data, true);
  return input;
};
DeepPropertyEditComponent.prototype._createSecondaryInputs = function(div) {
  var inputs = [];
  var properties = this._data.properties;
  if (!properties) {
    return inputs;
  }
  for(var i=0, len=properties.length; i<len; i++) {
    var input = this._createInput(div, properties[i]);
    inputs.push(input);
  }
  return inputs;
};
DeepPropertyEditComponent.prototype._createInput = function(parentDiv, data, primary) {
  var div = $elt("div", parentDiv);
  var input = AbstractMetawebListItem.createInput(div, data);
  var cn = getClassName(input);
  switch(cn) {
    case "AutocompleteInput":
      input.addEventListeners([
        "onAutocompleteSubmit",
        "onAutocompleteSelect", 
        "onAutocompleteCreateNew",  
        "onAutocompleteListSelectionChange",  // for AutocompleteFlyOut
        "onAutocompleteBlur"                  // for AutocompleteFlyOut
      ], this);
      break;
    case "EnumInput":
      input.addEventListeners([
        "onEnumSelect"
      ], this);
      break;
    default:
      input.addEventListeners([
        "onEnterKey"
      ], this);   
      break;
  };
  input.addEventListeners([
    "onEscapeKey",
    "onTextChange",
    "onChange"       // for BooleanInput
    ], this);
  input.setData(data);
  if (primary) {
    input.setPrompt(data.typeName);      
  }
  else {
    input.setPrompt(data.propertyName);
  }  
  return input;
};
DeepPropertyEditComponent.prototype._destroyInput = function(input) {
  input.destroy();
  input = null;
};
DeepPropertyEditComponent.prototype.onEscapeKey = function(e) {
  this.dispatchEvent("onSubmitCancel");
};
DeepPropertyEditComponent.prototype.destroy = function() {
  this._cleanInnerHTML();
  superMethod(AutocompleteFlyOut, this, "destroy");
  superMethod(BaseComponent, this, "destroy");
};
DeepPropertyEditComponent.prototype._cleanInnerHTML = function() {
  // destroy primary input
  if (this._primaryInput) {
    this._destroyInput(this._primaryInput);
  }
  // remove block (html) element from this._body
  if (this._primaryElt) {
    this._body.removeChild(this._primaryElt);
    this._primaryElt = null;
  }
  // destroy all secondary inputs
  if (this._secondaryInputs) {
    for(var i=0, len=this._secondaryInputs.length; i<len; i++) {
    	this._destroyInput(this._secondaryInputs[i]);
    }
  }
  this._secondaryInputs = [];
  // remove block (html) element from this._body
  if (this._secondaryElt) {
    this._body.removeChild(this._secondaryElt);
    this._secondaryElt = null;
  }
};
DeepPropertyEditComponent.prototype.onTextChange = function(e) {
  var input = e.target;
  var cn = getClassName(input);
  switch(cn) {
    case "AutocompleteInput":
    case "EnumInput":
      this._updateFieldData(input._data, input.getText(), null);
      break;
    default:   
      break;
  };
  var inputs = [this._primaryInput].concat(this._secondaryInputs);
  var enable = AbstractMetawebListItem.validateInputs(inputs);
  this.dispatchEvent({type:"onSubmitEnable", enable:enable});  
};
DeepPropertyEditComponent.prototype.onChange = function(e) {
  var inputs = [this._primaryInput].concat(this._secondaryInputs);
  var enable = AbstractMetawebListItem.validateInputs(inputs);
  //Debug.log("SimpleMetawebListItem.prototype.onTextChange", enable);
  this.dispatchEvent({type:"onSubmitEnable", enable:enable});  
};
DeepPropertyEditComponent.prototype.onEnumSelect = function(e) {
  var input = e.target;  
  var item = e.listItem;
  this._updateFieldData(input._data, item._data.text, item._data.value);
  var inputs = [this._primaryInput].concat(this._secondaryInputs);  
  this.dispatchEvent({type:"onSubmitEnable", enable:AbstractMetawebListItem.validateInputs(inputs)});     
  if (input == this._primaryInput) {
    this._stateMachine.handle({sigId:"AUTOCOMPLETE_SELECT"});
    this.dispatchEvent({type:"onLoadDeepProperty", id:item._data.value});
  }  
};
DeepPropertyEditComponent.prototype.onAutocompleteSelect = function(e) {
  var input = e.target;
  var acInfo = e.autocompleteInfo;
  this._updateFieldData(input._data, acInfo.getText(), acInfo.getValue());
  var inputs = [this._primaryInput].concat(this._secondaryInputs);    
  this.dispatchEvent({type:"onSubmitEnable", enable:AbstractMetawebListItem.validateInputs(inputs)});      
  if (input == this._primaryInput) {
    this._stateMachine.handle({sigId:"AUTOCOMPLETE_SELECT"});
    this.dispatchEvent({type:"onLoadDeepProperty", id:e.autocompleteInfo.getValue()});
  }
};
DeepPropertyEditComponent.prototype.onAutocompleteCreateNew = function(e) {
  var input = e.target;
  var acInfo = e.autocompleteInfo;
  this._updateFieldData(input._data, acInfo.getText(), acInfo.getValue());
  var inputs = [this._primaryInput].concat(this._secondaryInputs);    
  this.dispatchEvent({type:"onSubmitEnable", enable:AbstractMetawebListItem.validateInputs(inputs)});   
  if (input == this._primaryInput) {
    this._stateMachine.handle({sigId:"AUTOCOMPLETE_NEW"});
  }
};
DeepPropertyEditComponent.prototype.onEnterKey = function(e) {
  this.onSave();
};
DeepPropertyEditComponent.prototype.onAutocompleteSubmit = function(e) {

console.log("DPE cons",e);
alert ("DPE");


  this.onSave();
};
DeepPropertyEditComponent.prototype.onSave = function(e) {
  var inputs = [this._primaryInput].concat(this._secondaryInputs);   
  if (AbstractMetawebListItem.validateInputs(inputs))
    this.dispatchEvent({type:"onSubmit"})
};
DeepPropertyEditComponent.prototype._updateFieldData = function(fieldData, text, value) {
  fieldData.id = value;
  fieldData.text = text;
};
DeepPropertyEditComponent.prototype.loadDeepProperty = function(data) {
  var properties = data.properties;
  if (!properties) {
    return;
  }
  if (this._secondaryInputs) {
    for(var i=0, len=this._secondaryInputs.length; i<len; i++) {
      var currentData = this._secondaryInputs[i].getData();
      for(var j=0, len2=properties.length; j<len2; j++) {
        var property = properties[j];
        if (currentData["propertyId"] == property["propertyId"]) {
          if (typeof property["id"] != "undefined") {
            currentData["id"] = property["id"];
          }
          this._secondaryInputs[i].setText(property["text"]);
          break;
        }
      }
    }  
  }
};
DeepPropertyEditComponent.prototype.getInputComponents = function(){
  var inputs = [];
  if (this._primaryInput) {
    inputs.push(this._primaryInput);
  }
  return inputs.concat(this._secondaryInputs);
}
/******************************************************************************
 * 
 * Deep Property Editing States 
 *
 *****************************************************************************/
/*********************************************************** _DeepPropertyState
 *
 * All deep property editing states are subclasses of _DeepPropertyState
 *
 * @param editComponent:DeepPropertyEditComponent
 */
function _DeepPropertyState(editComponent) {
  setupInheritance(_DeepPropertyState);
  superConstructor(State, this, []);
  this._editComponent = editComponent;
};
_DeepPropertyState.superClass = [State];
registerClass(_DeepPropertyState, "_DeepPropertyState");
_DeepPropertyState.prototype.getEditComponent = function() {
  return this._editComponent;
};
/************************************************** _DeepPropertyAdding
 * @param editComponent:DeepPropertyEditComponent
 */
function _DeepPropertyAdding(editComponent) {
  setupInheritance(_DeepPropertyAdding);
  superConstructor(_DeepPropertyState, this, [editComponent]);
};
_DeepPropertyAdding.superClass = [_DeepPropertyState];
registerClass(_DeepPropertyAdding, "_DeepPropertyAdding");
_DeepPropertyAdding.prototype.enter = function(){//Debug.error("_DeepPropertyAdding.prototype.enter");
  superMethod(_DeepPropertyState, this, "enter"); 
  var sm = this.getStateMachine();
  var c = this.getEditComponent();
  c.disablePrimaryInput(false);
  c.disableSecondaryInputs(true);
};
/**************************************************** _DeepPropertyAdding.handle
 */
_DeepPropertyAdding.prototype.handle = function(signal) {
  if (!signal) {
    return;
  }
  var sm = this.getStateMachine();
  var c = this.getEditComponent();
  var sigId = signal["sigId"];
  switch(sigId) {
    case "AUTOCOMPLETE_SELECT":
      c.resetSecondaryInputs();
      c.dispatchEvent({type:"onSubmitEnable", enable:true});
      break;
    case"AUTOCOMPLETE_NEW":
      sm.transition("CREATING", signal);
      break;
    default:
      break;
  }
};
/******************************************************* _DeepPropertyCreating
 * @param editComponent:DeepPropertyEditComponent
 */
function _DeepPropertyCreating(editComponent) {
  setupInheritance(_DeepPropertyCreating);
  superConstructor(_DeepPropertyState, this, [editComponent]);
};
_DeepPropertyCreating.superClass = [_DeepPropertyState];
registerClass(_DeepPropertyCreating, "_DeepPropertyCreating");
_DeepPropertyCreating.prototype.enter = function() {//Debug.error("_DeepPropertyCreating.prototype.enter");
  superMethod(_DeepPropertyState, this, "enter"); 
  var sm = this.getStateMachine();
  var c = this.getEditComponent();
  c.disablePrimaryInput(false);
  c.disableSecondaryInputs(false);
};
/**************************************************** _DeepPropertyCreating.handle
 */
_DeepPropertyCreating.prototype.handle = function(signal) {
  if (!signal) {
    return;
  }
  var sm = this.getStateMachine();
  var c = this.getEditComponent();
  var sigId = signal["sigId"];
  switch(sigId) {
    case "AUTOCOMPLETE_SELECT":
      sm.transition("ADDING", signal);
      break;
    case "AUTOCOMPLETE_NEW":
      c.resetSecondaryInputs();
      c.dispatchEvent({type:"onSubmitEnable", enable:true});
      break;
    default:
      break;
  }
};
/********************************************************* _DeepPropertyEditing
 * @param editComponent:DeepPropertyEditComponent
 */
function _DeepPropertyEditing(editComponent) {
  setupInheritance(_DeepPropertyEditing);
  superConstructor(_DeepPropertyState, this, [editComponent]);
};
_DeepPropertyEditing.superClass = [_DeepPropertyState];
registerClass(_DeepPropertyEditing, "_DeepPropertyEditing");
_DeepPropertyEditing.prototype.enter = function() {//Debug.error("_DeepPropertyEditing.prototype.enter");
  superMethod(_DeepPropertyState, this, "enter"); 
  var sm = this.getStateMachine();
  var c = this.getEditComponent();
  c.disablePrimaryInput(true);
  c.disableSecondaryInputs(false);
};
/* DATA EXAMPLE:
_data = {
  propertyName: "Filmography", 
  propertyId: "/movie/actor/filmography",
  typeName:"Film",
  typeId: "/movie/film",	      
  id: "#guid",
  text:"Star Wars",
  url: "http://xyz.com",
  autocompleteURL: "http://autocomplete.html",
  properties: [
    {
      propertyName: "Director",
      propertyId: "/movie/film/director",
      typeName:"Director",
      typeId: "/movie/director",	
      id: "1234",
      text:"George Lucas",
      type:"person",
      url: "http://xyz.com",
      autocompleteURL: "http://autocomplete.html"
    },
    {
      propertyName: "Rating",
      propertyId: "/movie/film/rating",
      typeName:"Text",
      typeId: "/type/text",
      text:"PG-13"
    },
    {
      propertyName: "Length",
      propertyId: "/movie/film/length",
      typeName:"Duration",
      typeId: "/type/duration",
      text:"2:30"
    }
  ]
};
HTML:
  <li class="AbstractMetawebListItem">
    <span class="Filmography Label"><a href="http://xyz.com">Star Wars</a></span>
    <span class="Director Label"><a href="http://xyz.com">George Lucas</a></span>
    <span class="Rating Label">PG-13</span>
    <span class="Length Label">2:30</span>
    <button class="PopupButton Button">
      <span>&#9660;</span>
    </button>
  </li>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebDiscussList</b>
 * List of MetawebDiscussListItems
 *
 * @author stephanie@metaweb.com
 */
function MetawebDiscussList(div) {
    setupInheritance(MetawebDiscussList);
    superConstructor(SimpleMetawebList, this, [div]);
}
MetawebDiscussList.superClass = [SimpleMetawebList];
registerClass(MetawebDiscussList, "MetawebDiscussList");
MetawebDiscussList.prototype.CLASS_NAME = "MetawebDiscussList";
MetawebDiscussList.prototype._createListItem = function(div) {
    return new MetawebDiscussListItem(div);
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>SimpleMetawebListItem</b>
 *
 * @author daepark@apmindsf.com
 */
function SimpleMetawebListItem(div){  
  setupInheritance(SimpleMetawebListItem);
  superConstructor(AbstractMetawebListItem, this, [div]);
  superConstructor(AutocompleteFlyOut, this);
};
SimpleMetawebListItem.superClass = [AbstractMetawebListItem, AutocompleteFlyOut];
registerClass(SimpleMetawebListItem, "SimpleMetawebListItem");
SimpleMetawebListItem.prototype.CLASS_NAME = "SimpleMetawebListItem";
SimpleMetawebListItem.prototype.destroy = function() {
  superMethod(AutocompleteFlyOut, this, "destroy");
  superMethod(AbstractMetawebListItem, this, "destroy");  
};
SimpleMetawebListItem.prototype._createEditComponent = function(div) {
  var input = AbstractMetawebListItem.createInput(div, this._data);
  var cn = getClassName(input);
  switch(cn) {
    case "AutocompleteInput":
      input.addEventListeners([
        "onAutocompleteSubmit",
        "onAutocompleteSelect", 
        "onAutocompleteCreateNew",  
        "onAutocompleteListSelectionChange",  // for AutocompleteFlyOut
        "onAutocompleteBlur"                  // for AutocompleteFlyOut
      ], this);
      break;
    case "EnumInput":
      input.addEventListeners([
        "onEnumSelect"
      ], this);
      break;   
    default:
      input.addEventListeners([
        "onEnterKey"
      ], this);    
      break;
  };
  input.setPrompt(this._data.typeName);
  input.addEventListeners([
    "onEscapeKey",
    "onTextChange",
    "onChange"       // for BooleanInput
    ], this);
  //input.setFocus();
  return input;
};
SimpleMetawebListItem.prototype._destroyEditContent = function() {
  superMethod(AbstractMetawebListItem, this, "_destroyEditContent");
  superMethod(AutocompleteFlyOut, this, "hideFlyOut");
};
SimpleMetawebListItem.prototype.onTextChange = function(e) {
  var input = e.target;
  var cn = getClassName(input);
  switch(cn) {
    case "AutocompleteInput":
    case "EnumInput":
      this._updateFieldData(input._data, input.getText(), null);
      break;
    default:   
      break;
  };
  var enable = AbstractMetawebListItem.validateInputs([input]);
  //Debug.log("SimpleMetawebListItem.prototype.onTextChange", enable);
  input.dispatchEvent({type:"onSubmitEnable", enable:enable});  
};
SimpleMetawebListItem.prototype.onChange = function(e) {
  var input = e.target;
  var enable = AbstractMetawebListItem.validateInputs([input]);
  //Debug.log("SimpleMetawebListItem.prototype.onTextChange", enable);
  input.dispatchEvent({type:"onSubmitEnable", enable:enable});  
};
SimpleMetawebListItem.prototype.onEnumSelect = function(e) {
  var input = e.target;  
  var item = e.listItem;
  this._updateFieldData(input._data, item._data.text, item._data.value);
  input.dispatchEvent({type:"onSubmitEnable", enable:AbstractMetawebListItem.validateInputs([input])}); 
};
SimpleMetawebListItem.prototype.onAutocompleteSelect = function(e) {
  var input = e.target;
  var acInfo = e.autocompleteInfo;
  this._updateFieldData(input._data, acInfo.getText(), acInfo.getValue());
  input.dispatchEvent({type:"onSubmitEnable", enable:AbstractMetawebListItem.validateInputs([input])}); 
};
SimpleMetawebListItem.prototype.onAutocompleteCreateNew = function(e) {
  var input = e.target;
  var acInfo = e.autocompleteInfo;
  this._updateFieldData(input._data, acInfo.getText(), acInfo.getValue());
  input.dispatchEvent({type:"onSubmitEnable", enable:AbstractMetawebListItem.validateInputs([input])}); 
};
SimpleMetawebListItem.prototype._updateFieldData = function(fieldData, text, value) {
  fieldData.id = value;
  fieldData.text = text;
};
SimpleMetawebListItem.prototype.onEnterKey = function(e) {
  this.onSave();
};
SimpleMetawebListItem.prototype.onAutocompleteSubmit = function(e) {
console.log("SMLI cons",e);
alert ("oooo");
  this.onSave();
};
SimpleMetawebListItem.prototype.onSave = function(e) {
  if (AbstractMetawebListItem.validateInputs([this._editComponent]))
    superMethod(AbstractMetawebListItem, this, "onSave");
}
SimpleMetawebListItem.prototype.onEscapeKey = function(e) {
  var input = e.target;
  input.dispatchEvent("onSubmitCancel");
};
/* DATA EXAMPLE:
_data = {
  propertyName: "Actor",
  propertyId: "/movie/acting_gig/actor",
  typeName: "Actor",
  typeId: "/movie/actor",
  id: "1234",
  text:"Sean Connery",
  url: "http://www.yahoo.com",
  autocompleteURL: "../../autocomplete.json"
};
HTML:
  display:
  <li class="SimpleMetawebListItem">
    <span class="Actor Label"><a href="http://xyz.com">Roger Moore</a></span>
    <button class="PopupButton Button">
      <span>&#9660;</span>
    </button>
  </li>
  edit: 
  <li class="MetawebListItem">
    <div class="SimpleMetawebEditComponent">
      <div class="form">
        <div class="TextInput Autocomplete">
          <div class="Label">actor</div>
          <input name="actor" autocomplete="off" type="text">
        </div>
      </div>
      <div class="submit">
        <button class="Button"><span>Save</span></button>
        <button class="Button"><span>Cancel</span></button>
      </div>
    </div>
  </li>
*/
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>DeepPropertyList</b>
 *
 * @author daepark@apmindsf.com
 */
function DeepPropertyList(div) {
  setupInheritance(DeepPropertyList);
  superConstructor(SimpleMetawebList, this, [div]);
};
DeepPropertyList.superClass = [SimpleMetawebList];
registerClass(DeepPropertyList, "DeepPropertyList");
DeepPropertyList.prototype.CLASS_NAME = "DeepPropertyList";
DeepPropertyList.prototype._createMetawebListItem = function(div) {
  var li = new DeepPropertyListItem(div);
  li.addEventListener("onLoadDeepProperty", this);
  return li;
};
DeepPropertyList.prototype.onLoadDeepProperty = function(e) {
  var li = e.target;
  var id = e.id;
  this.dispatchEvent({type:"onLoadDeepProperty", listItem:li, id:id});
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebImageListItem</b>
 *
 * @author alee@metaweb.com
 */
function MetawebImageListItem(div){  
  setupInheritance(MetawebImageListItem);
  superConstructor(AbstractMetawebListItem, this, [div]);
};
MetawebImageListItem.superClass = [AbstractMetawebListItem];
registerClass(MetawebImageListItem, "MetawebImageListItem");
MetawebImageListItem.prototype.CLASS_NAME = "MetawebImageListItem";
MetawebImageListItem.prototype._createEditComponent = function(div) {
  return null;
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * UserWelcome class
 * stephanie@metaweb.com
 */
 
function Badges(div) {
  setupInheritance(Badges);
  superConstructor(BaseComponent, this, [div]);
}
Badges.superClass = [BaseComponent];
registerClass(Badges, "Badges");
Badges.prototype.CLASS_NAME = "Badges";
Badges.prototype._createDisplayContent = function()
{
  // create table for layout
  this._table = this.addChildTag("table");
  var tbody = $elt("tbody", this._table);
  this._tr = $elt("tr", tbody);
};
Badges.prototype._destroyDisplayContent = function() {
  if (!this._label)
    return;
  this._body.removeChild(this._table)
  
  this._label.destroy();
  delete this._label;
  this._previewImage.destroy();
  delete this._previewImage;
}
Badges.prototype._createCell = function() {
  var td = $elt("td", this._tr);
  return td;
}
Badges.prototype.setData = function(data) {
  superMethod(BaseComponent, this, 'setData', [data]);
  for (var i=0; i<data.badges.length; i++) {
    var badge = this.addChildComponent(Label, null, "div", null, null, "Badge-"+data.badges[i]);
  }
};
/**
 * Copyright 2005-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * <b>MetawebDiscussCollection</b>
 *
 * @author stephanie@metaweb.com
 */
function MetawebDiscussCollection(div){
  setupInheritance(MetawebDiscussCollection);
  superConstructor(SimpleMetawebCollection, this, [div]);
  // default menu
  this.setCollectionMenu({ menu: []});
};
MetawebDiscussCollection.superClass = [SimpleMetawebCollection];
registerClass(MetawebDiscussCollection, "MetawebDiscussCollection");
MetawebDiscussCollection.prototype.CLASS_NAME = "MetawebDiscussCollection";
MetawebDiscussCollection.prototype._createEditableList = function(div) {
  return new MetawebDiscussList(div);
};
/**
 * Copyright 2006-2007 Metaweb Technologies, Inc.  All Rights Reserved.
 *
 * PreviewImageListItem class
 * alecf@metaweb.com
 */
function PreviewImageListItem(div) {
  setupInheritance(PreviewImageListItem);
  superConstructor(PreviewListItem, this, [div]);
}
PreviewImageListItem.superClass = [PreviewListItem];
registerClass(PreviewImageListItem, "PreviewImageListItem");
PreviewImageListItem.prototype.CLASS_NAME = "PreviewImageListItem";
PreviewImageListItem.prototype._createDisplayContent = function()
{
  this._resetResources();  
  // create the main _label
  this._label = this.addChildComponent(PreviewLabel, null, "div");
  this._previewImage = this.addChildComponent(ImageComponent, null, "div", null, null, "PreviewImage");
  this._previewImage.addEventListener("onImageLoaded", this);
  this._typeLabel = this.addChildComponent(Label, null, "div", null, null, "TypeList");
  
  this._caption = this.addChildComponent(PreviewLabel, null, "div");
  
  this.addChildTag("div", null, null, "PreviewSpacer");
  
  // Additional image data
  this._dimensions = this.addChildComponent(Label, null, "div", 6, null, "ImageDimensions");  
  this._mediaType = this.addChildComponent(Label, null, "div", 7, null, "MediaType");  
};
PreviewImageListItem.prototype._destroyDisplayContent = function() {
  superMethod(PreviewListItem, this, '_destroyDisplayContent');
    
  this._caption.destroy();
  delete this._caption;
    
  this._dimensions.destroy();
  delete this._dimensions;
    
  this._mediaType.destroy();
  delete this._mediaType;
}
PreviewImageListItem.prototype.setData = function(data) {
  superMethod(PreviewListItem, this, 'setData', [data]);
  
  if (data.caption) 
	  this._caption.setData(data.caption);
  var labelData;
  if (data.size) {
    labelData = { text: sprintf("(%s,%s) ", data.size.x, data.size.y) };
    this._dimensions.setData(labelData);
  }
  if (data.mediaType) {
	labelData = { text: data.mediaType };
	this._mediaType.setData(labelData);
  }
};
/*
  data model
  this._data = {
    type: "/music/artist",
    id: "#291309209183...",
    text: 