
/*

 Generated by My Library Builder v1.0 (http://www.cinsoft.net/mylib-builder.asp)

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 Modified copies of the Software must be renamed.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.

*/
// Tue, 16 Nov 2010 23:57:40 UTC
// My Library (http://www.cinsoft.net/mylib.html)
// Copyright (c) 2007-2010 by David Mark. All Rights Reserved.
var API, global = this;
API = API || {};

(function() {
  var doc, html;

  // Feature testing support

  var reFeaturedMethod = new RegExp('^(function|object)$', 'i');

  // Test for properties of host objects that are never callable (e.g. document nodes, elements)

  var isRealObjectProperty = function(o, p) {
    return !!(typeof o[p] == 'object' && o[p]);
  };

  API.isRealObjectProperty = isRealObjectProperty;

  var isHostMethod = function(o, m) {
    var t = typeof o[m];
    return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');
  };

  API.isHostMethod = isHostMethod;

  var isHostObjectProperty = function(o, p) {
    var t = typeof o[p];
    return !!(reFeaturedMethod.test(t) && o[p]);
  };

  API.isHostObjectProperty = isHostObjectProperty;

  // Test multiple API properties

  var areFeatures = function() {
    var i = arguments.length;
    while (i--) {
      if (!API[arguments[i]]) {
        return false;
      }
    }
    return true;
  };

  API.areFeatures = areFeatures;

  // for-in filter

  var isOwnProperty = function(o, p) {
    var prop = o.constructor.prototype[p];
    return typeof prop == 'undefined' || prop !== o[p];
  };

  API.isOwnProperty = isOwnProperty;

  var canCall = !!Function.prototype.call;

  var bind, bindDeferred;

  if (canCall) {
    bind = function(fn, context) {
      var prependArgs = Array.prototype.slice.call(arguments, 2);

      if (prependArgs.length) {
        return function() {
          fn.apply(context, Array.prototype.concat.apply(prependArgs, arguments));
        };
      }
      return function() {
        fn.apply(context, arguments);
      };
    };

    bindDeferred = function(fn, context, delay) {
      var timeout;

      fn = bind.apply(this, [fn, context].concat(Array.prototype.slice(arguments, 3)));
      return function() {
        if (timeout) {
          global.clearTimeout(timeout);
          timeout = 0;
        }
        var args = Array.prototype.slice(arguments, 0);
        timeout = global.setTimeout(function() {
          fn.apply(this, args)
        }, delay);
      };
    };

    API.bind = bind;
    API.bindDeferred = bindDeferred;
  }

  if (isRealObjectProperty(this, 'document')) {
    doc = this.document;
  }
  
  var inherit = (function() {
    var Fn = function() {};
    return function(fnSub, fnSuper) {
      Fn.prototype = fnSuper.prototype;
      fnSub.prototype = new Fn();
      fnSub.superConstructor = fnSuper;
      fnSub.prototype.constructor = fnSub;
    };
  })();
  API.inherit = inherit;
  
  // Converts array-like objects to arrays
  function toArray(o) {
    var a = [];
    var i = o.length;
    a.length = i;
    while (i--) { a[i] = o[i]; }
    return a;
  }

  API.toArray = toArray;
  
  var getDocumentWindow;
  
  var elementUniqueId = (function() {
    var it = 0;
    return function(el) { return el.uniqueID || (el.uniqueID = '_api' + it++); };
  })();

  API.elementUniqueId = elementUniqueId;

  var attachDocumentReadyListener, bReady, documentReady, documentReadyListener, readyListeners = [];
  var canAddDocumentReadyListener, canAddWindowLoadListener, canAttachWindowLoadListener;

  if (doc) {
    canAddDocumentReadyListener = isHostMethod(doc, 'addEventListener');
    canAddWindowLoadListener = isHostMethod(this, 'addEventListener');
    canAttachWindowLoadListener = isHostMethod(this, 'attachEvent');

    //if (canAddDocumentReadyListener || canAddWindowLoadListener || canAttachWindowLoadListener) {
      bReady = false;
      documentReady = function() { return bReady; };
      documentReadyListener = function(e) {
        if (!bReady) {
          bReady = true;
          var i = readyListeners.length;
          var m = i - 1;
          // NOTE: e may be undefined (not always called by event handler)
          while (i--) { readyListeners[m - i](e); }
        }
      };

      attachDocumentReadyListener = function(fn, docNode) {
        var addListeners = function(win) {
          if (canAddDocumentReadyListener) {
            docNode.addEventListener('DOMContentLoaded', documentReadyListener, false);
          }
          if (canAddWindowLoadListener) {
            global.addEventListener('load', documentReadyListener, false);
          } else if (canAttachWindowLoadListener) {
            global.attachEvent('onload', documentReadyListener);
          } else {
            var oldOnLoad = global.onload;              
            global.onload = function(e) { if (oldOnLoad) { oldOnLoad(e); } documentReadyListener(); };
          }
        };
        docNode = docNode || global.document;
        if (docNode == global.document) {
          if (!readyListeners.length) {
            addListeners(global);
          }
          readyListeners[readyListeners.length] = fn;
          return true;
        }
        if (getDocumentWindow) {
          var win = getDocumentWindow(docNode);
          if (win) {
            addListeners(win);
            return true;
          }
        }
        return false;
      };

      API.documentReady = documentReady;
      API.documentReadyListener = documentReadyListener;
      API.attachDocumentReadyListener = attachDocumentReadyListener;
    //}
  }
  
  var hasAttribute;
  
  var addOption, addOptions, getOptionValue, formChanged, inputValue, inputChanged, removeOptions, serializeFormUrlencoded, urlencode;
  
  var changeImage;
  
  var clonePreloadedImage, preloadImage, preloaded = [];
  
  var getAnchor, getAnchors, getForm, getForms, getImage, getImages, getLink, getLinks;
  var getDOMCollectionFactory, getDOMCollectionItemFactory;
  
  var addElementHtml, elTemp, htmlToNodes, select, selectsBroken, setElementHtml, setElementOuterHtml, setSelectHtml, setTempHtml, transferTempHtml;
  
  var constructElementHtml, getDocumentHtml, getElementHtml, getElementOuterHtml, reAmp, reCollapsibleAttributes, reLT, reGT, reSelfClosing, reQuot;
  
  var addElementScript, addScript, setElementScript;
  
  var createElement, isXmlParseMode;
  
  // DOM section

  var commonElementsByTagName, disableElements, emptyNode, getAttribute, getAttributeProperty, getChildren, getEBI, getEBTN, getHeadElement, getFrameById, getIFrameDocument, removeAttribute, reUserBoolean, reURI, reSpan;
  var attributeAliases = {'for':'htmlFor', accesskey:'accessKey', codebase:'codeBase', frameborder:'frameBorder', framespacing:'frameSpacing', nowrap:'noWrap', maxlength:'maxLength', 'class':'className', readonly:'readOnly', longdesc:'longDesc', tabindex:'tabIndex', rowspan:'rowSpan', colspan:'colSpan', ismap:'isMap', usemap:'useMap', cellpadding:'cellPadding', cellspacing:'cellSpacing'};
  var attributesBad, numericAttributesBad, encTypeAttributeBad, valueHasAttributeBad;
  var camelize, reNotEmpty, reCamel = new RegExp('([^-]*)-(.)(.*)');
  
  var findReplacedElement;
  
  var createElementWithAttributes, createElementWithProperties, createAndAppendElementWithAttributes, createAndAppendElementWithProperties, setAttribute, setAttributeProperty, setAttributeProperties, createAndAppendElementWithAttributesFactory, setAttributes, setAttributesFactory;
  
  var addElementText, getElementText, setElementText, trim;
  

  if (doc) {
    getDocumentWindow = (function() {
      if (isRealObjectProperty(doc, 'parentWindow')) {
        return function(docNode) {
          return (docNode || global.document).parentWindow;
        };
      }
      if (isRealObjectProperty(doc, 'defaultView') && doc.defaultView == this) { // defaultView is not always window/global object (e.g. IceBrowser, Safari 2)
        return function(docNode) {
          return (docNode || global.document).defaultView;
        };
      }
      if (isRealObjectProperty(doc, '__parent__')) { // IceBrowser
        return function(docNode) {
          return (docNode || global.document).__parent__;
        };
      }
    })();
  }

  API.getDocumentWindow = getDocumentWindow;
  
  var purgeListeners;
  
  function getElementSize(el) { return [el.offsetHeight || 0, el.offsetWidth || 0, el.clientHeight || 0, el.clientWidth || 0, el.scrollHeight || 0, el.scrollWidth || 0]; }

  API.getElementSize = getElementSize;
  

  API.emptyNode = emptyNode = function(node) {
    while (node.firstChild) {
      node.removeChild(node.firstChild);
    }
  };

  function getElementDocument(el) {
    if (el.ownerDocument) {
      return el.ownerDocument;
    }
    if (el.parentNode) {
      while (el.parentNode) {
        el = el.parentNode;
      }
      if (el.nodeType == 9 || (!el.nodeType && !el.tagName)) {
        return el;
      }
      // FIXME: This is stupid, untangle attachListenerFactory
      if (el.document && typeof el.tagName == 'string') {
        return el.document;
      }
    }
    return null;
  }

  API.getElementDocument = getElementDocument;

  
  function getElementParentElement(el) {
    return (el.parentNode && (el.parentNode.tagName || el.parentNode.nodeType == 1))?el.parentNode:(el.parentElement || null);
  }

  API.getElementParentElement = getElementParentElement;

  function getElementNodeName(el) {
    var nn = (el.tagName || el.nodeName).toLowerCase();
    return (!nn.indexOf('html:'))?nn.substring(5):nn;
  }

  API.getElementNodeName = getElementNodeName;
  
  var decode = (function() {
    var fn = (isHostMethod(global, 'decodeURIComponent'))?global.decodeURIComponent:((isHostMethod(global, 'escape'))?global.escape:null);
    if (fn) { return function(c) { return fn(c); }; }
  })();

  var encode = (function() {
    var fn = (isHostMethod(global, 'encodeURIComponent'))?global.encodeURIComponent:((isHostMethod(global, 'escape'))?global.escape:null);
    if (fn) { return function(c) { return fn(c); }; }
  })();
  
  var transferListeners;
  // Finds new element created by outerHTML replacement

  findReplacedElement = function(el, parent, uid) {
    var m;
    m = parent.childNodes.length;
    while (m--) {
      if (parent.childNodes[m].id == el.id) {
        if (typeof transferListeners == 'function') { transferListeners(el, parent.childNodes[m]); }
        el = parent.childNodes[m];
        break;
      }
    }
    if (el.id == uid) { el.id = ''; }
    return el;
  };
  
  function elementCanHaveChildren(el) {
    return typeof el.canHaveChildren == 'undefined' || el.canHaveChildren;
  }
  
  var addStyleRule, setActiveStyleSheet;
  
  var canAdjustStyle, isStyleCapable, iStyle, styles;
  
  var allElements, getAnElement, getHtmlElement, isDescendant;
  if (doc) {

    if (isHostObjectProperty(doc, 'all')) {
      allElements = (function() {
        return function(el, bFilter) {
          var i, a, n, r;
          if (!bFilter) {
            return el.all;
          }
          else {
            a = toArray(el.all);
            i = a.length;
            r = [];

            while (i--) {
              // Code duplicated for performance
              n = a[i];
              if ((n.nodeType == 1 && n.tagName != '!') || (!n.nodeType && n.tagName)) {
                r[r.length] = a[i];
              }
            }
            return r.reverse();
          }
        };
      })();
    }

    // Returns the HTML element by default or optionally the first element it finds.
    getHtmlElement = function(docNode, bAnyElement) {
      var h, all;
      docNode = docNode || global.document;
      h = isRealObjectProperty(docNode, 'documentElement')?docNode.documentElement:((typeof getEBTN == 'function')?getEBTN('html', docNode)[0]:null);
      if (!h && allElements) {
        all = allElements(docNode); // Don't bother to filter for this
        h = all[(all[0].tagName == '!')?1:0];
        if (h && !bAnyElement && h.tagName.toLowerCase() != 'html') { h = null; }
      }
      return h;
    };

    API.getHtmlElement = getHtmlElement;

    // Returns any element
    getAnElement = function(docNode) {
      return getHtmlElement(docNode, true);
    };

    API.getAnElement = getAnElement;

    html = getAnElement();
    if (html && typeof html.parentNode != 'undefined') {
      isDescendant = function(el, elDescendant) {
        var parent = elDescendant.parentNode;
        while (parent && parent != el) {
          parent = parent.parentNode;
        }
        return parent == el;
      };
    }

    API.isDescendant = isDescendant;
  }
  
  if (doc) {
  
    attributesBad = !!(html && isHostMethod(html, 'getAttribute') && html.getAttribute('style') && typeof html.getAttribute('style') == 'object');
    if (!attributesBad && isHostMethod(doc, 'createElement')) {

      // TODO: Rename to missingAttributesBad--specs disagree with established quasi-standards (return null for missing attributes)

      numericAttributesBad = (function() {
        var el = doc.createElement('td');
        if (el && isHostMethod(el, 'getAttribute')) {
          return el.getAttribute('colspan') !== null;
        }
      })();

      encTypeAttributeBad = (function() {
        var el = doc.createElement('form');
        el.setAttribute('enctype', 'application/x-www-form-urlencoded');
        el.removeAttribute('enctype');
        return el.getAttribute('enctype') !== null;
      })();

      valueHasAttributeBad = (function() {
        var el = doc.createElement('input');
        el.type = 'checkbox';
        el.checked = true;
        return (typeof el.hasAttribute != 'undefined' && el.hasAttribute('value'));
      })();
    }
    hasAttribute = (function() {
      var attributeSpecified, v;
      if (html && isHostMethod(html, 'hasAttribute')) {
        return function(el, name) {
		var re, nameLower = name.toLowerCase();
		var alias = attributeAliases[nameLower];
		var valueType = typeof el[alias];

		if (numericAttributesBad) {
			if (reSpan.test(nameLower)) {

				// Some agents (e.g. Blackberry browser) return '' for missing span attributes

				if (!el.getAttribute(nameLower)) {
					return false;
				}

				// Check outer HTML as last resort (IE8 standards mode known to take this fork)

				if (typeof el.outerHTML == 'string') {
					re = new RegExp('^[^>]*\\s+' + name + '=([\'"])?\\w+\\1?', 'i');
					return re.test(el.outerHTML);
				}
			} else if (valueType == 'number' && !el.getAttribute(nameLower)) {
				return false;
			} else if (reUserBoolean.test(nameLower)) {
				var b = el['default' + nameLower.substring(0, 1).toUpperCase() + name.substring(1)];
				if (typeof b == 'boolean') { // XML documents will not feature these boolean properties
					return b;
				}
			}
		}
		if (encTypeAttributeBad && nameLower == 'enctype') {
			// TODO: Consolidate
			if (typeof el.outerHTML == 'string') {
				re = new RegExp('^[^>]*\\s+' + name + '=([\'"])?\\w+\\1?', 'i');
				return re.test(el.outerHTML);
			}
			return !!(el.attributes.enctype && el.attributes.enctype.specified);
		}
		if (valueHasAttributeBad && nameLower == 'value' && typeof el.outerHTML == 'string') {
			re = new RegExp('^[^>]*\\s+value=([\'"])?\\w*\\1?', 'i');
			return re.test(el.outerHTML);					
		}
		return el.hasAttribute(name);
        };
      }
      if (html && isHostMethod(html, 'attributes')) {
        attributeSpecified = function(el, name) {
          v = el.attributes[name];
          return !!(v && v.specified);
        };
        if (attributesBad) {
          return function(el, name) {

		// MSXML document

		var doc = arguments[2] || getElementDocument(el);
		if (doc && typeof(doc.selectNodes) != 'undefined') { return attributeSpecified(el, name); } // XML document
		var value, re, nameLower = name.toLowerCase();

		// NOTE: encType is a non-standard alias found only in broken MSHTML DOM's (only applies to attributes collection)

		var alias = nameLower == 'enctype' ? 'encType' : attributeAliases[nameLower];
					
		if (alias && alias.toLowerCase() == nameLower) {
			name = alias;
		}

		// NOTE: Broken MSHTML DOM is case-sensitive here with custom attributes

		if (el.attributes) {
			value = el.attributes[name] || el.attributes[nameLower];
		}
		if (value) {
			if (reSpan.test(nameLower) && value.value == '1') {
				re = new RegExp('^[^>]*\\s+' + name + '=([\'"])?\\w*\\1?', 'i');
				return re.test(el.outerHTML);
			}

			// NOTE: enctype and value attributes never specified

			if (value.specified) {
				return true;
			}

			if (typeof el[name] == 'boolean') {
				if (reUserBoolean.test(nameLower)) {
					return el['default' + nameLower.substring(0, 1).toUpperCase() + name.substring(1)];
				}
				return el[name];
			}

			// TODO: Consolidate

			if (nameLower == 'value' && ((/^input$/i.test(el.tagName) && /^text$/i.test(el.type)))) {
				return !!(el.defaultValue || el.defaultValue != el.value);
			}

			// TODO: Consolidate

			if (/^(enctype|value)$/.test(nameLower) && typeof el.outerHTML == 'string') {
				re = new RegExp('^[^>]*\\s+' + name + '=([\'"])?\\w*\\1?', 'i');
				return re.test(el.outerHTML);
			}
		}
		return false;
          };
        }
        return attributeSpecified;
      }
    })();

    API.hasAttribute = hasAttribute;
    

    disableElements = function() {
      var b, i = arguments.length;
      if (i > 1 && typeof arguments[i - 1] == 'boolean') {
        b = arguments[i - 1];
        i--;
      }
      while (i--) { arguments[i].disabled = !b; }
    };

    API.disableElements = disableElements;

    getEBI = (function() {
      function idCheck(el, id) {
        return (el && el.id == id)?el:null;
      }
      if (isHostMethod(doc, 'getElementById')) {
        return function(id, docNode) { return idCheck((docNode || global.document).getElementById(id), id); };
      }
      if (isHostMethod(doc, 'all')) {
        return function(id, docNode) { return idCheck((docNode || global.document).all[id], id); };
      }
    })();

    API.getEBI = getEBI;

    getFrameById = function(id, win) {
      if (!win) {
        win = global;
      }
      var frame = win.frames[id];
      if (!frame && getEBI) {
        frame = getEBI(id, win.document);
        if (frame && isHostObjectProperty(frame, 'contentWindow')) {
          frame = frame.contentWindow;
        } else {
          frame = null;
        }
      }
      return frame;
    };

    API.getFrameById = getFrameById;

    getIFrameDocument = function(el, win) {
      return el.contentDocument || (el.contentWindow || getFrameById(el.name, win) || el).document || null;
    };

    API.getIFrameDocument = getIFrameDocument;
	
    commonElementsByTagName = (function() {
      if (allElements) {
        return function (el, t) {
          return(t == '*' && el.all)?allElements(el, true):el.getElementsByTagName(t);
        };
      }
      return function (el, t) { return el.getElementsByTagName(t); };
    })();

    // Returns an array or array-like host object.
    getEBTN = (function() {
      var els;
      if (isHostMethod(doc, 'getElementsByTagName')) {
        els = doc.getElementsByTagName('*');      // Test "*" parameter, which fails in some agents (e.g. IE5, Safari 1)
        if (els && (els.length || allElements)) { // Need document.all as fallback for agents that cannot handle a "*" parameter (Safari 1 is excluded by this test.)
          return function(t, docNode) {
            return commonElementsByTagName(docNode || global.document, t);
          };
        }
      }
      if (isHostObjectProperty(doc, 'all') && isHostMethod(doc.all, 'tags')) {
        return function(t, docNode) {
          return (docNode || global.document).all.tags(t);
        };
      }
    })();

    API.getEBTN = getEBTN;

    if (getEBTN) {
      getHeadElement = function(docNode) {
        return getEBTN('head', docNode || global.document)[0] || null;
      };
    }

    API.getHeadElement = getHeadElement;

    camelize = function(name) {
      if (reCamel.test(name)) {
        var m = name.match(reCamel);
        return (m)?([m[1], m[2].toUpperCase(), m[3]].join('')):name;
      }
      return name;
    };

    if (html && isHostMethod(html, 'removeAttribute')) {
      removeAttribute = function(node, name) {
        var nameLower = name.toLowerCase();
        var alias = attributeAliases[nameLower];

        if (attributesBad) {
          // NOTE: encType alias does not apply here

          if (alias && alias.toLowerCase() == nameLower) {
            name = alias;
          } else {
            name = camelize(name);
          }
        }
        alias = alias || nameLower;
        if (typeof node[alias] == 'boolean') {
          node[alias] = false;
        } else {
          node.removeAttribute(name);
        }
      };
    }

    reUserBoolean = new RegExp('^(checked|selected)$');
    reSpan = new RegExp('^(row|col)?span$');
    reURI = new RegExp('^(href|src|data|usemap|longdesc|codebase|classid|profile|cite)$');

    API.removeAttribute = removeAttribute;

    // Returns null if attribute is not present

    getAttribute = (function() {
      var att, alias, nameC, originalName, nn, reEvent, reNewLine, reFunction;

      if (html && isHostMethod(html, 'getAttribute') && hasAttribute) {
        if (attributesBad) {
            reEvent = new RegExp('^on');
            reNewLine = new RegExp('[\\n\\r]', 'g');
            reFunction = new RegExp('^function [^\\(]*\\(\\) *{(.*)} *$');

            return function(el, name) { // Optional third argument speeds up by skipping getElementDocument
              var doc = arguments[2] || getElementDocument(el);
              if (doc && typeof(doc.selectNodes) != 'undefined') { return el.getAttribute(name); } // XML document
              if (hasAttribute(el, name)) {
                originalName = name;
                name = name.toLowerCase();
                alias = attributeAliases[name];
                if (!alias) {
                  if (reUserBoolean.test(name)) {
                    return el['default' + name.substring(0, 1).toUpperCase() + name.substring(1)] ? '' : null;
                  }
                  if (name == 'style') { return (el.style)?(el.style.cssText || ''):''; }
                  if (reURI.test(name)) { return el.getAttribute(name, 2); }
                  if (reEvent.test(name) && el[name]) {
                    att = el[name].toString();
                    if (att) {
                      att = att.replace(reNewLine, '');
                      if (reFunction.test(att)) { return att.replace(reFunction, '$1'); }
                    }
                    return null;
                  }
                  nn = el.tagName.toLowerCase();
                  if (nn == 'select' && name == 'type') { return null; }
                  if (nn == 'form' && el.getAttributeNode) {
                    att = el.getAttributeNode(name);
                    return (att && att.nodeValue)?att.nodeValue:null;
                  }
                }
                nameC = camelize(alias || name);
                if (typeof el[nameC] == 'unknown') {
                  return '[unknown]';
                } else {
                  if (reURI.test(nameC)) {
                    return el.getAttribute(nameC, 2);
                  }
                  if (typeof el[nameC] == 'boolean') {
                    return (el[nameC])?'':null;
                  }
                  if (typeof el[nameC] == 'undefined') { // No property, custom attribute
                    return el.getAttribute(originalName); // Case sensitive
                  }
                  if (name == 'value' && el.tagName.toLowerCase() == 'input' && el.type.toLowerCase() == 'text') {
                    return el.defaultValue;
                  }
	          return (typeof el[nameC] != 'string' && el[nameC] !== null && el[nameC].toString)?el[nameC].toString():el[nameC];
                }
              }
              return null;
            };
        }
        return function(el, name) {
          var nameLower = name.toLowerCase();
          var alias = attributeAliases[nameLower] || (nameLower.indexOf('-') != -1 && camelize(nameLower)) || nameLower;

          if (typeof el[alias] == 'boolean') {
            if (reUserBoolean.test(nameLower)) {
              return el['default' + nameLower.substring(0, 1).toUpperCase() + nameLower.substring(1)] ? '' : null;
            }
            return el[alias] ? '' : null;
          }

          if (numericAttributesBad && isHostMethod(el, 'hasAttribute')) {
            return hasAttribute(el, name) ? el.getAttribute(name) : null;
          }
          return el.getAttribute(name);
        };
      }
    })();

    API.getAttribute = getAttribute;

    var testAttributePath = '/favicon.ico';

    if (getAttribute) {
      var elA, badReflections, anchorHrefResolves = (function() {
        var el = doc.createElement('a');

        if (el && isHostMethod(el, 'setAttribute')) {
          el.setAttribute('href', testAttributePath);
          return el.href != testAttributePath;
        }
      })();
	
      if (anchorHrefResolves && isHostMethod(doc, 'createElement')) {
        badReflections = (function() {
          var el, result = {};
          var testReflection = function(tagName, propertyName, el) {
            var attributeName = propertyName.toLowerCase();

            el = el || doc.createElement(tagName);
            if (el && isHostMethod(el, 'setAttribute')) {
              el.setAttribute(attributeName, testAttributePath);
              result[attributeName] = el[propertyName] == testAttributePath;
            }
          };

          testReflection('form', 'action');
          el = doc.createElement('img');
          testReflection('img', 'useMap', el);
          testReflection('img', 'src', el);
          testReflection('img', 'longDesc', el);
          testReflection('link', 'href');
          testReflection('head', 'profile');
          el = doc.createElement('object');
          testReflection('object', 'codeBase', el);
          testReflection('object', 'classid', el);
          testReflection('object', 'data', el);
          testReflection('blockquote', 'cite');
          testReflection('area', 'href');
          return result;
        })();
        elA = doc.createElement('a');
      }

      // For use with HTML DOM's only

      getAttributeProperty = function(el, name) {
        var nameLower = name.toLowerCase();
        var alias = attributeAliases[nameLower] || camelize(nameLower);
        var elHref = elA;

        switch (typeof el[alias]) {
        case 'boolean':				
          return reUserBoolean.test(nameLower) ? el['default' + nameLower.substring(0, 1).toUpperCase() + nameLower.substring(1)] : el[alias];
        case 'undefined':

          // Missing expando or event handlers in some browsers (e.g. FF)

          return getAttribute(el, nameLower);
        default:
          if (hasAttribute(el, name)) {
            if (anchorHrefResolves && elA && (reURI.test(nameLower) || nameLower == 'action') && badReflections[nameLower] && !(/^a$/i).test(el.tagName)) {
            
              var doc = arguments[2] || getElementDocument(el);
              if (doc && doc != global.document && isHostMethod(doc, 'createElement')) {
                elHref = doc.createElement('a');
              }

              elHref.setAttribute('href', getAttribute(el, nameLower));
              return elHref.href;
            }
            return nameLower == 'value' ? getAttribute(el, nameLower) : el[alias];
          }
          return null;
        }
      };

      API.getAttributeProperty = getAttributeProperty;
    }

    // Note: returns element as some attributes (e.g. name, type) will create a new element
    // Ex. el = setAttribute(el, 'name', 'newname');
    
    setAttribute = (function() {
      var div, nn, nameC, reEvent, reNamed, setAttributeByReplacement;

      if (html && isHostMethod(html, 'setAttribute')) {
        if (attributesBad) {
          setAttributeByReplacement = function(el, name, value) {
            var re, uid, pn = el.parentNode;
            if (pn && isHostMethod(pn, 'childNodes') && typeof el.outerHTML == 'string') {
              uid = elementUniqueId(el);
              el.id = el.id || uid;
              re = new RegExp(name + '=[\'"]{0,1}[a-zA-Z0-9_]+[\'"]{0,1}', 'i');
              // should loop through attributes to build outer tag
              el.outerHTML = el.outerHTML.replace(re, ' ').replace('>',' ' + name + '="' + value + '">');
              
              if (el.parentNode !== pn) {
                el = findReplacedElement(el, pn, uid);
              }
            }
            else {
              el[name] = value;
            }
            return el;
          };

          reEvent = new global.RegExp('^on');
          reNamed = new RegExp('^(a|img|form|input|select|button|textarea|iframe)$');

          if (isHostMethod(global.document, 'createElement')) {
            div = global.document.createElement('div');
          }
          return function(el, name, value) {
            var doc = getElementDocument(el);
            if (doc && typeof(doc.selectNodes) != 'undefined') { el.setAttribute(name, value); return el; } // XML document
            name = name.toLowerCase();
            nn = el.tagName.toLowerCase();
            switch(name) {
            case 'style':
              if (el.style) { el.style.cssText = value; }
              break;
            case 'checked':
            case 'selected':
              el['default' + name.substring(0, 1).toUpperCase() + name.substring(1)] = value.toLowerCase() == name || !value;
              break;
            case 'defer':
            case 'disabled':
            case 'multiple':
            case 'readonly':
            case 'ismap':
              el[name] = (value.toLowerCase() == name || !value);
              break;
            case 'type':
              if (nn != 'select') { // no such attribute, but there is a property
                if (nn == 'input' && (el.parentNode || (el.attributes && el.attributes.type && el.attributes.type.specified)))  {
                  el = setAttributeByReplacement(el, 'type', value); // Can only set property once
                }
                else {
                  el.type = value;
                }
              }
              break;
            case 'name':
              var isNamed = reNamed.test(nn);
              var divAppend = (doc == global.document) ? div : (doc && doc.createElement('div'));
              if (el.parentNode === null && isNamed && divAppend) {
                divAppend.appendChild(el); // Causes IE to add name to DOM collections
              }
              el = setAttributeByReplacement(el, 'name', value);
              if (isNamed && divAppend && el.parentNode == divAppend) {
                divAppend.removeChild(el);
              }
              break;
            default:
              if (reEvent.test(name)) {
                el[name] = new Function(value);
              } else {
                nameC = camelize(attributeAliases[name] || name);
                if (typeof el[nameC] == 'undefined') { // Custom attribute
                  el.setAttribute(name, value); // Case sensitive
                } else {
                  el[nameC] = value;
                }
              }					
            }
            return el;
          };
        }
        return function(el, name, value) {
          var nameLower = name.toLowerCase();
          var alias = attributeAliases[nameLower] || nameLower;

          if (typeof el[alias] == 'boolean') {
            var b = !value || nameLower == value.toLowerCase();
            if (reUserBoolean.test(nameLower)) {
              if (b) {
                el.setAttribute(nameLower, nameLower);
              } else {
                el.removeAttribute(nameLower);
              }
            } else {
              el[alias] = b;
            }
          } else {
            if (nameLower == 'value' && typeof el.defaultValue == 'string') {
              el.defaultValue = value;
            } else {
              el.setAttribute(name, value);
            }
          }
          return el;
        };
      }
    })();

    API.setAttribute = setAttribute;

    if (setAttribute) {
      setAttributeProperty = function(el, name, value) {
        var nameLower = name.toLowerCase();
        var alias = attributeAliases[nameLower] || camelize(nameLower);

        if (typeof value == 'string' && typeof el[alias] == 'undefined' && alias == nameLower) {

			// Expando (implemented as a custom attribute)

			el.setAttribute(nameLower, value);
        } else {
			if (nameLower == 'value') {
				setAttribute(el, 'value', value);
			} else if (numericAttributesBad && typeof el[alias] == 'number') {
				setAttribute(el, nameLower, value + '');
			} else if (reUserBoolean.test(nameLower)) {
				if (value) {
					setAttribute(el, nameLower, '');
				} else {
					removeAttribute(el, nameLower);
				}
			} else {
				el[alias] = value;
			}
        }
        return el;
      };

      API.setAttributeProperty = setAttributeProperty;
    }

    

    if (html) {
      getChildren = (function() {
        if (isHostObjectProperty(html, 'children')) {
          return function(el) {
            var a, c, i, elC;

            if (arguments[1]) {
              return el.children;
            }

            // Filter comment nodes

            a = [];
            c = el.children;
            for (i = c.length; i--;) {
              elC = c[i];
              if ((elC.nodeType == 1 && elC.tagName != '!') || (!elC.nodeType && elC.tagName)) {
                a[a.length] = elC;
              }
            }
            return a.reverse();
          };
        }
        if (isHostMethod(html, 'childNodes')) {
          return function(el) {
            var n, nl = el.childNodes, r = [];
            var i = nl.length;

            while (i--) {
              // Code duplicated for performance
              n = nl[i];
              if ((n.nodeType == 1 && n.tagName != '!') || (!n.nodeType && n.tagName)) {
                r.push(nl[i]);
              }
            }
            return r.reverse();
          };
        }
      })();
    }

    API.getChildren = getChildren;

    reNotEmpty = new RegExp('[^\\t\\n\\r ]');

    API.isEmptyTextNode = function(n) {
      return !reNotEmpty.test(n.data);
    };
    
    var isXmlParseModeDocument;

    
    isXmlParseMode = function(docNode) {
      return false;
    };
    

    isXmlParseModeDocument = isXmlParseMode();

    API.isXmlParseMode = isXmlParseMode;

    createElement = (function() {
      if (isHostMethod(doc, 'createElement')) {
        return (function() {
          if (isXmlParseModeDocument && isHostMethod(doc, 'createElementNS')) {
            return function(tag, docNode) {
              return (docNode || global.document).createElementNS('http://www.w3.org/1999/xhtml', 'html:' + tag);
            };
          }
          return function(tag, docNode) {
            return (docNode || global.document).createElement(tag);
          };
        })();
      }
    })();

    API.createElement = createElement;
    
    if (createElement && setAttribute) {
      setAttributesFactory = function(setAttribute) {
        return function(el, attributes) {
            var att;

            // TODO: Clone attributes object!

            // Do type first

            if (typeof attributes.type != 'undefined' && isOwnProperty(attributes, 'type')) {
              el.setAttribute('type', attributes.type);
              delete attributes.type;
            }

            var name = attributes.name;

            // Set name second as it may require MSHTML workaround

            if (name) {
              el = setAttribute(el, 'name', name);
              if (el.tagName.toLowerCase() == 'iframe' && isHostObjectProperty(el, 'contentWindow')) {
                el.contentWindow.name = name;
              }
              delete attributes.name;
            }

            for (att in attributes) {
              if (isOwnProperty(attributes, att)) { el = setAttribute(el, att, attributes[att]); }
            }
            return el;
        };
      };

      API.setAttributes = setAttributes = setAttributesFactory(setAttribute);
      API.setAttributeProperties = setAttributeProperties = setAttributesFactory(setAttributeProperty);

      createElementWithAttributes = function(tag, attributes, docNode) {
        var el = createElement(tag, docNode || global.document);
        if (el) {
          el = setAttributes(el, attributes);
        }
        return el;
      };

      API.createElementWithAttributes = createElementWithAttributes;

      createElementWithProperties = function(tag, properties, docNode) {
        var el = createElement(tag, docNode || global.document);
        if (el) {
          el = setAttributeProperties(el, properties);
        }
        return el;
      };

      API.createElementWithProperties = createElementWithProperties;

      if (html && isHostMethod(html, 'appendChild')) {

        // Optional docNode argument is redundant, but faster and more robust when used
        // TODO: advisor message if mismatch between docNode and appendTo

        createAndAppendElementWithAttributesFactory = function(setAttributes) {
          return function(tag, attributes, appendTo, docNode) {
            if (!docNode) {
              docNode = getElementDocument(appendTo);
            }
            if (docNode) {
              var el = createElement(tag, docNode);
              if (el) {
                el = setAttributes(el, attributes);
                appendTo.appendChild(el);
              }
              return el;
            }
            return null;
          };
        };
        createAndAppendElementWithAttributes = API.createAndAppendElementWithAttributes = createAndAppendElementWithAttributesFactory(setAttributes);
        createAndAppendElementWithProperties = API.createAndAppendElementWithProperties = createAndAppendElementWithAttributesFactory(setAttributeProperties);
      }
    }
    
    getDOMCollectionFactory = function(name, tag, fnFilter) {
      if (isHostMethod(doc, name)) {
        return function(docNode) { return (docNode || global.document)[name]; };
      }
      if (getEBTN && (!fnFilter || typeof filter == 'function')) {
        return function(docNode) {
          var col, i;

          if (typeof tag == 'string') {
            col = getEBTN(tag, docNode);
          }
          else {
            col = [];
            i = tag.length;
            while (i--) {
              col = col.concat(toArray(getEBTN(tag[i], docNode)));
            }
          }
          return (fnFilter)?filter(col, fnFilter):col;
        };
      }
    };

    getDOMCollectionItemFactory = function(name, tag, fnCollection) {
      if (isHostMethod(doc, name)) {
        return function(i, docNode) { return (docNode || global.document)[name][i]; };
      }
      if (getEBI) {
        return function(i, docNode) {
          var el, col, j, nn;
          if (typeof i == 'string') {
            el = getEBI(i, docNode);
            if (el && (!el.name || el.name == i)) {
              if (typeof tag == 'string') {
                if (getElementNodeName(el) == tag) { return el; }
              }
              else {
                j = tag.length;
                nn = getElementNodeName(el);
                while (j--) {
                  if (nn == tag[j]) { return el; }
                }
              }
            }
          }
          col = fnCollection();
          if (typeof i == 'string') {
            j = col.length;
            while (j--) {
              if (col[j].name == i || (!col[j].name && col[j].id == i)) {
                return col[j];
              }
            }
            return null;
          }
          return col[i] || null;
        };
      }
    };

    getImages = API.getImages = getDOMCollectionFactory('images', 'img');
    if (getImages) {
      getImage = API.getImage = getDOMCollectionItemFactory('images', 'img', getImages);
    }

    getForms = API.getForms = getDOMCollectionFactory('forms', 'form');
    if (getForms) {
      getForm = API.getForm = getDOMCollectionItemFactory('forms', 'form', getForms);
    }

    getAnchors = API.getAnchors = getDOMCollectionFactory('anchors', 'a', function(el) { return typeof el.href != 'unknown' && !el.href; });
    if (getAnchors) {
      getAnchor = API.getAnchor = getDOMCollectionItemFactory('anchors', 'a', getAnchors);
    }

    getLinks = API.getLinks = getDOMCollectionFactory('links', ['a', 'area'],  function(el) { return typeof el.href == 'unknown' || el.href; });
    if (getLinks) {
      getLink = API.getLink = getDOMCollectionItemFactory('links', ['a', 'area'], getLinks);
    }
    
    // Image section

    changeImage = function(el, src) {
      el.src = src;
    };

    API.changeImage = changeImage;

    
    if (this.Image) {
      preloadImage = function(uri, h, w) {
        var p = new global.Image();
        p.src = uri;
        p.height = h;
        p.width = w;
        preloaded[preloaded.length] = p;
        return preloaded.length - 1;
      };

      API.preloadImage = preloadImage;

      if (createElement) {
        clonePreloadedImage = function(handle) {
          var img = createElement('img');
          var p = preloaded(handle);
          img.src = p.src;
          img.height = p.height;
          img.width = p.width;
          return img;
        };

        API.clonePreloadedImage = clonePreloadedImage;
      }
    }

    changeImage = function(el, src) {
      if (typeof src == 'number') {
        if (preloaded[src]) {
          el.src = preloaded[src].src;
        }
      }
      else {
        el.src = src;
      }
    };

    API.changeImage = changeImage;
    
    // Form section

    if (hasAttribute) {
      getOptionValue = function(o) {    
        return (o.value || (hasAttribute(o, 'value')?o.value:o.text)); 
      };
    }

    API.getOptionValue = getOptionValue;

    if (isHostMethod(global, 'Option')) {
      addOption = function(el, text, value) {
        var o = new global.Option(), len = el.options.length;
        o.text = text;
        if (typeof value != 'undefined') { o.value = value; }
        if (el.options.add) {
          el.options.add(o, el.options.length);
        }
        if (len == el.options.length) {
          el.options[el.options.length] = o;
        }
        return o;
      };

      API.addOption = addOption;

      addOptions = function(el, options) {
        var opt;
        for (opt in options) { if (isOwnProperty(options, opt)) { addOption(el, options[opt], opt); } }
      };

      API.addOptions = addOptions;
    }

    removeOptions = function(el) {
      el.options.length = 0;
      var l = el.options.length;		
      while (l--) { el.options[l] = null; }
    };

    API.removeOptions = removeOptions;

    urlencode = (function() {
      var f = function(s) {
      return encodeURIComponent(s).replace(/%20/g,'+').replace(/(.{0,3})(%0A)/g,
        function(m, a, b) {return a+(a=='%0D'?'':'%0D')+b;}).replace(/(%0D)(.{0,3})/g,
        function(m, a, b) {return a+(b=='%0A'?'':'%0A')+b;});
      };

      if (typeof encodeURIComponent != 'undefined' && String.prototype.replace && f('\n \r') == '%0D%0A+%0D%0A') {
         return f;
      }
    })();

    API.urlencode = urlencode;

    if (getOptionValue) {
    inputValue = function(el, bDefault) {
      var a, o, t = el.type;

      if (t && !t.indexOf('select')) {
        a = [];
        for (var j = 0, jlen = el.options.length; j < jlen; j++) {
          o = el.options[j];
          if (o[(bDefault)?'defaultSelected':'selected']) {
            a[a.length] = getOptionValue(o);
          }
        }
        if (a.length == 1) { a = a[0]; }
        return a;
      }
      switch(t) {
      case 'checkbox':
      case 'radio':
        return (el[(bDefault)?'defaultChecked':'checked'])?el.value || 'on':'';
      default:
        return el[(bDefault)?'defaultValue':'value'];
      }
    };

    API.inputValue = inputValue;

    inputChanged = function(el) {
      var i;
      var d = inputValue(el, true);
      var v = inputValue(el);
      
      if (typeof d == 'string') {
        return (d == v);
      }
      i = d.length;
      while (i--) {
        if (d[i] != v[i]) { return true; }
      }
      return false;
    };

    API.inputChanged = inputChanged;

    formChanged = function(el) {
      var i, els = el.elements;

      i = els.length;
      while (i--) {
        if (inputChanged(els[i])) { return true; }
      }
      return false;
    };
    }

    
    if (urlencode && getOptionValue) {
      serializeFormUrlencoded = function(f) {
        var e, // form element
            n, // form element's name
            t, // form element's type
            o, // option element
            es = f.elements,
            c = []; // the serialization data parts

        var reCheck = new RegExp('^(checkbox|radio)$');
        var reText = new RegExp('^(text|password|hidden|textarea)$');

        function add(n, v) {
          c[c.length] = urlencode(n) + "=" + urlencode(v);
        }

        for (var i=0, ilen=es.length; i<ilen; i++) {
          e = es[i];
          n = e.name;
          if (n && !e.disabled) {
            t = e.type;
            if (!t.indexOf('select')) {
              // The 'select-one' case could reuse 'select-multiple' case
              // The 'select-one' case code is an optimization for
              // serialization processing time.
              if (t == 'select-one' || e.multiple === false) {
                if (e.selectedIndex >= 0) {
                  add(n, getOptionValue(e.options[e.selectedIndex]));
                }
              }
              else {
                for (var j = 0, jlen = e.options.length; j < jlen; j++) {
                  o = e.options[j];
                  if (o.selected) {
                    add(n, getOptionValue(o));
                  }
                }
              } 
            }
            else if (reCheck.test(t)) {
              if (e.checked) {
                add(n, e.value || 'on');
              }          
            }
            else if (reText.test(t)) {
              add(n, e.value);
            }
          }
        }
        return c.join('&');
      };
    }
    API.serializeFormUrlencoded = serializeFormUrlencoded;
    
    // Text section

    if (String.prototype.trim) {
      trim = function(text) {
        return text.trim();
      };
    } else {
      if (String.fromCharCode(160).replace(/\s/, '').length) {
        trim = function(text) {
          return text.replace(/^[\s\xA0](\s|\xA0)*/, '').replace(/(\s|\xA0)*[\s\xA0]$/, '');
        };
      } else {
        trim = function(text) {
          return text.replace(/^\s\s*/, '').replace(/\s*\s$/, '');
        };
      }
    }

    if (html) {
      getElementText = (function() {
        if (typeof html.innerText == 'string') { return function(el) { return el.innerText; }; }
        if (typeof html.textContent == 'string') { return function(el) { return el.textContent; }; }
        if (isRealObjectProperty(html, 'firstChild')) {
          return function(el) {
            var text = [];
            var c = el.firstChild;
            while (c) {
              if (c.nodeType == 3 || c.nodeType == 4) {
                if (reNotEmpty.test(c.data)) { text[text.length] = c.data; }
              }
              else {
                if (c.nodeType == 1) { text[text.length] = getElementText(c); }
              }
              c = c.nextSibling;
            }
            return text.join('');
          };
        }
      })();

      API.getElementText = getElementText;

      setElementText = (function() {
        var reLT, reGT, reAmp;
        if (typeof html.innerText == 'string') { return function(el, text) { el.innerText = text; }; }
        if (isHostMethod(html, 'removeChild') && isHostMethod(doc, 'createTextNode')) {
          return function(el, text) {
            var docNode = getElementDocument(el);
            while (el.firstChild) { el.removeChild(el.firstChild); }
            el.appendChild(docNode.createTextNode(text));
          };
        }
        if (typeof html.innerHTML == 'string') {
          reLT = new RegExp('<', 'g');
          reGT = new RegExp('>', 'g');
          reAmp = new RegExp('&', 'g');
          return function(el, text) { el.innerHTML = text.replace(reAmp, "&amp;").replace(reLT, "&lt;").replace(reGT, "&gt;"); };
        }
      })();

      API.setElementText = setElementText;
    }
    if (setElementText && getElementText) {
      addElementText = function(el, text) { setElementText(el, getElementText(el) + text); };

      API.addElementText = addElementText;
    }
    
    // HTML section

    if (html && typeof html.innerHTML == 'string') {
      if (createElement && !isXmlParseMode() && typeof html.outerHTML == 'string' && isHostMethod(html, 'childNodes')) {
        select = createElement('select');
        if (select && isHostMethod(select, 'insertAdjacentHTML') && typeof select.canHaveChildren == 'boolean') {
          select.innerHTML = '<option>T</option>';
          selectsBroken = (!select.options.length);
        }
      }

      if (selectsBroken) {
        setSelectHtml = function(el, html) {
          var uid = elementUniqueId(el);
          el.id = el.id || uid;
          var pn = el.parentNode;
          el.innerHTML = '';
          el.outerHTML = el.outerHTML.replace('>', '>' + html + '</select>');					
          if (el.parentNode !== pn) {
            el = findReplacedElement(el, pn, uid);
          }
          return el;
        };
      }

      setTempHtml = (function() {
        var nn;
        var reScriptFirst = new RegExp('^[^<]*<script', 'i');

        function preProcess(html, docNode) {
          switch(nn) {
          case 'table':
            elTemp = createElement('div', docNode);
            return '<table>' + html + '</table>';
          case 'caption':
          case 'colgroup':
          case 'col':
          case 'thead':
          case 'tbody':
          case 'tfoot':
            elTemp = createElement('div', docNode);
            return ['<table><', nn, '>', html, '</', nn, '></table>'].join('');
          case 'form':
            elTemp = createElement('div', docNode);							
          }
          return html;
        }

        function postProcess(html) {
          switch(nn) {
          case 'table':
            return elTemp.firstChild;
          case 'caption':
          case 'colgroup':
          case 'col':
          case 'thead':
          case 'tbody':
          case 'tfoot':
            return elTemp.firstChild.firstChild;
          default:
            return elTemp;
          }
        }

        // In Opera 7.23, insertAdjacentHTML on the documentElement is a false flag
        // Test created DIV when possible as it is more direct

        if (isHostMethod(createElement && createElement('div') || html, 'insertAdjacentHTML')) {
          return function(html, bAppend) {
            nn = getElementNodeName(elTemp);
            if (!bAppend && reScriptFirst.test(html) && !isXmlParseMode()) { html = '&nbsp;' + html; }
            html = preProcess(html);
            elTemp.insertAdjacentHTML('afterBegin', html);
            elTemp = postProcess(html);
          };
        }

        return function(html, bAppend) {
          nn = getElementNodeName(elTemp);
          if (!bAppend && reScriptFirst.test(html) && !isXmlParseMode()) { html = '&nbsp;' + html; }
          html = preProcess(html);
          elTemp.innerHTML = html;
          elTemp = postProcess(html);
        };
      })();

      if (isHostMethod(html, 'removeChild') && createElement && isRealObjectProperty(html, 'firstChild')) {
        transferTempHtml = function(el, html, docNode, bAppend) {
          var nn = getElementNodeName(el);
          if (nn == 'head') { nn = 'div'; }
          elTemp = createElement(nn, docNode);
          if (elTemp) {
            setTempHtml(html, bAppend);
            while (elTemp.firstChild) {
              el.appendChild(elTemp.firstChild);
            }
            elTemp = null;
          }
        };

        addElementHtml = function(el, html, docNode) {
          if (elementCanHaveChildren(el)) {
            transferTempHtml(el, html, getElementDocument(el), true);
          }
        };

        API.addElementHtml = addElementHtml;
      }

      htmlToNodes = function(html, docNode) {
        var c;
        elTemp = createElement('div', docNode);
        if (elTemp) {
          setTempHtml(html);
          c = elTemp.childNodes;
          elTemp = null;
        }
        return c;
      };

      API.htmlToNodes = htmlToNodes;

      setElementHtml = (function() {				
        if (transferTempHtml) {
          return function(el, html) {
            if (setSelectHtml && getElementNodeName(el) == 'select') {
              return setSelectHtml(el, html);
            }
            if (elementCanHaveChildren(el)) {
              //if (typeof purgeListeners == 'function') { purgeListeners(el, true, true); } // Purge children only
              while (el.firstChild) {
                el.removeChild(el.firstChild);
              }
              transferTempHtml(el, html, getElementDocument(el));
            }
            return el;
          };
        }
      })();

      API.setElementHtml = setElementHtml;

      setElementOuterHtml = (function() {
        if (typeof html.outerHTML == 'string') {
          return function(el, html) {
            //if (typeof purgeListeners == 'function') { purgeListeners(el, true); }
            el.outerHTML = html;
            return el;
          };
        }
        if (isHostMethod(html, 'insertBefore') && isRealObjectProperty(html, 'firstChild')) {
          return function(el, html) {
            var nodeNew;
            var docNode = getElementDocument(el);
            var parent = getElementParentElement(el);
            var next = el.nextSibling;

            //if (typeof purgeListeners == 'function') { purgeListeners(el); }
            elTemp = createElement('div', docNode);
            if (elTemp && parent) {
              setTempHtml(html, docNode);
              parent.removeChild(el);
              nodeNew = elTemp.firstChild;
              if (next) {
                parent.insertBefore(elTemp.firstChild, next);
              }
              else {
                parent.appendChild(elTemp.firstChild);
              }
              elTemp = null;
              return nodeNew;
            }
            return el;
          };
        }
      })();

      API.setElementOuterHtml = setElementOuterHtml;
    }
    
    if (html && isRealObjectProperty(html, 'firstChild') && typeof html.nodeType == 'number' && getAttribute && hasAttribute) {
      reSelfClosing = new RegExp('^(br|hr|img|meta|link|input|base|param|col|area)$');
      reCollapsibleAttributes = new RegExp('^(checked|selected|disabled|multiple|ismap|readonly)$');
      reLT = new RegExp('<', 'g');
      reGT = new RegExp('>', 'g');
      reAmp = new RegExp('&', 'g');
      reQuot = new RegExp('"', 'g');

      constructElementHtml = function(el, bOuter, bXHTML, docNode) {
        var i, nn, name, value, result, selfClose = (bXHTML)?' />':'>';
        var attributes = [], attribute;

        switch (el.nodeType) {
          case 1:
            nn = getElementNodeName(el);
            result = [];
            if (el.attributes && el.attributes.length) {
              i = el.attributes.length;
              attributes.length = i;

              while (i--) {
                attribute = el.attributes[i];
                if (attribute) {
                  name = (attribute.nodeName || attribute.name || '').toLowerCase();
                  if (name) {
                    value = getAttribute(el, name, docNode); // Third argument skips getElementDocument call in getAttribute
                    if (value === '' && reCollapsibleAttributes.test(name) && hasAttribute(el, name)) { // Last test in case implementation returns empty string instead of null for missing attributes
                      value = name;
                    }
                    if (value !== null) { attributes[i] = [' ', name, '="', value.replace(reAmp, '&amp;').replace(reLT, '&lt;').replace(reGT, '&gt;').replace(reQuot, '&quot;'), '"'].join(''); }
                  }
                }
              }
            }
						
            if (reSelfClosing.test(nn)) {
              result = (bOuter)?['<', nn].concat(attributes, selfClose):[''];
            }
            else {
              result = [];
              if (nn == '!') { return result; } // IE5 thinks comments are elements
              if (el.childNodes && el.childNodes.length) {
                i = el.childNodes.length;
                while (i--) {
                  result = constructElementHtml(el.childNodes[i], true, bXHTML, docNode).concat(result);
                }
                result = (bOuter)?['<', nn].concat(attributes, '>', result, '</', nn, '>'):result;
              }
              else {
                if (nn == 'style') {
                  if (el.styleSheet && el.styleSheet.cssText) {
                    result = [el.styleSheet.cssText];
                  }
                }
                else {
                  if (el.innerText) {
                    result = [el.innerText];
                  }
                  else {
                    if (el.text) { result = [el.text]; }
                  }
                }
                result = (bOuter)?['<', nn].concat(attributes, '>', result, '</', nn, '>'):[''];
              }
            }
            return result;
          case 3:
            return (reNotEmpty.test(el.nodeValue))?[el.nodeValue]:[''];
          case 4:
            return ['<![CDATA[', el.nodeValue, ']]>'];
          case 8:
            return ['<!--', el.nodeValue, '-->'];
          case 10:
            return [el.nodeValue];
          default:
            return [];
        }
      };

      // Native properties used only if requested and actual document types match

      getElementHtml = function(el, bXHTML, bUseNative) {
        var docNode = getElementDocument(el);
	var isXML = isXmlParseMode(docNode);
        if (typeof el.innerHTML == 'string' && bUseNative && bXHTML === isXML) {
          return el.innerHTML;
        }
        if (typeof bXHTML == 'undefined') {
          bXHTML = isXML;
        }
        return constructElementHtml(el, false, bXHTML, docNode).join('');
      };

      API.getElementHtml = getElementHtml;

      getElementOuterHtml = function(el, bXHTML, bUseNative) {
        var docNode = getElementDocument(el);
	var isXML = isXmlParseMode(docNode);
        if (typeof el.outerHTML == 'string' && bUseNative && bXHTML === isXML) {
          return el.outerHTML;
        }
        if (typeof bXHTML == 'undefined') {
          bXHTML = isXML;
        }
        return constructElementHtml(el, true, bXHTML, docNode).join('');
      };

      API.getElementOuterHtml = getElementOuterHtml;

      getDocumentHtml = function(bXHTML, bUseNative, docNode) {
        docNode = docNode || global.document;
        var sHTML, el = getHtmlElement(docNode);

        if (el) {
          sHTML = getElementOuterHtml(el);
        }
        return sHTML;
      };

      API.getDocumentHtml = getDocumentHtml;
    }
    
    // Style sheets section

    addStyleRule = (function() {
      if (getEBTN && createElement && html && isHostMethod(html, 'appendChild')) {
        var addRule = (function() {
          var el = createElement('style');
          if (el) {
            if (isHostMethod(global.document, 'styleSheets')) { // Could conceivably be callable like document.images
              return function(selector, rule, el, docNode) {
                var ss;
                if (docNode.styleSheets && docNode.styleSheets.length) {
                  ss = docNode.styleSheets[docNode.styleSheets.length - 1];
                  if (ss.addRule) {
                    ss.addRule(selector, rule);
                  }
                  else {
                    if (typeof ss.cssText == 'string') { // IE Mac, which throws "permission denied" on insertRule
                      ss.cssText = selector + ' {' + rule + '}';
                    }
                    else {
                      if (ss.insertRule) { // old Mozilla versions, which don't support cssText property for style sheet objects
                        ss.insertRule(selector + ' {' + rule + '}', ss.cssRules.length);
                      }
                    }
                  }
                }
              };
            }
            // NOTE: This branch used to be first. Moved for the sake of Mac IE (which could prove to be a mistake)
            if (elementCanHaveChildren(el) && isHostMethod(global.document, 'createTextNode')) {
              return function(selector, rule, el, docNode) {
                var html = getHtmlElement(docNode);
                var scrollHeight = html.scrollHeight;
                el.appendChild(docNode.createTextNode(selector + ' {' + rule + '}'));
                // Some browsers (e.g. Opera 7.23 render the text nodes (!)
                if (html.scrollHeight != scrollHeight) {
                  if (el.parentNode.offsetHeight) {
                    el.parentNode.removeChild(el);
                    return false;
                  }
                }
              };
            }
            if (typeof el.innerText == 'string') { return function(selector, rule, el) { el.innerText += selector + ' {' + rule + '}'; }; }
            el = null;
          }
        })();
        if (addRule) {
          return function(selector, rule, media, docNode) {
            var el, result;
            var h = getEBTN("head", docNode);
            if (h[0]) {
              el = createElement("style", docNode);
              if (el) {
                el.setAttribute("type", "text/css");
                el.setAttribute("media", media || 'screen');
                h[0].appendChild(el);
                result = addRule(selector, rule, el, docNode || global.document);
                el = null;
                return result;
              }
            }
          };
        }
      }
    })();

    API.addStyleRule = addStyleRule;

    if (html && getAttribute && getEBTN) {
      setActiveStyleSheet = function(id, docNode) {
        var a, i, l, t;
        a = getEBTN('link', docNode);
        i = 0;
        l = a.length;
        while (i < l) {
          t = a[i].getAttribute('title');
          if (t && a[i].getAttribute('rel') && a[i].getAttribute('rel').indexOf('style') != -1) {
            a[i].disabled = true;
            if (t.toLowerCase() == id) { a[i].disabled = false; }
          }
          i++;
        }
      };

      API.setActiveStyleSheet = setActiveStyleSheet;
    }
    
  }
  
  var getElementBordersOrigin = function(el) {
    return [el.clientTop || 0, el.clientLeft || 0];
  };

  API.getElementBordersOrigin = getElementBordersOrigin;
  
  // MS has been known to implement elements as ActiveX objects
  // (e.g. anchors that link to news resources)
  isStyleCapable = !!(html && isRealObjectProperty(html, 'style'));
  if (isStyleCapable) {
    // These flags dictate which style-related functions should be initialized
    canAdjustStyle = {};
    styles = ['display', 'visibility', 'position'];
    iStyle = 3;
    while (iStyle--) {
      canAdjustStyle[styles[iStyle]] = typeof html.style[styles[iStyle]] == 'string';
    }
    API.canAdjustStyle = function(style) { return canAdjustStyle[style]; };
  }
  
  // Style section
  var floatStyle, getCascadedStyle, getKomputedStyle, getInlineStyle, getObjectStyle, getOverrideStyle, getStyle, getStylePixels, setStyle, setStyles;
  var getPositionedParent, isPositionable, isPresent, isVisible;
  
  var positionElement;
  
  var adjustElementSize, sizeElement, sizeElementOuter;
  
  var positionAndSizeElement, positionAndSizeElementOuter;
  
  var colorHex, hexByte, hexNibble, hexRGB;
  var rePixels = new RegExp('^(-)?[\\d\\.]*px$', 'i');
  var reOtherUnits = new RegExp('^(-)?[\\d\\.]*(em|pt|cm|in)$', 'i');
  var reColor = new RegExp('color', 'i');
  var reRGB = new RegExp('rgb[a]*\\((\\d*),[\\s]*(\\d*),[\\s]*(\\d*)[),]', 'i');
  var reTransparent = new RegExp('^rgba\\(\\d+,\\s*,\\d+,\\s*\\d,\\s*0\\)$', 'i');
  var colors = { aqua:'00FFFF', green:'008000', navy:'000080', silver:'C0C0C0', black:'000000', gray:'808080', olive:'808000', teal:'008080', blue:'0000FF', lime: '00FF00', purple:'800080', white:'FFFFFF', fuchsia:'FF00FF', maroon:'800000', red:'FF0000', yellow:'FFFF00' };

  
  var setOpacity, getOpacity, opacityStyles = ['WebkitOpacity', 'KhtmlOpacity', 'MozOpacity', 'opacity'];
  
  var addClass, hasClass, removeClass;
  
  var sideNames = ['Top', 'Left', 'Bottom', 'Right'];
  
  var getElementBorder, getElementBorders;
  
  var getElementMargin, getElementMargins, getElementMarginsOrigin;
  
  if (html) {
    if (isStyleCapable) {
      
      positionElement = (function() {
        var px = (typeof html.style.top == 'number')?0:'px';
        return function(el, y, x) {
          if (y !== null) { el.style.top = y + px; }
          if (x !== null) { el.style.left = x + px; }
        };
      })();

      API.positionElement = positionElement;
      
      sizeElement = (function() {
        var px = (typeof html.style.height == 'number')?0:'px';
        return function(el, h, w) {
          if (h !== null && h >= 0) { el.style.height = h + px; } // >= 0 check does not belong in here (not this function's responsibility)
          if (w !== null && w >= 0) { el.style.width = w + px; }
        };
      })();

      API.sizeElement = sizeElement;

      adjustElementSize = function(el, dim) {
         if (el.offsetHeight != dim[0]) {
           dim[0] -= (el.offsetHeight - dim[0]);
           if (dim[0] >= 0) { el.style.height = dim[0] + 'px'; }
         }
         if (el.offsetWidth != dim[1]) {
           dim[1] -= (el.offsetWidth - dim[1]);
           if (dim[1] >= 0) { el.style.width = dim[1] + 'px'; }
         }
         return dim;
      };

      sizeElementOuter = function(el, dim) {
        sizeElement(el, dim);
        adjustElementSize(el, dim);
      };

      API.sizeElementOuter = sizeElementOuter;

      
      positionAndSizeElement = function(el, r) {
        positionElement(el, r[0], r[1]);
        sizeElement(el, r[2], r[3]);
      };

      positionAndSizeElementOuter = function(el, r) {
        positionAndSizeElement(el, r);
        adjustElementSize(el, [r[2], r[3]]);
      };

      API.positionAndSizeElement = positionAndSizeElement;
      API.positionAndSizeElementOuter = positionAndSizeElementOuter;
      
      floatStyle = (typeof html.style.cssFloat == 'string')?'cssFloat':'styleFloat';

      getObjectStyle = function(o, style) {
        if (style == 'float') { style = floatStyle; }
        return (o)?o[style] || null:null;
      };

      getCascadedStyle = (function() {
        if (isRealObjectProperty(html, 'currentStyle')) {
          return function(el, style) {
            return getObjectStyle(el.currentStyle, style);
          };
        }
      })();

      API.getCascadedStyle = getCascadedStyle;

      getKomputedStyle = (function() {
        var rePositioned = new RegExp('^(absolute|fixed)$');
        var rePixelConvert = new RegExp('^(height|width|left|top|right|bottom|margin.+|border.+|padding.+)$', 'i');

        if (isRealObjectProperty(doc, 'defaultView') && isHostMethod(doc.defaultView, 'getComputedStyle')) {
          return function(el, style) {
            var docNode = getElementDocument(el);
            var s = docNode.defaultView.getComputedStyle(el, null);
            return getObjectStyle(s, style);
          };
        }
        if (getCascadedStyle) {
          return function(el, style) {
            var floatS, parent, position, styleInline, styleRuntime;
            var value = getCascadedStyle(el, style);
            if (value == 'inherit') {
              parent = getElementParentElement(el);
              if (parent) {
                return getKomputedStyle(parent, style);
              }
            }
            switch(style) {
            case 'float':
              position = getKomputedStyle(el, 'position');
              return (position && rePositioned.test(position))?'none':value;
            case 'display':
              if (value != 'none') {

                // FIXME: Lose this (display bit)

                parent = getElementParentElement(el);
                while (parent) {
                  if (getCascadedStyle(parent, 'display') == 'none') {
                    return 'none';
                  }
                  parent = getElementParentElement(parent);
                }
                position = getKomputedStyle(el, 'position');
                if (position && rePositioned.test(position)) {
                  value = 'block';
                }
                else {
                  floatS = getCascadedStyle(el, 'float');
                  if (floatS && floatS != 'none') {
                    value = 'block';
                  }
                }
              }
              return value;
            }

            if (rePixels.test(value)) { return value; }
            if (reOtherUnits.test(value)) {
              if (rePixelConvert.test(style)) {
                if (parseFloat(value)) {
                  if (isRealObjectProperty(el, 'runtimeStyle')) {
                    styleInline = el.style.left;
                    styleRuntime = el.runtimeStyle.left;
                    el.runtimeStyle.left = el.currentStyle.left;
                    el.style.left = value;
                    value = el.style.pixelLeft;
                    el.style.left = styleInline;
                    el.runtimeStyle.left = styleRuntime;
                    return value + 'px';
                  }
                }
                else {
                  return '0px';
                }
              }
              return null;
            }
            return ((value == 'auto' && style != 'overflow') || value == 'inherit')?null:value;
          };
        }
      })();

      API.getComputedStyle = getKomputedStyle;

      getInlineStyle = function(el, style) {
        var value = getObjectStyle(el.style, style);
        if (typeof value == 'number') { value += 'px'; }
	return value;
      };

      API.getInlineStyle = getInlineStyle;

      getOverrideStyle = (function() {
        if (isRealObjectProperty(doc, 'defaultView') && isHostMethod(doc.defaultView, 'getOverrideStyle')) {
          return function(el, style) {
            var s = getElementDocument(el).defaultView.getOverrideStyle(el, null);
            return getObjectStyle(s, style);
          };
        }
        if (isRealObjectProperty(html, 'runtimeStyle')) {
          return function(el, style) {
            return getObjectStyle(el.runtimeStyle, style);
          };
        }
      })();

      API.getOverrideStyle = getOverrideStyle;

      
      setOpacity = (function(el) { 
        var i, s, so;
        var reOpacity = new RegExp('alpha\\(opacity=[^\\)]+\\)', 'i');
        var fn = function(el, o) { el.style[s] = o; };

        i = opacityStyles.length;
        while (i--) {
          if (typeof el.style[opacityStyles[i]] == 'string') {
            s = opacityStyles[i];
            return fn;
          }
        }

        if (typeof el.style.filter == 'string') {
          return function(el, o) {
            so = el.style;
            //var f;

            //if (typeof el.filters != 'undefined') {
              if (el.currentStyle && !el.currentStyle.hasLayout) { so.zoom = 1; }
              //f = el.filters.alpha;
              //if (typeof f != 'undefined') {
              //  f.enabled = o == 1;
              //  f.opacity = o * 100;
              //}
              //else {
                if (!reOpacity.test(so.filter)) {
                  so.filter += ' alpha(opacity=' + (o * 100) + ')';
                }
                else {
                  so.filter = so.filter.replace(reOpacity, (o >= 0.9999)?'':'alpha(opacity=' + (o * 100) + ')');
                }
              //}
            //}
          };
        }
      })(html);

      API.setOpacity = setOpacity;

      getOpacity = (function(el) {
        var i, s, reOpacity = new RegExp('opacity\\s*=\\s*([^\\)]*)', 'i');
        var fn = function(el) {
          var o = el.style[s];
          if (o) { return parseFloat(o); }
          if (getKomputedStyle) {
            o = getKomputedStyle(el, 'opacity');
            if (o !== null) { return parseFloat(o); }
          }
          return 1;
        };
        
        i = opacityStyles.length;
        while (i--) {
          if (typeof el.style[opacityStyles[i]] == 'string') {
            s = opacityStyles[i];
            return fn;
          }
        }

        if (typeof html.style.filter == 'string' && getCascadedStyle) {
          return (function() {
            var m;
//            if (html.filters) {
//              return function(el) {
//                return (typeof el.filters.alpha != 'undefined' && el.filters.alpha.enabled)?el.filters.alpha.opacity / 100:1;
//              };
//            }				
            return function(el) {
              var style = getCascadedStyle(el, 'filter');
              if (style) {
                m = style.match(reOpacity);
                return (m)?parseFloat(m[1]) / 100:1;
              }
              return 1;
            };				
          })();
        }


      })(html);

      API.getOpacity = getOpacity;
      
      hexNibble = function(d) {
        return '0123456789ABCDEF'.substring(d, d + 1);
      };

      hexByte = function(d) {
        return hexNibble(Math.floor(d / 16)) + hexNibble(d % 16);
      };

      hexRGB = function(rgb) {
        return [hexByte(rgb[0]), hexByte(rgb[1]), hexByte(rgb[2])].join('');
      };

      colorHex = function(color) {
        var m = reRGB.exec(color);
        if (m) { return ['#', hexByte(parseInt(m[1], 10)), hexByte(parseInt(m[2], 10)), hexByte(parseInt(m[3], 10))].join(''); }
        if (reTransparent.test(color)) { return 'transparent'; }
        return (colors[color])?'#' + colors[color]:null;
      };

      getStyle = (function() {
        var get = (function() {
          if (getKomputedStyle) {
            return function(el, style) {
              var styleO, styleI, styleC;

              if (getOverrideStyle) {
                styleO = getOverrideStyle(el, style);
              }
              if (styleO) { return styleO; }
              styleC = getKomputedStyle(el, style);
              if (styleC) {
                return styleC;
              }
              else {
                styleI = getInlineStyle(el, style);
                styleC = getCascadedStyle && getCascadedStyle(el, style); // Computed value is changed to null for some rules in IE (e.g. font-size:1em)
                if (styleC) {
                  return styleC;
                }
                return ((style == 'display')?'none':styleI) || null;
              }
            };
          }
          return function(el, style) {
            var parent, s = getInlineStyle(el, style);

            // TODO: Lose this (display bit)

            if (style == 'display' && (s != 'none')) {
              parent = getElementParentElement(el);
              while (parent) {
                if (parent.style.display == 'none') {
                  return 'none';
                }
                parent = getElementParentElement(parent);
              }
            }				
            return s;
          };
        })();

        return function(el, style) {
          var value = get(el, style);

          return (reColor.test(style))?colorHex(value) || value:value;
        };
      })();

      API.getStyle = getStyle;

      setStyle = function(el, style, value) {       
        if (style == 'float') { style = floatStyle; }
        el.style[style] = value;
      };

      API.setStyle = setStyle;

      setStyles = function(el, styles) {
        var s;

        for (s in styles) { if (isOwnProperty(styles, s)) { setStyle(el, s, styles[s]); } }
      };

      API.setStyles = setStyles;

      getStylePixels = function(el, style) {
        var p = getStyle(el, style);
        if (rePixels.test(p)) { return parseFloat(p); }
        if (reOtherUnits.test(p) && !parseFloat(p)) { return 0; } // 0pt
        return null;
      };

      API.getStylePixels = getStylePixels;

      
      getElementBorder = function(el, border) {
        var b = getStylePixels(el, 'border' + border + 'Width');
        if (b === null) {
          switch(border) {
            case 'Left':
            case 'Right':
              return el.clientLeft || 0;
            case 'Top':
            case 'Bottom':
              return el.clientTop || 0;				
          }
        }
        return b;
      };

      API.getElementBorder = getElementBorder;

      getElementBordersOrigin = function(el) {
        return [el.clientTop || getElementBorder(el, 'Top'), el.clientLeft || getElementBorder(el, 'Left')];
      };

      API.getElementBordersOrigin = getElementBordersOrigin;

      getElementBorders = (function() {
        var i;
        return function(el) {
          var borders = {};
          i = sideNames.length;
          while (i--) { borders[sideNames[i]] = getElementBorder(el, sideNames[i]); }
          return borders;
        };
      })();

      API.getElementBorders = getElementBorders;
      
      getElementMargin = function(el, margin) {
        return getStylePixels(el, 'margin' + margin) || 0;
      };

      API.getElementMargin = getElementMargin;

      getElementMarginsOrigin = function(el, margin) {
        return [getElementMargin(el, 'Top'), getElementMargin(el, 'Left')];
      };

      API.getElementMarginsOrigin = getElementMarginsOrigin;

      getElementMargins = (function() {
        var i;
        return function(el) {
          var margins = {};
          i = sideNames.length;
          while (i--) { margins[sideNames[i]] = getElementMargin(el, sideNames[i]); }
          return margins;
        };
      })();

      API.getElementMargins = getElementMargins;
      

      getPositionedParent = function(el) {
        var style;
        if (getStyle(el, 'position') != 'fixed' && isRealObjectProperty(el, 'offsetParent')) {
          do {
            el = el.offsetParent;
            if (el) { style = getStyle(el, 'position'); }
          }
          while (el && (!style || style == 'static'));
          return el;
        }
      };

      API.getPositionedParent = getPositionedParent;

      // Determines if an element is logically, rather than empirically visible
      // Display rules of the element or its parents are irrelevant, except that display:none rules obscure even logical visibility in some agents.
      // As with all rules, other than display, it is not possible to get consistent, accurate results for elements that are not part of the layout.
      // For compatibility with agents that cannot compute styles, initially mirror cascaded visibility:hidden rules inline.

      isVisible = function(el) {
        return getStyle(el, 'visibility') != 'hidden';
      };

      API.isVisible = isVisible;

      // For compatibility with agents that cannot compute styles, initially mirror cascaded display:none rules of the element and its parents inline.
      // When the compatibility directions for isVisible and isPresent are followed, empirical visibility can be accurately determined in all agents by isVisible(el) && isPresent(el)

      isPresent = function(el) {
        return getStyle(el, 'display') != 'none';
      };

      API.isPresent = isPresent;

      isPositionable = function(el) {
         var style = getStyle(el, 'position');
         return (style)?(style != 'static'):false;
      };

      API.isPositionable = isPositionable;

      
      addClass = function(el, className) {
        var re;
        if (!el.className) {
          el.className = className;
        }
        else {
          re = new RegExp('(^|\\s)' + className + '(\\s|$)');
          if (!re.test(el.className)) { el.className += ' ' + className; }
        }
      };

      API.addClass = addClass;
	
      removeClass = function(el, className) {
        var re, m;
        if (el.className) {
          if (el.className == className) {
            el.className = '';
          }
          else {		
            re = new RegExp('(^|\\s)' + className + '(\\s|$)');
            m = el.className.match(re);
            if (m && m.length == 3) { el.className = el.className.replace(re, (m[1] && m[2])?' ':''); }
          }
        }
      };

      API.removeClass = removeClass;

      hasClass = function(el, className) {
        return (new RegExp('(^|\\s)' + className + '(\\s|$)')).test(el.className);
      };

      API.hasClass = hasClass;
      
      API.findAncestor = function(el, tagName, className) { 
        el = getElementParentElement(el); 

        while (el && (tagName && getElementNodeName(el) != tagName || 
className && !hasClass(el, className))) { 
          el = getElementParentElement(el); 
        } 
        return el;
      };
      
    }
  }
  
  // Cookie section

  var cookieCheck, cookiesEnabled, deleteCookie, getCookie, setCookie;
  var reEncodeRegExp = new RegExp('([\\.])', 'g');
  
  function encodeRegExp(s) {
    return s.replace(reEncodeRegExp, '\\$1');
  }

  if (encode && decode && doc && typeof doc.cookie == 'string') {
    setCookie = function(name, value, expires, path, secure, docNode) {
      var date, sExpires = '';
      path = path || '/';
      if (typeof expires == 'undefined') { expires = 30; }
      if (expires) {
        if (typeof expires == 'number') {
          date = new Date();
          date.setTime(date.getTime() + (expires * 86400000));
          expires = date;
        }
        sExpires = '; expires=' + expires.toUTCString();
      }
      (docNode || global.document).cookie = encode(name) + '=' + encode(value) + sExpires + '; path=' + path + ((secure)?'; secure':'');
    };

    API.setCookie = setCookie;

    getCookie = function(name, defaultValue, encoded, docNode) {
      var re = new RegExp('( |;|^)' + encodeRegExp(encode(name)) + '=([^;]+)');
      var value = (docNode || global.document).cookie.match(re);
      return (value !== null)?((encoded)?value[2]:decode(value[2])):((typeof defaultValue == 'undefined')?null:defaultValue);
    };

    API.getCookie = getCookie;

    deleteCookie = function(name, path, docNode) {
      setCookie(name, '', -1, path, null, docNode);
    };

    API.deleteCookie = deleteCookie;

    cookiesEnabled = function() {
      if (typeof cookieCheck == 'undefined') {
        setCookie('_cookietest', '1');
        cookieCheck = (getCookie('_cookietest', '0') == '1');
        deleteCookie('_cookietest');
      }
      return cookieCheck;
    };

    API.cookiesEnabled = cookiesEnabled;
    
  }

  
  // Events section

  var attachListener, attachDocumentListener, attachWindowListener, detachListener, detachDocumentListener, detachWindowListener;
  var cancelPropagation, cancelDefault, getEventTarget, getEventTargetRelated, getKeyboardKey, getMouseButtons, isEventSupported;
  var normalizedListeners = {}, originalListeners = {}, supportedEvents = {};
  var fnId = 0;

  if (isHostMethod(doc, 'createElement')) {
    isEventSupported = function(eventName, el) {
      eventName = 'on' + eventName;
      var eventKey = eventName + (el && el.tagName || '');

      if (typeof supportedEvents[eventKey] == 'undefined') {
        supportedEvents[eventKey] = true;
        el = el || global.document.createElement('div');
        if (el && isHostMethod(el, 'setAttribute')) {
          if (typeof el[eventName] == 'undefined') {
            el.setAttribute(eventName, 'window.alert(" ");');
            supportedEvents[eventKey] = isHostMethod(el, eventName);
          }
        }
      }
      return supportedEvents[eventKey];
    };
  }

  API.isEventSupported = isEventSupported;
  // These two functions are to clean up leaks created by closures in user code
  // They are attached to the unload event of the window in browsers that do not support addEventListener

  // IE5+ (or any browser that supports attachEvent, but not addEventListener)

  function cleanup() {
    var i = API.attachedListeners.length;
    while (i--) {
      API.attachedListeners[i].el.detachEvent('on' + API.attachedListeners[i].ev, API.attachedListeners[i].fn);
    }
    API.attachedListeners = null;
    //API.eventContexts = null; // Other unload events may fire after this and need their context (attaching unload listeners needs further abstraction)
    global.detachEvent('onunload', cleanup);
  }
  

  var uniqueTargetHandle = 0;

  function getTargetId(el) {
    if (el.tagName) { // IE document nodes change unique ID's constantly (?)
      return elementUniqueId(el);
    }
    else {
      if (el == global) { return '_apiwin'; } // Don't pollute primary global namespace (window listeners)
      return (el._targetId = el._targetId || ('_api' + uniqueTargetHandle++)); // Use expando for documents
    }
  }

  function addNormalizedListener(el, ev, fnId, fnNormalized, fnOriginal, context) {
    var uid = getTargetId(el);
    if (!normalizedListeners[uid]) { normalizedListeners[uid] = {}; }
    if (!normalizedListeners[uid][ev]) { normalizedListeners[uid][ev] = {}; }
    normalizedListeners[uid][ev][fnId] = fnNormalized;
    originalListeners[fnId] = { fn:fnOriginal, context:context };
  }

  function getNormalizedListener(el, ev, fnId) {
    var uid = getTargetId(el);
    return (normalizedListeners[uid] && normalizedListeners[uid][ev] && normalizedListeners[uid][ev][fnId]);
  }

  function removeNormalizedListener(el, ev, fnId) {
    var uid = getTargetId(el);
    if (normalizedListeners[uid] && normalizedListeners[uid][ev]) {
      normalizedListeners[uid][ev][fnId] = null;
    }
  }
  
  API.eventContexts = []; // Keeps normalized event closures clean of DOM references
  var eventContextHandle = 0;

  var attachListenerFactory = function(d) {
    return (function() {
      var normalizeFunction = (function() {
        if (Function.prototype.call) {
          return function(fn, handle) {
            return function(e) { return fn.call(API.eventContexts[handle].context, e || API.eventContexts[handle].globalContext.event); };
          };
        }
        else {    
          return function(fn, handle) {
            return function(e) { var context = API.eventContexts[handle].context; context.__mylibevent = fn; var r = context.__mylibevent(e || API.eventContexts[handle].globalContext.event); context.__mylibevent = null; return r; };
          };
        }
      })();

      var attach;
      if (isHostMethod(d, 'addEventListener')) {
        return function(d, ev, fn, context) {
          var fnNormalized = (context)?function(e) { return fn.call(context, e); }:fn;
          if (!fn._fnId) {
            fn._fnId = ++fnId;
          }
          addNormalizedListener(d, ev, fn._fnId, fnNormalized, fn, context);
          return d.addEventListener(ev, fnNormalized, false);
        };
      }

      if (isHostMethod(d, 'attachEvent')) {
        attach = function(d, ev, fn, fnNormalized, context) {
          if (!API.attachedListeners) {
            API.attachedListeners = [];
            global.attachEvent('onunload', cleanup);
          }
          addNormalizedListener(d, ev, fn._fnId, fnNormalized, fn, context);
          d.attachEvent('on' + ev, fnNormalized);
          if (!(ev == 'unload' && d == global)) { API.attachedListeners[API.attachedListeners.length] = { el:d, ev:ev, fn:fnNormalized }; }
          d = null;
        };
      }
      

      if (attach) {
      return function(d, ev, fn, context) {
        var globalContext, docNode = getElementDocument(d);
        if ((docNode || d) == global.document) {
          globalContext = global;
        }
        else {
          if (getDocumentWindow) {
            globalContext = (docNode && getDocumentWindow(docNode)) || getDocumentWindow(d) || d; // element, document or window
          }
        }
        if (globalContext) {
          API.eventContexts[eventContextHandle] = { context:context || d, globalContext:globalContext };
          var fnNormalized = normalizeFunction(fn, eventContextHandle++);
          if (!fn._fnId) {
            fn._fnId = ++fnId;
          }
          if (!getNormalizedListener(d, ev, fn._fnId)) {
            attach(d, ev, fn, fnNormalized, context);
          }
          return true;
        }
        return false;
      };
      }
    })();
  };

  var detachListenerFactory = function(d) {
     var detach = (function() {
       if (isHostMethod(d, 'removeEventListener')) {
         return function(d, ev, fn) {
           d.removeEventListener(ev, fn, false);
         };
       }
       if (isHostMethod(d, 'detachEvent')) {
         return function(d, ev, fn) {
           d.detachEvent('on' + ev, fn);
         };
       }
       
     })();
     if (detach) {
       return function(d, ev, fn) {
         var fnNormalized;
         if (fn._fnId) {
           fnNormalized = getNormalizedListener(d, ev, fn._fnId);
           if (fnNormalized) {
             removeNormalizedListener(d, ev, fn._fnId);
             detach(d, ev, fnNormalized);
           }
         }
         else {
           detach(d, ev, fn);
         }
       };
     }
  };

  function attachSpecificListenerFactory(obj) {
    var fnAttach = attachListenerFactory(obj);
    return fnAttach && function(ev, fn, objAlt, context) { return fnAttach(objAlt || obj, ev, fn, context); };
  }

  function detachSpecificListenerFactory(obj) {
    var fnDetach = detachListenerFactory(obj);
    return fnDetach && function(ev, fn, objAlt) { return fnDetach(objAlt || obj, ev, fn); };
  }

  if (attachListenerFactory) {

  if (html) {
    attachListener = attachListenerFactory(html);
    if (attachListener) { detachListener = detachListenerFactory(html); }

    API.attachListener = attachListener;
    API.detachListener = detachListener;
  }

  if (doc) {
    attachDocumentListener = attachSpecificListenerFactory(doc);
    if (attachDocumentListener) { detachDocumentListener = detachSpecificListenerFactory(doc); }

    API.attachDocumentListener = attachDocumentListener;
    API.detachDocumentListener = detachDocumentListener;
  }

  attachWindowListener = attachSpecificListenerFactory(this);
  if (attachWindowListener) { detachWindowListener = detachSpecificListenerFactory(this); }

  }

  API.attachWindowListener = attachWindowListener;
  API.detachWindowListener = detachWindowListener;
  
  // Called when outerHTML replaces an element (e.g. setting inner HTML of a select or the type or name attribute of an input in IE)
  // Attaches all listeners from the old (and now orphaned) element to the replacement

  transferListeners = function(elFrom, elTo) {
    var index, j, original, uid = elementUniqueId(elFrom), uidTo = elementUniqueId(elTo), nl = normalizedListeners[uid];

    if (nl && uid != uidTo) { // Second check for safety
      for (index in nl) {
        if (isOwnProperty(nl, index)) {
          for (j in nl[index]) {
            if (isOwnProperty(nl[index], j) && nl[index][j]) {
              original = originalListeners[j];
              if (original) {
                detachListener(elFrom, index, original.fn);
                attachListener(elTo, index, original.fn, original.context);
              }
            }            
          }
        }
      }
    }
  };
  
  purgeListeners = function(el, bChildren, bChildrenOnly) {
    var index, j, original, uid = elementUniqueId(el), nl = normalizedListeners[uid];

    if (!bChildrenOnly) {
      if (nl) {
        for (index in nl) {
          if (isOwnProperty(nl, index)) {
            for (j in nl[index]) {
              if (isOwnProperty(nl[index], j) && nl[index][j]) {
                original = originalListeners[j];
                if (original) {
                  detachListener(el, index, original.fn);
                }
              }            
            }
          }
        }
      }
    }
    if (bChildren && el.childNodes) {
      index = el.childNodes.length;
      while (index--) {
        if (el.childNodes[index].nodeType == 1) { purgeListeners(el.childNodes[index], true); }
      } 
    }
  };

  API.purgeListeners = purgeListeners;
  
  var callInContext = (function() {
    if (Function.prototype.call) {
      return function(fn, context, arg1, arg2) {
        return fn.call(context, arg1, arg2);
      };
    }
    return function(fn, context, arg1, arg2) {
      context.__mylibevent = fn;
      var r = context.__mylibevent(arg1, arg2);
      context.__mylibevent = null;
      return r;
    };
  })();
  
  var assistedEvents = {}, assistedEventListeners = {};

  function hasAssistedEventListeners(el, ev) {
    var uid = elementUniqueId(el);
    return !!(assistedEventListeners[uid] && assistedEventListeners[uid][ev]);
  }

  function addAssistedEventListeners(el, ev, listeners) {
    var uid = elementUniqueId(el);
    assistedEventListeners[uid] = assistedEventListeners[uid] || {};
    if (!assistedEventListeners[uid][ev]) {
      assistedEventListeners[uid][ev] = listeners;
      return true;
    }
    return false;
  }

  function removeAssistedEventListeners(el, ev) {
    var listeners, uid = elementUniqueId(el);
    if (assistedEventListeners[uid] && assistedEventListeners[uid][ev]) {
      listeners = assistedEventListeners[uid][ev];
      assistedEventListeners[uid][ev] = null;
    }
    return listeners;
  }

  function detachAssistedEventListeners(el, ev) {
    var index, listeners = removeAssistedEventListeners(el, ev);
    if (listeners) {
      for (index in listeners) {
        if (isOwnProperty(listeners, index)) {
          detachListener(el, index, listeners[index]);
        }
      }
    }
  }

  
  var attachContextClickListener, contextClickListenerFactory, contextEventType, detachContextClickListener;
  
  var attachMousewheelListener, detachMousewheelListener, getMousewheelDelta, mousewheelListenerFactory;
  
  var attachRolloverListeners, detachRolloverListeners, rolloverStatus, rolloverListener, rolloverListenerWithParentCheck;
  
  // Event normalization
  if (attachListener) {
    getKeyboardKey = function(e) {
      return (e.type == 'keypress')?e.charCode || e.keyCode || e.which:e.which || e.keyCode;
    };

    API.getKeyboardKey = getKeyboardKey;

    cancelPropagation = function(e) {
      if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; }
    };

    API.cancelPropagation = cancelPropagation;

    cancelDefault = function(e) {
      if (e.preventDefault) { e.preventDefault(); }
      if (global.event) { global.event.returnValue = false; }
      return false;
    };

    API.cancelDefault = cancelDefault;

    getMouseButtons = function(e) {
      var b = {};
      if (typeof e.which != 'undefined') {
        b.left = (e.which == 1);
        b.middle = (e.which == 2);
        b.right = (e.which == 3);
      }
      else {
        b.left = (e.button & 1);
        b.middle = (e.button & 4);
        b.right = (e.button & 2);
      }
      return b;
    };

    API.getMouseButtons = getMouseButtons;

    getEventTarget = function(e) {
      return (e.target)?((e.target.nodeType == 3)?e.target.parentNode:e.target):e.srcElement;
    };

    API.getEventTarget = getEventTarget;

    getEventTargetRelated = function(e) {
      if (e.relatedTarget) { return (e.relatedTarget.nodeType == 3)?e.relatedTarget.parentNode:e.relatedTarget; }
      if (e.srcElement) {
        if (e.srcElement == e.fromElement) { return e.toElement; }
        if (e.srcElement == e.toElement) { return e.fromElement; }
      }
      return null;
    };

    API.getEventTargetRelated = getEventTargetRelated;
    
    // Assisted events
    
    contextClickListenerFactory = function(fn) {
      return function(e) {
        if ((getMouseButtons(e).right || e.type == 'contextmenu')) {
          if (typeof contextEventType == 'undefined') { contextEventType = e.type; }
          if (contextEventType == e.type) { callInContext(fn, this, e); }
          return cancelDefault(e);
        }
      };
    };

    attachContextClickListener = (function() {
      function attach(el, fn, context) {
        if (!hasAssistedEventListeners(el, 'contextclick')) {
	  attachListener(el, 'mouseup', fn, context);
          attachListener(el, 'contextmenu', fn, context);
          addAssistedEventListeners(el, 'contextclick', { mouseup:fn, contextmenu:fn });
        }
      }
      return function(el, fn, context) {
        attach(el, contextClickListenerFactory(fn), context);
      };
    })();

    detachContextClickListener = function(el, fn) {
      detachAssistedEventListeners(el, 'contextclick');
    };

    assistedEvents.ContextClick = { attach:attachContextClickListener, detach:detachContextClickListener };
    API.attachContextClickListener = attachContextClickListener;
    API.detachContextClickListener = detachContextClickListener;
    

    getMousewheelDelta = function(e) {
      return (e.detail)?-(e.detail) / 3:e.wheelDelta / 120;
    };

    API.getMousewheelDelta = getMousewheelDelta;

    mousewheelListenerFactory = function(fn) {
      return function(e) {
        callInContext(fn, this, e, getMousewheelDelta(e));
        return cancelDefault(e);
      };
    };

    //if (!isEventSupported || isEventSupported('DOMMouseScroll') || isEventSupported('mousewheel')) {
      attachMousewheelListener = (function() {
        function attach(el, fn, context) {
          if (!hasAssistedEventListeners(el, 'mousewheel')) {
            attachListener(el, 'mousewheel', fn, context);
            attachListener(el, 'DOMMouseScroll', fn, context);
            addAssistedEventListeners(el, 'mousewheel', { mousewheel:fn, DOMMouseScroll:fn });
          }
        }
        return function(el, fn, context) {
          attach(el, mousewheelListenerFactory(fn), context);
        };
      })();
    
      detachMousewheelListener = function(el) {
        detachAssistedEventListeners(el, 'mousewheel');
      };

      assistedEvents.Mousewheel = { attach:attachMousewheelListener, detach:detachMousewheelListener };
    //}
    API.attachMousewheelListener = attachMousewheelListener;
    API.detachMousewheelListener = detachMousewheelListener;
    
    if (typeof setStatus != 'undefined') {
      rolloverStatus = function(el, b) { return (b && el.title)?setStatus(el.title):setStatus(''); };
    }

    rolloverListener = function(fnOver, fnOut, bSetStatus, el) {
      return function(e) {
        var over = e.type == 'mouseover' || e.type == 'focus';
        callInContext((over)?fnOver:fnOut, this, e);
        if (bSetStatus && rolloverStatus) { rolloverStatus(el, over); }
      };
    };

    rolloverListenerWithParentCheck = function(fnOver, fnOut, bSetStatus, el) {
      return function(e) {
        var relatedTarget = getEventTargetRelated(e);
        var target = getEventTarget(e);
        var over = e.type == 'mouseover' || e.type == 'focus';	
        var isAncestorOfRelatedTarget = el != relatedTarget && (relatedTarget && isDescendant(el, relatedTarget));
        var isAncestorOfTarget = target && el != target && isDescendant(el, target);
        if (isAncestorOfRelatedTarget && isAncestorOfTarget) {
          return;
        }
        if (relatedTarget == el && isAncestorOfTarget || target == el && isAncestorOfRelatedTarget) {
          return;
        }
        callInContext((over)?fnOver:fnOut, this, e);
        if (bSetStatus && rolloverStatus) { rolloverStatus(el, over); }
      };
    };

    if (isDescendant) {
    attachRolloverListeners = function(el, fnOver, fnOut, context, bAddFocusListeners, bSetStatus) {
      var listener = (typeof getEBTN != 'undefined' && getEBTN('*', el).length)?rolloverListenerWithParentCheck(fnOver, fnOut, bSetStatus, el):rolloverListener(fnOver, fnOut, bSetStatus, el);
      var listeners = { mouseover:listener, mouseout:listener };
      attachListener(el, 'mouseover', listener, context);
      attachListener(el, 'mouseout', listener, context);
      if (bAddFocusListeners) {
        attachListener(el, 'focus', listener, context);
        attachListener(el, 'blur', listener, context);
        listeners.focus = listeners.blur = listener;
      }
      addAssistedEventListeners(el, 'roll', listeners);
    };

    API.attachRolloverListeners = attachRolloverListeners;

    detachRolloverListeners = function(el, fn) {
      detachAssistedEventListeners(el, 'roll');
    };

    API.detachRolloverListeners = detachRolloverListeners;
    }
    
  }
  
  var getBodyElement, getContainerElement;
  
  var defaultDisplay, presentElement, toggleElementPresence;
  
  var showElement, toggleElement;
  
  var getElementPositionStyle;
  
  var getElementSizeStyle;
  
  var getScrollPosition, getScrollPositionMax, getElementScrollPosition, getElementScrollPositionMax, setScrollPosition, setElementScrollPosition;
  
  var getScrollElement, getViewportSize, getViewportClientRectangle, getViewportScrollRectangle, getViewportScrollSize;
  
  var getMousePosition;
  
  var addElementNodes, importNode, setElementNodes;
  
  var ensureSizable;
  
  var contained = function(pos1, dim1, pos2, dim2) {
    return ((pos1[0] >= pos2[0]) && (pos1[0] <= pos2[0] + dim2[0]) && (pos1[1] >= pos2[1]) && (pos1[1] <= pos2[1] + dim2[1]) && (pos1[0] + dim1[0] <= pos2[0] + dim2[0]) && (pos1[1] + dim1[1] <= pos2[1] + dim2[1]));
  };
  var overlaps = function(pos1, dim1, pos2, dim2) {
    return (((pos1[1] >= pos2[1]) && (pos1[1] <= pos2[1] + dim2[1])) || ((pos1[1] < pos2[1]) && (pos1[1] + dim1[1] > pos2[1]))) && (((pos1[0] >= pos2[0]) && (pos1[0] <= pos2[0] + dim2[0])) || ((pos1[0] < pos2[0]) && (pos1[0] + dim1[0] > pos2[0])));
  };	
  
  var constrainPosition = function(c, co, dim, constraint) {
    if (co[0] < constraint[0]) { c[0] += (constraint[0] - co[0]); }
    if (co[1] < constraint[1]) { c[1] += (constraint[1] - co[1]); }
    if (co[0] + dim[0] > constraint[0] + constraint[2]) { c[0] += (constraint[0] + constraint[2] - (co[0] + dim[0])); }
    if (co[1] + dim[1] > constraint[1] + constraint[3]) { c[1] += (constraint[1] + constraint[3] - (co[1] + dim[1])); }
  };
  
  var constrainSize = function(c, co, pos, constraint) {
    if (co[0] + pos[0] > constraint[0] + constraint[2]) { c[0] += (constraint[0] + constraint[2] - (co[0] + pos[0])); }
    if (co[1] + pos[1] > constraint[1] + constraint[3]) { c[1] += (constraint[1] + constraint[3] - (co[1] + pos[1])); }
  };
  
  var elementContained, elementContainedInElement, elementOverlaps, elementOverlapsElement;
  
  var absoluteElement, ensurePositionable, getElementPositionedChildPosition, relativeElement;
  
  var getElementPosition;
  
  var effects, spring;
  
  var attachDrag, detachDrag, initiateDrag, attachedDrag = {};
  
  if (attachDocumentReadyListener) {
    attachDocumentReadyListener(function() {
      var body, containerElement;

      getBodyElement = function(docNode) {
        docNode = docNode || global.document;
        if (isRealObjectProperty(docNode, 'body')) { return docNode.body; }
        if (typeof getEBTN == 'function') { return getEBTN('body', docNode)[0] || null; }
        return null;
      };

      API.getBodyElement = getBodyElement;

      body = getBodyElement();

      // Returns documentElement or body as best deemed appropriate
      // Result is ambiguous in all but modern browsers, so don't assume too much from it.
      getContainerElement = function(docNode) {
        docNode = docNode || global.document;
        return (docNode.documentElement && (!docNode.compatMode || docNode.compatMode.indexOf('CSS') != -1))?docNode.documentElement:getBodyElement(docNode);
      };

      API.getContainerElement = getContainerElement;

      containerElement = getContainerElement();
      
      var computedSizeBad;
      
      var computedPositionBad;
      
      var div, div2;
      
      // Scroll section

      function normalizeScroll(t, l, p) {
        if (t) {
          if (t > p[0]) { t = p[0]; }
          if (t < 0) { t = 0; }
        }
        if (l) {
          if (l > p[1]) { l = p[1]; }
          if (l < 0) { l = 0; }
        }
        return [t, l];
      }

      function findWindow(docNode) {        
        if (!docNode || docNode == global.document) {
          return global;
        }
        return (getDocumentWindow)?getDocumentWindow(docNode):null;
      }
		
      getScrollPosition = (function() {
        if (typeof global.pageXOffset == 'number') {
          return function(docNode) {
            var win = findWindow(docNode);
            return (win)?[win.pageYOffset, win.pageXOffset]:null;
          };
        }

        function findScroll(o) {
          return (o && (o.scrollTop || o.scrollLeft)) && (getScrollPosition = API.getScrollPosition = function() { return [o.scrollTop, o.scrollLeft]; })();
        }

        function findScrollChanged(o, docNode) {
          return (o.scrollTop != docNode._scrollPositionSetLast[0] || o.scrollLeft != docNode._scrollPositionSetLast[1]) && (getScrollPosition = API.getScrollPosition = function() { return [o.scrollTop, o.scrollLeft]; })();
        }

        if (global.document.expando || typeof global.document.expando == 'undefined') {
          return function(docNode) {
            var b = getBodyElement(docNode);
            var c = getContainerElement(docNode);

            docNode = docNode || global.document;
            if (!docNode._scrollPositionSetLast) {
              docNode._scrollPositionSetLast = [];
            }
            return ((typeof docNode._scrollPositionSetLast[0] == 'number' && (findScrollChanged(c, docNode) || findScrollChanged(b, docNode))) || findScroll(docNode.documentElement) || findScroll(b)) || [0, 0];
          };
        }
      })();

      API.getScrollPosition = getScrollPosition;

      setScrollPosition = (function() {
        var pos, sp;

        var scroll = (function() {
          if (isHostMethod(global, 'scrollTo')) {
            return function(t, l, docNode) {
              var win = arguments[3] || findWindow(docNode); // Fourth argument is for internal optimization (scrolling effects)

              if (win) { win.scrollTo(pos[1], pos[0]); }
            };
          }
          // Ambiguous branch for agents that do not support window.scrollTo
          if (global.document.expando || typeof global.document.expando == 'undefined') {
            if ((containerElement && typeof containerElement.scrollTop == 'number') || (body && typeof body.scrollTop == 'number')) {
              return function(t, l, docNode) {
                var b = getBodyElement(docNode);
                var c = getContainerElement(docNode);

                docNode = docNode || global.document;
                if (!docNode._scrollPositionSetLast) {
                  docNode._scrollPositionSetLast = [];
                }
                if (b) { // b implies c
                  c.scrollTop = b.scrollTop = docNode._scrollPositionSetLast[0] = t;
                  c.scrollLeft = b.scrollLeft = docNode._scrollPositionSetLast[1] = l;
                }
              };
            }
          }				
        })();
        if (scroll) {
          return function(t, l, docNode, isNormalized) {
            pos = (!getScrollPositionMax || isNormalized || (!t && !l))?[t, l]:normalizeScroll(t, l, getScrollPositionMax(docNode));
            if (pos[0] === null || pos[1] === null) {
              sp = getScrollPosition(docNode);
              if (pos[0] === null) { pos[0] = sp[0]; }
              if (pos[1] === null) { pos[1] = sp[1]; }
            }
            scroll(pos[0], pos[1]);
          };
        }
      })();

      API.setScrollPosition = setScrollPosition;

      getScrollPositionMax = (function() {
        if (typeof global.scrollMaxX != 'undefined') {
          return function(docNode) {
            var win = findWindow(docNode);
            return (win)?[win.scrollMaxY, win.scrollMaxX]:null;
          };
        }
      })();

      API.getScrollPositionMax = getScrollPositionMax;

      getElementScrollPosition = function(el) {
        return [el.scrollTop || 0, el.scrollLeft || 0];
      };

      API.getElementScrollPosition = getElementScrollPosition;

      setElementScrollPosition = function(el, t, l, isNormalized) {
        var pos = (isNormalized || (!t && !l))?[t, l]:normalizeScroll(t, l, getElementScrollPositionMax(el));
        if (pos[0] !== null) { el.scrollTop = pos[0]; }
        if (pos[1] !== null) { el.scrollLeft = pos[1]; }
      };

      API.setElementScrollPosition = setElementScrollPosition;

      getElementScrollPositionMax = function(el) {
        var d = getElementSize(el);
        return [d[4] - d[2], d[5] - d[3]];
      };

      
      // Viewport section

      getScrollElement = function(docNode) {
        docNode = docNode || global.document;
        var body = getBodyElement(docNode);
        var se = getContainerElement(docNode);

        if (body && se != body && se.clientWidth === 0 && typeof body.scrollWidth == 'number') { // IE5.x
          se = body;
        }
        return se;
      };

      API.getScrollElement = getScrollElement;

      getViewportSize = (function() {
        var containerElement = getScrollElement();
        var scrollbarThickness = 0;
        var viewportClientIncludesBorders, viewportClientIncludesMargins, containerClientHeightIsDocumentHeight;

        // Experimental simplified replacement is at http://www.cinsoft.net/viewport.asp

        if (typeof global.window.document.clientHeight == 'number') {
          return function(win) {
            win = win || global; return [win.document.clientHeight, win.document.clientWidth];
          };
        }

        if (containerElement && typeof containerElement.clientHeight != 'undefined' && containerElement.clientHeight) {
          return function(win) {
            win = win || global;
            var cb, cm; // Container element borders and margins
            var containerElement = getScrollElement(win.document); // Changed from getContainerElement to fix IE5
            var containerClientWidth = containerElement.clientWidth;

            if (typeof getElementBorders == 'function') {
              cb = getElementBorders(containerElement);
              if (cb) {
                containerClientWidth += cb.Left + cb.Right;
              }
            }
            if (typeof getElementMargins == 'function') {
              cm = getElementMargins(containerElement);
            }

            var clientWidth, clientHeight, scrollbarHorizontalPresent;
            var globalInnerWidth = global.innerWidth;
            var containerWidthWithoutScrollbar = containerClientWidth;

            // Can only detect viewport width includes container margins if vertical scrollbar is present
            // As designed, test doesn't matter if vertical scrollbar is not present
            if (cm && (cm.Left || cm.Right)) {
              if (!viewportClientIncludesMargins) { viewportClientIncludesMargins = ((cm.Left || cm.Right) && containerClientWidth == containerElement.offsetWidth && containerElement.offsetWidth != globalInnerWidth); }
              if (viewportClientIncludesMargins) { containerWidthWithoutScrollbar += cm.Left + cm.Right; }
            }
            if (!scrollbarThickness && globalInnerWidth && globalInnerWidth > containerWidthWithoutScrollbar) { scrollbarThickness = globalInnerWidth - containerWidthWithoutScrollbar; }
            viewportClientIncludesBorders = !((containerElement.offsetWidth || 0) - containerClientWidth);
            clientWidth = containerElement.clientWidth;
            if (viewportClientIncludesBorders && cb && (cb.Left || cb.Right)) {
              clientWidth += cb.Left + cb.Right;
            }
            if (viewportClientIncludesMargins && cm && (cm.Left || cm.Right)) {
              clientWidth += cm.Left + cm.Right;
            }
            clientHeight = containerElement.clientHeight;
            if (viewportClientIncludesBorders && cb && (cb.Top || cb.Bottom)) {
              clientHeight += cb.Top + cb.Bottom;
            }
            if (viewportClientIncludesMargins && cm && (cm.Top || cm.Bottom)) {
              clientHeight += cm.Top + cm.Bottom;
            }
            scrollbarHorizontalPresent = !(scrollbarThickness && containerElement.scrollWidth - clientWidth <= scrollbarThickness);
            containerClientHeightIsDocumentHeight = ((cb)?cb.Top + cb.Bottom:0) + ((cm)?cm.Top + cm.Bottom:0) + containerElement.clientHeight == containerElement.scrollHeight;

            if (containerClientHeightIsDocumentHeight && !scrollbarThickness && containerClientWidth > globalInnerWidth) {
               var body = getBodyElement(win.document);
               return [body.clientHeight, containerElement.clientWidth];           
            }

            return [(win.innerHeight && scrollbarThickness && ((containerClientHeightIsDocumentHeight || ((scrollbarHorizontalPresent) && win.innerHeight - clientHeight >= scrollbarThickness))))?(win.innerHeight - ((scrollbarHorizontalPresent)?scrollbarThickness:0)):clientHeight, clientWidth];
          };
        }

        // Includes scrollbars if present
		
        if (typeof global.innerHeight != 'undefined') {
          return function(win) {
            win = win || global; return [win.innerHeight, win.innerWidth];
          };
        }
      })();

      API.getViewportSize = getViewportSize;
      
      var htmlOffsetsOrigin, html = getHtmlElement();
      var viewportToHtmlOrigin, htmlToViewportOrigin;

      if (html && isHostMethod(global.document, 'getBoxObjectFor') && typeof getElementBordersOrigin == 'function' && typeof getElementMarginsOrigin == 'function') {
        (function() {
          var m = getElementMarginsOrigin(html);
          var b = getElementBordersOrigin(html);

          var rect = global.document.getBoxObjectFor(html);

          if (!rect) { return; } // Embedded Gecko browsers

          if ((m[0] || m[1] || b[0] || b[1]) && (rect.y == m[0] + b[0] && rect.x == m[1] + b[1])) { // Gecko
            htmlOffsetsOrigin = function(docNode) {
              var x, y;
              var html = getHtmlElement(docNode);
              var borders = getElementBordersOrigin(html);
              var margins = getElementMarginsOrigin(html);
              y = borders[0] + margins[0];
              x = borders[1] + margins[1];
              return [y, x];
            };
          }
          else { // Mozilla
            if (rect.x || rect.y) {
              htmlOffsetsOrigin = function(docNode) {
                docNode = docNode || global.document;
                var html = getHtmlElement(docNode);
                var rect = docNode.getBoxObjectFor(html);
                return [rect.x, rect.y];
              };
            }			
          }

          if (htmlOffsetsOrigin) {
            viewportToHtmlOrigin = function(pos, docNode) {
              var o = htmlOffsetsOrigin(docNode);
              o[0] += pos[0];
              o[1] += pos[1];
              return o;
            };

            htmlToViewportOrigin = function(pos, docNode) {
              var o = htmlOffsetsOrigin(docNode);
              o[0] = pos[0] - o[0];
              o[1] = pos[1] - o[1];
              return o;
            };

            API.viewportToHtmlOrigin = viewportToHtmlOrigin;
            API.htmlToViewportOrigin = htmlToViewportOrigin;
          }
        })();
      }
      
      var getTopRenderedElement = function(docNode) {
         var se = getHtmlElement(docNode);
         if (!se || se.clientWidth === 0) {
           se = getBodyElement(docNode);
         }
         return se;
      };

      var scrollElement = getTopRenderedElement();
      if (scrollElement) {
      getViewportScrollSize = (function() {
        var cm, addMargin;
        if (typeof getElementMarginsOrigin == 'function') {
          cm = getElementMarginsOrigin(containerElement);
          // IE quirk with scrollWidth/Height + HTML element margin
          addMargin = (scrollElement.offsetWidth == cm[0] + scrollElement.scrollWidth);
        }

        if (typeof scrollElement.scrollWidth != 'undefined') {			
          return function(docNode) {
            var se = getTopRenderedElement(docNode);
            return [se.scrollHeight + ((addMargin)?cm[0]:0), se.scrollWidth + ((addMargin)?cm[1]:0)];
          };
        }
        if (typeof global.document.width != 'undefined') {
          return function(docNode) {
            docNode = docNode || global.document;
            return [docNode.height, docNode.width];
          };
        }
        if (typeof scrollElement.offsetWidth != 'undefined') {
          return function(docNode) {
            var ce = getTopRenderedElement(docNode);
            return [ce.offsetHeight, ce.offsetWidth];
          };
        }
      })();

      API.getViewportScrollSize = getViewportScrollSize;

      if (getViewportScrollSize) {
        getViewportScrollRectangle = function(docNode) {
          var rect, r = [0, 0];
          var d = getViewportScrollSize(docNode);
          var body = getBodyElement(docNode);
          var html = getHtmlElement(docNode);
          if (htmlToViewportOrigin) {
            r = htmlToViewportOrigin(r, docNode);
          }
          // Adjustment for Mozilla with body margin
          docNode = docNode || global.document;
          if (isHostMethod(docNode, 'getBoxObjectFor') && docNode.width && body && typeof body.clientWidth == 'undefined') {
            rect = docNode.getBoxObjectFor(html);
            if (rect && rect.width && rect.width != docNode.width) {
              d[0] += (rect.height - body.offsetHeight);
              d[1] += (rect.width - body.offsetWidth);
            }
          }				
          r[2] = d[0];
          r[3] = d[1];
          return r;
        };

        API.getViewportScrollRectangle = getViewportScrollRectangle;
        }
      }
      
      if (!getScrollPositionMax && getViewportScrollSize && getDocumentWindow) {
        getScrollPositionMax = function(docNode) {
          var sp = getViewportScrollSize(docNode);
          var v = getViewportSize(getDocumentWindow(docNode));
          //var se = getScrollElement(docNode);

          //sp[0] -= se.clientHeight;
          //sp[1] -= se.clientWidth;
          sp[0] -= v[0];
          sp[1] -= v[1];
          return sp;
        };
        API.getScrollPositionMax = getScrollPositionMax;
      }
      
      if (typeof getDocumentWindow == 'function') {
        getViewportClientRectangle = function(docNode) {
          var r;
          var sp = (typeof getScrollPosition == 'function')?getScrollPosition(docNode):[0, 0];
          r = [sp[0], sp[1]];
          if (htmlToViewportOrigin) { r = htmlToViewportOrigin(r, docNode); }
          var d = getViewportSize(getDocumentWindow(docNode));
          r[2] = d[0];
          r[3] = d[1];
          return r;
        };
        API.getViewportClientRectangle = getViewportClientRectangle;
      }
      scrollElement = null;
      
      if (setScrollPosition) {			
        API.setScrollPositionTop = function(docNode, options, fnDone) { setScrollPosition(0, null, docNode, null, options, fnDone); };
        API.setScrollPositionLeft = function(docNode, options, fnDone) { setScrollPosition(null, 0, docNode, null, options, fnDone); };
        if (getScrollPositionMax) {
          API.setScrollPositionBottom = function(docNode, options, fnDone) { setScrollPosition(getScrollPositionMax(docNode)[0], null, docNode, true, options, fnDone); };
          API.setScrollPositionRight = function(docNode, options, fnDone) { setScrollPosition(null, getScrollPositionMax(docNode)[1], docNode, true, options, fnDone); };
        }
	API.setScrollPositionRelative = function(t, l, docNode, options, fnDone) {
          var sp = getScrollPosition(docNode);
          if (t !== null) { t += sp[0]; }
          if (l !== null) { l += sp[1]; }
          setScrollPosition(t, l, docNode, false, options, fnDone);
        };
      }

      API.setElementScrollPositionTop = function(el, options, fnDone) {
        setElementScrollPosition(el, 0, null, null, options, fnDone);
      };

      API.setElementScrollPositionLeft = function(el, options, fnDone) {
        setElementScrollPosition(el, null, 0, null, options, fnDone);
      };

      API.setElementScrollPositionBottom = function(el, options, fnDone) {
        setElementScrollPosition(el, getElementScrollPositionMax(el)[0], null, true, options, fnDone);
      };

      API.setElementScrollPositionRight = function(el, options, fnDone) {
        setElementScrollPosition(el, null, getElementScrollPositionMax(el)[1], true, options, fnDone);
      };

      API.setElementScrollPositionRelative = function(el, t, l, options, fnDone) {
        var sp = getElementScrollPosition(el);
        if (t !== null) { t += sp[0]; }
        if (l !== null) { l += sp[1]; }
        setElementScrollPosition(el, t, l, false, options, fnDone);
      };
      
      // Offset section

      getElementPosition = (function() {
        var b;
        var scrollerOffsetSubtractsBorder, offsetAbsoluteExcludesBodyBorder, nestedElementsOffsetFromBody, nestedInlineOffsetIncludesScroll, offsetIncludesBorder, offsetIncludesBodyBorder;

        if (typeof isStyleCapable == 'boolean' && isStyleCapable && createElement && body && body.appendChild && body.removeChild) {
          // Feature tests for browser quirks
          var divOuter = createElement('div');
          var divInner = createElement('div');

          if (divInner && divOuter) {
            // Windows Safari indicates false positive for this test
            scrollerOffsetSubtractsBorder = (function() {
              setStyles(divOuter, {position:'absolute', visibility:'hidden', left:'0', top:'0', padding:'0', border:'solid 1px black', 'overflow':'auto'});
              setStyles(divInner, {position:'static', left:'0', top:'0'});
              divOuter.appendChild(divInner);
              body.appendChild(divOuter);
              b = divInner.offsetLeft == -1;
              body.removeChild(divOuter);
              divOuter.removeChild(divInner);
              return b;
            })();

            // Windows Safari oddity
            offsetAbsoluteExcludesBodyBorder = (function() {
              var bbl = getElementBordersOrigin(body)[1];
              if (bbl) {
                body.appendChild(divOuter);
                b = divOuter.offsetLeft == -bbl;
                body.removeChild(divOuter);
                return b;
              }
            })();

            // All but IE
            nestedElementsOffsetFromBody = (function() {
              setStyles(divOuter, {position:'static', height:'0', width:'0', border:'none'});
              setStyles(divInner, {height:'0', width:'0'});
              divOuter.appendChild(divInner);
              body.appendChild(divOuter);
              b = divInner.offsetParent === body;
              body.removeChild(divOuter);
              divOuter.removeChild(divInner);				
              return b;
            })();

            // Opera oddity
            if (isHostMethod(global.document, 'createTextNode')) {
              nestedInlineOffsetIncludesScroll = (function() {
                var span = createElement('span');
                var tn = global.document.createTextNode('Initializing...');
                span.appendChild(tn);
                divInner.appendChild(span);
                setStyles(divOuter, {overflow:'auto'});
                divOuter.appendChild(divInner);
                body.appendChild(divOuter);
                var o = span.offsetTop;
                divOuter.scrollTop = 1;
                b = o != span.offsetTop;
                body.removeChild(divOuter);
                return b;
              })();
            }

            // Opera oddity
            offsetIncludesBorder = (function() {
              setStyles(divOuter, {position:'absolute', visibility:'hidden', left:'0', top:'0', padding:'0', border:'solid 1px'});
              setStyles(divInner, {position:'absolute', left:'0', top:'0', margin:'0'});
              divOuter.appendChild(divInner);
              body.appendChild(divOuter);
              b = divInner.offsetLeft == 1;
              body.removeChild(divOuter);
              divOuter.removeChild(divInner);
              return b;
            })();

            var bodyBorderWidth = getStylePixels(body, 'borderLeftWidth');

            if (bodyBorderWidth) {
              offsetIncludesBodyBorder = (function() {
                divOuter = API.createElement('div');
                setStyles(divOuter, {position:'static', visibility:'hidden', padding:'0', border:'0'});
                if (body.firstChild) {
                  body.insertBefore(divOuter, body.firstChild);
                } else {
                  body.appendChild(divOuter);
                }
                b = divOuter.offsetLeft == bodyBorderWidth;
                body.removeChild(divOuter);
                return b;
              })();
            }

            divInner = divOuter = null;
          }
        }

        function scrollFix(el, body) {
          var scrollerPos, scrollerOverflow, borders, p = [0, 0, 0, 0];
          var pos = getStyle(el, 'position');

          if (pos != 'fixed') { // TODO: Check before calling
            while (el.parentNode && el.parentNode !== body) {
              el = el.parentNode;
              scrollerPos = getStyle(el, 'position') || 'static';
              scrollerOverflow = getStyle(el, 'overflow');
              if (!((scrollerPos == 'static' && pos == 'absolute') || pos == 'fixed')) {
                if ((el.scrollTop || el.scrollLeft) && scrollerOverflow != 'visible') {
                  p[0] -= (el.scrollTop || 0);
                  p[1] -= (el.scrollLeft || 0);
                  if (scrollerOffsetSubtractsBorder) {
                    borders = getElementBordersOrigin(el);
                    p[2] += borders[0];
                    p[3] += borders[1];
                  }
                }
              }
            }
          }
          return p;
        }

        function positionOffset(el, elContainer, noScrollFix) {
          var x = 0, y = 0, body, borders, pos;
          var scrollPos, sf = [0, 0, 0, 0];
          var elOriginal = el;
          var docNode = getElementDocument(el);

          if (docNode) {
            body = getBodyElement(docNode);
          }

          // Fixed position in Opera has no offsetParent
          if (!el.offsetParent && typeof getElementPositionStyle == 'function') {
            return getElementPositionStyle(el, null);
          }

          if (el.offsetParent === body) {
            if (elContainer && nestedElementsOffsetFromBody) {
              var po = positionOffset(elContainer);
              y = -po[0];
              x = -po[1];
            }
          }
          if (elContainer && (offsetIncludesBorder || (nestedElementsOffsetFromBody && el.offsetParent == body))) {
            borders = getElementBordersOrigin(elContainer);
            y -= borders[0];
            x -= borders[1];
          }
          if (typeof getStyle == 'function') {
            var d = getStyle(el, 'display');
            sf = scrollFix(el, body);
            if (noScrollFix) {
              if (nestedInlineOffsetIncludesScroll && d == 'inline') {
                y -= sf[0];
                x -= sf[1];
              }
            }
            else {
              if ((!nestedInlineOffsetIncludesScroll || (!d || d != 'inline'))) {
                y += sf[0];
                x += sf[1];					
              }
            }
            if (scrollerOffsetSubtractsBorder) {
              y += sf[2];
              x += sf[3];
            }
          }

          // TODO: Convert internal typeof checks to resolved references (API.*) as can allow global identifier collisions in partial builds

          if (typeof getStyle == 'function' && docNode && typeof getScrollPosition == 'function') {
            pos = getStyle(el, 'position');

            if (pos == 'fixed') {
              scrollPos = getScrollPosition(docNode);
              x += scrollPos[1];
              y += scrollPos[0];
            }
          }

          do {
            if (el !== body || (el.offsetLeft > 0 && el.offsetTop > 0)) {
              if (el.offsetLeft) { x += el.offsetLeft; }
              if (el.offsetTop) { y += el.offsetTop; }
            }

            if (!offsetIncludesBorder && el !== elOriginal && (!offsetIncludesBodyBorder || el !== body)) {
              b = getElementBordersOrigin(el);
              y += b[0];
              x += b[1];
            }

            if ((pos == 'absolute' || pos == 'fixed') && el.offsetParent === body) {
              if (offsetAbsoluteExcludesBodyBorder) {
                b = getElementBordersOrigin(body);
                y += b[0];
                x += b[1];
              }
              el = null;
            } else {
              el = el.offsetParent;
            }
          }
          while (el && el !== elContainer);
          return [y, x];
        }

        if (isHostMethod(global.document, 'getBoxObjectFor')) {
          return function(el, elContainer, noScrollFix) {
            var pos, borders, parentNode, sf, sp, stylePos;
            var posContainer = (elContainer)?getElementPosition(elContainer):[0, 0];
            var docNode = getElementDocument(el);
            var body = getBodyElement(docNode);
            var rect = docNode.getBoxObjectFor(el);

            if (!rect) { return positionOffset(el, elContainer, noScrollFix); } // Embedded Gecko browsers
				
            pos = [rect.y, rect.x];
            if (typeof getStyle == 'function') {
              parentNode = el;
              while (parentNode && parentNode.nodeType == 1 && stylePos != 'fixed') {
                stylePos = getStyle(parentNode, 'position');
                parentNode = parentNode.parentNode;
              }
              if (stylePos == 'fixed') {
                sp = (typeof getScrollPosition == 'function')?getScrollPosition(docNode):[0, 0];
                pos[0] += sp[0];
                pos[1] += sp[1];
                if (htmlToViewportOrigin) { pos = htmlToViewportOrigin(pos, docNode); }
              }
              else {
                if (!noScrollFix) {
                  sf = scrollFix(el, body);
                  pos[0] += sf[0] + sf[2];
                  pos[1] += sf[1] + sf[3];
                }
              }
            }
            borders = getElementBordersOrigin(el);
            return [pos[0] - borders[0] - posContainer[0], pos[1] - borders[1] - posContainer[1]];
          };
        } 
        if (body && isHostMethod(body, 'getBoundingClientRect')) {
          return function(el, elContainer, noScrollFix) {
            if (elContainer) {
              return positionOffset(el, elContainer, noScrollFix);
            }
            else {
              var x, y;
              var rect = el.getBoundingClientRect();
              var docNode = getElementDocument(el);
              var body = getBodyElement(docNode);
              var ce = getHtmlElement(docNode);
              var sp = [0, 0];

              y = rect.top;				
              x = rect.left;

              if (ce) {
                if (body && ce != body && ce.clientWidth === 0 && typeof body.clientTop == 'number') {
                  ce = body;
                }
                if (ce.clientTop) { y -= ce.clientTop; }
                if (ce.clientLeft) { x -= ce.clientLeft; }
                if (ce.scrollTop || ce.scrollLeft) { sp = [ce.scrollTop, ce.scrollLeft]; }
              }
              if (typeof getScrollPosition == 'function') {
                sp = getScrollPosition(docNode);
              }
              y += sp[0];
              x += sp[1];
              return [y, x];
            }
          };
        }
        return positionOffset;
      })();

      API.getElementPosition = getElementPosition;
      
      if (setScrollPosition) {
        API.setScrollPositionToElement = function(el, offset, options, fnDone) {
          var docNode, p;
          if (isHostMethod(el, 'scrollIntoView') && !offset && !options) {
            el.scrollIntoView();
          }
          else {
            docNode = getElementDocument(el);
            p = getElementPosition(el);
            if (viewportToHtmlOrigin) { p = viewportToHtmlOrigin(p, docNode); }
            offset = offset || [];
            setScrollPosition(p[0] - (offset[0] || 0), p[1] - (offset[1] || 0), docNode, false, options, fnDone);
          }
        };

        API.setScrollPositionCenterElement = function(el, options, fnDone) {
          var vs = getViewportSize(getElementDocument(el));
          var es = getElementSize(el);
          API.setScrollPositionToElement(el, [Math.round((vs[0] - es[0]) / 2), Math.round((vs[1] - es[1]) / 2)], options, fnDone);
        };
      }

      API.setElementScrollPositionToElement = function(el, elTo, offset, options, fnDone) {
        var p;
        offset = offset || [];
        p = (getPositionedParent(elTo) === el)?getElementPositionedChildPosition(elTo, el):getElementPosition(elTo, el);
        setElementScrollPosition(el, p[0] - (offset[0] || 0), p[1] - (offset[1] || 0), false, options, fnDone);
      };

      API.setElementScrollPositionCenterElement = function(el, elTo, options, fnDone) {
        var h, w, d;
        d = getElementSize(el);
        h = d[2];
        w = d[3];
        d = getElementSize(elTo);
        setElementScrollPosition(el, elTo, [Math.round((h - d[0]) / 2), Math.round((w - d[1]) / 2)], false, options, fnDone);
      };
      
      getMousePosition = (function() {
        var sp, ce, body;

        return function(e) {						
          if (typeof e.pageX == 'number') {
            getMousePosition = API.getMousePosition = function(e) { return [e.pageY, e.pageX]; };
            return getMousePosition(e);
          }
          if (typeof e.clientX == 'number' && typeof getScrollPosition == 'function') {
            getMousePosition = API.getMousePosition = function(e, docNode) {
              docNode = docNode || getElementDocument(getEventTarget(e));
              if (docNode) {
                body = getBodyElement(docNode);
                ce = getContainerElement(docNode);
                if (body && ce != body && ce.clientWidth === 0 && typeof body.scrollWidth == 'number') {
                  ce = body; // TODO: call getScrollElement here instead
                }
                sp = getScrollPosition(docNode);
                return [e.clientY + sp[0] - (ce.clientTop || 0), e.clientX + sp[1] - (ce.clientLeft || 0)];
              }
            };
            return getMousePosition(e);
          }
        };
      })();

      API.getMousePosition = getMousePosition;
      
      if (attachListener) {
      attachDrag = (function() {
        var i, c, m;
        var elDrag, elDragHandle, docNode, win, mode, ondrag, ondraginit, ondragterminate, ondragstart, ondragover, ondragout, ondrop, ghost, speed, accelerate, revert, axes, constrain, constraint, keyboardOnly, callbackContext;
        var targets, targetPositions, targetDimensions, targetsOver, targetObstacles, obstaclesFatal, collision;
        var mouse, dim, pos, dimO, posO, scrollPos, oldStyle = {};
        var initialized;
        var oldMouseDown, oldKeyDown, oldKeyUp;
        var oldSpeed;

        function callback(c, co) {
          var targetsOut;
          if (targets) {
            if (mode == 'move') {
              targetsOut = [];
              i = targetsOver.length;

              while (i--) {
                if (!overlaps(co, dimO, targetPositions[targetsOver[i]], targetDimensions[targetsOver[i]])) {
                  targetsOut[targetsOut.length] = i;
                }
              }
              if (targetsOut.length) {
                if (ondragout) { callInContext(ondragout, callbackContext, targetsOut); }
              }
              targetsOver = [];
              i = targets.length;
              while (i--) {
                if (overlaps(co, dimO, targetPositions[i], targetDimensions[i])) {
                  targetsOver[targetsOver.length] = i;
                }
              }
              if (targetsOver.length) {
                if (ondragover) { callInContext(ondragover, callbackContext, c, targetsOver, m); }
                if (targetObstacles) { if (obstaclesFatal) { collision = true; } return false; }
              }
            }				
          }
          if (constrain) {
            switch(mode) {
            case 'move':
              if (!contained(co, dimO, [constraint[0], constraint[1]], [constraint[2], constraint[3]])) {
                constrainPosition(c, co, dimO, constraint);
              }
              break;
            case 'size':
              if (!contained(posO, co, [constraint[0], constraint[1]], [constraint[2], constraint[3]])) {
                constrainSize(c, co, posO, constraint);
              }
            }
          }
          return !ondrag || !callInContext(ondrag, callbackContext, c, co, m);
        }

        function initializeMove() {
          initialized = true;
          if (ghost && typeof setOpacity == 'function') { setOpacity(elDrag, ghost); }

          if (!isPositionable(elDrag)) {
            absoluteElement(elDrag);
            pos = getElementPositionStyle(elDrag);
          }
			
          if (targets) {
            targetsOver = [];
            targetPositions = [];
            targetDimensions = [];
            i = targets.length;
            while (i--) {
              if (isRealObjectProperty(targets[i], 'offsetParent')) { // Must be part of document
                targetPositions[i] = getElementPosition(targets[i]);
                targetDimensions[i] = getElementSize(targets[i]);
              }
            }
          }
        }
	
        function initializeSize() {
          ensureSizable(elDrag);
          initialized = true;
        }

        function size(e) {
          var co;
          if (!initialized) { if (ondragstart) { callInContext(ondragstart, callbackContext); } initializeSize(); }
          m = getMousePosition(e, docNode);
          if (m) {
            c = [(axes == 'horizontal')?null:(dim[0] + (m[0] - mouse[0])), (axes == 'vertical')?null:(dim[1] + (m[1] - mouse[1]))];
            if (constrain) { co = [(axes == 'horizontal')?null:(dimO[0] + (m[0] - mouse[0])), (axes == 'vertical')?null:(dimO[1] + (m[1] - mouse[1]))]; }
            if (callback(c, co)) {
              if (c[0] < 0) { c[0] = 0; }
              if (c[1] < 0) { c[1] = 0; }
              sizeElement(elDrag, c[0], c[1]);
            }
          }
          return cancelDefault(e);
        }

        function scroll(e) {
          if (!initialized) {
            if (ondragstart) { callInContext(ondragstart, callbackContext); }
            initialized = true;
          }
          m = getMousePosition(e, docNode);
          if (m) {
            c = [(axes == 'horizonal')?null:(scrollPos[0] + (m[0] - mouse[0])), (axes == 'vertical')?null:(scrollPos[1] + (m[1] - mouse[1]))];
            if (callback(c)) {
              setElementScrollPosition(elDrag, c[0], c[1]);
            }
          }
          return cancelDefault(e);
        }

        var finish;

        function drag(e) {
          var co;
          if (!initialized) { if (ondragstart) { callInContext(ondragstart, callbackContext); } initializeMove(); }
          m = getMousePosition(e, docNode);
          if (m) {
            c = [(axes == 'horizontal')?null:(pos[0] + (m[0] - mouse[0])), (axes == 'vertical')?null:(pos[1] + (m[1] - mouse[1]))];
            if (targets || constrain) { co = [(axes == 'horizontal')?null:(posO[0] + (m[0] - mouse[0])), (axes == 'vertical')?null:(posO[1] + (m[1] - mouse[1]))]; }
            if (callback(c, co)) {
              positionElement(elDrag, c[0], c[1]);
            }
            else {
              if (collision) { finish(); }
            }
          }
          return cancelDefault(e);
        }

        finish = function() {
          if (ondragterminate) { callInContext(ondragterminate, callbackContext); }
          if (!keyboardOnly) {
            detachListener(docNode, 'mousemove', (mode == 'size')?size:(mode == 'scroll')?scroll:drag);
            detachListener(docNode, 'mouseup', finish);
          }
          docNode.onmousedown = oldMouseDown;
          docNode.onkeydown = oldKeyDown;
          docNode.onkeyup = oldKeyUp;
          if (initialized) {
            if (!ondrop || !callInContext(ondrop, callbackContext, targetsOver)) {
              if (revert) {
                setStyles(elDrag, { position:oldStyle.position, left:oldStyle.left, top:oldStyle.top, width:oldStyle.width, height:oldStyle.height });
                if (mode == 'scroll') { setElementScrollPosition(elDrag, scrollPos[0], scrollPos[1]); }
              }
            }
            if (mode == 'move' && ghost && typeof setOpacity == 'function') { setOpacity(elDrag, 1); }
          }
          docNode = null;
          win = null;
          elDrag = null;
          elDragHandle = null;
          targetsOver = [];
          initialized = false;
        };

        function key(e) {	
          if (win) { e = e || win.event; } // DOM0 used for arrow keys (should be changed)
          if (!e) { return true; }
          var k = getKeyboardKey(e);
          var co = [];

          switch(mode) {
          case 'size':
            if (!initialized) { initializeSize(); }
            c = getElementSizeStyle(elDrag);
            if (constrain) { co = getElementSize(elDrag); }
            break;
          case 'scroll':
            initialized = true;
            c = getElementScrollPosition(elDrag);
            break;
          case 'move':
            if (!initialized) { initializeMove(); }
            c = getElementPositionStyle(elDrag);
            if (targets || constrain) { co = getElementPosition(elDrag); }
          }

          if (e.type == 'keydown') {
            switch (k) {
            case 37:
              c[1] -= speed;
              co[1] -= speed;
              break;
            case 38:
              c[0] -= speed;
              co[0] -= speed;
              break;
            case 39:
              c[1] += speed;
              co[1] += speed;
              break;
            case 40:
              c[0] += speed;
              co[0] += speed;
              break;
            case 13:
              finish();
            }
            if (k != 13) {
              speed += accelerate;
              if (axes == 'vertical') { c[1] = null; }
              if (axes == 'horizontal') { c[0] = null; }
              if (callback(c, co)) {
                switch(mode) {
                case 'size':
                  if (c[0] < 0) { c[0] = 0; }
                  if (c[1] < 0) { c[1] = 0; }
                  sizeElement(elDrag, c[0], c[1]);
                  break;
                case 'scroll':
                  setElementScrollPosition(elDrag, scrollPos[0] + c[0] - mouse[0], scrollPos[1] + c[1] - mouse[1]);
                  break;
                case 'move':
                  positionElement(elDrag, c[0], c[1]);
                }
              }
              else {
                if (collision) { finish(); }
              }
            }
          }
          else {
            speed = oldSpeed;
          }
          return cancelDefault(e);			
        }
        function cancel(e) { // DOM0 used for document mousedown event
          if (win) { e = e || win.event; }
          if (e) { cancelDefault(e); }
          return false;
        }
        function start(e, el, options, elHandle) {
          var d, mb, parent;

          keyboardOnly = options.keyboardOnly;

          mb = getMouseButtons(e);
          if (!keyboardOnly && (mb.right || mb.middle || mb.left) && !getMouseButtons(e)[options.button || 'left']) { return true; }

          //if (!keyboardOnly && !getMouseButtons(e)[options.button || 'left']) { return true; }
          elDrag = el;
          docNode = getElementDocument(el);
          win = (getDocumentWindow)?getDocumentWindow(docNode):null;
          if (!win && docNode == global.document) { win = global; } // TODO: Should generalize this logic in getDocumentWindow
          elDragHandle = elHandle;
          callbackContext = options.callbackContext || elDrag;
          ondraginit = options.ondraginit;
          ondragterminate = options.ondragterminate;
          ondragstart = options.ondragstart;
          ondrag = options.ondrag;
          ondragover = options.ondragover;
          ondragout = options.ondragout;
          ondrop = options.ondrop;
          if (typeof getElementPosition == 'function') {
            targets = options.targets;
            constrain = options.constrain;
          }
          else {
            targets = constrain = false;
          }
          targetObstacles = options.targetObstacles;
          collision = false;
          obstaclesFatal = options.obstaclesFatal;
          revert = options.revert;
          axes = options.axes;			
          ghost = options.ghost;
          if (typeof ghost == 'undefined') { ghost = 0.5; }
          mode = options.mode || 'move';
          speed = oldSpeed = options.speed || 10;
          accelerate = options.accelerate || 1;

          if (constrain) {
            parent = getPositionedParent(el);
            if (parent) {
              d = getElementSize(parent);
              constraint = [0, 0, d[2], d[3]];
            }
            else {
              if (getViewportClientRectangle) { constraint = getViewportClientRectangle(); }
            }
          }
          oldStyle.position = elDrag.style.position;
          oldStyle.left = elDrag.style.left;
          oldStyle.top = elDrag.style.top;
          oldStyle.width = elDrag.style.width;
          oldStyle.height = elDrag.style.height;
          pos = getElementPositionStyle(elDrag);

          if (mode == 'size') { dim = getElementSizeStyle(elDrag); }
          if (targets || constrain) {
            posO = getElementPosition(elDrag);
            dimO = getElementSize(elDrag);
          }
          if (mode == 'scroll') { scrollPos = getElementScrollPosition(elDrag); }
          mouse = getMousePosition(e);
          if (!keyboardOnly) {
            attachListener(docNode, 'mousemove', (mode == 'size')?size:(mode == 'scroll')?scroll:drag);
            attachListener(docNode, 'mouseup', finish);
          }
          oldMouseDown = docNode.onmousedown;
          docNode.onmousedown = cancel;
          oldKeyDown = docNode.onkeydown;
          docNode.onkeydown = key;
          oldKeyUp = docNode.onkeyup;
          docNode.onkeyup = key;
          if (ondraginit) { callInContext(ondraginit, callbackContext, e); }
          return cancelDefault(e);
        }
        return function(el, elHandle, options) {
          options = options || {};
          var mode = options.mode || 'move';
          var listener, elTarget = elHandle || el;
          var uid = elementUniqueId(elTarget);
          if (!attachedDrag[uid] && (mode == 'move' && (isPositionable(el) || typeof absoluteElement == 'function') || (mode == 'size' && typeof getElementSizeStyle == 'function') || mode == 'scroll')) {
            listener = function(e) {
              if (elTarget == elDrag) { return; }
              if (elDrag) { finish(); } start(e, el, options, elHandle);
            };
            attachListener(elTarget, 'mousedown', listener);
            attachedDrag[uid] = listener;
            return true;
          }
          return false;
        };
      })();

      API.attachDrag = attachDrag;

      detachDrag = function(el, elHandle) {
        var elTarget = elHandle || el;
        var uid = elementUniqueId(elTarget);
        if (attachedDrag[uid]) {
          detachListener(elTarget, 'mousedown', attachedDrag[uid]); 
          attachedDrag[uid] = null;
        }
      };

      API.detachDrag = detachDrag;
      }

      if (typeof fireEvent == 'function') {
        initiateDrag = function(el, elHandle, e) {
          fireEvent(elHandle || el, 'mousedown', null, e);
        };

        API.initiateDrag = initiateDrag;
      }
      
      // Import Section

      if (html && createElement && getAttribute && setAttribute && isHostMethod(global.document, 'createTextNode') && isHostMethod(global.document, 'childNodes') && isHostMethod(html, 'appendChild') && typeof html.nodeType == 'number') {
        importNode = function(node, bImportChildren, docNode) {
          var i, name, nodeNew, nodeChild, value;

          docNode = docNode || global.document;
          switch (node.nodeType) {
          case 1:
            nodeNew = createElement(getElementNodeName(node), docNode);
            if (nodeNew) {
              if (node.attributes && node.attributes.length) {
                i = node.attributes.length;
                while (i--) {
                  if (node.attributes[i].specified) {
                    name = node.attributes[i].nodeName;
                    value = getAttribute(node, node.attributes[i].nodeName);
                    if (value !== null) { nodeNew = setAttribute(nodeNew, name, value); }
                  }
                }
              }
              if (bImportChildren && node.childNodes && node.childNodes.length) {
                i = node.childNodes.length;						
                while (i--) {
                  nodeChild = importNode(node.childNodes[i], bImportChildren, docNode);
                  if (nodeChild) {
                    if (nodeChild.nodeType != 1) {
                      if (elementCanHaveChildren(nodeNew) && (reNotEmpty.test(nodeChild.data) || getElementNodeName(nodeNew) == 'pre')) {
                        nodeNew.appendChild(nodeChild);
                      }
                      else {
                        if (getElementNodeName(nodeNew) == 'script' && typeof setElementScript == 'function') {
                          setElementScript(nodeNew, nodeChild.nodeValue);
                        }
                      }
                    }
                    else {
                      if (elementCanHaveChildren(nodeNew)) {
                        nodeNew.appendChild(nodeChild);
                      }
                    }
                  }
                }
              }
              return nodeNew;
            }
            break;
          case 3:
            return docNode.createTextNode(node.nodeValue);
          }
        };

        API.importNode = importNode;

        setElementNodes = function(el, elNewNodes) {
          elNewNodes = importNode(elNewNodes, true, getElementDocument(el));
          if (elNewNodes) {
            //if (typeof purgeListeners == 'function') { purgeListeners(el, true, true); } // Purge children only
            while (el.firstChild) {
              el.removeChild(el.firstChild);
            }
            while (elNewNodes.firstChild) { el.appendChild(elNewNodes.firstChild); }
            return true;
          }
          return false;
        };

        API.setElementNodes = setElementNodes;

        addElementNodes = function(el, elNewNodes) {
          elNewNodes = importNode(elNewNodes, true, getElementDocument(el));
          while (elNewNodes.firstChild) { el.appendChild(elNewNodes.firstChild); }
        };

        API.addElementNodes = addElementNodes;
      }
      
      elementContained = function(el, pos, dim) {
        return contained(getElementPosition(el), getElementSize(el), pos, dim);
      };

      elementContainedInElement = function(el, el2) {
        return contained(getElementPosition(el), getElementSize(el), getElementPosition(el2), getElementSize(el2));
      };

      API.elementContainedInElement = elementContainedInElement;

      elementOverlaps = function(el, pos, dim) {
        return (overlaps(getElementPosition(el), getElementSize(el), pos, dim));
      };

      elementOverlapsElement = function(el, el2) {
        return (overlaps(getElementPosition(el), getElementSize(el), getElementPosition(el2), getElementSize(el2)));
      };

      API.elementOverlapsElement = elementOverlapsElement;
      
      if (canAdjustStyle) {
        
        // Defect test for getElementPositionStyle and getElementSizeStyle (Opera 9 is a known offender)
        if (body && getKomputedStyle && isHostMethod(body, 'appendChild') && typeof createElement == 'function') {
          div = createElement('div');
          if (div) {
            setStyles(div, {height:'0', width:'0', padding:'0', top:'0', left:'0', position:'absolute', visibility:'hidden', border:'solid 1px'});
            body.appendChild(div);
            computedSizeBad = (getKomputedStyle(div, 'height') == '2px');
            div2 = createElement('div');
            if (div2) {
              setStyles(div2, {margin:'0', position:'absolute'});
              div.appendChild(div2);
              computedPositionBad = (getKomputedStyle(div2, 'left') == '1px');
              body.removeChild(div);
              div2 = null;
            }
            div = null;
          }
        }
        
        // Returns dimensions suitable to pass to sizeElement
        // Works for browsers incapable of computing styles or broken in that regard
        
        getElementSizeStyle = function(el) {
          var dim = [];

          dim[0] = getStylePixels(el, 'height');
          dim[1] = getStylePixels(el, 'width');
          if ((dim[0] === null || dim[1] === null || computedSizeBad)) {
            if (typeof el.offsetHeight == 'number') {
              dim[0] = el.offsetHeight;
              dim[1] = el.offsetWidth;
              el.style.height = dim[0] + 'px';
              el.style.width = dim[1] + 'px';
              adjustElementSize(el, dim);
            }
            else {
              return null;
            }
          }
          return dim;
        };

        API.getElementSizeStyle = getElementSizeStyle;

        ensureSizable = function(el, display) {
          var d;
          if (typeof el.height != 'number') {
            d = getStyle(el, 'display');
            if (!d || d == 'inline') {
              if (typeof presentElement == 'function') {
                presentElement(el, true);
                if (el.style.display == 'inline') {
                  el.style.display = display || 'block';
                }
                return true;
              }
              return false;
            }
          }
          return true;
        };
        
        // Returns coordinates suitable to pass to positionElement
        // Works for browsers incapable of computing styles or broken in that regard

        getElementPositionStyle = (function() {
          var fix;

          if (body && typeof body.offsetLeft == 'number') {
            fix = function(el, y, x) {
              var oldX = el.offsetLeft;
              var oldY = el.offsetTop;
              positionElement(el, y, x);
              if (oldX != el.offsetLeft) { x -= (el.offsetLeft - oldX); }
              if (oldY != el.offsetTop) { y -= (el.offsetTop - oldY); }
              if (oldX != x || oldY != y) { positionElement(el, y, x); }
              return [y, x];
            };
          }

          return function(el, posParent) {
            var pos = getStyle(el, 'position');
            var x = getStylePixels(el, 'left');
            var y = getStylePixels(el, 'top');

            if (x !== null && y !== null && (!computedPositionBad || isPositionable(el))) {
              return (computedPositionBad && fix && (x + 'px' != el.style.left || y + 'px' != el.style.top))?fix(el, y, x):[y, x];
            }

            // Calculate offset from root element or positioned parent

            if (isRealObjectProperty(el, 'offsetParent') && typeof getElementPosition == 'function') {
              posParent = (typeof posParent == 'undefined')?getPositionedParent(el):posParent;
              if (!posParent && pos == 'relative') {
                x = y = 0;
              } else {
                var p = (posParent)?getElementPosition(el, posParent, true, posParent):getElementPosition(el, null, true, posParent);
                y = p[0];
                x = p[1];
              }
            } else {
              if (isPositionable(el)) {
                x = y = 0;
              }
              else {
                return null;
              }
            }
            return (fix)?fix(el, y, x):[y, x];
          };
        })();

        API.getElementPositionStyle = getElementPositionStyle;
        
        defaultDisplay = (function() {
          var reBlockDisplay, blockDisplay = 'ADDRESS|BLOCKQUOTE|BODY|DD|DIV|DL|DT|FIELDSET|FORM|IFRAME|I?FRAME|FRAMESET|H\\d|OL|P|UL|CENTER|DIR|HR|MENU|PRE', otherDisplayTypes = {};
          var elTable, el;

          if (body && getKomputedStyle && isHostMethod(body, 'appendChild') && typeof createElement == 'function') {
            el = createElement('div');
            if (el) {
              setStyles(el, {position:'absolute', left:'0', top:'0'});
              el.style.position = 'absolute';
              body.appendChild(el);
              elTable = createElement('table');
              if (elTable) {
                el.appendChild(elTable);
                if (getKomputedStyle(elTable, 'display') == 'block') {
                  blockDisplay += '|TABLE|TBODY|TH|TFOOT|TR|TD|COL.*|CAPTION|LI';
                }
                else {
                  otherDisplayTypes = {li:'list-item', table:'table', tbody:'table-row-group', tr:'table-row', td:'table-cell', th:'table-header-group', tfoot:'table-footer-group', caption:'table-caption', colgroup:'table-column-group', col:'table-column'};
                }
                body.removeChild(el);
                elTable = null;
                el = null;
              }
            }
          }
          reBlockDisplay = new RegExp('^(' + blockDisplay + ')$', 'i');
          return function(nn) {
            if (reBlockDisplay.test(nn)) { return 'block'; }
            return otherDisplayTypes[nn] || 'inline';
          };
        })();

        if (canAdjustStyle.display) {
          presentElement = function(el, b, display) {
            var d;

            if (typeof b == 'undefined') { b = true; }
            if (b) {
              if (display) {
                el.style.display = display;
              }
              else {
                el.style.display = '';
                if (getKomputedStyle) {
                  d = getKomputedStyle(el, display);
                  if (d && d != 'none') { return; }
                }
                el.style.display = defaultDisplay(getElementNodeName(el));
              }
            }
            else {
              el.style.display = 'none';
            }
          };
        }

        API.presentElement = presentElement;

        toggleElementPresence = function(el, display) {
          presentElement(el, !isPresent(el), display);
        };

        API.toggleElementPresence = toggleElementPresence;

        
        if (canAdjustStyle.visibility) {
          showElement = function(el, b, options) {
            if (typeof b == 'undefined') { b = true; }
            options = options || {};
            if (b && typeof presentElement != 'undefined') {
              if (options.skipPresenceCheck || getStyle(el, 'display') == 'none') {
                presentElement(el, options.display);
              }
            }
            el.style.visibility = (b)?'visible':'hidden';
            if (!b && typeof presentElement !='undefined' && options.removeOnHide) { el.style.display = 'none'; }
          };

          API.showElement = showElement;

          toggleElement = function(el, options) {
            showElement(el, !isVisible(el) || !isPresent(el), options);
          };

          API.toggleElement = toggleElement;
        }
        
        if (canAdjustStyle.position) {
          relativeElement = function(el, posParent) {
            var pos;
            posParent = (typeof posParent == 'undefined')?getPositionedParent(el):posParent;
            var p = getStyle(el, 'position');
            if (p != 'relative') {
              if (posParent) {
                pos = getElementPosition(el, posParent);
                positionElement(el, pos[0], pos[1]);
              }
              else {
                positionElement(el, 0, 0);
              }
              el.style.position = 'relative';
            }
          };

          API.relativeElement = relativeElement;

          absoluteElement = function(el, posParent) {
            posParent = (typeof posParent == 'undefined')?getPositionedParent(el):posParent;
            var pos = getElementPosition(el, posParent);
            var dim = getElementSizeStyle(el);
            var margins = (typeof getElementMarginsOrigin == 'function')?getElementMarginsOrigin(el):[0, 0];

            if (dim) { sizeElement(el, dim[0], dim[1]); }
            if (pos) { positionElement(el, pos[0] - margins[0], pos[1] - margins[1]); }
            el.style.position = 'absolute';
          };

          API.absoluteElement = absoluteElement;
        }
	
        ensurePositionable = function(el, posParent, position) {
          var p = getStyle(el, 'position');
          if (!p || p == 'static') {
            if (canAdjustStyle.position) {
              ((!position || position == 'absolute')?absoluteElement:relativeElement)(el, posParent);
              return true;
            }
            return false;
          }
          return true;
        };

        getElementPositionedChildPosition = function(el, elParent) {
          return (getStyle(el, 'position') == 'relative')?getElementPosition(el, elParent, true):getElementPositionStyle(el, elParent);
        };
        
      }
      
      // Special Effects

      if (!canAdjustStyle) { return; }

      var EffectTimer, recordInlineStyles, restoreInlineStyles;
      var clip, drop;
      var oldPositionElement, oldCenterElement, oldMaximizeElement, oldRestoreElement, oldChangeImage, oldSetElementHtml, oldSetElementNodes, oldShowElement, oldSizeElement;
      var ensurePresent = function(el, display) {
        if (!isPresent(el)) {
          if (typeof presentElement == 'function') {
            presentElement(el, true, display);
            return isPresent(el);
          }
          return false;
        }
        return true;
      };

      EffectTimer = function(options) {
        var el, fn, bIn, dir, rpt, revert, duration, ease, cb, cbBounce, cbBeforeDone, pt, ptTemp, fps, interval, started, f, t, p, e, a;
        var myOptions = options || {};

        function processFunctions(prog, endCode) {
          fn(el, prog, pt, endCode);
        }

        function clearInterval() {
          global.clearInterval(interval);
          interval = 0;
        }

        function finish(interrupt) {			
          clearInterval();
          if ((!cbBeforeDone || !cbBeforeDone(el, interrupt)) && (interrupt || revert)) { processFunctions((bIn)?t:f, (revert)?3:2); }
          if (cb) { cb(el, interrupt); }
        }

        function repeat() {
          if (--rpt) {
            if (dir == 'inandout' || dir == 'outandin') { bIn = !bIn; }
            started = new Date();
          }
          else {
            finish();
          }
        }

        function bounce() {
          if ((dir == 'inandout' && bIn) || (dir == 'outandin' && !bIn)) {
            processFunctions(e * (t - f));
            bIn = !bIn;
            if (cbBounce) { cbBounce(el, rpt); }
            started = new Date();
          }
          else {
            finish();
          }			
        }

        function process() {
          p = ((new Date() - started) / duration);
          if (p > 1) { p = 1; }
          e = p;
          if (!bIn) { e = 1 - e; }
          if (ease && p < 1) { e = ease(e); }
          processFunctions(e * (t - f));
          if (p == 1) {
            if (dir == 'in' || dir == 'out' || (dir == 'inandout' && !bIn) || (dir == 'outandin' && bIn)) {
              repeat();
            }
            else {
              bounce();
            }
          }
        }

        function interrupt() {
          finish(true);
        }

        function getOption(options, name) {
          return (typeof options[name] == 'undefined')?myOptions[name]:options[name];
        }

        function fnCombineEffect(fn1, fn2) {
          return function(el, prog, pt, endCode) { fn1(el, prog, pt, endCode); fn2(el, prog, pt, endCode); };
        }

        this.start = function(element, options, fnDone, fnBounce, fnBeforeDone) {
          var index;
          if (interval) { interrupt(); }
          options = options || {};
          el = element;
          f = getOption(options, 'from') || 0;
          t = getOption(options, 'to') || 1;
          dir = (options.dir || 'in').toLowerCase();
          bIn = dir != 'out' && dir != 'outandin';
          duration = getOption(options, 'duration');
          ease = getOption(options, 'ease');
          cb = fnDone;
          cbBounce = fnBounce;
          cbBeforeDone = fnBeforeDone;
          pt = {};
          ptTemp = getOption(options, 'effectParams');
          if (ptTemp) { // Copy effect workspace
            for (index in ptTemp) {
              if (isOwnProperty(ptTemp, index)) {
                pt[index] = ptTemp[index];
              }
            }
          }
          revert = getOption(options, 'revert');
          a = getOption(options, 'effects');
          if (typeof a != 'function') {
            index = a.length;
            while (index--) { fn = (fn)?fnCombineEffect(fn, a[index]):a[index]; }
          }
          else {
            fn = a;
          }
          fps = getOption(options, 'fps') || 30;
          started = new Date();
          rpt = getOption(options, 'repeat') || 1;
          interval = global.setInterval(process, 1000 / fps);
          processFunctions((bIn)?f:t, 1);
        };

        this.stop = function(complete) { if (complete) { bIn = (dir == 'in' || dir == 'outandin'); interrupt(); } else { finish(); } };
        this.busy = function() { return !!interval; };
      };

      API.EffectTimer = EffectTimer;

      recordInlineStyles = function(el, styles, scratch) {
        var i = styles.length;
        while (i--) { scratch[styles[i]] = el.style[styles[i]]; }
      };
	
      restoreInlineStyles = function(el, styles, scratch) {
        var i = styles.length;	
        while (i--) { el.style[styles[i]] = scratch[styles[i]]; }
      };

      if (typeof showElement == 'function') {
        oldShowElement = showElement;

	showElement = API.showElement = (function() {
          var activeEffects = {};
          var cb = {};

          function finish(el, b, options) {
            if (!b && (options && options.removeOnHide) && typeof presentElement == 'function') { presentElement(el, false); }
          }

          return function(el, b, options, fnDone) {
            var effect, fnDoneInternal, fnBeforeDone, uid;

            options = options || {};

            uid = elementUniqueId(el);
            if (activeEffects[uid]) {
              activeEffects[uid].stop(true);
              presentElement(el, options.display);
            }

            if (options.effects) {
              effect = new EffectTimer();
              options.dir = (b)?'in':'out';
              options.revert = true;
              cb[uid] = fnDone;
              if (ensurePresent(el, options.display)) {
                fnDoneInternal = function(el, interrupt) {
                  finish(el, b, options);
                  effect = null;
                  activeEffects[uid] = null;
                  if (cb[uid]) { cb[uid](el, b); }
                };
                fnBeforeDone = function(el, interrupt) {
                  if (!b && !interrupt) { oldShowElement(el, false); }
                };
                activeEffects[uid] = effect;
                effect.start(el, options, fnDoneInternal, null, fnBeforeDone);
              }
            }
            else {
              oldShowElement(el, b, options);
              if (fnDone) { fnDone(); }
            }
            return el;
          };
	})();

        toggleElement = API.toggleElement = function(el, options, fnDone) {
          return showElement(el, !isVisible(el) || !isPresent(el), options, fnDone);
        };

        showElement.async = true;
        toggleElement.async = true;
      }

      if (typeof sizeElement == 'function') {
        oldSizeElement = sizeElement;
        sizeElement = (function() {
          var activeEffects = {};
          var cb = {};
          return function(el, h, w, options, fnDone) {
            var effect, fnDoneInternal, pt, uid;
            if (options && options.duration) {
              uid = elementUniqueId(el);
              if (activeEffects[uid]) { activeEffects[uid].stop(true);}
              effect = new EffectTimer();
              options.effects = effects.grow;
              pt = options.effectParams || {};
              pt.targetSize = [h, w];
              options.effectParams = pt;
              cb[elementUniqueId(el)] = fnDone;
              fnDoneInternal = function() {
                effect = null;
                activeEffects[uid] = null;
                if (cb[uid]) { cb[uid](el); }
              };
              activeEffects[uid] = effect;
              effect.start(el, options, fnDoneInternal);
            }
            else {
              oldSizeElement(el, h, w);
              if (fnDone) { fnDone(el); }
            }			
          };
        })();
        sizeElement.async = true;
        API.sizeElement = sizeElement;
      }

      if (typeof positionElement == 'function') {      
        oldPositionElement = positionElement;

        positionElement = (function() {
          var activeEffects = {};
          var cb = {};
          return function(el, t, l, options, fnDone) {
            var effect, fnDoneInternal, pt, uid;
            if (options && options.duration) {
              uid = elementUniqueId(el);
              if (activeEffects[uid]) { activeEffects[uid].stop(true);}
              effect = new EffectTimer();
              options.effects = effects.move;
              pt = options.effectParams || {};
              pt.targetPosition = [t, l];
              options.effectParams = pt;
              cb[uid] = fnDone;
              fnDoneInternal = function(el, interrupt) {
                effect = null;
                activeEffects[uid] = null;
                if (cb[uid]) { cb[uid](el, interrupt); }
              };
              activeEffects[uid] = effect;
              effect.start(el, options, fnDoneInternal);
            }
            else {
              oldPositionElement(el, t, l);
              if (fnDone) { fnDone(el); }
            }
          };				
        })();

        positionElement.async = true;
        API.positionElement = positionElement;
      }

      effects = {};

      if (typeof setOpacity == 'function') {
        effects.fade = function(el, p, scratch, endCode) {
          if (endCode) {
            if (endCode > 2) {
                p = (typeof scratch.targetOpacity != 'undefined')?0:1;
            }
            if (endCode == 2) { p = 1; }
            if (endCode == 1) {
              scratch.opacity = getOpacity(el) || 1;
              if (scratch.opacity >= 0.9999) { scratch.opacity = 1; }
              if (canAdjustStyle.visibility) { el.style.visibility = 'visible'; }
            }
          }
          p = (typeof scratch.targetOpacity != 'undefined')?scratch.opacity + (scratch.targetOpacity - scratch.opacity) * p:scratch.opacity * p;
          setOpacity(el, (p >= 1)?0.9999:p); // Some versions of Firefox blink on 1
        };
      }

      drop = function(side, pos, dim, p) {
        var y, x, h, w;
        y = pos[0];
        x = pos[1];
        h = dim[0];
        w = dim[1];
        switch(side) {					
        case 'left':
          x = ((x + w) - (p * w));
          break;
        case 'top':
          y = ((y + h) - (p * h));
          break;
        case 'diagonalsw':
          y = ((y + h) - (p * h)); 
          x = ((x - w) + (p * w)); 
          break;
        case 'diagonalnw':
          y = ((y - h) + (p * h)); 
          x = ((x - w) + (p * w)); 
          break;
        case 'diagonalse':
          y = ((y + h) - (p * h)); 
          x = ((x + w) - (p * w)); 
          break;
        case 'diagonalne':
          y = ((y - h) + (p * h)); 
          x = ((x + w) - (p * w));
          break;
        case 'bottom':
          y = ((y - h) + (p * h));
          break;
        default:
          x = ((x - w) + (p * w));
        }
	return [y, x];
      };

      clip = function(side, dim, p) {
        var h = dim[0];
        var w = dim[1];
        switch(side) {
        case 'top':
          return "rect(0px " + w + "px " + Math.round(p * h) + "px 0px)";
        case 'left':
          return "rect(0px " + Math.round(p * w) + "px " + h + "px 0px)";
        case 'zoom':
          return "rect(" + Math.round(((1 - p) / 2) * h) + "px " + Math.round((p / 2) * w + w / 2) + "px " + Math.round((p / 2) * h + h / 2) +  "px " + Math.round(((1 - p) / 2) * w) + "px)";
        case 'horizontal':
          return "rect(" + (Math.round(((1 - p) / 2) * h)) + "px " + w + "px " + Math.round((p / 2) * h + h / 2) + "px 0px)";
        case 'vertical':
          return "rect(0px " + Math.round((p / 2) * w + w/2) + "px " + h +  "px " + (Math.round(((1 - p) / 2) * w)) + "px)";
        case 'diagonalnw':
          return "rect(" + Math.round((1 - p) * h) + "px " + w + "px " + h + "px " + Math.round((1 - p) * w) + "px)";
        case 'diagonalne':
          return "rect(" + Math.round((1 - p) * h) + "px " + Math.round(p * w) + "px " + h + "px 0px)";
        case 'diagonalsw':
          return "rect(0px " + w + "px " + Math.round(p * h) + "px " + Math.round((1 - p) * w) + "px)";
        case 'diagonalse':
          return "rect(0px " + Math.round(p * w) + "px " + Math.round(p * h) + "px 0px)";
        case 'bottom':
          return "rect(" + Math.round((1 - p) * h) + "px " + w + "px " + h + "px 0px)";
        default:
          return "rect(0px " + w + "px " + h + "px " + Math.round((1 - p) * w) + "px)";
        }
      };

      if (typeof oldPositionElement == 'function') {
        effects.drop = (function() {
          var posNew;
          return function(el, p, scratch, endCode) {
            if (endCode) {
              if (endCode > 2) {
                restoreInlineStyles(el, ['top', 'left', 'position'], scratch);
                return;
              }
              if (endCode == 1) {
                recordInlineStyles(el, ['top', 'left', 'position'], scratch);
                if (typeof ensurePositionable == 'undefined' || ensurePositionable(el)) {
                  if (!scratch.dimOuter) { scratch.dimOuter = getElementSize(el); }
                  if (!scratch.pos) { scratch.pos = getElementPositionStyle(el); }
                }
                else {
                  scratch.pos = null;
                }
                if (canAdjustStyle.visibility) { el.style.visibility = 'visible'; }
              }
            }
            if (scratch.pos) {
              posNew = drop(scratch.side, scratch.pos, scratch.dimOuter, p);
              oldPositionElement(el, posNew[0], posNew[1]);
            }
          };
        })();
      }

      if (typeof oldSizeElement == 'function') {
        effects.grow = (function() {
          var axes, h, w;
          var reUnits = new RegExp('(.+)(em|px|pt|%)');

          function getFont(el) {
            var match;
            var font = getInlineStyle(el, 'fontSize') || getStyle(el, 'fontSize');

            if (font) {
              match = font.match(reUnits);
            }

            return match && { size:parseFloat(match[1]), unit:match[2] };
          }

          return function(el, p, scratch, endCode) {
            if (endCode) {
              if (endCode > 2) {
                restoreInlineStyles(el, ['overflow', 'fontSize', 'height', 'width'], scratch);
                return;
              }
              if (endCode == 1) {
                recordInlineStyles(el, ['overflow', 'fontSize', 'height', 'width'], scratch);
                el.style.overflow = 'hidden';
                if (ensureSizable(el)) {
                  if (!scratch.dim) { scratch.dim = getElementSizeStyle(el); }
                }
                else {
                  scratch.dim = null;
                }
                if (getKomputedStyle) { scratch.font = getFont(el); }
                if (canAdjustStyle.visibility) { el.style.visibility = 'visible'; }
              }
            }
			
            if (scratch.dim) {
              if (scratch.targetSize) {
                h = (scratch.dim[0] + (scratch.targetSize[0] - scratch.dim[0]) * p);
                w = (scratch.dim[1] + (scratch.targetSize[1] - scratch.dim[1]) * p);
              }
              else {
                axes = scratch.axes;
                h = (!axes || axes == 1)?scratch.dim[0] * p:null;
                w = (!axes || axes == 2)?scratch.dim[1] * p:null;
                if (!axes && scratch.font && p >= 0) { el.style.fontSize = (scratch.font.size * p) + scratch.font.unit; }
              }
              oldSizeElement(el, (h < 0)?null:h, (w < 0)?null:w);
            }			
          };
	})();

        effects.fold = function(el, p, scratch, endCode) {
          if (endCode == 1) { scratch.axes = scratch.axes || 2; }
          effects.grow(el, p, scratch, endCode);
        };
      }

      // TODO: Detect clip style

      if (API.unclipElement) { // Add-on as function has conditional compilation, which fouls up minification
        effects.clip = (function() {
          return function(el, p, scratch, endCode) {
            if (endCode) {
              if (endCode > 2) {
                restoreInlineStyles(el, ['overflow', 'position'], scratch);
                API.unclipElement(el);
                return;
              }
              if (endCode == 1) {
                recordInlineStyles(el, ['overflow', 'position'], scratch);
                el.style.overflow = 'hidden';
                if (typeof ensurePositionable == 'function') { ensurePositionable(el); }
                if (!scratch.dimOuter) { scratch.dimOuter = getElementSize(el); }
                if (canAdjustStyle.visibility) { el.style.visibility = 'visible'; }
              }
            }
            el.style.clip = clip(scratch.side, scratch.dimOuter, p);
          };
        })();

        effects.zoom = function(el, p, scratch, endCode) {
          if (endCode == 1) { scratch.side = 'zoom'; }
          effects.clip(el, p, scratch, endCode);
        };

        effects.horizontalBlinds = function(el, p, scratch, endCode) {
          if (endCode == 1) { scratch.side = 'horizontal'; }
          effects.clip(el, p, scratch, endCode);
        };

        effects.verticalBlinds = function(el, p, scratch, endCode) {
          if (endCode == 1) { scratch.side = 'vertical'; }
          effects.clip(el, p, scratch, endCode);
	};

        if (typeof oldPositionElement == 'function') {
          effects.slide = (function() {
            var posNew;
            return function(el, p, scratch, endCode) {
              if (endCode) {
                if (endCode > 2) {
                  restoreInlineStyles(el, ['overflow', 'top', 'left', 'position'], scratch);
                  API.unclipElement(el);
                  return;
                }
                if (endCode == 1) {
                  recordInlineStyles(el, ['overflow', 'top', 'left', 'position'], scratch);
                  el.style.overflow = 'hidden';
                  if (typeof ensurePositionable == 'undefined' || ensurePositionable(el)) {
                    if (!scratch.dimOuter) { scratch.dimOuter = getElementSize(el); }
                    if (!scratch.pos) { scratch.pos = getElementPositionStyle(el); }
                  }
                  else {
                    scratch.pos = null;
                  }
                  if (canAdjustStyle.visibility) { el.style.visibility = 'visible'; }
                }
              }
              if (scratch.pos) {
                posNew = drop(scratch.side, scratch.pos, scratch.dimOuter, p);
                oldPositionElement(el, posNew[0], posNew[1]);
                el.style.clip = clip(scratch.side, scratch.dimOuter, p);
              }			
            };
          })();
        }
      }
      

      if (typeof oldPositionElement == 'function') {
        effects.move = function(el, p, scratch, endCode) {
          if (endCode) {
            if (endCode > 2) {
              if (scratch.pos) { oldPositionElement(el, scratch.pos[0], scratch.pos[1]); }
              return;
            }
            if (endCode == 1) {
              if (typeof ensurePositionable == 'undefined' || ensurePositionable(el)) {
                if (!scratch.pos) { scratch.pos = getElementPositionStyle(el); }
                if (scratch.pos) {
                  if (scratch.targetPosition[0] === null) { scratch.targetPosition[0] = scratch.pos[0]; }
                  if (scratch.targetPosition[1] === null) { scratch.targetPosition[1] = scratch.pos[1]; }
                }
              }
              else {
                scratch.pos = null;
              }
            }
          }
          if (scratch.pos) { oldPositionElement(el, (scratch.pos[0] + ((scratch.targetPosition[0] - scratch.pos[0]) * p)), (scratch.pos[1] + ((scratch.targetPosition[1] - scratch.pos[1]) * p))); }
	};

        if (typeof maximizeElement == 'function') {
          oldMaximizeElement = maximizeElement;
          oldRestoreElement = restoreElement;
          maximizeElement = API.maximizeElement = function(el, options, fnDone) {
            var l, t, h, r, w, oldT, oldL, oldH, oldW, oldV, oldP;
            if (options) {
              oldT = el.style.top;
              oldL = el.style.left;
              oldH = el.style.height;
              oldW = el.style.width;
              oldV = el.style.visibility;
              oldP = el.style.position;
              el.style.visibility = 'hidden';
              r = oldMaximizeElement(el);
              if (oldP == el.style.position) {
                t = getStylePixels(el, 'top');
                l = getStylePixels(el, 'left');
                h = getStylePixels(el, 'height');
                w = getStylePixels(el, 'width');
                el.style.top = oldT;
                el.style.left = oldL;
                el.style.height = oldH;
                el.style.width = oldW;
                el.style.visibility = oldV;
                positionElement(el, t, l, options, fnDone);
                sizeElement(el, h, w, options, fnDone);
              }
              else {
                el.style.visibility = oldV;
                if (fnDone) { fnDone(el); }
              }
            }
            else {
              r = oldMaximizeElement(el);
              if (fnDone) { fnDone(el); }
            }
            return r;
          };
          maximizeElement.async = true;
          restoreElement = API.restoreElement = function(el, options, fnDone) {
            var a, l, t, h, r, w, oldT, oldL, oldH, oldW, oldV, oldP;
            if (options) {
              oldT = el.style.top;
              oldL = el.style.left;
              oldH = el.style.height;
              oldW = el.style.width;
              oldV = el.style.visibility;
              oldP = el.style.position;
              el.style.visibility = 'hidden';
              r = oldRestoreElement(el);
              if (oldP == el.style.position) {
                t = getStylePixels(el, 'top');
                l = getStylePixels(el, 'left');
		a = getElementSizeStyle(el);
		h = a[0];
		w = a[1];
                el.style.top = oldT;
                el.style.left = oldL;
                el.style.height = oldH;
                el.style.width = oldW;
                el.style.visibility = oldV;
                positionElement(el, t, l, options, fnDone);
                sizeElement(el, h, w, options, fnDone);
              }
              else {
                el.style.visibility = oldV;
                if (fnDone) { fnDone(el); }
              }
            }
            else {
              r = oldRestoreElement(el);
              if (fnDone) { fnDone(el); }
            }
            return r;
          };
          restoreElement.async = true;
        }

        if (typeof centerElement == 'function') {
          oldCenterElement = centerElement;
          centerElement = API.centerElement = function(el, options, fnDone) {
            var l, t, oldL, oldT, oldV, oldP;
            if (options) {
              oldT = el.style.top;
              oldL = el.style.left;
              oldV = el.style.visibility;
              oldP = el.style.position;
              el.style.visibility = 'hidden';
              oldCenterElement(el);
              t = getStylePixels(el, 'top');
              l = getStylePixels(el, 'left');
              el.style.top = oldT;
              el.style.left = oldL;
              el.style.visibility = oldV;
              if (oldP == el.style.position) {
                positionElement(el, t, l, options, fnDone);
                return;
              }
            }
            oldCenterElement(el);
            if (fnDone) { fnDone(el); }
          };
          centerElement.async = true;

          if (effects.grow && typeof overlayElement == 'function' && typeof showElement == 'function') {
            spring = function(el, elFrom, b, options, callback) {
              var pos, posOld, optionsNew = { duration:options.duration, ease:options.ease, removeOnHide:options.removeOnHide };
              if (options.effects && typeof options.effects == 'function') {
                optionsNew.effects = [options.effects];
              }
              else {
                optionsNew.effects = (options.effects || []).concat([effects.grow]);
              }
              if (b) {
		pos = getElementPositionStyle(el);
                overlayElement(el, elFrom);
		if (options.springMode == 'center') {
			centerElement(el, { duration:options.duration, ease:options.ease });
		}
		else {
			positionElement(el, pos[0], pos[1], { duration:options.duration, ease:options.ease });
		}
              }
              else {
                posOld = getElementPositionStyle(el);
                el.style.visibility = 'hidden';
                overlayElement(el, elFrom);
                pos = getElementPositionStyle(el);
                positionElement(el, posOld[0], posOld[1]);
                el.style.visibility = 'visible';
                positionElement(el, pos[0], pos[1], { duration:options.duration, ease:options.ease, revert:true });
              }
              showElement(el, b, optionsNew, callback);
            };

            API.spring = spring;
          }
        }

        effects.shake = function(el, p, scratch, endCode) {
          if (endCode) {
            if (endCode > 2) {
              restoreInlineStyles(el, ['top', 'left', 'position'], scratch);
              return;
            }
            if (endCode == 1) {
              recordInlineStyles(el, ['top', 'left', 'position'], scratch);
              if (typeof ensurePositionable == 'undefined' || ensurePositionable(el)) {
                if (!scratch.pos) { scratch.pos = getElementPositionStyle(el); }
              }
              else {
                scratch.pos = null;
              }
              scratch.severity = scratch.severity || 5;
              if (canAdjustStyle.visibility) { el.style.visibility = 'visible'; }
            }
          }
          if (scratch.pos) { oldPositionElement(el, scratch.pos[0] + (!scratch.axes || scratch.axes == 1)?(scratch.severity - Math.floor(Math.random() * (scratch.severity * 2 + 1))):0, scratch.pos[1] + (!scratch.axes || scratch.axes == 2)?(scratch.severity - Math.floor(Math.random() * (scratch.severity * 2 + 1))):0); }
	};
      }

      API.effects = effects;
      
      API.ease = {};

      API.ease.sine = function(p) {
        return (Math.sin(p * Math.PI / 2));
      };

      API.ease.cosine = function(p) {
        return ((-Math.cos(p * Math.PI) / 2) + 0.5);
      };

      API.ease.tan = function(p) {
        var tan = Math.tan;
        return (tan(1*(2*p-1))/tan(1)+1)/2;
      };

      API.ease.flicker = function(p) {
        return ((-Math.cos(p * Math.PI) / 4) + 0.75) + Math.random() * 0.25;
      };

      API.ease.wobble = function(p) {
        return (-Math.cos(p * Math.PI * (9 * p)) / 2) + 0.5;
      };

      API.ease.square = function(p) {
        return(Math.pow(p, 2));
      };

      API.ease.circle = function(p) {
        return Math.sqrt(1 - Math.pow((p - 1), 2));
      };

      API.ease.pulsate = function(p) {
        return (0.5 + Math.sin(17 * p) / 2);
      };

      API.ease.expo = function(p) {
        return Math.pow(2, 8 * (p - 1));
      };

      API.ease.quad = function(p) {
        return Math.pow(p, 2);
      };

      API.ease.cube = function(p) {
        return Math.pow(p, 3);
      };

      // Sigmoid functions based upon work by Emmanuel Pietriga.
      // http://www.docjar.net/html/api/com/xerox/VTM/engine/AnimManager.java.html
      // Copyright (c) Xerox Corporation, XRCE/Contextual Computing, 2002.

      API.ease.sigmoid = function(p, steepness) { // Currently no way to pass second parameter to easing function
        var atan = Math.atan;
        steepness = steepness || 1;
        return (atan(steepness*(2*p-1))/atan(steepness)+1)/2;
      };

      API.ease.sigmoid2 = function(p) {
        var atan = Math.atan;
        return (atan(2*(2*p-1))/atan(2)+1)/2;
      };

      API.ease.sigmoid3 = function(p) {
        var atan = Math.atan;
        return (atan(3*(2*p-1))/atan(3)+1)/2;
      };

      API.ease.sigmoid4 = function(p) {
        var atan = Math.atan;
        return (atan(4*(2*p-1))/atan(4)+1)/2;
      };
	
      API.ease.loop = function(p) {
        return (-Math.cos(2*p*Math.PI)/2) + 0.5;
      };

      API.ease.bounce = function(p) { 
        return 1 - (Math.cos(p * 4.5 * Math.PI) * Math.exp(-p * 6)); 
      };

      // Based on Easing Equations v2.0 
      // (c) 2003 Robert Penner, all rights reserved. 
      // This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html

      API.ease.swingTo = function(p) { 
        var s = 1.70158; 
        return (p-=1)*p*((s+1)*p + s) + 1;
      };

      API.ease.swingToFrom = function(p) {
        var s = 1.70158; 
        if ((p/=0.5) < 1) { return 0.5*(p*p*(((s*=(1.525))+1)*p - s)); }
        return 0.5*((p-=2)*p*(((s*=(1.525))+1)*p + s) + 2); 
      };

      
      body = containerElement = null;
    });
    
  }
  
  // Script section

  if (attachDocumentReadyListener) {
    attachDocumentReadyListener(function() {
      var add, body, div, el, findAndAddScripts, head, iMethod, methods, oldAddElementHtml, oldSetElementHtml, s, script, addScriptHtmlFailed, setScriptHtmlFailed;
      if (getHeadElement) { head = getHeadElement(); }
      if (head && isHostMethod(head, 'appendChild') && createElement) {
        methods = [];
        s = createElement('script');
        if (s) {
          add = function(method, t, docNode) {
            method(s, t, docNode);
          };

          if (isHostMethod(global.document, 'createTextNode') && elementCanHaveChildren(s)) {
            methods[methods.length] = function(s, t, docNode) {
              s.appendChild((docNode || global.document).createTextNode(t));
            };
          }

          if (typeof s.text == 'string') {
            methods[methods.length] = function(s, t) {
              s.text = t;
            };
          }

          iMethod = methods.length;
          while (!API._testscriptinsertion && iMethod--) {
            head.appendChild(s);
            add(methods[iMethod], 'this.API._testscriptinsertion = true;');
            head.removeChild(s);
          }
          if (API._testscriptinsertion) {
            setElementScript = API.setElementScript = function(el, t) {
              s = el;
              while (s.firstChild) { s.removeChild(s.firstChild); }
              add(methods[iMethod], t, getElementDocument(el));
              s = null;
            };
            addElementScript = API.addElementScript = function(el, t) {
              s = el;
              add(methods[iMethod], t, getElementDocument(el));
              s = null;
            };
            addScript = API.addScript = function(t, docNode) {
              head = getHeadElement(docNode);
              s = createElement('script');
              if (s && head) {
                head.appendChild(s);
                add(methods[iMethod], t, docNode);
                head.removeChild(s);
                s = null;
                head = null;
              }
            };
            delete API._testscriptinsertion;
          }
          s = null;
        }

        if (addScript && getEBTN && !isXmlParseMode()) {
          findAndAddScripts = function(el) {
            var i, scripts = getEBTN('script', el);
            i = scripts.length;
            while (i--) {
              if (scripts[i].text) { addScript(scripts[i].text, getElementDocument(el)); }
            }
          };

          if (typeof addElementHtml == 'function') {
            try {
              addElementHtml(head, '<script id="testaddhtmlscript" type="text/javascript">this.API._testaddhtmlscript = true;</script>');
            } catch(e) {
              addScriptHtmlFailed = true;
            }
            if (!API._testaddhtmlscript) {
              oldAddElementHtml = addElementHtml;
              addElementHtml = API.addElementHtml = function(el, html) {
                if (addScriptHtmlFailed) {
                  // TODO: Pull out inline SCRIPT's and add to HEAD before set
                }
                oldAddElementHtml(el, html);
                findAndAddScripts(el);
              };
            }
            el = getEBI('testaddhtmlscript');
            if (el) {
              head.removeChild(el);
              el = null;
            }
            delete API._testaddhtmlscript;
          }
          if (typeof setElementHtml == 'function') {
            body = getBodyElement();
            script = '<script type="text/javascript">this.API._testsethtmlscript = true;</script>';
            if (body && isHostMethod(body, 'appendChild')) {
              div = createElement('div');						
              if (div) {
                if (div.style) {
                  div.style.position = 'absolute';
                  div.style.top = '0';
                }
                body.appendChild(div);
                try {
                  setElementHtml(div, script);
                } catch(e) {
                  setScriptHtmlFailed = true;
                }
                if (!API._testsethtmlscript) {
                  oldSetElementHtml = setElementHtml;
                  setElementHtml = API.setElementHtml = function(el, html, options, callback) {
                    if (setScriptHtmlFailed) {
                      // TODO: Pull out inline SCRIPT's and add to HEAD before set
                    }
                    el = oldSetElementHtml(el, html, options, callback);
                    if (!options || typeof options.execScripts == 'undefined' || options.execScripts) { findAndAddScripts(el); }
                    return el;
                  };
                }
                delete API._testsethtmlscript;
                body.removeChild(div);
                div = null;
              }
            }
          }
        }
      }
      head = null;
    });
  }
  
  // Ajax section

  var createXmlHttpRequest = (function() { 
    var i, 
      fs = [// for legacy eg. IE 5 
            function() { 
              return new global.ActiveXObject("Microsoft.XMLHTTP"); 
            }, 
            // for fully patched Win2k SP4 and up 
            function() { 
              return new global.ActiveXObject("Msxml2.XMLHTTP.3.0"); 
            }, 
            // IE 6 users that have updated their msxml dll files. 
            function() { 
              return new global.ActiveXObject("Msxml2.XMLHTTP.6.0"); 
            }, 
            // IE7, Safari, Mozilla, Opera, etc (NOTE: IE7+ native version does not support overrideMimeType or local file requests)
            function() { 
              var o = new global.XMLHttpRequest();

              // Disallow IE7+ XHR if overrideMimeType (hack) method is required

              if (API.requireMimeTypeOverride) {
                if (!isHostMethod(o, 'overrideMimeType')) {
                  o = null;
                }
              }
              return o;
            }];

    // If local Xhr required and ActiveX constructor present, check ActiveX first

    if (API.requireLocalXhr && isHostMethod(global, 'ActiveXObject')) {
      fs.reverse();
    }

    // Loop through the possible factories to try and find one that
    // can instantiate an XMLHttpRequest object that works.

    for (i=fs.length; i--; ) { 
      try { 
        if (fs[i]()) { 
          return fs[i]; 
        } 
      } 
      catch (e) {} 
    }
  })();

  API.createXmlHttpRequest = createXmlHttpRequest;

  var submitAjaxForm;

  if (createXmlHttpRequest && Function.prototype.apply && isHostMethod(global, 'setTimeout')) {
  API.ajax = (function() {    
    var xmlhttp, pendingRequests = 0, groupRequests = {};
    var defaultTimeoutTime = 30000;
    var fnJsonFilter, Requester;

    var tempRequesterHandle = 0;

    var reQuery = new RegExp('\\?(.*)$');

    var parseJsonString = (function() {
      if (isRealObjectProperty(global, 'JSON') && typeof global.JSON.parse == 'function') {
        return function(s) { return global.JSON.parse(s); };
      }
      else {
        return function(s) { return (new Function('return (' + s + ')'))(); };
      }
    })();

    API.parseJson = parseJsonString;

    var empty = function() {};

    function callback(sEvent, o, args) {
      var context = o.callbackContext || o;
      var m = o['on' + sEvent];
      if (m) {
        m.apply(context, args);
      }
    }

    function sessionCallback(sEvent, o, args) {
      args = args || [];
      callback(sEvent, API.ajax, [o.id(), o.group()].concat(args));
    }

    function bindCallbacks(a, objFrom, objTo) {
      var cb;
      var i = a.length;
      while (i--) {
        cb = 'on' + a[i];
        objFrom[cb] = objTo[cb];
      }
    }

    function requestStart(requester) {
      var sGroup = requester.group();

      pendingRequests++;
      if (pendingRequests == 1) { sessionCallback('start', requester); }
      if (sGroup) {
        if (typeof groupRequests[sGroup] == 'undefined') { groupRequests[sGroup] = 0; }
        groupRequests[sGroup]++;
        if (groupRequests[sGroup] == 1) { sessionCallback('groupstart', requester); }
      }
    }

    function requestFinish(requester) {
      var sGroup = requester.group();

      pendingRequests--;
      if (sGroup) {
        groupRequests[sGroup]--;
        if (!groupRequests[sGroup]) { sessionCallback('groupfinish', requester); }
      }
      if (!pendingRequests) { sessionCallback('finish', requester); }
    }

    function update(el, requester, xmlhttp, fnUpdate, bAppend, updateOptions, fnUpdated, context) {
      var method, xml, html = xmlhttp.responseText;
      var result = html;

      if (xmlhttp.responseXML && xmlhttp.responseXML.childNodes && xmlhttp.responseXML.childNodes.length) { xml = xmlhttp.responseXML; }
      if (fnUpdate) { result = fnUpdate.call(context || requester, html, xml); }
      if (typeof result == 'string') {
        method = (bAppend)?addElementHtml:setElementHtml;
      }
      else {
        // Import is not part of required combination (DOM + HTML + Requester)
        // Combination should be (DOM + (HTML | Import) + Requester)
        if (typeof addElementNodes == 'function') {
          method = (bAppend)?addElementNodes:setElementNodes;
        }
      }
      if (result) { method(el, result, updateOptions, fnUpdated); }
    }

    xmlhttp = createXmlHttpRequest();

    if (xmlhttp && isHostMethod(xmlhttp, 'setRequestHeader')) {
      Requester = function(sId, sGroup) {
        var timeout, evalJSON, bLocalFile;
        var xmlhttp = createXmlHttpRequest();
        var done = true;
        var that = this;
        var readyStateCallbacks = { '1':'loading', '2':'loaded', '3':'interactive' };
        var readyStateCallbacksCalled = [];
        var timeoutTime = defaultTimeoutTime;

        function stateChange() {
          var state = xmlhttp.readyState;
          if (state == 4) {
            if (!done) {
              done = true;
              xmlhttp.onreadystatechange = empty;
              global.clearTimeout(timeout);
              requestFinish(that);
              if (xmlhttp.status >= 200 && xmlhttp.status < 300 || xmlhttp.status == 1223 || (typeof xmlhttp.status == 'undefined' && xmlhttp.responseText) || (!xmlhttp.status && bLocalFile)) {
                that.dispatch('success', [xmlhttp, (evalJSON && xmlhttp.responseText)?parseJsonString(xmlhttp.responseText):null]);
              }
              else {
                that.dispatch('fail', [xmlhttp]);
              }
            }
          }
          else {
            if (!readyStateCallbacksCalled[state]) {
              that.dispatch(readyStateCallbacks[state], [xmlhttp]);
              readyStateCallbacksCalled[state] = true;
            }
          }
        }

        function abort() {
          if (!done) {
            done = true;
            global.clearTimeout(timeout);
            xmlhttp.onreadystatechange = empty;
            xmlhttp.abort();			
            requestFinish(that);
            that.dispatch('cancel', [xmlhttp]);
          }
        }

        function send(cmd, uri, postData, postDataType, bNoCache, bJSON) {
          if (done) {
            try {
              xmlhttp.open(cmd, uri, true, that.username, that.password);
            }
            catch(e) {
              //that.dispatch('error', [xmlhttp, e, uri]);
              sessionCallback('error', that, [xmlhttp, e, uri]);
              return false;
            }
            bLocalFile = !uri.indexOf('file:');
            postDataType = postDataType || 'application/x-www-form-urlencoded';
            xmlhttp.setRequestHeader("Content-Type", postDataType);
            xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
            if (bNoCache && cmd == 'GET') {
              xmlhttp.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 1990 00:00:00 GMT');
              xmlhttp.setRequestHeader('Cache-Control', 'no-cache');
            }
            that.dispatch('send', [xmlhttp, uri]);
            xmlhttp.onreadystatechange = stateChange;
            requestStart(that);
            readyStateCallbacksCalled = [];
            done = false;
            evalJSON = bJSON;
            try {
              xmlhttp.send((cmd == 'POST' || cmd == 'PUT')?postData:null);
              if (!done) { timeout = global.setTimeout(abort, timeoutTime); }
            }
            catch(E) {
              xmlhttp.onreadystatechange = empty;
              done = true;
              requestFinish(that);
              //that.dispatch('error', [xmlhttp, E, uri]);
              sessionCallback('error', that, [xmlhttp, E, uri]);
              return false;
            }
            return true;
          }
          return false;
        }
        
        this.busy = function() { return !done; };
				
        this.cancel = function() {
          abort();
        };

        if (isHostMethod(xmlhttp, 'overrideMimeType')) {
          this.overrideMimeType = function(mimeType) { xmlhttp.overrideMimeType(mimeType); };
        }
				
        this.get = function(uri, bJSON, bAllowCache) {
          return send('GET', uri, null, null, !bAllowCache, bJSON);
        };

        this.head = function(uri) {
          return send('HEAD', uri);
        };

        this.post = function(uri, data, type, bJSON) {
          return send('POST', uri, data, type, false, bJSON);
        };

        this.put = function(uri, data, type, bJSON) {
          return send('PUT', uri, data, type, false, bJSON);
        };

        this.group = function() {
          return sGroup;
        };

        this.id = function() {
          return sId;
        };

        this.setTimeoutTime = function(t) {
          timeoutTime = t;
        };
      };

      Requester.prototype.bindToObject = function(obj, bCallInContext) {
        bindCallbacks(['send', 'success', 'fail', 'cancel', 'loading', 'loaded', 'interactive'], this, obj);
        this.callbackContext = (typeof bCallInContext == 'undefined' || bCallInContext)?obj:null;
      };

      Requester.prototype.dispatch = function(sEvent, args) {
        callback(sEvent, this, args);
      };

      API.Requester = Requester;

      if (getAttribute && serializeFormUrlencoded) {
        submitAjaxForm = function(frm, bJSON, requester) {
          var data, method, action, type;

          if (!requester) { requester = new Requester('_temp' + tempRequesterHandle++); }
          data = serializeFormUrlencoded(frm);
          method = getAttribute(frm, 'method');
          // FIXME: Resolve correct global
          action = getAttribute(frm, 'action') || global.location.href;
          type = getAttribute(frm, 'enctype');
          if (action) {
            if (method && method.toUpperCase() == 'POST') {
              return requester.post(action, data, type, bJSON);
            }
            else {
              return requester.get([action.replace(reQuery, ''), '?', data].join(''), bJSON, false);
            }		
          }
        };

        API.submitAjaxForm = submitAjaxForm;

        if (typeof attachListener == 'function') {
          API.ajaxForm = function(frm, sId, sGroup, bJSON, fnSubmit) {
            var requester = new Requester(sId, sGroup);

            attachListener(frm, 'submit', function(e) {
              if (!fnSubmit || fnSubmit.call(this, e)) {
                if (submitAjaxForm(this, bJSON, requester)) {
                  return cancelDefault(e);
                }
              }
            });
            frm = null;
            return requester;
          };
        }
      }

      return {
        getPendingRequests:function() { return pendingRequests; },
        bindToObject:function(obj, bCallInContext) { bindCallbacks(['start', 'finish', 'error', 'groupstart', 'groupfinish'], this, obj); this.callbackContext = (typeof bCallInContext == 'undefined' || bCallInContext)?obj:null; },
        setTimeoutTime:function(t) { defaultTimeoutTime = t; },
        setJsonFilter:function(fn) { fnJsonFilter = fn; }
      };
    }
    xmlhttp = null;
  })();
  }
  
  // Query section

  var traversedElements, traversedElements2, getEBCN, getEBXP, parseAtom, resolve, selectByXPath, xPathChildSelectorsBad;
  
  if (doc) {
    
    if (isHostMethod(doc, 'evaluate')) {
      resolve = function() { return 'http://www.w3.org/1999/xhtml'; };
      getEBXP = function(s, d) {
        d = d || global.document;
        var i, q = [], r, docNode = (d.nodeType == 9)?d:(d.ownerDocument);
        r = docNode.evaluate(s, d,
                             (isXmlParseMode(docNode))?resolve:null,
                             global.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                             null);
        if (!arguments[2]) {
          i = r.snapshotLength;
          while (i--) { q[i] = r.snapshotItem(i); }
          return q;
        }
        return r;
      };

      API.getEBXP = getEBXP;
    }

    parseAtom = function(s) {
      var ai, m, mv, ml;
      var o = {};

      m = s.match(/^([>\+~])/);
      if (m) {
        o.combinator = m[1];
        s = s.substring(1);
      }

      m = s.match(/^([^#\.\[:]+)/);
      o.tag = m ? m[1] : '*';

      m = s.match(/#([^\.\:]+)/);
      o.id = m ? m[1] : null;

      m = s.match(/\.([^\[\:]+)/);
      o.cls = m ? m[1] : null;

      m = s.match(/:([^\[]+)(\[|$)/);
      o.pseudo = m ? m[1] : null;

      m = s.match(/\[[^\]]+\]/g);
      if (m) {
        ml = m.length;
        o.attributes = [];
        o.attributeValues = [];
        o.attributeOperators = [];
        for (ai = 0; ai < ml; ai++) {
          var mai = m[ai];
          o.attributes[ai] = mai.substring(1, mai.length - 1);
          mai = mai.replace(/^%/, '').replace(/\]$/, '');
          mv = mai.match(/(~|!|\||\*|\$|\^)?=(["'])?([^'"]*)\1?/);
          if (mv) {
            o.attributeOperators[ai] = mv[1];
            o.attributeValues[ai] = mv[3].replace(/\x00\x00\x01/g, ']').replace(/\x00\x00\x02/g, '"').replace(/\x00\x00\x03/g, "'").replace(/\x00\x00\x04/g, '#').replace(/\x00\x00\x05/g, '.').replace(/\x00\x00\x06/g, ':').replace(/\x00\x00\x07/g, '>').replace(/\x00\x00\x08/g, '~').replace(/\x00\x00\x11/g, '+').replace(/\x00\x00\x00/g, ' ');
            o.attributes[ai] = o.attributes[ai].replace(/(~|\||\*|!|\$|\^)?=.*/, '');
          }
        }
      }
      return o;
    };

    var parsePositional = function(pseudo) {

      // TODO: Change to use switch

      if (pseudo == 'nth-of-type(even)') {
        pseudo = 'nth-of-type(2n)';
      } else if (pseudo == 'nth-of-type(odd)') {
        pseudo = 'nth-of-type(2nplus1)';
      } else if (pseudo == 'nth-last-of-type(even)') {
        pseudo = 'nth-last-of-type(2n)';
      } else if (pseudo == 'nth-last-of-type(odd)') {
        pseudo = 'nth-last-of-type(2nplus1)';
      }

      var o, matchIndex = pseudo.match(/^nth(-last)?-(child|of-type)\((-)?(\d+)?n(-|plus)?(\d+)?\)$/);
      if (matchIndex) {
        o = {};
        o.firstOrLast = !matchIndex[1];
        o.multiplier = +(matchIndex[4] || '1');
        o.multiplierNegative = matchIndex[3] == '-';
        if (matchIndex[5]) {
          o.modulus = +matchIndex[6] * (matchIndex[5] == 'plus' ? 1 : -1);
        } else {
          o.modulus = 0;
        }
        o.ofType = matchIndex[2] != 'child';
      }
      return o;
    };

    if (typeof getEBXP != 'undefined') {
      selectByXPath = function(d, a, sel, multiple) {
        var atts, m, o, s, positional, ofTypeSimple;
        var docNode = (d.nodeType == 9)?d:getElementDocument(d);
        var i = a.length;
        while (i--) {
          o = parseAtom(a[i]);
          ofTypeSimple = false;

          if (o.pseudo) {
            if (o.pseudo == 'first-of-type') {
              o.pseudo = 'nth-of-type(1)';
            } else if (o.pseudo == 'last-of-type') {
              o.pseudo = 'nth-last-of-type(1)';
            }
            positional = parsePositional(o.pseudo);
            if (positional && !positional.multiplier) {
              o.pseudo = 'nth' + (positional.firstOrLast ? '' : '-last') + '-' + (positional.ofType ? 'of-type' : 'child') + '(' + positional.modulus + ')';
              positional = null;
            }
            ofTypeSimple = /^nth-of-type\(\d+\)$/.test(o.pseudo);
          }

          // TODO: Test with leading combinator

          if (s) {
            if (o.combinator) {
              s += (o.combinator == '>')?'/':'/following-sibling::';
            } else {
              s += '//';
            }
          } else {
            s = './/';
          }
          //s = [s, ((isXmlParseMode(docNode))?'html:':''), (o.pseudo || (o.combinator && o.combinator != '>'))?'*':o.tag, (o.combinator && o.combinator != '>')?'[(name() = "' + o.tag.toLowerCase() + '" or name() = "' + o.tag.toUpperCase() + '")' + (o.combinator == '+' ? ' and (position() = 1)' : '') + ']' :''];
          s = [s, ((isXmlParseMode(docNode))?'html:':''), ((o.pseudo && (!ofTypeSimple && (!positional || !positional.ofType))) || (o.combinator && o.combinator != '>'))?'*':o.tag, (o.combinator && o.combinator != '>')?'[(name() = "' + o.tag.toLowerCase() + '" or name() = "' + o.tag.toUpperCase() + '")' + (o.combinator == '+' ? ' and (position() = 1)' : '') + ']' :''];
          if (o.cls) {
            if (o.cls.indexOf('.') == -1) {
              s[s.length] = "[contains(concat(' ', @class, ' '), ' " + o.cls + " ')]";
            } else {
              var classNames = o.cls.split('.');
              for (var z = classNames.length; z--;) {
                s[s.length] = "[contains(concat(' ', @class, ' '), ' " + classNames[z] + " ')]";
              }
            }
          }
          s = s.join('');
          var htmlChildDisclaimer = "[not(name(.) = 'html') and not(name(.) = 'HTML')]";
          if (o.pseudo) {
            switch(o.pseudo) {
            case 'target':
              var uri = docNode.URL;
              if (uri) {
                var hash = uri.match(/\#(.*)$/);
                if (hash && hash[1]) {
                  s += '[@name = "' + hash[1] + '" or @id = "' + hash[1] + '"]';
                } else {
                  return [];
                }
              } else {
                return [];
              }
              break;
            case 'root':
              //s += "[name(.) = 'html' or name(.) = 'HTML']"
              s += '[not(parent::*)]';
              break;
            case 'checked':
              s += "[(@checked) and (name(.) = 'input' or name(.) = 'INPUT')]";
              break;
            case 'enabled':
            case 'disabled':
              s += "[(" + (o.pseudo == 'enabled'?'not(@disabled)':'@disabled') + ") and (name(.) = 'input' or name(.) = 'textarea' or name(.) = 'select' or name(.) = 'button' or name(.) = 'INPUT' or name(.) = 'TEXTAREA' or name(.) = 'SELECT' or name(.) = 'BUTTON')]";
              break;
            case 'empty':
              s+= '[not(*) and not(normalize-space())]';
              break;
            case 'nth-child(even)':
              s += "[position() mod 2=0]" + htmlChildDisclaimer;
              break;
            case 'nth-child(odd)':
              s += "[position() mod 2=1]" + htmlChildDisclaimer;
              break;
            case 'last-child':
              s += "[last()]" + htmlChildDisclaimer;
              break;
            case 'only-child':
              s += "[count(../*)=1]" + htmlChildDisclaimer;
              break;
            case 'first-child':
              s += "[1]" + htmlChildDisclaimer;
              break;
            default:
              if (positional) {
                if (positional.firstOrLast) {
                  s += '[(position() - ' + positional.modulus + ') mod ' + positional.multiplier + ' = 0 and position() ' + (positional.multiplierNegative ? '<=' : '>=') + ' ' + positional.modulus + ']';
                } else {
                  s += "[(last() - position() - " + (+(positional.modulus - 1)) + ") mod " + positional.multiplier + " = 0 and position() " + (positional.multiplierNegative ? '>=' : '<=') + " (last() - " + (+(positional.modulus - 1)) + ")]";
                }
              } else {
                var matchIndex = o.pseudo.match(/nth-(child|of-type)\((\d+)\)/);
                if (matchIndex) {
                  s += '[position()=' + matchIndex[2] + ']' + htmlChildDisclaimer;
                } else if (matchIndex = o.pseudo.match(/nth-last-(child|of-type)\((\d+)\)/)) {
                  s += '[position()=last() - ' + (matchIndex[2] - 1) + ']' + htmlChildDisclaimer;
                } else {
                  matchIndex = o.pseudo.match(/contains\((.*)\)/);
                  if (matchIndex) {
                    s += '[contains(string(.), "' + matchIndex[1] + '")]';
                  }
                }
              }
            }
            s += '[self::' + o.tag + ']';
          }
          if (o.id) {
            s += ['[@id="', o.id, '"]'].join('');
          }
          if (o.attributes) {
            atts = [];
            m = o.attributes.length;
            var quoteCharacter = '"';
            while (m--) {
              if (o.attributeValues[m] && o.attributeValues[m].indexOf('"') != -1) {
                quoteCharacter = "'";
              }
              switch(o.attributeOperators[m]) {
              case '^':
                atts.push(['starts-with(@', o.attributes[m], ',', quoteCharacter, o.attributeValues[m], quoteCharacter, ')'].join(''));
                break;
              case '$':
                atts.push(['substring(@', o.attributes[m], ', string-length(@', o.attributes[m], ') - string-length(', quoteCharacter, o.attributeValues[m], quoteCharacter, ') + 1, string-length(@', o.attributes[m] ,')) = ', quoteCharacter, o.attributeValues[m], quoteCharacter].join(''));
                break;
              case '~':
                atts.push(['contains(concat(" ", @', o.attributes[m], ', " "),', quoteCharacter, ' ' + o.attributeValues[m] + ' ', quoteCharacter, ')'].join(''));
                break;
              case '!':
                atts.push(['not(@', o.attributes[m], '=', quoteCharacter, o.attributeValues[m], quoteCharacter, ')'].join(''));
                break;
              case '*':
                atts.push(['contains(@', o.attributes[m], ',', quoteCharacter, o.attributeValues[m], quoteCharacter, ')'].join(''));
                break;
              case '|':
                atts.push(['@', o.attributes[m], '=', quoteCharacter, o.attributeValues[m], quoteCharacter, ' or starts-with(@', o.attributes[m], ',', quoteCharacter, o.attributeValues[m], '-', quoteCharacter, ')'].join(''));
                break;
              case '*':
                atts.push(['contains(@', o.attributes[m], ',', quoteCharacter, o.attributeValues[m], quoteCharacter, ')'].join(''));
                break;
              default:
                atts.push((typeof o.attributeValues[m] == 'string')?['@', o.attributes[m], '=', quoteCharacter, o.attributeValues[m], quoteCharacter].join(''):['@', o.attributes[m]].join(''));
              }
            }
            s = [s, '[', atts.join(' and '), ']'].join('');
          }
        }
        var result = getEBXP(s, d, multiple), q = [];
        if (multiple) { // Multiple returns snapshot so now filter dupes
          i = result.snapshotLength;
          while (i--) {
            var el = result.snapshotItem(i);
            var uid = elementUniqueId(el);
            if (!traversedElements[uid]) { q[q.length] = el; traversedElements[uid] = true; }
          }
          return q.reverse();
        }
        return result;
      };
    }

    var getEBCS = (function() {
      var html = getAnElement();
      var els,     // candidate elements for return
      ns,          // elements to return
      o,           // selector atom object
      docNode,
      cache = {},  // cached select functions
      aCache = {}, // cached select atom functions
      qid = 0,     // query id (marks branches as traversed)
      bAll = html && isHostMethod(html, 'all');        // indicates if "all" object is featured for elements

      function getDocNode(d) {
        return (d.nodeType == 9 || (!d.nodeType && !d.tagName))?d:getElementDocument(d);
      }

      var globalDocumentLanguage;

      var documentLanguage = function(doc) {
        var el, els, lang;

        if (!doc) {
          doc = global.document;
        }
        if (globalDocumentLanguage && doc == global.document) {
          return globalDocumentLanguage;
	}
        els = getEBTN('meta');
        for (var i = els.length; i--;) {
          el = els[i];
          if (getAttribute(el, 'http-equiv') == 'Content-Language') {
            lang = getAttribute(el, 'content');
          }
	}
	if (lang && doc == global.document) {
          globalDocumentLanguage = lang;
	}
	return lang;
      };

      var elementLanguage = function(el) {
        var lang, doc = getElementDocument(el);
        var hasGetAttributeNS = isHostMethod(el, 'getAttributeNS');
        while (!lang && el && el.tagName) {
          lang = getAttribute(el, 'lang');
          if (!lang && hasGetAttributeNS) {
            lang = el.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang');
          }
          el = el.parentNode;
        }
        return lang || documentLanguage(doc);
      };

      var previousAtom; // adjacent selectors check this to determine comparison (currently only checking for tag)
      var selectAtomFactory = function(id, tag, cls, combinator, attributes, attributeValues, attributeOperators, pseudo) {
        var ai, al, att, b, c, cj, d, el, foundSelf, i, j, k, m, r, sibling;
        return function(a, docNode, last) {
          if (attributes) { al = attributes.length; }
          r = [];
          k = a.length;

          var lastProcessedParent, childrenLength, done, elParent, elTagName, evenCounter, filteredDescendants, matchIndex;
          var matchingLastChild, uid, uidElement, traversedParents = {}, containsText, lang, positional, multiplier, modulus, multiplierNegative, firstOrLast, ofType;

          // TODO: Use switch

          if (pseudo == 'first-of-type') {
            pseudo = 'nth-of-type(1)';
          } else if (pseudo == 'last-of-type') {
            pseudo = 'nth-last-of-type(1)';
          }

          if (pseudo) {
            matchIndex = pseudo.match(/^contains\((.+)\)$/);
            if (matchIndex) {
              containsText = matchIndex[1];
            } else {
              matchIndex = pseudo.match(/^lang\((.+)\)$/);
              if (matchIndex) {
                lang = matchIndex[1].toLowerCase();
              } else {
                positional = parsePositional(pseudo);
                if (positional) {
                  multiplier = positional.multiplier;
                  multiplierNegative = positional.multiplierNegative;
                  modulus = positional.modulus;
                  firstOrLast = positional.firstOrLast;
                  ofType = positional.ofType;
                  if (!multiplier) {
                    pseudo = 'nth' + (firstOrLast ? '' : '-last') + '-' + (ofType ? 'of-type' : 'child') + '(' + modulus + ')';
                    positional = null;
                  }
                }
                if (!positional) {
                  matchIndex = pseudo.match(/nth-(child|of-type)\((\d+)\)/);
                  if (!matchIndex) {
                    matchIndex = pseudo.match(/nth-last-(child|of-type)\((\d+)\)/);
                    matchingLastChild = true;
                  }
                  ofType = matchIndex && matchIndex[1] != 'child';
                }
              }
            }
          }

          while (k--) {
            d = a[k];
            if (id && !combinator) {
              if (!d.tagName) {
                els = (el = getEBI(id, docNode)) ? [el] : [];
              } else {
                if (bAll && (el = d.all[id])) {
                  els = [el];
                } else {
                  els = getEBTN(tag, d);
                }
              }
            } else {
              switch(combinator) {
              case '>':
                els = getChildren(d, true);
                break;
              case '~':
                // TODO: This is inefficient
                els = getChildren(d.parentNode).reverse();
                foundSelf = false;
                break;
              case '+':
                sibling = d;
                do {
                  sibling = sibling.nextSibling;
                } while (sibling && sibling.nodeType != 1);
                els = sibling ? [sibling] : [];
                break;
              default:
                els = getEBTN(tag, d);
              }
            }

            if (pseudo && !lang && !containsText && (positional || ofType || pseudo.indexOf('child') != -1)) {
              filteredDescendants = [];
              i = els.length;

              lastProcessedParent = null;
              while (i--) {
                el = els[i];
                elParent = el.parentNode;
                uid = elParent && elementUniqueId(elParent);

                if (elParent && elParent != lastProcessedParent && elParent.tagName && !traversedParents[uid]) {
                  if (positional && !firstOrLast) {
                    c = getChildren(elParent).reverse();
                  } else {
                    c = getChildren(elParent, true);
                  }

                  evenCounter = j = 0;
                  childrenLength = c.length;
                  lastProcessedParent = elParent;
                  traversedParents[uid] = true;

                  done = false;
                  j = childrenLength;
                  while (j && !done) {
                    cj = c[--j];
                    done = cj.tagName != '!';
                    if (ofType) {
                      done = done && tag == cj.tagName.toLowerCase();
                    }
                  }
                  if (done) {
                    childrenLength = j + 1;
                  }
                  j = 0;
                  done = false;
                  while (j < childrenLength && !done) {
                    cj = c[j];
                    elTagName = cj.tagName;
                    if (elTagName != '!') {
                      if ((!tag || tag == '*' || elTagName.toLowerCase() == tag) && (!id || cj.id == id)) {
                        switch (pseudo) {
                        case 'nth-child(even)':
                          if (evenCounter % 2) {
                            filteredDescendants[filteredDescendants.length] = cj;
                          }
                          break;
                        case 'nth-child(odd)':
                          if (!(evenCounter % 2)) {
                            filteredDescendants[filteredDescendants.length] = cj;
                          }
                          break;
                        case 'first-child':
                          if (!evenCounter) {
                            filteredDescendants[filteredDescendants.length] = cj;
                            done = true;
                          }
                          break;
                        case 'last-child':
                          if (j == childrenLength - 1) {
                            filteredDescendants[filteredDescendants.length] = cj;
                            done = true;
                          }
                          break;
                        case 'only-child':
                          if (evenCounter == childrenLength - 1) {
                            filteredDescendants[filteredDescendants.length] = cj;
                          }
                          done = true;
                          break;
                        default:
                          if (positional) {
                            if (multiplierNegative) {
                              if ((evenCounter + 1 <= modulus) && !((evenCounter + 1 - modulus) % multiplier)) {
                                 filteredDescendants[filteredDescendants.length] = cj;
                              }
                            } else {
                              if ((evenCounter + 1 >= modulus) && !((evenCounter + 1 - modulus) % multiplier)) {
                                 filteredDescendants[filteredDescendants.length] = cj;
                              }
                            }
                          } else if (matchIndex) {
                            // TODO: Convert match index once
                            if (matchingLastChild) {
                              if (j == childrenLength - (+matchIndex[2])) {
                                filteredDescendants[filteredDescendants.length] = cj;
                                done = true;
                              }
                            } else {
                              if (evenCounter == +matchIndex[2] - 1) {
                                filteredDescendants[filteredDescendants.length] = cj;
                                done = true;
                              }
                            }
                          }
                        }
                      }
                      if (!ofType || tag == elTagName.toLowerCase()) {
                        evenCounter++;
                      }
                    }
                    j++;
                  }
                }
              }
              els = filteredDescendants;
            }

            i = els.length;
            while (i--) {
              el = els[i];
              uidElement = elementUniqueId(el);
              if (traversedElements[uidElement] == qid || (last && traversedElements2[uidElement])) { continue; }
              b = true;
              if (cls) {
                m = el.className;
                if (m) {
                  if (typeof cls == 'string') {
                    b = (' ' + m + ' ').indexOf(cls) > -1;
                  } else { // Array
                    for (var z = cls.length; z-- && b;) {
                      b = (' ' + m + ' ').indexOf(cls[z]) > -1;                      
                    }
                  }
                } else {
                  b = false;
                }
              }
              b = b && (!id || el.id == id);
              if (b) {
                if (combinator == '~') {
                  b = foundSelf && (tag == '*' || tag == el.tagName.toLowerCase());
                  if (!foundSelf && el == d) { foundSelf = true; }
                } else {
                  b = tag == '*' || tag == el.tagName.toLowerCase();
                }

                if (b && pseudo) {                  
                  switch (pseudo) {
                  case 'empty':
                    var childNodes = el.childNodes;
                    done = false;
                    for (var childNodeIndex = childNodes.length; childNodeIndex-- && !done;) {
                      var childNode = childNodes[childNodeIndex];
                      switch(childNode.nodeType) {
                        case 1:
                          done = childNode.tagName != '!';
                          break;
                        case 3:
                        case 4:
                          done = reNotEmpty.test(childNode.data);
                      }
                    }
                    b = !done;
                    break;
                  case 'root':
                    b = el.tagName.toLowerCase() == 'html';
                    break;
                  case 'enabled':
                  case 'disabled':
                    if (/^(input|textarea|button|select|option)$/i.test(el.tagName)) {
                      b = hasAttribute(el, 'disabled');
                      if (pseudo == 'enabled') {
                        b = !b;
                      }
                    } else {
                      b = false;
                    }
                    break;
                  case 'checked':
                    if (/^(input)$/i.test(el.tagName)) {
                      b = hasAttribute(el, 'checked');
                    } else {
                      b = false;
                    }
                    break;
                  case 'target':
                    var uri = (docNode.location && docNode.location.href) || docNode.URL;
                    if (uri) {
                      var hash = uri.match(/\#(.*)$/);
                      if (hash && hash[1]) {
                        b = el.name == hash[1] || el.id == hash[1];
                      } else {
                        b = false;
                      }
                    }
                    break;
                  default:
                    if (containsText) {
                      b = (el.textContent || el.innerText || '').indexOf(containsText) != -1;
                    } else if (lang) {
                      var langMatch = elementLanguage(el).toLowerCase();
                      b = lang == langMatch || !langMatch.indexOf(lang + '-');
                    }
                  }
                }
                if (b && attributes) {
                  ai = al;
                  while (ai-- && b) {
                    att = getAttribute(el, attributes[ai], docNode);
                    switch(attributeOperators[ai]) {
                    case '^':
                      b = b && att && !att.indexOf(attributeValues[ai]);
                      break;
                    case '$':
                      b = b && att && att.slice(-attributeValues[ai].length) == attributeValues[ai];
                      break;
                    case '~':
                      b = b && att && ([' ', att, ' '].join('')).indexOf([' ', attributeValues[ai], ' '].join('')) != -1;
                      break;
                    case '!':
                      b = b && att !== attributeValues[ai];
                      break;
                    case '|':
                      b = b && att && (attributeValues[ai] == att || !att.indexOf(attributeValues[ai] + '-'));
                      break;
                    case '*':
                      b = b && att && att.indexOf(attributeValues[ai]) != -1;
                      break;
                    default:
                      b = b && (typeof attributeValues[ai] == 'string')?att == attributeValues[ai]:(!hasAttribute && att) || hasAttribute(el, attributes[ai], docNode);
                    }
                  }
                }
                if (b) {
                  r[r.length] = el;
                  traversedElements[uidElement] = qid;
                  if (last) {
                    traversedElements2[uidElement] = true;
                  }
                  if (id) { break; }
                }
              }
            }
          }
          return r;
        };
      };

      var selectFactory = function(a) {
         var i, j;
       
         return function(d) {
           var classNames, className;
           i = a.length;
           j = 1;
           docNode = getDocNode(d);
           ns = [[d]];
           while (i--) {
             classNames = null;
             o = parseAtom(a[i]);
             if (!aCache['_' + a[i]]) {
               className = o.cls;
               if (className) {
                 if (className.indexOf('.') == -1) {
                    classNames = [' ', className, ' '].join('');
                 } else {
                    classNames = o.cls.split('.');
                    for (var z = classNames.length; z--;) {
                      classNames[z] = [' ', classNames[z], ' '].join('');
                    }
                 }
               }
               // TODO: Do this concatenation once
               aCache['_' + a[i]] = selectAtomFactory(o.id, o.tag.toLowerCase(), classNames, o.combinator, o.attributes, o.attributeValues, o.attributeOperators, o.pseudo);
             }
             ns[j] = aCache['_' + a[i]](ns[j - 1], docNode, !i);
             qid++;
             previousAtom = o;
             j++;
           }
           return ns[j - 1].reverse();
         };
      };

      var get = (function() {
        var el, getD, r;

        if (typeof getEBI != 'undefined' &&
            typeof getEBTN != 'undefined' &&
            typeof getChildren != 'undefined' &&
            typeof getAttribute != 'undefined' && (global.document.expando || typeof global.document.expando == 'undefined')) {
          getD = function(d, a, s, multiple) {
            if (a.length == 1 && !multiple) {
              o = parseAtom(a[0]);
              if (!o.pseudo && !o.attributes) {
                if (o.id && !o.cls && d.nodeType == 9) {
                  // Optimization for #foo
                  el = getEBI(o.id, getDocNode(d));
                  return (el && el.id == o.id && (o.tag == '*' || o.tag == el.tagName.toLowerCase()))?[el]:[];
                }
                if (!o.id && !o.cls) {
                  // Optimization for foo
                  r = getEBTN(o.tag, d);
                  // TODO: Add hidden argument to force array return
                  return (typeof r.reverse == 'function')?r:toArray(r);
                }
              }
            }
	    s = '_' + s; // avoid toString conflict

            // TODO: De-tokenize before using as property name
            if (!cache[s]) {
              cache[s] = selectFactory(a);
            }
            return cache[s](d);
          };
        }

        if (getD) {
          return function(d, a, s, multiple) {

            // TODO: Really only need to disable XPath for specific selectors
            // TODO: Pass document type flags to this, check for MSXML here, pass case sensitive flag to all but MSXML get (which is always case sensitive)

            if (selectByXPath && !xPathChildSelectorsBad) {
              get = function(d, a, s, multiple) {
                // Defer to DOM traversal for language selector

                // TODO: But not for XML documents

                return s.indexOf(':lang(') != -1 ? getD(d, a, s, multiple) : selectByXPath(d, a, s, multiple);
              };
              return get(d, a, s, multiple);
            }
            get = getD;
            return get(d, a, s, multiple);
          };
        }
      })();

      if (get) {
        return function(s, d) {
          var a = [], aSel = [], chr, i, inQuotes, r = [], used = {};

          d = d || global.document;
          s = s.replace(/^\s+/,'').replace(/\s+$/,''); // trim
          s = s.replace(/\s+,/g, ',').replace(/,\s+/g, ','); // remove spaces before and after commas
          i = s.length;
          while (i--) {
            chr = s.charAt(i);
            switch (chr) {
            case ',':
              if (inQuotes) {
                aSel[aSel.length] = chr;
              }
              else {
                a[a.length] = aSel.reverse().join('');
                aSel = [];
              }
              break;
            case ' ':
              // change quoted spaces to nulls temporarily
              // changed back in parseAtom
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x00':' ';
              break;
            case ']':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x01':']';
              break;
            case '#':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x04':'#';
              break;
            case '.':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x05':'.';
              break;
            case ':':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x06':':';
              break;
            case '>':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x07':'>';
              break;
            case '~':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x08':'~';
              break;
            case '+':
              aSel[aSel.length] = (inQuotes)?'\x00\x00\x11':'+';
              break;
            case '"':
            case "'":
              var quoteCharacter;
              if (!inQuotes || quoteCharacter == chr) {
                inQuotes = !inQuotes;
                quoteCharacter = chr;
                aSel[aSel.length] = chr;
              } else {
                aSel[aSel.length] = (chr == '"')?'\x00\x00\x02':'\x00\x00\x03';
              }
              break;
            default:
              aSel[aSel.length] = chr;
            }
          }
          if (aSel.length) {
            a[a.length] = aSel.reverse().join('').replace(/:nth(-last)?-(child|of-type)\((-)?(\d+)?n\+(\d+)\)/g, ':nth$1-$2($3$4nplus$5)');
          }
          a.reverse();
          traversedElements = {};
          traversedElements2 = {};
          i = a.length;
          var multiple = i > 1;
          while (i--) {
	    a[i] = a[i].replace(/\s+/g, ' '); // collapse multiple spaces
            a[i] = a[i].replace(/([^\s])([>\+])/g, '$1 $2');
            a[i] = a[i].replace(/([^\s])([~])([^=])/g, '$1 $2$3');
            a[i] = a[i].replace(/([>\+~])\s/g, '$1');
            if (!used['_' + a[i]]) { // prevent dupes (e.g. div, div, div) // TODO: Should not used tokenized string for property name
              r = r.concat(get(d, a[i].split(' ').reverse(), a[i], multiple));
              used['_' + a[i]] = 1; // TODO: Do this concatenation once
            }
          }
          return r;
        };
      }
      html = null;
    })();

    if (getEBCS) {
      
      getEBCN = function(s, d) {
        var a = s.split(' ');
        for (var i = a.length; i--;) {
          a[i] = '.' + a[i];
        }
        return getEBCS(a.join(''), d);
      };

      API.getEBCS = getEBCS;
      API.getEBCN = getEBCN;
    }

    // Safari 3 bug test (tested Windows Beta version)

    if (attachDocumentReadyListener && getEBXP) {
      attachDocumentReadyListener(function() {
        xPathChildSelectorsBad = !!getEBXP('.//*[1][self::body]').length;
      });
    }
    
  }

  doc = null;
  html = null;
})();

